diff --git a/bin/build_standalone_docsrc.sh b/bin/build_standalone_docsrc.sh index a2540f8c..6c7c7323 100755 --- a/bin/build_standalone_docsrc.sh +++ b/bin/build_standalone_docsrc.sh @@ -313,6 +313,16 @@ if [[ "${lang}" != "en" ]]; then "${work_tree}/doc/src/sgml/Makefile" fi +# Pre-PG10 doc makefiles build HTML via DSSSL (jade) by default. Reroute the +# html target to the XSL pipeline (xslthtml-stamp, osx -> postgres.xml -> +# xsltproc chunked HTML) that those makefiles already provide, matching what +# PG10+ do natively. The guard is a no-op for PG10 and later. +if grep -q '^xslthtml-stamp:' "${work_tree}/doc/src/sgml/Makefile"; then + sed -i.bak \ + -e 's/^html: html-stamp$/html: xslthtml-stamp/' \ + "${work_tree}/doc/src/sgml/Makefile" +fi + # Incremental generated-text overlays are kept with each Chinese source. # Generate from the selected upstream inputs before applying exact-hash edits. if [[ "${lang}" == "zh" && -f "${doc_src_root}/localize-generated.py" ]]; then diff --git a/zh/9.6/.gitignore b/zh/9.6/.gitignore new file mode 100644 index 00000000..a72b7ccb --- /dev/null +++ b/zh/9.6/.gitignore @@ -0,0 +1,24 @@ +# Stuff shipped in tarballs +/html/ +/html-stamp +/man1/ +/man3/ +/man7/ +/man-stamp +# Other popular build targets +/INSTALL +/postgres-US.pdf +/postgres-A4.pdf +/postgres.html +/postgres.txt +# GENERATED_SGML +/features-supported.sgml +/features-unsupported.sgml +/errcodes-table.sgml +/version.sgml +# Assorted byproducts from building the above +/postgres.xml +/INSTALL.html +/INSTALL.xml +/postgres-US.fo +/postgres-A4.fo diff --git a/zh/9.6/Makefile b/zh/9.6/Makefile new file mode 100644 index 00000000..4d8f84d9 --- /dev/null +++ b/zh/9.6/Makefile @@ -0,0 +1,78 @@ +# zh/9.6/Makefile + +REPO_ROOT := $(abspath $(CURDIR)/../..) +DOC_VERSION := $(notdir $(CURDIR)) +PG_VERSION ?= 9.6.24 +# Official 9.6.24 release package, checksum pinned from en/SOURCES.json. +PGDOC_SOURCE_ARCHIVE ?= $(REPO_ROOT)/.cache/upstream/postgresql-9.6.24.tar.bz2 +PGDOC_SOURCE_SHA256 ?= aeb7a196be3ebed1a7476ef565f39722187c108dd47da7489be9c4fcae982ace +export PGDOC_SOURCE_ARCHIVE PGDOC_SOURCE_SHA256 +# PG9.x uses DocBook SGML 4.2 and OpenSP; its doc makefile defaults html to +# DSSSL/jade, so build_standalone_docsrc.sh reroutes html to the XSL pipeline. +PGDOC_SGML_DEPS := $(REPO_ROOT)/tmp/pg10-13-from-14/20260909-150220/agents/archive_build/deps +OSX ?= $(PGDOC_SGML_DEPS)/bin/osx +NSGMLS ?= $(PGDOC_SGML_DEPS)/bin/onsgmls +SGML_CATALOG_FILES ?= $(PGDOC_SGML_DEPS)/sgml-unicode.catalog +SP_ENCODING ?= UTF-8 +SP_CHARSET_FIXED ?= YES +export OSX NSGMLS SGML_CATALOG_FILES SP_ENCODING SP_CHARSET_FIXED +DOC_LANG := $(notdir $(abspath $(CURDIR)/..)) + +BUILD_SCRIPT := $(REPO_ROOT)/bin/build_standalone_docsrc.sh +PDF_SCRIPT := $(REPO_ROOT)/bin/build_standalone_pdfsrc.sh +DEFAULT_BUILD_OUT := $(CURDIR)/html +BUILD_OUT ?= $(DEFAULT_BUILD_OUT) +PAPER ?= A4 +PDF_OUT ?= $(REPO_ROOT)/tmp/pdf/$(DOC_LANG)/postgresql-$(DOC_VERSION)-$(DOC_LANG)-$(PAPER).pdf +PYTHON ?= python3 +HOST ?= 127.0.0.1 +PORT ?= 8000 + +# Files generated during build (not checked into repo) +GENERATED_FILES := version.sgml pgdoccn-notes.sgml postgres-full.xml + +.PHONY: all html pdf clean clean-pdf distclean s serve check-deps show-config + +all: html + +html: + @"$(BUILD_SCRIPT)" "$(CURDIR)" "$(DOC_LANG)" "$(PG_VERSION)" "$(BUILD_OUT)" + +pdf: + @"$(PDF_SCRIPT)" "$(CURDIR)" "$(DOC_LANG)" "$(PG_VERSION)" "$(PDF_OUT)" "$(PAPER)" + +distclean: clean clean-pdf + +s: serve +serve: + @echo "Serving $(BUILD_OUT) at http://$(HOST):$(PORT)/" + @cd "$(BUILD_OUT)" && "$(PYTHON)" -m http.server "$(PORT)" --bind "$(HOST)" + +c: clean +clean: + rm -rf "$(BUILD_OUT)" + +clean-pdf: + rm -f "$(PDF_OUT)" + +check-deps: + @echo "Checking build dependencies ..." + @command -v xmllint >/dev/null 2>&1 || { echo "MISSING: xmllint (brew install libxml2)"; exit 1; } + @command -v xsltproc >/dev/null 2>&1 || { echo "MISSING: xsltproc (brew install libxslt)"; exit 1; } + @command -v rsync >/dev/null 2>&1 || { echo "MISSING: rsync"; exit 1; } + @command -v java >/dev/null 2>&1 || { echo "MISSING: java"; exit 1; } + @command -v fc-match >/dev/null 2>&1 || { echo "MISSING: fc-match (brew install fontconfig)"; exit 1; } + @echo "All dependencies found." + +show-config: + @echo "doc_dir=$(CURDIR)" + @echo "lang=$(DOC_LANG)" + @echo "version=$(DOC_VERSION)" + @echo "pg_version=$(PG_VERSION)" + @echo "pgdoc_source_archive=$(PGDOC_SOURCE_ARCHIVE)" + @echo "pgdoc_source_sha256=$(PGDOC_SOURCE_SHA256)" + @echo "build_out=$(BUILD_OUT)" + @echo "pdf_out=$(PDF_OUT)" + @echo "paper=$(PAPER)" + @echo "host=$(HOST)" + @echo "port=$(PORT)" diff --git a/zh/9.6/README.links b/zh/9.6/README.links new file mode 100644 index 00000000..f64b8573 --- /dev/null +++ b/zh/9.6/README.links @@ -0,0 +1,46 @@ + + +Linking within SGML documents can be confusing, so here is a summary: + + +Intra-document Linking +---------------------- + + + use to get chapter/section number from the title of the target + link, or xreflabel if defined at the target, or refentrytitle if target + is a refentry; has no close tag + http://www.oasis-open.org/docbook/documentation/reference/html/xref.html + + + use to supply text for the link, requires + http://www.oasis-open.org/docbook/documentation/reference/html/link.html + +linkend= + controls the target of the link/xref, required + +endterm= + for , allows the text of the link/xref to be taken from a + different link target title + + +External Linking +---------------- + + + like , but uses a URL (not a document target); requires + ; if no text is specified, the URL appears as the link + text + http://www.oasis-open.org/docbook/documentation/reference/html/ulink.html + +url= + used by to specify the URL, required + + +Guidelines +---------- + +o If you want to supply text, use , else +o Do not use text with so the URL appears in printed output +o Specific nouns like GUC variables, SQL commands, and contrib modules + usually have xreflabels diff --git a/zh/9.6/acronyms.sgml b/zh/9.6/acronyms.sgml new file mode 100644 index 00000000..8a19fa6b --- /dev/null +++ b/zh/9.6/acronyms.sgml @@ -0,0 +1,723 @@ + + + + 缩略词 + + 这是在PostgreSQL文档和有关PostgreSQL的讨论中常用的缩略词列表。 + + + ANSI + + + + 美国国家标准协会 + + + + + + API + + + 应用程序编程接口 + + + + + + ASCII + + + 美国信息交换标准代码 + + + + + + BKI + + + 后端接口 + + + + + + CA + + + 证书颁发机构 + + + + + + CIDR + + + 无类域间路由 + + + + + + CPAN + + + Perl综合档案网络 + + + + + + CRL + + + 证书吊销列表 + + + + + + CSV + + + 逗号分隔值 + + + + + + CTE + + + 公共表表达式 + + + + + + CVE + + + 常见漏洞与暴露 + + + + + + DBA + + + 数据库管理员 + + + + + + DBI + + + 数据库接口(Perl) + + + + + + DBMS + + + 数据库管理系统 + + + + + + DDL + + + 数据定义语言,例如SQL命令CREATE + TABLEALTER USER + + + + + + DML + + + 数据操纵语言,例如SQL命令INSERT、 + UPDATEDELETE + + + + + + DST + + + 夏令时 + + + + + + ECPG + + + 用于 PostgreSQL 的嵌入式 C + + + + + + ESQL + + + 嵌入式SQL + + + + + + FAQ + + + 常见问题 + + + + + + FSM + + + 空闲空间映射 + + + + + + GEQO + + + 遗传查询优化器 + + + + + + GIN + + + 通用倒排索引 + + + + + + GiST + + + 通用搜索树 + + + + + + Git + + + Git + + + + + + GMT + + + 格林尼治标准时间 + + + + + + GSSAPI + + + 通用安全服务应用程序接口 + + + + + + GUC + + + Grand Unified Configuration, + 即负责处理服务器配置的PostgreSQL子系统 + + + + + + HBA + + + 基于主机的认证 + + + + + + HOT + + + 堆内元组 + + + + + + IEC + + + 国际电工委员会 + + + + + + IEEE + + + 电气和电子工程师学会 + + + + + + IPC + + + 进程间通信 + + + + + + ISO + + + 国际标准化组织 + + + + + + ISSN + + + 国际标准连续出版物编号 + + + + + + JDBC + + + Java数据库连接 + + + + + + LDAP + + + 轻量级目录访问协议 + + + + + + MSVC + + + Microsoft + Visual C + + + + + + MVCC + + + 多版本并发控制 + + + + + + NLS + + + 本地语言支持 + + + + + + ODBC + + + 开放数据库连接 + + + + + + OID + + + 对象标识符 + + + + + + OLAP + + + 联机分析处理 + + + + + + OLTP + + + 联机事务处理 + + + + + + ORDBMS + + + 对象关系数据库管理系统 + + + + + + PAM + + + 可插拔认证模块 + + + + + + PGSQL + + + PostgreSQL + + + + + + PGXS + + + PostgreSQL扩展系统 + + + + + + PID + + + 进程标识符 + + + + + + PITR + + + 时间点恢复(持续归档) + + + + + + PL + + + 过程语言(服务器端) + + + + + + POSIX + + + 可移植操作系统接口 + + + + + + RDBMS + + + 关系数据库管理系统 + + + + + + RFC + + + 征求意见稿 + + + + + + SGML + + + 标准通用标记语言 + + + + + + SPI + + + 服务器编程接口 + + + + + + SP-GiST + + + 空间分区通用搜索树 + + + + + + SQL + + + 结构化查询语言 + + + + + + SRF + + + 集合返回函数 + + + + + + SSH + + + 安全外壳协议 + + + + + + SSL + + + 安全套接字层 + + + + + + SSPI + + + 安全支持提供程序接口 + + + + + + SYSV + + + Unix System V + + + + + + TCP/IP + + + 传输控制协议(TCP)/ 互联网协议(IP) + + + + + + TID + + + 元组标识符 + + + + + + TLS + + + + 传输层安全 + + + + + + TOAST + + + 超长属性存储技术 + + + + + + TPC + + + 事务处理性能委员会 + + + + + + URL + + + 统一资源定位符 + + + + + + UTC + + + 协调世界时 + + + + + + UTF + + + Unicode 转换格式 + + + + + + UTF8 + + + 8 位 Unicode 转换格式 + + + + + + UUID + + + 通用唯一标识符 + + + + + + WAL + + + 预写式日志 + + + + + + XID + + + 事务 ID + + + + + + XML + + + 可扩展标记语言 + + + + + + + + diff --git a/zh/9.6/adminpack.sgml b/zh/9.6/adminpack.sgml new file mode 100644 index 00000000..8e80dd68 --- /dev/null +++ b/zh/9.6/adminpack.sgml @@ -0,0 +1,109 @@ + + + + adminpack + + + adminpack + + + adminpack提供了一些支持函数,pgAdmin和其他管理工具可以使用它们来提供额外功能,例如远程管理服务器日志文件。所有这些函数都只允许超级用户使用。 + + 所示的函数提供对服务器所在机器上文件的写入访问。(另请参阅中的函数,它们提供只读访问。)只能访问数据库集簇目录内的文件,但相对路径和绝对路径都可以使用。 + + + <filename>adminpack</filename> 函数 + + + 名称 返回类型 + 描述 + + + + + + + pg_catalog.pg_file_write(filename text, data text, append boolean) + bigint + 写入文本文件或向其追加内容 + + + pg_catalog.pg_file_rename(oldname text, newname text , archivename text) + boolean + 重命名文件 + + + pg_catalog.pg_file_unlink(filename text) + boolean + 删除文件 + + + pg_catalog.pg_logdir_ls() + setof record + 列出log_directory目录中的日志文件 + + + +
+ + + pg_file_write + + + pg_file_write将指定的data写入由filename指定的文件中。如果append为 false,则该文件必须尚不存在。如果append为 true,则该文件可以已经存在,并且若已存在则会向其追加内容。返回写入的字节数。 + + + + pg_file_rename + + + pg_file_rename重命名文件。如果省略archivename或其值为空值,则它只是将oldname重命名为newname(后者必须尚不存在)。如果提供了archivename,它会先将newname重命名为archivename(后者必须尚不存在),然后再将oldname重命名为newname。如果第二个重命名步骤失败,它会在报告错误之前尝试将archivename再改回newname。成功时返回 true;如果源文件不存在或不可写,则返回 false;其他情况会抛出错误。 + + + + pg_file_unlink + + + pg_file_unlink删除指定的文件。成功时返回 true;如果指定的文件不存在,或者unlink()调用失败,则返回 false;其他情况会抛出错误。 + + + + pg_logdir_ls + + + pg_logdir_ls返回目录中所有日志文件的起始时间戳和路径名。要使用此函数,参数必须保持默认设置(postgresql-%Y-%m-%d_%H%M%S.log)。 + + + 所示的函数已经弃用,不应在新应用中使用;应改用所示的函数。adminpack提供这些函数只是为了兼容旧版本的pgAdmin + + + 已弃用的<filename>adminpack</filename>函数 + + + 名称 返回类型 + 描述 + + + + + + + pg_catalog.pg_file_read(filename text, offset bigint, nbytes bigint) + text + pg_read_file()的别名 + + + pg_catalog.pg_file_length(filename text) + bigint + pg_stat_file()返回的size列相同 + + + pg_catalog.pg_logfile_rotate() + integer + pg_rotate_logfile()的别名,但请注意它返回整数 0 或 1,而不是 boolean + + + +
+ +
diff --git a/zh/9.6/advanced.sgml b/zh/9.6/advanced.sgml new file mode 100644 index 00000000..943b2e67 --- /dev/null +++ b/zh/9.6/advanced.sgml @@ -0,0 +1,487 @@ + + + + 高级特性 + + + 简介 + + + 在前一章中,我们已经介绍了如何使用SQLPostgreSQL中存储和访问数据的基础知识。现在我们将讨论SQL的一些更高级特性,它们能够简化管理,并防止数据丢失或损坏。最后,我们还将看看一些PostgreSQL扩展。 + + + + 本章有时会引用中的示例,对其加以修改或改进,因此事先读过那一章会很有帮助。本章中的一些示例也可以在教程目录中的advanced.sql文件中找到。该文件还包含一些要装载的样例数据,这里不再重复。(关于如何使用该文件,参见。) + + + + + + 视图 + + + view + + + + 回过头看中的查询。假设天气记录和城市位置的组合清单对你的应用特别有用,但你又不想每次需要它时都键入这条查询。你可以在该查询之上创建一个视图,为该查询命名,以后就可以像引用普通表一样引用它: + + +CREATE VIEW myview AS + SELECT name, temp_lo, temp_hi, prcp, date, location + FROM weather, cities + WHERE city = name; + +SELECT * FROM myview; + + + + + 大量使用视图是良好的 SQL 数据库设计的一个关键方面。视图允许你通过一致的接口封装表结构的细节,而这些细节可能会随着应用演进而变化。 + + + + 几乎凡是真实表可以使用的地方,都可以使用视图。在其他视图之上再构建视图也很常见。 + + + + + + 外键 + + + foreign key + + + + referential integrity + + + + 回想中的weathercities表。考虑这样一个问题:你希望确保没有人能向weather表中插入在cities表里没有匹配项的行。这称为维护数据的引用完整性。在简单得多的数据库系统中,这通常是通过先查看cities表、检查是否存在匹配记录,然后再插入新的weather记录或拒绝插入来实现的(如果系统压根支持的话)。这种做法有很多问题,而且很不方便,所以PostgreSQL可以替你完成这件事。 + + + + 新的表声明如下: + + +CREATE TABLE cities ( + name varchar(80) primary key, + location point +); + +CREATE TABLE weather ( + city varchar(80) references cities(name), + temp_lo int, + temp_hi int, + prcp real, + date date +); + + + 现在试着插入一条无效记录: + + +INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28'); + + + +ERROR: insert or update on table "weather" violates foreign key constraint "weather_city_fkey" +DETAIL: Key (city)=(Berkeley) is not present in table "cities". + + + + + 外键的行为可以针对应用进行精细调整。本教程不再超出这个简单例子继续展开,更多信息请参见。正确使用外键肯定能提高数据库应用的质量,因此强烈建议你了解它们。 + + + + + + 事务 + + + transaction + + + + 事务是所有数据库系统中的一个基本概念。事务的要点在于,它把多个步骤打包成一个单一的、要么全部成功要么全部失败的操作。步骤之间的中间状态对其他并发事务不可见;如果发生某种故障使事务无法完成,那么其中任何一步都不会对数据库产生影响。 + + + + 例如,考虑一个银行数据库,其中保存着各个客户账户的余额,以及各营业网点的总存款余额。假设我们要记录一笔从Alice的账户向Bob的账户支付100.00美元的款项。大幅简化后,相应的 SQL 命令可能是: + + +UPDATE accounts SET balance = balance - 100.00 + WHERE name = 'Alice'; +UPDATE branches SET balance = balance - 100.00 + WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice'); +UPDATE accounts SET balance = balance + 100.00 + WHERE name = 'Bob'; +UPDATE branches SET balance = balance + 100.00 + WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob'); + + + + + 这些命令的细节在这里并不重要;重要的是,为了完成这个相当简单的操作,涉及了若干次彼此独立的更新。银行管理人员会希望确信这些更新要么全部发生,要么一个也不发生。显然,不能因为系统故障导致Bob收到了100.00美元,而Alice那边却没有被扣款。反过来,如果Alice被扣了款而Bob没有入账,她也不会满意。我们需要保证:如果操作进行到一半出了问题,到目前为止已执行的步骤都不会生效。把这些更新归为一个事务,就能提供这种保证。事务被称为原子的:从其他事务的角度看,它要么完整发生,要么根本不发生。 + + + + 我们还希望保证,一旦事务完成并得到数据库系统确认,它确实已经被永久记录下来,即便紧接着发生崩溃也不会丢失。例如,如果我们正在记录Bob的一次现金提款,我们当然不希望他刚走出银行大门,对他账户的扣款就在崩溃后消失。事务型数据库保证,在把事务报告为完成之前,会先将该事务所做的全部更新记入永久存储(即磁盘)。 + + + + 事务型数据库的另一个重要性质与原子更新的概念密切相关:当多个事务并发运行时,每个事务都不应该看到其他事务尚未完成的更改。例如,如果某个事务正在统计所有营业网点的余额,就不能让它只算进Alice所在网点的扣款,却没有算进Bob所在网点的入账,反过来也不行。因此,事务不仅在对数据库的持久影响上必须是全有或全无的,在其发生过程中的可见性上也必须如此。一个未结束事务到目前为止所做的更新,对其他事务不可见;等到该事务完成时,所有更新会同时变得可见。 + + + + 在PostgreSQL中,可以通过将事务中的 SQL 命令置于BEGINCOMMIT命令之间来建立一个事务。因此,我们的银行事务实际上会是这样: + + +BEGIN; +UPDATE accounts SET balance = balance - 100.00 + WHERE name = 'Alice'; +-- etc etc +COMMIT; + + + + + 如果事务执行到一半时,我们决定不想提交了(例如刚刚发现Alice的余额变成了负数),就可以发出ROLLBACK而不是COMMIT,这样到目前为止的全部更新都会被取消。 + + + + PostgreSQL实际上把每条 SQL 语句都视为在一个事务中执行。如果你没有显式发出BEGIN,那么每条独立语句外围都会隐式包上一对BEGIN和(若成功)COMMIT。由BEGINCOMMIT包围起来的一组语句,有时称为事务块。 + + + + + 某些客户端库会自动发出BEGINCOMMIT命令,因此你可能在没有主动要求的情况下,也得到了事务块的效果。请查阅你所使用接口的文档。 + + + + + 也可以借助保存点,以更细粒度控制事务中的语句。保存点允许你有选择地丢弃事务的一部分,同时提交其余部分。使用SAVEPOINT定义保存点之后,如有需要,可以用ROLLBACK TO回滚到该保存点。事务中从定义保存点到回滚到它之间所做的数据库修改都会被丢弃,而早于该保存点的修改会被保留。 + + + + 回滚到某个保存点之后,该保存点仍然保持定义状态,因此你可以多次回滚到它。反过来,如果你确定不再需要回滚到某个保存点,可以将其释放,以便系统回收一些资源。请记住,无论是释放某个保存点,还是回滚到某个保存点,都会自动释放在它之后定义的所有保存点。 + + + + 所有这些都发生在事务块内部,因此其他数据库会话都看不到。等到你提交整个事务块时,被提交的动作才会作为一个整体对其他会话可见,而被回滚的动作则永远不会可见。 + + + + 回到银行数据库,假设我们从Alice的账户扣除了100.00美元,并记入Bob的账户,后来却发现其实应该记入Wally的账户。我们可以像下面这样利用保存点来处理: + + +BEGIN; +UPDATE accounts SET balance = balance - 100.00 + WHERE name = 'Alice'; +SAVEPOINT my_savepoint; +UPDATE accounts SET balance = balance + 100.00 + WHERE name = 'Bob'; +-- oops ... forget that and use Wally's account +ROLLBACK TO my_savepoint; +UPDATE accounts SET balance = balance + 100.00 + WHERE name = 'Wally'; +COMMIT; + + + + + 当然,这个例子是过度简化的,但在事务块中借助保存点可以进行大量控制。此外,对于一个由于错误而被系统置为中止状态的事务块,要重新取得控制权,唯一的办法就是使用ROLLBACK TO;否则就只能把整个事务完全回滚并重新开始。 + + + + + + + 窗口函数 + + + window function + + + + 窗口函数会在一组与当前行存在某种关联的表行上执行计算。这与聚合函数能够完成的计算类型类似。但与普通聚合函数不同,使用窗口函数不会把多行分组为一条输出行 — 各行仍然保留各自的独立身份。在幕后,窗口函数能够访问的不仅仅是查询结果中的当前行。 + + + + 下面的例子说明如何将每个雇员的工资与其所在部门的平均工资进行比较: + + +SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary; + + + + depname | empno | salary | avg +-----------+-------+--------+----------------------- + develop | 11 | 5200 | 5020.0000000000000000 + develop | 7 | 4200 | 5020.0000000000000000 + develop | 9 | 4500 | 5020.0000000000000000 + develop | 8 | 6000 | 5020.0000000000000000 + develop | 10 | 5200 | 5020.0000000000000000 + personnel | 5 | 3500 | 3700.0000000000000000 + personnel | 2 | 3900 | 3700.0000000000000000 + sales | 3 | 4800 | 4866.6666666666666667 + sales | 1 | 5000 | 4866.6666666666666667 + sales | 4 | 4800 | 4866.6666666666666667 +(10 rows) + + + 前三个输出列直接来自表empsalary,并且该表中的每一行对应一条输出行。第四列表示在所有depname值与当前行相同的表行上求得的平均值。(这其实与普通的avg聚合函数是同一个函数,但OVER子句使它被当作窗口函数处理,并在一组适当的行上计算。) + + + + 窗口函数调用总是包含一个紧跟在窗口函数名和参数之后的OVER子句。这正是它在语法上区别于普通函数或聚合函数的地方。OVER子句精确决定查询中的哪些行会被拆分出来供窗口函数处理。OVER内部的PARTITION BY列表指定将具有相同PARTITION BY表达式值的行划分为组,也就是分区。对于每一行,窗口函数都是在与当前行处于同一分区的那些行上计算的。 + + + 你也可以用以下子句来控制窗口函数处理行的顺序:ORDER BY,将其放在OVER内部。(窗口中的ORDER BY甚至不必与行的输出顺序一致。)下面是一个例子: +SELECT depname, empno, salary, + rank() OVER (PARTITION BY depname ORDER BY salary DESC) +FROM empsalary; + + + + depname | empno | salary | rank +-----------+-------+--------+------ + develop | 8 | 6000 | 1 + develop | 10 | 5200 | 2 + develop | 11 | 5200 | 2 + develop | 9 | 4500 | 4 + develop | 7 | 4200 | 5 + personnel | 2 | 3900 | 1 + personnel | 5 | 3500 | 2 + sales | 1 | 5000 | 1 + sales | 4 | 4800 | 2 + sales | 3 | 4800 | 2 +(10 rows) +如上所示,rank函数会为当前行所在分区中每个不同的ORDER BY值,按ORDER BY子句定义的顺序产生一个数值排名。rank不需要显式参数,因为它的行为完全由OVER子句定义。 + + + 窗口函数所考虑的行,是查询的FROM子句产生并经WHEREGROUP BYHAVING子句(如果有)过滤后的那个虚拟表中的行。例如,由于不满足WHERE条件而被删除的行,不会被任何窗口函数看到。一个查询可以包含多个窗口函数,它们可以通过不同的OVER子句以不同方式划分数据,但它们都作用于这个虚拟表所定义的同一组行。 + + + + 我们已经看到,如果行的顺序并不重要,就可以省略ORDER BYPARTITION BY也可以省略,这时所有行构成一个分区。 + + + + 与窗口函数相关的另一个重要概念是:对于每一行,在其所在分区内有一组行,称为它的窗口帧。许多(但不是全部)窗口函数只作用于窗口帧中的行,而不是整个分区中的所有行。默认情况下,如果提供了ORDER BY,则窗口帧包含从分区起始处直到当前行的所有行,再加上根据ORDER BY子句与当前行相等的所有后续行。如果省略ORDER BY,默认窗口帧则包含该分区中的所有行。 + + + 还可以用其他方式定义窗口帧,但本教程不涉及这些选项。细节见。 + + + 下面是一个使用sum的例子: + + + +SELECT salary, sum(salary) OVER () FROM empsalary; + + + + salary | sum +--------+------- + 5200 | 47100 + 5000 | 47100 + 3500 | 47100 + 4800 | 47100 + 3900 | 47100 + 4200 | 47100 + 4500 | 47100 + 4800 | 47100 + 6000 | 47100 + 5200 | 47100 +(10 rows) + + + + 上面由于OVER子句中没有ORDER BY,窗口帧与分区相同;而在没有PARTITION BY的情况下,这个分区就是整张表。换言之,每个求和都是在整张表上计算的,因此每条输出行得到的结果都相同。但是如果加上ORDER BY子句,结果就会大不一样: + + + +SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary; + + + + salary | sum +--------+------- + 3500 | 3500 + 3900 | 7400 + 4200 | 11600 + 4500 | 16100 + 4800 | 25700 + 4800 | 25700 + 5000 | 30700 + 5200 | 41100 + 5200 | 41100 + 6000 | 47100 +(10 rows) + + + + 这里的求和是从第一条(最低)工资一直累加到当前行,包括与当前行工资相同的所有重复值(注意那些重复工资对应的结果)。 + + + + 窗口函数只允许出现在查询的SELECT列表和ORDER BY子句中。它们不允许出现在其他地方,例如GROUP BYHAVINGWHERE子句中。这是因为从逻辑上讲,它们在这些子句处理完成之后才执行。另外,窗口函数在普通聚合函数之后执行。这意味着在窗口函数的参数中包含聚合函数调用是合法的,反过来则不行。 + + + + 如果需要在窗口计算执行之后再过滤或分组行,可以使用子查询。例如: + + +SELECT depname, empno, salary, enroll_date +FROM + (SELECT depname, empno, salary, enroll_date, + rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos + FROM empsalary + ) AS ss +WHERE pos < 3; + + + 上面的查询只显示内部查询中rank小于3的那些行(也就是每个部门的前两行)。 + + + + 当查询涉及多个窗口函数时,可以为每个函数分别写一个独立的OVER子句;但如果多个函数都需要相同的窗口行为,这样写既重复,又容易出错。替代方法是,在WINDOW子句中给每一种窗口行为命名,然后在OVER中引用它。例如: + + +SELECT sum(salary) OVER w, avg(salary) OVER w + FROM empsalary + WINDOW w AS (PARTITION BY depname ORDER BY salary DESC); + + + + + 关于窗口函数的更多细节可以在以及参考页中找到。 + + + + + + 继承 + + + inheritance + + + + 继承是面向对象数据库中的一个概念。它为数据库设计打开了一些有趣的新可能性。 + + + + 让我们创建两个表:表cities和表capitals。很自然,首都也是城市,因此当你列出所有城市时,应该有某种办法能隐式地把首都也显示出来。如果你很有巧思,也许会想出这样的方案: + + +CREATE TABLE capitals ( + name text, + population real, + elevation int, -- (in ft) + state char(2) +); + +CREATE TABLE non_capitals ( + name text, + population real, + elevation int -- (in ft) +); + +CREATE VIEW cities AS + SELECT name, population, elevation FROM capitals + UNION + SELECT name, population, elevation FROM non_capitals; + + + 就查询而言,这样做还行,但一旦需要更新多行,它就会变得很麻烦。 + + + + 更好的解决办法是: + + +CREATE TABLE cities ( + name text, + population real, + elevation int -- (in ft) +); + +CREATE TABLE capitals ( + state char(2) UNIQUE NOT NULL +) INHERITS (cities); + + + + + 在这种情况下,capitals的一行会从它的父表cities继承全部列(namepopulationelevation)。列name的类型是text,这是PostgreSQL内置的一种变长字符串类型。capitals表还有一个额外的列state,用来表示所在州的缩写。在PostgreSQL中,一个表可以从零个或多个其他表继承。 + + + + 例如,下面的查询会找出所有海拔超过500英尺的城市名称,其中也包括州首府: + + +SELECT name, elevation + FROM cities + WHERE elevation > 500; + + + 返回: + + + name | elevation +-----------+----------- + Las Vegas | 2174 + Mariposa | 1953 + Madison | 845 +(3 rows) + + + + + 另一方面,下面的查询会找出所有海拔超过500英尺且不是州首府的城市: + + +SELECT name, elevation + FROM ONLY cities + WHERE elevation > 500; + + + + name | elevation +-----------+----------- + Las Vegas | 2174 + Mariposa | 1953 +(2 rows) + + + + + 这里,放在cities前面的ONLY表示该查询只针对cities表执行,而不包括继承层次中位于cities之下的表。我们前面已经讨论过的许多命令 — SELECTUPDATEDELETE — 都支持这种ONLY记法。 + + + + + 尽管继承经常很有用,但它尚未与唯一约束或外键集成,这限制了它的实用性。更多细节见。 + + + + + + + 小结 + + + PostgreSQL还有许多特性在这个面向SQL新用户的入门教程中没有涉及。本书余下部分会更详细地讨论这些特性。 + + + + 如果你觉得还需要更多入门材料,请访问 PostgreSQL 官方网站,其中有指向更多资源的链接。 + + + diff --git a/zh/9.6/arch-dev.sgml b/zh/9.6/arch-dev.sgml new file mode 100644 index 00000000..3728ebd6 --- /dev/null +++ b/zh/9.6/arch-dev.sgml @@ -0,0 +1,272 @@ + + + + PostgreSQL 内部概述 + + + 作者 + + 本章最初源自 Stefan Simkovics 在维也纳技术大学完成的硕士论文,该论文由 O.Univ.Prof.Dr. Georg Gottlob 和 Univ.Ass. Mag. Katrin Seyr 指导。 + + + + + 本章概述 PostgreSQL 后端的内部结构。阅读以下各节之后,你应该能够大致了解一个查询是如何被处理的。本章并不试图详细描述 PostgreSQL 的内部运作,因为那样的文档会非常庞大。相反,本章旨在帮助读者理解,从后端收到一个查询开始,到将结果返回给客户端为止,内部通常有哪些操作步骤。 + + + + 查询的路径 + + + 这里将简要概述一个查询为得到结果必须经过的各个阶段。 + + + + + + 必须先建立从应用程序到 PostgreSQL 服务器的连接。应用程序将查询发送给服务器,并等待接收服务器返回的结果。 + + + + + + 解析器阶段检查应用程序传来的查询是否具有正确的语法,并创建一棵 查询树。 + + + + + + 重写系统接收由解析器阶段创建的查询树,并查找任何可应用于该查询树的 规则(存储在 系统目录 中)。它执行这些 规则体 中给出的转换。 + + + + 重写系统的一个应用是实现 视图。每当对一个视图(即 虚拟表)发出查询时,重写系统都会将用户的查询重写成一个改为访问 视图定义中给出的 基表 的查询。 + + + + + + 规划器/优化器接收(重写后的)查询树,并创建一个查询计划,将作为 执行器 的输入。 + + + + 它首先生成所有能得到同一结果的可能 路径。例如,如果待扫描的某个关系上有一个索引,那么该扫描就有两条路径:一种是简单的顺序扫描,另一种是使用该索引。接着会估算执行每条路径的代价,并选择代价最低的路径。代价最低的路径会被展开成一个完整的计划,供执行器使用。 + + + + + + 执行器会递归地遍历 计划树,并以计划所表示的方式提取行。执行器在扫描关系时会使用 存储系统,执行 排序连接,计算 限定条件,最后返回得到的行。 + + + + + + 在后续各节中,我们将更详细地介绍上述各项内容,以便更好地理解 PostgreSQL 的内部控制和数据结构。 + + + + + 连接是如何建立的 + + PostgreSQL 使用一种简单的每用户一个进程客户端/服务器模型。在这种模型中,一个客户端进程恰好连接到一个服务器进程。由于事先不知道会建立多少个连接,因此必须使用一个主进程,在每次收到连接请求时派生一个新的服务器进程。这个主进程称为 postgres,在指定的 TCP/IP 端口上监听传入连接。每当检测到连接请求时,postgres 进程就会派生一个新的服务器进程。服务器任务之间使用信号量共享内存通信,以确保并发数据访问期间的数据完整性。 + + + 客户端进程可以是任何理解 PostgreSQL 协议(见 )的程序。很多客户端基于 C 语言库 libpq,但该协议也有若干独立实现,例如 Java 的 JDBC 驱动。 + + + 一旦连接建立起来,客户端进程就可以向后端(服务器)发送查询。查询以纯文本方式传输,也就是说,前端(客户端)不进行解析。服务器解析查询,创建一个执行计划,执行该计划,并通过已建立的连接把检索到的行返回给客户端。 + + + + 解析器阶段 + + + 解析器阶段由两部分组成: + + + + + 解析器定义在 gram.yscan.l 中,它使用 Unix 工具 bisonflex 构建。 + + + + + 转换过程,它会对解析器返回的数据结构做修改和补充。 + + + + + + + 解析器 + + + 解析器必须检查查询字符串(它以纯文本形式到达)是否具有有效语法。如果语法正确,就会构造出一棵 语法解析树 并返回;否则会返回错误。解析器和词法分析器是使用著名的 Unix 工具 bisonflex 实现的。 + + + + 词法分析器定义在文件 scan.l 中,负责识别 标识符SQL 关键字 等。每找到一个关键字或标识符,就会生成一个 词元 并交给解析器。 + + + + 解析器定义在文件 gram.y 中,由一组 语法规则动作 组成,每当某条规则被触发时,对应动作就会执行。动作中的代码(实际上是 C 代码)用于构造语法解析树。 + + + + 文件 scan.l 会被转换成 C 源文件 scan.c,所用程序是 flex;而 gram.y 则会被转换成 gram.c,所用程序是 bison。这些转换完成之后,就可以使用普通的 C 编译器来构建解析器。绝不要修改这些生成出来的 C 文件,因为下一次调用 flexbison 时,它们都会被覆盖。 + + + + 上述转换和编译通常都是借助 makefiles 自动完成的,这些文件随 PostgreSQL 源代码发行版一同提供。 + + + + + + 对 bison 的详细描述,或者对 gram.y 中给出的语法规则的说明,都超出了本文的范围。有许多书籍和文档专门讨论 flexbison。在开始研究之前,你应该先熟悉 bison,然后再去看 gram.y 中给出的语法,否则你将无法理解其中发生了什么。 + + + + + + 转换过程 + + + 解析器阶段仅依据 SQL 语法结构的固定规则创建一棵语法解析树。它不会在系统目录中做任何查找,因此不可能理解所请求操作的详细语义。解析器完成之后,转换过程会接收解析器返回的树,并进行必要的语义解释,以理解查询引用了哪些表、函数和操作符。为表示这些信息而构建的数据结构称为 查询树。 + + + + 之所以将原始解析与语义分析分开,是因为系统目录查找只能在事务内部进行,而我们不希望在刚收到查询字符串时就立刻启动事务。原始解析阶段已经足以识别事务控制命令(BEGINROLLBACK 等),因此这些命令可以在无需进一步分析的情况下被正确执行。一旦我们知道当前处理的是实际查询(例如 SELECTUPDATE),如果尚未处于事务中,就可以启动事务。只有在那之后才能调用转换过程。 + + + + 转换过程创建的查询树在大多数地方的结构都与原始语法解析树相似,但在细节上有许多不同。例如,语法解析树中的一个 FuncCall 节点表示某个在语法上看起来像函数调用的东西。它可能被转换成 FuncExpr 节点,也可能被转换成 Aggref 节点,这取决于被引用的名称最终被判定为普通函数还是聚合函数。此外,关于列和表达式结果的实际数据类型的信息也会被加入到查询树中。 + + + + + + <productname>PostgreSQL</productname> 规则系统 + + + PostgreSQL 提供了一个强大的 规则系统,用于定义 视图 以及处理有歧义的 视图更新。最初,PostgreSQL 的规则系统由两种实现组成: + + + + + 第一种实现使用 行级 处理,并且深度实现于 执行器 内部。每当访问到单独一行时,规则系统就会被调用。这种实现在 1995 年被移除,当时 Berkeley Postgres 项目的最后一个官方发行版被转换成了 Postgres95。 + + + + + + 规则系统的第二种实现是一种称为 查询重写 的技术。重写系统 是位于 解析器阶段规划器/优化器 之间的一个模块。这种技术至今仍在使用。 + + + + + + + 关于查询重写器, 中已有相当详细的讨论,因此这里没有必要再展开。我们只指出一点:重写器的输入和输出都是查询树,也就是说,树的表示形式以及语义细节层次都不会发生变化。重写可以被看作某种形式的宏展开。 + + + + + + 规划器/优化器 + + + 规划器/优化器的任务是创建一个最优的执行计划。给定的一个 SQL 查询(因此也就是一棵查询树)实际上可以用多种不同方式执行,而这些方式都会产生同样的结果集。如果在计算上可行,查询优化器将考察这些可能执行计划中的每一种,并最终选择预计运行最快的那个执行计划。 + + + + + 在某些情况下,考察查询的每一种可能执行方式会耗费过多时间和内存。尤其是在执行涉及大量连接操作的查询时更是如此。为了在合理时间内确定一个合理的(不一定最优的)查询计划,PostgreSQL 会使用 遗传查询优化器(见 ),前提是连接数量超过某个阈值(见 )。 + + + + + 规划器的搜索过程实际上使用一种称为 路径 的数据结构,它只是计划的简化表示,仅包含规划器做出决策所需的信息。在确定出代价最低的路径之后,会构建一棵完整的 计划树 传递给执行器。这棵树以足够细致的方式表示了期望的执行计划,使执行器能够运行它。在本节余下部分,我们将忽略路径与计划之间的区别。 + + + + 生成可能的计划 + + + 规划器/优化器首先为查询中使用的每个单独关系(表)生成扫描计划。可能的计划由每个关系上可用的索引决定。对一个关系总是可以执行顺序扫描,因此一定会生成顺序扫描计划。假设某个关系上定义了一个索引(例如一个 B-树索引),并且查询中包含限制条件 relation.attribute OPR constant。如果 relation.attribute 恰好匹配该 B-树索引的键,而且 OPR 是该索引 操作符类 中列出的操作符之一,那么就会生成另一个使用该 B-树索引扫描该关系的计划。如果还存在其他索引,并且查询中的限制条件恰好匹配某个索引的键,则还会考虑更多计划。对于那些排序顺序能够匹配查询 ORDER BY 子句(如果存在)或者可能有助于进行归并连接(见下文)的索引,也会生成索引扫描计划。 + + + 如果查询需要连接两个或更多关系,那么会在找出所有可行的单关系扫描计划之后,再考虑关系连接计划。有以下三种可用的连接策略: + + + 嵌套循环连接:左关系中找到的每一行,都会使右关系被扫描一次。这种策略实现起来很容易,但可能非常耗时。(不过,如果右关系可以通过索引扫描,那么这也可能是一种不错的策略。可以把左关系当前行中的值作为右关系索引扫描的键。) + + + + + + 归并连接:在连接开始之前,每个关系都会先按照连接属性排序。然后两个关系并行扫描,将匹配的行组合成连接结果行。这种连接方式很有吸引力,因为每个关系只需扫描一次。所需的排序既可以通过显式排序步骤完成,也可以利用连接键上的索引按适当顺序扫描关系来完成。 + + + + + + 哈希连接:首先扫描右关系,并使用其连接属性作为哈希键将其装入一个哈希表。接着扫描左关系,并将找到的每一行中的相应值作为哈希键,用来在该哈希表中定位匹配的行。 + + + + + + + 当查询涉及两个以上的关系时,最终结果必须由一棵连接步骤树构建出来,每个连接步骤都有两个输入。规划器会考察不同的可能连接顺序,以找出代价最低的那个。 + + + + 如果查询使用的关系少于 个,就会执行一次近乎穷举的搜索,以找出最佳连接顺序。对于任意两个关系,只要在 WHERE 限定条件中存在相应的连接子句(即存在类似 where rel1.attr1=rel2.attr2 这样的限制),规划器就会优先考虑它们之间的连接。没有连接子句的连接对只有在别无选择时才会被考虑,也就是说,某个关系与其他任何关系之间都没有可用的连接子句。对于规划器所考虑的每一对连接,都会生成所有可能的计划,并选择其中估计代价最低的那个。 + + + + 当超过 geqo_threshold 时,被考虑的连接顺序将由启发式方法决定,如 中所述。除此之外,处理过程与前面相同。 + + + + 最终完成的计划树由基表的顺序扫描或索引扫描,再加上所需的嵌套循环、归并或哈希连接节点,以及任何需要的辅助步骤(例如排序节点或聚合函数计算节点)组成。这些计划节点类型中的大多数还具有执行 选择(丢弃不满足指定布尔条件的行)和 投影(根据给定列值计算派生列集,也就是在需要时计算标量表达式)的额外能力。规划器的职责之一,就是把来自 WHERE 子句的选择条件以及所需输出表达式的计算附加到计划树中最合适的节点上。 + + + + + + 执行器 + + + 执行器接收由规划器/优化器创建的计划,并递归地处理它,以提取所需的行集合。这本质上是一种按需拉取的流水线机制。每当某个计划节点被调用时,它必须再交付一行,或者报告自己已经完成了行的交付。 + + + + 为了给出一个具体示例,假设顶层节点是一个 MergeJoin 节点。在执行归并之前,必须先取到两行(每个子计划各一行)。因此执行器会递归调用自身去处理这些子计划(从挂接在 lefttree 上的子计划开始)。新的顶层节点,也就是左子计划的顶层节点,假设是一个 Sort 节点,那么又需要通过递归来取得一个输入行。Sort 的子节点可能是一个 SeqScan 节点,它表示真正去读取一个表。执行该节点会使执行器从表中取出一行,并把它返回给调用者节点。Sort 节点会反复调用它的子节点,以取得所有待排序的行。当输入耗尽时(子节点返回的是 NULL 而不是一行),Sort 代码就执行排序,并最终能够返回它的第一条输出行,也就是排序后的第一行。它会把其余行保存起来,以便在后续请求中按排序后的顺序交付这些行。 + + + + MergeJoin 节点同样会向它的右子计划请求第一行。然后它比较这两行,看它们是否可以连接;如果可以,就向调用者返回一条连接结果行。在下一次调用时,或者在当前这一对输入无法连接时立即,它会前进到某一个表的下一行或另一个表的下一行(取决于比较结果),并再次检查是否匹配。最终,其中一个子计划会耗尽,MergeJoin 节点就返回 NULL,以表明无法再形成更多连接结果行。 + + + + 复杂查询可能涉及许多层计划节点,但总体方法是相同的:每个节点在每次被调用时,都会计算并返回它的下一条输出行。每个节点还负责应用规划器分配给它的任何选择或投影表达式。 + + + + 执行器机制用于求值全部四种基本 SQL 查询类型:SELECTINSERTUPDATEDELETE。对于 SELECT,顶层执行器代码只需要把查询计划树返回的每一行发送给客户端。INSERT ... SELECTUPDATEDELETE 本质上都是 SELECT,位于一个名为 ModifyTable 的特殊顶层计划节点之下。 + + + + INSERT ... SELECT 会把各行上传给 ModifyTable 以执行插入。对于 UPDATE,规划器会安排每个计算得到的行包含所有被更新列的值,再加上原始目标行的 TID(tuple ID,即元组 ID 或行 ID);这些数据会被上传给 ModifyTable 节点,该节点利用这些信息创建一个新的更新后行,并将旧行标记为已删除。对于 DELETE,计划实际返回的唯一列就是 TID,而 ModifyTable 节点只需利用该 TID 访问每个目标行并将其标记为已删除。 + + + + 一个简单的 INSERT ... VALUES 命令会创建一棵极为简单的计划树,它只包含一个 Result 节点。该节点仅计算一条结果行,并将其上传给 ModifyTable 执行插入。 + + + + + diff --git a/zh/9.6/array.sgml b/zh/9.6/array.sgml new file mode 100644 index 00000000..fd449b60 --- /dev/null +++ b/zh/9.6/array.sgml @@ -0,0 +1,585 @@ + + + + 数组 + + + array + + + PostgreSQL 允许将表列定义为变长多维数组。可以创建任何内置或用户定义的基础类型、枚举类型或复合类型的数组。尚不支持域的数组。 + + + 数组类型的声明 + + + array + declaration + + + + 为了说明数组类型的用法,我们创建下面这个表: + +CREATE TABLE sal_emp ( + name text, + pay_by_quarter integer[], + schedule text[][] +); + + 如上所示,数组数据类型通过在数组元素的数据类型名后附加方括号([])来命名。上述命令将创建一个名为 sal_emp 的表,其中有一个 text 类型的列(name)、一个表示员工各季度薪资的一维 integer 数组(pay_by_quarter),以及一个表示员工每周日程安排的二维 text 数组(schedule)。 + + + + CREATE TABLE 的语法允许指定数组的确切大小,例如: + + +CREATE TABLE tictactoe ( + squares integer[3][3] +); + + + 不过,当前实现会忽略给出的任何数组大小限制,也就是说,其行为与未指定长度的数组相同。 + + + + 当前实现也不会强制执行所声明的维度数。对于某一特定元素类型的数组,无论大小或维度数如何,都会被视为同一种类型。因此,在 CREATE TABLE 中声明数组大小或维度数仅仅是文档说明;它不会影响运行时行为。 + + + + 另一种使用关键字 ARRAY、并且符合 SQL 标准的语法可用于一维数组。pay_by_quarter 也可以定义为: + + pay_by_quarter integer ARRAY[4], + + 或者,如果不指定数组大小: + + pay_by_quarter integer ARRAY, + + 不过,与前面一样,PostgreSQL 在任何情况下都不会强制执行大小限制。 + + + + + 数组值输入 + + + array + constant + + + + 要把数组值写成字面常量,请将元素值放在花括号内,并用逗号分隔。(如果你了解 C,这与 C 中初始化结构体的语法有些类似。)你可以给任意元素值加上双引号;如果它包含逗号或花括号,则必须这样做。(更多细节见下文。)因此,数组常量的一般格式如下: + +'{ val1 delim val2 delim ... }' + + 其中 delim 是该类型的分隔符字符,它记录在其 pg_type 条目中。在 PostgreSQL 发行版提供的标准数据类型里,除类型 box 使用分号(;)外,其余都使用逗号(,)。每个 val 要么是数组元素类型的常量,要么是一个子数组。数组常量的一个示例是: + +'{{1,2,3},{4,5,6},{7,8,9}}' + + 这个常量是一个 3 x 3 的二维数组,由三个整数子数组构成。 + + + + 要把数组常量中的某个元素设为 NULL,请把该元素值写成 NULL。(NULL 的任意大小写变体都可以。)如果你想要实际的字符串值 NULL,就必须为它加上双引号。 + + + + (这类数组常量实际上只是中讨论的通用类型常量的一种特例。该常量最初会被当作字符串处理,然后传递给数组输入转换例程。必要时可能需要显式指定类型。) + + + + 现在我们来看几个 INSERT 语句: + + +INSERT INTO sal_emp + VALUES ('Bill', + '{10000, 10000, 10000, 10000}', + '{{"meeting", "lunch"}, {"training", "presentation"}}'); + +INSERT INTO sal_emp + VALUES ('Carol', + '{20000, 25000, 25000, 25000}', + '{{"breakfast", "consulting"}, {"meeting", "lunch"}}'); + + + + + 前两条插入语句的结果如下: + + +SELECT * FROM sal_emp; + name | pay_by_quarter | schedule +-------+---------------------------+------------------------------------------- + Bill | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}} + Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}} +(2 rows) + + + + 多维数组在每个维度上的长度必须匹配。不匹配会导致错误,例如: +INSERT INTO sal_emp + VALUES ('Bill', + '{10000, 10000, 10000, 10000}', + '{{"meeting", "lunch"}, {"meeting"}}'); +ERROR: multidimensional arrays must have array expressions with matching dimensions + + + + + 也可以使用 ARRAY 构造器语法: + +INSERT INTO sal_emp + VALUES ('Bill', + ARRAY[10000, 10000, 10000, 10000], + ARRAY[['meeting', 'lunch'], ['training', 'presentation']]); + +INSERT INTO sal_emp + VALUES ('Carol', + ARRAY[20000, 25000, 25000, 25000], + ARRAY[['breakfast', 'consulting'], ['meeting', 'lunch']]); + + 注意,数组元素是普通的 SQL 常量或表达式;例如,字符串字面值用单引号而不是双引号包围,而在数组字面量中则会用双引号。关于 ARRAY 构造器语法的更多细节见。 + + + + + 访问数组 + + + array + accessing + + + + 现在,我们可以对该表执行一些查询。首先,演示如何访问数组中的单个元素。下面的查询取回第二季度薪资发生变化的员工姓名: + + +SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2]; + + name +------- + Carol +(1 row) + + + 数组下标写在方括号内。默认情况下,PostgreSQL 对数组采用从 1 开始的编号约定,也就是说,一个有 n 个元素的数组从 array[1] 开始,到 array[n] 结束。 + + + + 下面这个查询取回所有员工第三季度的薪资: + + +SELECT pay_by_quarter[3] FROM sal_emp; + + pay_by_quarter +---------------- + 10000 + 25000 +(2 rows) + + + + + 我们还可以访问数组或子数组的任意矩形切片。数组切片通过在一个或多个数组维度上写成 + lower-bound:upper-bound + 的形式来表示。例如,下面这个查询取回 Bill 在一周前两天日程安排中的第一个项目: + + +SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------ + {{meeting},{training}} +(1 row) + + + 如果任何一个维度写成切片形式,也就是包含冒号,那么所有维度都会被当作切片处理。任何只有单个数字(没有冒号)的维度都会被视为从 1 到该数字指定的范围。例如,[2] 会被当作 [1:2],如下例所示: + + +SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------------------------- + {{meeting,lunch},{training,presentation}} +(1 row) + + + 为了避免与非切片情况混淆,最好对所有维度都使用切片语法,例如写成 [1:2][1:1],而不是 [2][1:1]。 + + + + 切片说明符中的 lower-bound 和/或 + upper-bound 可以省略;缺失的边界会分别由数组下标的下界或上界代替。例如: + + +SELECT schedule[:2][2:] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------ + {{lunch},{presentation}} +(1 row) + +SELECT schedule[:][1:1] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------ + {{meeting},{training}} +(1 row) + + + + + 如果数组本身或任一下标表达式为 NULL,则数组下标表达式将返回空值。此外,如果下标超出数组边界,也会返回空值(这种情况不会报错)。例如,如果 schedule 当前的维度是 [1:3][1:2],那么引用 schedule[3][3] 会得到 NULL。类似地,使用错误数量的下标访问数组,得到的也是空值而不是错误。 + + + + 同样地,如果数组本身或任一下标表达式为 NULL,数组切片表达式也会返回空值。不过,在其他情况下,例如选择一个完全位于当前数组边界之外的数组切片时,切片表达式返回的是空(零维)数组而不是空值。(这与非切片行为不一致,是出于历史原因。)如果所请求的切片与数组边界仅部分重叠,那么它会被静默缩减为重叠区域,而不是返回空值。 + + + + 任意数组值的当前维度都可以用 array_dims 函数取回: + + +SELECT array_dims(schedule) FROM sal_emp WHERE name = 'Carol'; + + array_dims +------------ + [1:2][1:2] +(1 row) + + + array_dims 产生的是一个 text 结果,这对人工阅读比较方便,但对程序来说可能不太方便。也可以用 array_upperarray_lower 取回维度信息,它们分别返回指定数组维度的上界和下界: + + +SELECT array_upper(schedule, 1) FROM sal_emp WHERE name = 'Carol'; + + array_upper +------------- + 2 +(1 row) + + + array_length 会返回指定数组维度的长度: + + +SELECT array_length(schedule, 1) FROM sal_emp WHERE name = 'Carol'; + + array_length +-------------- + 2 +(1 row) + + + cardinality 返回数组跨所有维度的元素总数。它实际上就是调用 unnest 会产生的行数: + + +SELECT cardinality(schedule) FROM sal_emp WHERE name = 'Carol'; + + cardinality +------------- + 4 +(1 row) + + + + + + 修改数组 + + + array + modifying + + + + 数组值可以被整体替换: + + +UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}' + WHERE name = 'Carol'; + + + 或者使用 ARRAY 表达式语法: + + +UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] + WHERE name = 'Carol'; + + + 也可以更新数组中的单个元素: + + +UPDATE sal_emp SET pay_by_quarter[4] = 15000 + WHERE name = 'Bill'; + + + 或者更新其中一个切片: + + +UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' + WHERE name = 'Carol'; + + + 也可以使用省略 lower-bound 和/或 + upper-bound 的切片语法,但前提是被更新的数组值不是 NULL,也不是零维数组(否则就没有现有的下标边界可供替代)。 + + + + 已存储的数组值可以通过给尚不存在的元素赋值来扩展。原有元素与新赋值元素之间的任何位置都将用空值填充。例如,如果数组 myarray 当前有 4 个元素,那么在一次更新把值赋给 myarray[6] 之后,它将有 6 个元素;myarray[5] 将包含空值。目前,以这种方式扩展只允许用于一维数组,不允许用于多维数组。 + + + + 带下标的赋值也允许创建不使用从 1 开始下标的数组。例如,可以给 myarray[-2:7] 赋值,从而创建一个下标值范围为 -2 到 7 的数组。 + + + + 新的数组值也可以使用连接操作符 || 构造: + +SELECT ARRAY[1,2] || ARRAY[3,4]; + ?column? +----------- + {1,2,3,4} +(1 row) + +SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]]; + ?column? +--------------------- + {{5,6},{1,2},{3,4}} +(1 row) + + + + + 连接操作符允许把单个元素添加到一维数组的开头或末尾。它也接受两个 N 维数组,或者一个 N 维数组与一个 N+1 维数组。 + + + + 当把单个元素添加到一维数组的开头或末尾时,结果数组会保留该数组操作数的下界下标。例如: + +SELECT array_dims(1 || '[0:1]={2,3}'::int[]); + array_dims +------------ + [0:2] +(1 row) + +SELECT array_dims(ARRAY[1,2] || 3); + array_dims +------------ + [1:3] +(1 row) + + + + + 当连接两个维度数相同的数组时,结果会保留左侧操作数外层维度的下界下标。结果数组由左侧操作数的所有元素后跟右侧操作数的所有元素组成。例如: + +SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]); + array_dims +------------ + [1:5] +(1 row) + +SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]); + array_dims +------------ + [1:5][1:2] +(1 row) + + + + + 当把一个 N 维数组添加到一个 N+1 维数组的开头或末尾时,其结果与前面“单个元素与数组相连”的情况类似。每个 N 维子数组本质上都是该 N+1 维数组外层维度中的一个元素。例如: + +SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]); + array_dims +------------ + [1:3][1:2] +(1 row) + + + + + 也可以使用 array_prependarray_appendarray_cat 函数来构造数组。前两个只支持一维数组,而 array_cat 支持多维数组。示例如下: + + +SELECT array_prepend(1, ARRAY[2,3]); + array_prepend +--------------- + {1,2,3} +(1 row) + +SELECT array_append(ARRAY[1,2], 3); + array_append +-------------- + {1,2,3} +(1 row) + +SELECT array_cat(ARRAY[1,2], ARRAY[3,4]); + array_cat +----------- + {1,2,3,4} +(1 row) + +SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]); + array_cat +--------------------- + {{1,2},{3,4},{5,6}} +(1 row) + +SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]); + array_cat +--------------------- + {{5,6},{1,2},{3,4}} + + + + + 在简单情况下,优先使用上面讨论的连接操作符,而不是直接调用这些函数。不过,由于连接操作符被重载以同时服务于这三种情形,所以在某些场景下使用这些函数之一有助于避免歧义。例如,考虑: + + +SELECT ARRAY[1, 2] || '{3, 4}'; -- the untyped literal is taken as an array + ?column? +----------- + {1,2,3,4} + +SELECT ARRAY[1, 2] || '7'; -- so is this one +ERROR: malformed array literal: "7" + +SELECT ARRAY[1, 2] || NULL; -- so is an undecorated NULL + ?column? +---------- + {1,2} +(1 row) + +SELECT array_append(ARRAY[1, 2], NULL); -- this might have been meant + array_append +-------------- + {1,2,NULL} + + + 在上面的示例中,解析器看到连接操作符的一侧是整数数组,另一侧是类型未定的常量。它用来解析该常量类型的启发式规则是假定其类型与该操作符另一侧的输入相同,在这里就是整数数组。因此,连接操作符会被假定为表示 array_cat,而不是 array_append。如果这是错误的选择,可以通过把常量显式转换为数组的元素类型来修正;但显式使用 array_append 也许是更好的解决方案。 + + + + + 在数组中搜索 + + + array + searching + + + + 要在数组中搜索某个值,就必须检查每一个值。如果你知道数组的大小,这可以手工完成。例如: + + +SELECT * FROM sal_emp WHERE pay_by_quarter[1] = 10000 OR + pay_by_quarter[2] = 10000 OR + pay_by_quarter[3] = 10000 OR + pay_by_quarter[4] = 10000; + + + 不过,对于大型数组,这样很快就会变得繁琐;而在数组大小未知时,这也没有帮助。另一种方法见。上面的查询可以改写为: + + +SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter); + + + 此外,如果要查找数组中所有值都等于 10000 的行,可以使用: + + +SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter); + + + + + + 另外,也可以使用 generate_subscripts 函数。例如: + + +SELECT * FROM + (SELECT pay_by_quarter, + generate_subscripts(pay_by_quarter, 1) AS s + FROM sal_emp) AS foo + WHERE pay_by_quarter[s] = 10000; + + + 关于该函数的说明见。 + + + 还可以使用&&操作符搜索数组,它检查左操作数是否与右操作数重叠。例如: +SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000]; +关于此操作符和其他数组操作符的更多说明,参见。可以通过适当的索引来加速这种搜索,参见。 + + + 还可以使用array_positionarray_positions函数在数组中搜索特定值。前者返回某个值在数组中首次出现位置的下标;后者返回一个数组,其中包含该值在数组中所有出现位置的下标。例如: +SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon'); + array_positions +----------------- + 2 + +SELECT array_positions(ARRAY[1, 4, 3, 1, 3, 4, 2, 1], 1); + array_positions +----------------- + {1,4,8} + + + + + + 数组不是集合;搜索特定数组元素可能是数据库设计不当的信号。可以考虑使用一个独立的表,让原本会成为数组元素的每个项各占一行。这样会更容易搜索,而且在元素数量很多时通常也有更好的可伸缩性。 + + + + + + 数组输入和输出语法 + + + array + I/O + + + + 数组值的外部文本表示由若干项构成,这些项会按照数组元素类型的 I/O 转换规则进行解释,再加上一些表示数组结构的修饰。修饰部分包括数组值外层的花括号({}),以及相邻项之间的分隔符字符。分隔符字符通常是逗号(,),但也可能是别的字符:它由数组元素类型的 typdelim 设置决定。在 PostgreSQL 发行版提供的标准数据类型中,除类型 box 使用分号(;)外,其余都使用逗号。在多维数组中,每个维度(行、平面、立方体等)都有自己的一层花括号,并且同一级别相邻的花括号实体之间必须写出分隔符。 + + + + 如果元素值是空字符串、包含花括号、分隔符字符、双引号、反斜线或空白,或者与单词 NULL 匹配,数组输出例程就会在元素值外面加上双引号。元素值中嵌入的双引号和反斜线会用反斜线转义。对于数字数据类型,可以安全地假定不会出现双引号;但对于文本数据类型,则应准备好处理带引号和不带引号两种情况。 + + + + 默认情况下,数组各维度的下界索引值都设为 1。要表示具有其他下界的数组,可以在写出数组内容之前显式指定数组下标范围。这种修饰由包围每个数组维度上下界的方括号([])构成,中间以冒号(:)作为分隔符字符。数组维度修饰后面再跟一个等号(=)。例如: + +SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2 + FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss; + + e1 | e2 +----+---- + 1 | 6 +(1 row) + + 只有当一个或多个下界不等于 1 时,数组输出例程才会在结果中包含显式维度信息。 + + + + 如果某个元素写入的值是 NULL(任何大小写变体都可以),该元素就会被视为 NULL。若存在任何引号或反斜线,则会禁用这种行为,从而可以输入字面字符串值 NULL。另外,为了与 PostgreSQL 8.2 之前的版本保持向后兼容,可以将配置参数设为 off,从而禁止把 NULL 识别为 NULL。 + + + + 如前所述,在写数组值时,可以给任意单个数组元素加上双引号。如果元素值在其他情况下会让数组值解析器产生混淆,那么你必须这样做。例如,包含花括号、逗号(或者该数据类型的分隔符字符)、双引号、反斜线,或者前后带有空白的元素值,必须使用双引号。空字符串以及与单词 NULL 匹配的字符串也必须加引号。要在一个带双引号的数组元素值中放入双引号或反斜线,需要在它前面加反斜线。或者,你也可以避免使用引号,而改用反斜线转义来保护所有原本会被当作数组语法的数据字符。 + + + + 你可以在左花括号之前或右花括号之后添加空白。也可以在任意单个项字符串之前或之后添加空白。在所有这些情况下,这些空白都会被忽略。不过,双引号元素内部的空白,或者元素中夹在两个非空白字符之间的空白,不会被忽略。 + + + + + 在 SQL 命令中写数组值时,ARRAY 构造器语法(见)通常比数组字面量语法更容易使用。在 ARRAY 中,各个元素值的写法与它们不作为数组成员时完全相同。 + + + + + diff --git a/zh/9.6/auth-delay.sgml b/zh/9.6/auth-delay.sgml new file mode 100644 index 00000000..a0d4ee14 --- /dev/null +++ b/zh/9.6/auth-delay.sgml @@ -0,0 +1,57 @@ + + + + auth_delay + + + auth_delay + + + + auth_delay会使服务器在报告认证失败之前短暂暂停,从而增加对数据库密码进行暴力破解攻击的难度。注意,它并不能阻止拒绝服务攻击,甚至可能加剧此类攻击,因为在报告认证失败前处于等待状态的进程仍会占用连接槽位。 + + + + 为了使该模块正常工作,必须在postgresql.conf中通过加载它。 + + + + 配置参数 + + + + + auth_delay.milliseconds (int) + + auth_delay.milliseconds 配置参数 + + + + + 在报告认证失败之前等待的毫秒数。默认值为 0。 + + + + + + + 这些参数必须在postgresql.conf中设置。典型的用法如下: + + + +# postgresql.conf +shared_preload_libraries = 'auth_delay' + +auth_delay.milliseconds = '500' + + + + + 作者 + + + KaiGai Kohei kaigai@ak.jp.nec.com + + + + diff --git a/zh/9.6/auto-explain.sgml b/zh/9.6/auto-explain.sgml new file mode 100644 index 00000000..ed1aad67 --- /dev/null +++ b/zh/9.6/auto-explain.sgml @@ -0,0 +1,214 @@ + + + + auto_explain + + + auto_explain + + + + auto_explain模块提供了一种自动记录慢语句执行计划的方法,而无须手工运行。这对于在大型应用中追查未优化的查询尤其有帮助。 + + + + 该模块不提供可通过 SQL 访问的函数。要使用它,只需将其加载到服务器中。可以将其加载到单个会话中: + + +LOAD 'auto_explain'; + + + (要这样做,必须是超级用户。)更典型的用法是,在postgresql.conf中的里包含auto_explain,从而将其预加载到部分或全部会话中。这样,无论查询何时意外变慢,都可以追踪到它们。当然,这要付出额外开销。 + + + + 配置参数 + + + 有若干配置参数可控制auto_explain的行为。注意,其默认行为是什么都不做,因此如果希望得到任何结果,至少必须设置auto_explain.log_min_duration。 + + + + + + auto_explain.log_min_duration (integer) + + auto_explain.log_min_duration配置参数 + + + + auto_explain.log_min_duration是会导致记录该语句计划的最小语句执行时间,单位为毫秒。将其设为 0 会记录所有计划。-1(默认值)会禁用计划记录。例如,如果将其设为250ms,则所有运行 250ms 或更久的语句都会被记录。只有超级用户可以更改此设置。 + + + + + + auto_explain.log_analyze (boolean) + + auto_explain.log_analyze配置参数 + + + + + auto_explain.log_analyze使得在记录执行计划时输出EXPLAIN ANALYZE,而不只是输出EXPLAIN。该参数默认关闭。只有超级用户可以更改此设置。 + + + + 当该参数开启时,所有已执行语句都会对每个计划节点计时,无论它们是否实际运行得足够久而最终被记录。这可能对性能造成极其负面的影响。关闭auto_explain.log_timing可以缓解性能开销,但代价是获得的信息会更少。 + + + + + + + + auto_explain.log_buffers (boolean) + + auto_explain.log_buffers配置参数 + + + + + auto_explain.log_buffers控制在记录执行计划时是否打印缓冲区使用统计信息;它等效于EXPLAINBUFFERS选项。除非启用auto_explain.log_analyze,否则该参数没有效果。该参数默认关闭。只有超级用户可以更改此设置。 + + + + + + + auto_explain.log_timing (boolean) + + auto_explain.log_timing配置参数 + + + + + auto_explain.log_timing控制在记录执行计划时是否打印每个计划节点的计时信息;它等效于EXPLAINTIMING选项。在某些系统上,反复读取系统时钟的开销可能会显著拖慢查询,因此如果只需要实际行计数而不需要精确时间,将该参数设为 off 可能会有用。除非启用auto_explain.log_analyze,否则该参数没有效果。该参数默认开启。只有超级用户可以更改此设置。 + + + + + + + auto_explain.log_triggers (boolean) + + auto_explain.log_triggers配置参数 + + + + + auto_explain.log_triggers使得在记录执行计划时包含触发器执行统计信息。除非启用auto_explain.log_analyze,否则该参数没有效果。该参数默认关闭。只有超级用户可以更改此设置。 + + + + + + + auto_explain.log_verbose (boolean) + + auto_explain.log_verbose配置参数 + + + + + auto_explain.log_verbose控制在记录执行计划时是否打印详细信息;它等效于EXPLAINVERBOSE选项。该参数默认关闭。只有超级用户可以更改此设置。 + + + + + + + auto_explain.log_format (enum) + + auto_explain.log_format配置参数 + + + + + auto_explain.log_format选择要使用的EXPLAIN输出格式。允许的值有textxmljsonyaml。默认值为 text。只有超级用户可以更改此设置。 + + + + + + + auto_explain.log_nested_statements (boolean) + + auto_explain.log_nested_statements配置参数 + + + + + auto_explain.log_nested_statements使嵌套语句(在函数内部执行的语句)也会被纳入记录考虑范围。关闭该参数时,只记录顶层查询计划。该参数默认关闭。只有超级用户可以更改此设置。 + + + + + + + auto_explain.sample_rate (real) + + auto_explain.sample_rate配置参数 + + + + + auto_explain.sample_rate使 auto_explain 在每个会话中只解释一部分语句。默认值为 1,表示解释所有查询。对于嵌套语句,要么全部解释,要么全部不解释。只有超级用户可以更改此设置。 + + + + + + + 在通常用法中,这些参数都在postgresql.conf中设置,尽管超级用户也可以在自己的会话中动态修改它们。典型的用法可能是: + + + +# postgresql.conf +session_preload_libraries = 'auto_explain' + +auto_explain.log_min_duration = '3s' + + + + + 示例 + + +postgres=# LOAD 'auto_explain'; +postgres=# SET auto_explain.log_min_duration = 0; +postgres=# SET auto_explain.log_analyze = true; +postgres=# SELECT count(*) + FROM pg_class, pg_index + WHERE oid = indrelid AND indisunique; + + + + 这可能产生如下日志输出: + + + Hash Join (cost=4.17..16.55 rows=92 width=0) (actual time=3.349..3.594 rows=92 loops=1) + Hash Cond: (pg_class.oid = pg_index.indrelid) + -> Seq Scan on pg_class (cost=0.00..9.55 rows=255 width=4) (actual time=0.016..0.140 rows=255 loops=1) + -> Hash (cost=3.02..3.02 rows=92 width=4) (actual time=3.238..3.238 rows=92 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 4kB + -> Seq Scan on pg_index (cost=0.00..3.02 rows=92 width=4) (actual time=0.008..3.187 rows=92 loops=1) + Filter: indisunique +]]> + + + + 作者 + + + Takahiro Itagaki itagaki.takahiro@oss.ntt.co.jp + + + + diff --git a/zh/9.6/backup.sgml b/zh/9.6/backup.sgml new file mode 100644 index 00000000..f23783bd --- /dev/null +++ b/zh/9.6/backup.sgml @@ -0,0 +1,724 @@ + + + + 备份和恢复 + + 备份 + + + 与任何保存重要数据的系统一样,PostgreSQL数据库也应定期备份。虽然其过程基本简单,但清楚理解其底层技术和前提假设非常重要。 + + + + 备份PostgreSQL数据主要有三种根本不同的方法: + + SQL转储 + 文件系统级备份 + 持续归档 + + 每种方法都有各自的优缺点,下面各节将依次讨论。 + + + + <acronym>SQL</acronym>转储 + + + 这种转储方法的思路是生成一个包含 SQL 命令的文件;将其重新送回服务器后,服务器就会把数据库重建为转储时的状态。PostgreSQL为此提供了工具程序。该命令的基本用法是: + +pg_dump dbname > dumpfile + + 如前所示,pg_dump把结果写到标准输出。稍后我们会看到这一点有何用处。虽然上述命令会创建一个文本文件,但pg_dump也可以创建其他格式的文件,从而支持并行处理以及对对象恢复进行更细粒度的控制。 + + + + pg_dump是一个普通的PostgreSQL客户端应用程序(尽管它实现得相当巧妙)。这意味着你可以从任何能够访问该数据库的远程主机执行这种备份过程。但请记住,pg_dump并不会以特殊权限运行。特别是,它必须对你想要备份的所有表具有读权限,因此若要备份整个数据库,几乎总是必须以数据库超级用户身份运行它。(如果你没有足够权限备份整个数据库,仍然可以使用诸如的选项来备份你有权访问的那部分数据库。) + + + + 要指定pg_dump应连接哪个数据库服务器,请使用命令行选项。默认主机是本地主机,或者由环境变量PGHOST指定的主机。类似地,默认端口由环境变量PGPORT指定;如果未设置,则采用编译时内置的默认值。(方便的是,服务器通常也会使用同样的内置默认值。) + + + + 和其他PostgreSQL客户端应用程序一样,pg_dump默认会以与当前操作系统用户名相同的数据库用户名进行连接。若要覆盖这一点,可以指定选项,或者设置环境变量PGUSER。请记住,pg_dump连接同样受常规客户端认证机制约束(见)。 + + + + 与后文介绍的其他备份方法相比,pg_dump的一个重要优点在于,pg_dump的输出通常可以重新装入较新版本的PostgreSQL,而文件级备份和持续归档都高度依赖具体的服务器版本。pg_dump也是在不同机器体系结构之间迁移数据库时唯一可行的方法,例如从 32 位服务器迁移到 64 位服务器。 + + + + 由pg_dump创建的转储在内部是一致的,也就是说,该转储表示的是pg_dump开始运行时的数据库快照。pg_dump在工作期间不会阻塞数据库上的其他操作。(例外是那些需要独占锁的操作,例如大多数形式的ALTER TABLE。) + + + + 恢复转储 + + pg_dump 创建的文本文件供 psql 程序读取。恢复转储的通用命令格式是 +psql dbname < dumpfile +其中,dumpfile 是由 pg_dump 命令输出的文件。数据库 dbname 不会由此命令创建,因此必须自行基于 template0 创建它,再执行 psql(例如使用 createdb -T template0 dbname)。 psql 支持与 pg_dump 类似的选项,用于指定要连接的数据库服务器及所用的用户名。更多信息参见 参考页面。非文本格式的转储使用 工具恢复。 + + + 在恢复 SQL 转储之前,所有拥有对象或者在被转储数据库中曾被授予对象权限的用户都必须已经存在。否则,恢复将无法以原有所有权和/或权限重新创建这些对象。(有时这正是你想要的,但通常不是。) + + + + 默认情况下,psql脚本在遇到 SQL 错误后仍会继续执行。你可能希望让psql在设置了ON_ERROR_STOP变量的情况下运行,以改变这一行为,并让psql在发生 SQL 错误时以退出状态码 3 退出: + +psql --set ON_ERROR_STOP=on dbname < dumpfile + + 无论采用上述哪种方式,你最终只会得到一个部分恢复的数据库。另一种做法是指定将整个转储作为单个事务恢复,这样恢复要么全部完成,要么全部回滚。可以通过把命令行选项传给psql来启用这种模式。使用这种模式时要注意,即便是一个很小的错误,也可能回滚一个已经运行了许多小时的恢复过程。不过,这仍可能比在部分恢复后手工清理一个复杂数据库更可取。 + + + + pg_dumppsql能够写入或读取管道,这使得可以把数据库直接从一台服务器转储到另一台服务器,例如: + +pg_dump -h host1 dbname | psql -h host2 dbname + + + + + + pg_dump生成的转储是以template0为基准的。这意味着通过template1增加的任何语言、过程等也都会被pg_dump转储。因此,恢复时如果你使用的是定制过的template1,就必须像上面的例子那样,从template0创建空数据库。 + + + + + 恢复完备份后,最好对每个数据库运行,这样查询优化器就能拥有有用的统计信息;更多信息见。关于如何高效地将大量数据装入PostgreSQL,请参见。 + + + + + 使用<application>pg_dumpall</application> + + + pg_dump一次只转储一个数据库,而且不会转储角色或表空间的信息(因为这些是整个集簇级别的,而不是每个数据库各自的)。为了方便地转储整个数据库集簇的全部内容,提供了程序。pg_dumpall会备份给定集簇中的每个数据库,并保留角色定义和表空间定义等集簇范围的数据。该命令的基本用法是: + +pg_dumpall > dumpfile + + 生成的转储可以用psql恢复: + +psql -f dumpfile postgres + + (实际上,可以指定任意一个现有数据库名作为起点,但如果要装载到一个空集簇中,通常应使用postgres。)恢复pg_dumpall转储时始终需要数据库超级用户权限,因为恢复角色和表空间信息必须使用该权限。如果使用了表空间,请确保转储中的表空间路径适合新的安装。 + + + + pg_dumpall的工作方式是先发出重新创建角色、表空间和空数据库的命令,然后针对每个数据库调用pg_dump。这意味着每个数据库本身都是内部一致的,但不同数据库的快照并不同步。 + + + + 集簇范围的数据也可以单独使用pg_dumpall选项进行转储。如果你对各个数据库分别运行pg_dump命令,而又希望对整个集簇进行完整备份,这一步就是必需的。 + + + + + 处理大型数据库 + + + 某些操作系统对文件大小有上限,这会在创建大型pg_dump输出文件时带来问题。幸运的是,pg_dump可以把输出写到标准输出,因此你可以利用标准 Unix 工具绕过这个潜在问题。有几种可行方法: + + + + 使用压缩转储。 + + 你可以使用自己喜欢的压缩程序,例如gzip: + + +pg_dump dbname | gzip > filename.gz + + + 恢复时: + + +gunzip -c filename.gz | psql dbname + + + 或者: + + +cat filename.gz | gunzip | psql dbname + + + + + + 使用<command>split</command>。 + + split命令允许你把输出分割成文件系统可接受大小的较小文件。例如,要切分为 2 GB 的块: + + +pg_dump dbname | split -b 2G - filename + + + 恢复时: + + +cat filename* | psql dbname + + + 如果使用 GNU split,也可以把它和gzip结合起来: + + +pg_dump dbname | split -b 2G --filter='gzip > $FILE.gz' + + + 恢复时可使用zcat。 + + + + + 使用<application>pg_dump</application>的自定义转储格式。 + + 如果构建PostgreSQL所用的系统安装了zlib压缩库,自定义转储格式会在向输出文件写入数据时对其进行压缩。这会产生与使用gzip大致相同大小的转储文件,但额外的优点是其中的表可以有选择地恢复。下面的命令使用自定义转储格式转储数据库: + + +pg_dump -Fc dbname > filename + + + 自定义格式的转储不是供psql执行的脚本,而必须通过pg_restore恢复,例如: + + +pg_restore -d dbname filename + + + 详细信息见参考页。 + + + + + 对于非常大的数据库,你可能需要把split与另外两种方法之一结合使用。 + + + + 使用<application>pg_dump</application>的并行转储特性。 + + 为了加快大型数据库的转储速度,你可以使用pg_dump的并行模式。这会同时转储多个表。你可以通过-j参数控制并行度。并行转储只支持“目录”归档格式。 + + +pg_dump -j num -F d -f out.dir dbname + + + 你可以使用pg_restore -j并行恢复转储。这对于任何“自定义”或“目录”模式的归档都有效,无论它是否是用pg_dump -j创建的。 + + + + + + + 文件系统级备份 + + + 另一种备份策略是直接复制PostgreSQL用来存储数据库数据的文件;解释了这些文件的位置。你可以使用自己喜欢的任何方法进行文件系统备份,例如: + + +tar -cf backup.tar /usr/local/pgsql/data + + + + + 不过,这种方法有两个限制,使得它并不实用,至少也不如pg_dump方法: + + + + + 为了得到可用的备份,数据库服务器必须关闭。像禁止所有连接这样的折中办法不起作用(部分原因是tar和类似工具不会对文件系统状态执行原子快照,另一部分原因是服务器内部也存在缓冲)。有关停止服务器的信息见。不言而喻,恢复数据前也必须关闭服务器。 + + + + + + 如果你已经深入了解数据库文件系统布局的细节,可能会想尝试仅通过某些表或数据库各自的文件或目录来备份或恢复它们。这是行不通的,因为这些文件中的信息离开记录所有事务提交状态的提交日志文件pg_clog/*就无法使用。一个表文件只有和这些信息一起才可用。当然,也不可能只恢复一个表以及相关的pg_clog数据,因为那会让数据库集簇中的其他所有表都无法使用。因此,文件系统备份只适用于整个数据库集簇的完整备份与恢复。 + + + + + + + 另一种文件系统备份方法是对数据目录制作一个一致快照,前提是文件系统支持该功能(并且你愿意相信其实现是正确的)。典型流程是:对包含数据库的卷制作一个冻结快照,然后从该快照把整个数据目录(不是其中一部分,见上文)复制到备份设备,再释放这个冻结快照。即使数据库服务器正在运行,这种方法也能工作。不过,以这种方式创建的备份会把数据库文件保存为一种仿佛服务器没有被正确关闭的状态;因此,当你基于备份数据启动数据库服务器时,它会认为前一个服务器实例发生了崩溃,并重放 WAL 日志。这不是问题;只要意识到这一点即可(并且务必把 WAL 文件也包含在备份中)。你可以在获取快照之前执行一次CHECKPOINT,以缩短恢复时间。 + + + + 如果数据库分布在多个文件系统上,就可能根本无法对所有卷获得完全同步的冻结快照。例如,如果数据文件和 WAL 日志位于不同磁盘,或者表空间位于不同文件系统上,就可能无法使用快照备份,因为这些快照必须同时获取。在这种情况下,在信任一致快照技术之前,一定要非常仔细地阅读文件系统文档。 + + + + 如果无法获得同时的快照,一种选择是将数据库服务器关闭足够长的时间,以建立所有冻结快照。另一种选择是执行持续归档基础备份(),因为这种备份不受备份期间文件系统变化的影响。这种方法只需在备份期间启用持续归档;恢复则使用持续归档恢复()。 + + + + 另一种选择是使用rsync进行文件系统备份。做法是先在数据库服务器运行时执行一次rsync,然后将数据库服务器关闭足够长的时间,再执行一次rsync --checksum。(之所以需要,是因为rsync对文件修改时间的粒度只有 1 秒。)第二次rsync会比第一次更快,因为它需要传输的数据相对较少,而最终结果会因为服务器已经关闭而保持一致。这种方法可以在最小停机时间下完成文件系统备份。 + + + + 注意,文件系统备份通常会比 SQL 转储更大。(例如,pg_dump不需要转储索引内容,只需转储重建索引的命令。)不过,进行文件系统备份可能更快。 + + + + + 持续归档和时间点恢复(PITR) + + + 持续归档 + + + + 时间点恢复 + + + + PITR + + + + 在任何时候,PostgreSQL都会维护一个预写式日志(WAL),它位于集簇数据目录的pg_xlog/子目录中。该日志记录对数据库数据文件所做的每一次修改。这个日志首先是为崩溃安全而存在:如果系统崩溃,可以通过重放自上次检查点以来的日志记录,将数据库恢复到一致状态。不过,日志的存在也使第三种数据库备份策略成为可能:我们可以把文件系统级备份与 WAL 文件备份结合起来。如果需要恢复,就先恢复文件系统备份,再重放已备份的 WAL 文件,使系统回到当前状态。与前两种方法相比,这种方法管理起来更复杂,但它有一些显著优点: + + + + 我们不需要一个完全一致的文件系统备份作为起点。备份中的任何内部不一致性都会通过日志重放纠正(这与崩溃恢复期间发生的事情并没有本质不同)。因此,我们不需要文件系统快照能力,只需要tar或类似的归档工具。 + + + + + 由于我们可以把任意长的一串 WAL 文件串联起来进行重放,只要持续归档 WAL 文件,就可以实现连续备份。这对于大型数据库尤其有价值,因为此时频繁进行完整备份可能并不方便。 + + + + + 不必把 WAL 记录一直重放到最后。我们可以在任意位置停止重放,并得到数据库在当时的一致快照。因此,这项技术支持时间点恢复:可以把数据库恢复到自基础备份之后任意时刻的状态。 + + + + + 如果我们持续把这一串 WAL 文件输送给另一台已经装载了同一基础备份文件的机器,就得到了一个温备系统:我们随时都可以启用第二台机器,而它将拥有数据库几乎是最新的副本。 + + + + + + + + pg_dumppg_dumpall不会生成文件系统级备份,因此不能作为持续归档方案的一部分使用。这类转储是逻辑备份,不包含 WAL 重放所需的足够信息。 + + + + + 和普通文件系统备份技术一样,这种方法只能支持整个数据库集簇的恢复,而不支持其中某个子集的恢复。此外,它需要大量归档存储:基础备份可能很庞大,繁忙系统也会产生许多兆字节的、必须归档的 WAL 流量。尽管如此,在很多需要高可靠性的场景中,它仍是首选的备份技术。 + + + + 要想利用持续归档(许多数据库厂商也称之为在线备份)成功恢复,你需要一串连续的已归档 WAL 文件,其时间至少要追溯到备份的开始时刻。因此,入门时应在进行第一次基础备份之前就建立并测试好归档 WAL 文件的流程。下面先讨论归档 WAL 文件的机制。 + + + + 设置 WAL 归档 + + 从抽象角度看,运行中的 PostgreSQL 系统会产生一个无限延伸的 WAL 记录序列。在物理存储上,系统将该序列分为 WAL 段文件,通常每个为 16MB(但可以在构建 PostgreSQL 时修改段大小)。段文件使用数字名称,反映其在抽象 WAL 序列中的位置。不使用 WAL 归档时,系统通常只创建少量段文件,然后通过将不再需要的段文件重命名为更大的段号来回收它们。系统假定,内容早于倒数第二个检查点的段文件已不再需要,可以回收。 + + + 在归档 WAL 数据时,我们需要在每个段文件写满后捕获其内容,并在该段文件被回收重用之前把数据保存到某处。根据应用场景和可用硬件的不同,把数据保存到某处可以有很多不同的方法:可以把段文件复制到另一台机器上的 NFS 挂载目录,把它们写到磁带机中(确保你有办法识别每个文件的原始文件名),把它们批量打包后刻录到 CD 上,或者采用完全不同的方式。为了给数据库管理员提供灵活性,PostgreSQL尽量不对归档方式作任何假设。相反,PostgreSQL允许管理员指定一个 shell 命令,以便把已完成的段文件复制到它应去的地方。该命令可以只是一次简单的cp调用,也可以调用一个复杂的 shell 脚本,全由你决定。 + + + 要启用 WAL 归档,请将 配置参数设为 replica 或更高值,将 设为 on,并在 配置参数中指定要执行的 shell 命令。实际使用中,这些设置始终放在 postgresql.conf 文件中。对于 archive_command, + %p 会被替换为要归档文件的路径名,而 %f 只会被替换为文件名。(路径名相对于当前工作目录,即集簇的数据目录。)使用 %% 可以在命令中嵌入实际的 % 字符。最简单的可用命令类似于: +archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' # Unix +archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"' # Windows +这会将可归档的 WAL 段复制到目录 /mnt/server/archivedir。(这只是示例,并非推荐做法,而且不一定适用于所有平台。)在替换 %p%f 参数后,实际执行的命令可能如下: +test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_xlog/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065 +每个需要归档的新文件都会生成一条类似的命令。 + + 归档命令将以运行 PostgreSQL 服务器的同一用户身份执行。由于归档的一系列 WAL 文件实际上包含数据库中的全部内容,应确保归档数据不会被他人窥视;例如,将其归档到不允许所属组或其他用户读取的目录。 + + 归档命令必须当且仅当成功时才返回退出状态零。收到零状态后,PostgreSQL 会认为该文件已成功归档,并将其删除或回收。非零状态则告诉 PostgreSQL 该文件尚未归档;它会定期重试,直到成功。 + + 通常应将归档命令设计为拒绝覆盖任何已存在的归档文件。这是一项重要的安全保护措施,能在管理员操作失误时(例如将两台不同服务器的输出发送到同一归档目录)维护归档的完整性。 + + + 建议测试所拟定的归档命令,确保它确实不会覆盖已有文件,并且在这种情况下返回非零状态。上面给出的 Unix 示例命令通过单独加入一个test步骤,确保这两点。在某些 Unix 平台上,cp提供了诸如之类的开关,也可以更简洁地实现同样目的,但在你确认它会返回正确的退出状态之前,不应依赖这些开关。(尤其是 GNU cp在使用且目标文件已存在时会返回状态零,这不是我们想要的行为。) + + + + 在设计归档方案时,请考虑如果归档命令因为某些环节需要操作员干预,或者归档空间耗尽而反复失败,会发生什么。例如,如果你在没有自动换带器的情况下向磁带写入数据,那么磁带满了以后,在更换磁带之前将无法继续归档。你应确保任何错误情况或对人工操作员的请求都能得到适当报告,以便问题能够比较快地解决。在问题解决之前,pg_xlog/目录会继续堆积 WAL 段文件。(如果包含pg_xlog/的文件系统被写满,PostgreSQL将执行 PANIC 关闭。不会丢失已提交的事务,但在释放出一些空间之前,数据库会一直离线。) + + + + 归档命令的速度并不重要,只要它能跟上服务器生成 WAL 数据的平均速度即可。即使归档过程稍有滞后,正常操作也会继续。如果归档明显落后,灾难发生时可能丢失的数据量就会增加。这还意味着pg_xlog/目录会包含大量尚未归档的段文件,最终可能耗尽可用磁盘空间。建议监控归档过程,确保它按你的预期工作。 + + + + 在编写归档命令时,应假定待归档文件名最长可达 64 个字符,并且可以包含 ASCII 字母、数字和点号的任意组合。无需保留原始相对路径(%p),但必须保留文件名(%f)。 + + + + 注意,虽然 WAL 归档允许你恢复对PostgreSQL数据库中数据所做的任何修改,但它不会恢复对配置文件(即postgresql.confpg_hba.confpg_ident.conf)的修改,因为这些文件是手工编辑的,而不是通过 SQL 操作修改的。你可能希望把配置文件放在常规文件系统备份过程能够覆盖的位置。关于如何重定位配置文件,见。 + + + + 归档命令只会针对已完成的 WAL 段调用。因此,如果服务器产生的 WAL 流量很小(或者存在低谷期),事务完成到其被安全写入归档存储之间可能会有很长延迟。为了限制未归档数据可能有多旧,你可以设置,使服务器强制切换到新 WAL 段文件的间隔不超过这个值。注意,由强制切换而提前归档的文件长度仍与装满的文件相同。因此,把archive_timeout设得很短并不明智,这会使归档存储膨胀。archive_timeout设为大约 1 分钟通常是合理的。 + + + + 此外,如果你希望确保一个刚刚完成的事务尽快被归档,可以使用pg_switch_xlog手工强制一次段切换。其他与 WAL 管理相关的实用函数列在中。 + + + + 当wal_levelminimal时,某些 SQL 命令会像中所述那样被优化为避免 WAL 记录。如果在执行这些语句期间启用了归档或流复制,WAL 将不包含归档恢复所需的足够信息。(崩溃恢复不受影响。)因此,wal_level只能在服务器启动时更改。然而,archive_command可以通过重新加载配置文件来更改。如果你希望暂时停止归档,一种办法是将archive_command设置为空字符串('')。这会导致 WAL 文件在pg_xlog/中累积,直到重新建立可用的archive_command。 + + + + + 进行基础备份 + + + 执行基础备份最简单的方法是使用工具。它可以把基础备份创建为普通文件或 tar 归档。如果需要比提供的更高灵活性,也可以使用低级 API 制作基础备份(见)。 + + + + 不必过于担心制作基础备份所需的时间。不过,如果你平时在关闭full_page_writes的情况下运行服务器,可能会注意到备份运行期间性能下降,因为在备份模式下full_page_writes实际上会被强制开启。 + + + + 要让该备份可用,你需要保留在文件系统备份期间以及之后生成的所有 WAL 段文件。为帮助完成这件事,基础备份过程会创建一个备份历史文件,并立即将其存入 WAL 归档区域。该文件以文件系统备份所需的第一个 WAL 段文件命名。例如,如果起始 WAL 文件是0000000100001234000055CD,备份历史文件的名称将类似于0000000100001234000055CD.007C9330.backup。(文件名第二部分表示该 WAL 文件中的一个精确位置,通常可以忽略。)一旦你已经安全归档了文件系统备份以及备份期间使用到的 WAL 段文件(如备份历史文件所指定),所有名称在数值上更小的已归档 WAL 段就不再是恢复该文件系统备份所必需的,可以删除。不过,你仍应考虑保留多个备份集,以绝对确保能够恢复数据。 + + + + 备份历史文件只是一个很小的文本文件。它包含你提供给的标签字符串,以及备份的起止时间和起止 WAL 段。如果你用该标签标识了关联的备份文件,那么已归档的历史文件就足以告诉你应恢复哪个备份文件。 + + + + 由于你必须保留自上一次基础备份以来的所有已归档 WAL 文件,基础备份之间的间隔通常应根据你愿意为已归档 WAL 文件投入多少存储空间来决定。你还应考虑在确实需要恢复时,你愿意花多长时间进行恢复,因为系统必须重放所有这些 WAL 段;如果距离上次基础备份已经过去很久,这可能会花费一些时间。 + + + + + 使用低级 API 进行基础备份 + 使用低级 API 制作基础备份的过程,比 方法多几个步骤,但相对简单。务必按顺序执行这些步骤,并在继续下一步之前确认当前步骤成功。 + 低级基础备份可以采用非排他或排他方式。建议使用非排他方式;排他方式已弃用,最终将被移除。 + + 制作非排他低级备份 + 非排他低级备份允许同时运行其他备份,包括使用同一备份 API 启动的备份和使用 启动的备份。 + + + + 确保 WAL 归档已启用并正常工作。 + + + 以有权运行 pg_start_backup 的用户身份(超级用户,或已被授予该函数 EXECUTE 权限的用户)连接到服务器(连接哪个数据库都可以),并执行命令: +SELECT pg_start_backup('label', false, false); +其中,label 是用于唯一标识此次备份操作的任意字符串。调用 pg_start_backup 的连接必须一直保持到备份结束,否则备份会自动中止。 + + 默认情况下,pg_start_backup 可能需要很长时间才能完成。这是因为它会执行一次检查点,而检查点所需的 I/O 会分散在较长时间内,默认是检查点间隔的一半(参见配置参数 )。这通常是理想的行为,因为它尽量减少了对查询处理的影响。如果希望尽快开始备份,请将第二个参数改为 true,这会尽可能利用可用 I/O 立即执行检查点。 + + 第三个参数为 false,会告知 pg_start_backup 启动非排他基础备份。 + + + + 使用任何方便的文件系统备份工具执行备份,例如tarcpio(不要使用pg_dumppg_dumpall)。在此过程中既没有必要,也不希望停止数据库的正常运行。关于执行此备份时需要注意的事项,见。 + + + + 在之前的同一个连接中,执行命令: +SELECT * FROM pg_stop_backup(false); +这会终止备份模式。在主库上,还会自动切换到下一个 WAL 段。在备库上,无法自动切换 WAL 段,因此可以运行 pg_switch_xlog,在主库上手动切换。切换的目的是让备份期间写入的最后一个 WAL 段文件准备好归档。 + + pg_stop_backup会返回一行,包含三个值。其中第二个字段应写入备份根目录下名为backup_label的文件中。第三个字段除非为空,否则应写入名为tablespace_map的文件中。这些文件对备份能否正常工作至关重要,必须逐字节原样写入,不能做任何修改,这可能意味着需要以二进制模式打开文件。 + + + + + 一旦备份期间活跃的 WAL 段文件都已归档,备份就完成了。由pg_stop_backup第一个返回值标识的文件,是形成完整备份文件集所需的最后一个段。在主库上,如果启用了archive_modepg_stop_backup在最后一个段归档前不会返回。由于你已经配置好了archive_command,这些文件的归档会自动进行。多数情况下这会很快完成,但仍建议监控归档系统,确保没有延迟。如果归档进程因归档命令失败而落后,它会持续重试,直到归档成功并且备份完成。如果你希望对pg_stop_backup的执行设置时间限制,请设置合适的statement_timeout值,但要注意如果pg_stop_backup因此终止,备份可能无效。 + + + 注意,在备库上pg_stop_backup不会等待 WAL 段归档,因此备份过程必须确保备份所需的所有 WAL 段都已成功归档。 + + + + + + + 制作排他低级备份 + 排他备份的过程与非排他备份基本相同,但有几个关键步骤不同。这种备份只能在主库上进行,且不允许并发备份。在 PostgreSQL 9.6 之前,这是唯一可用的低级方法,但现在建议所有用户尽可能更新脚本,改用非排他备份。 + + + + 确保 WAL 归档已启用并正常工作。 + + + 以有权运行 pg_start_backup 的用户身份(超级用户,或已被授予该函数 EXECUTE 权限的用户)连接到服务器(连接哪个数据库都可以),并执行命令: +SELECT pg_start_backup('label'); +其中,label 是用于唯一标识此次备份操作的任意字符串。pg_start_backup 会创建一个备份标签文件,名为 backup_label,位于集簇目录中,包含备份信息,例如开始时间和标签字符串。该函数还会创建一个表空间映射文件,名为 tablespace_map,位于集簇目录中,包含 pg_tblspc/ 中表空间符号链接的信息(如果存在一个或多个这样的链接)。如果需要从备份恢复,这两个文件对备份的完整性都至关重要。 + + 默认情况下,pg_start_backup 可能需要很长时间才能完成。这是因为它会执行一次检查点,而检查点所需的 I/O 会分散在较长时间内,默认是检查点间隔的一半(参见配置参数 )。这通常是理想的行为,因为它尽量减少了对查询处理的影响。如果希望尽快开始备份,请使用: +SELECT pg_start_backup('label', true); +这会强制尽快完成检查点。 + + + + 使用任何方便的文件系统备份工具执行备份,例如tarcpio(不要使用pg_dumppg_dumpall)。在此过程中既没有必要,也不希望停止数据库的正常运行。关于执行此备份时需要注意的事项,见。 + + 注意,如果服务器在备份期间崩溃,可能必须手动删除 PGDATA 目录中的 backup_label 文件后才能重启。 + + + 再次以有权运行 pg_stop_backup 的用户身份(超级用户,或已被授予该函数 EXECUTE 权限的用户)连接到数据库,并执行命令: +SELECT pg_stop_backup(); +这会终止备份模式,并自动切换到下一个 WAL 段。切换的目的是让备份期间写入的最后一个 WAL 段文件准备好归档。 + + + 备份期间处于活动状态的 WAL 段文件全部归档后,备份就完成了。pg_stop_backup 返回结果所标识的文件,是构成完整备份文件集所需的最后一个段。如果启用了 archive_modepg_stop_backup 会等到最后一个段归档后才返回。由于已经配置了 archive_command,这些文件会自动归档。大多数情况下,归档很快就能完成,但建议监控归档系统,确保没有延迟。如果归档进程因归档命令失败而落后,它会不断重试,直到归档成功、备份完成。如果希望限制 pg_stop_backup 的执行时间,可以设置合适的 statement_timeout 值,但要注意,如果 pg_stop_backup 因此终止,备份可能无效。 + + + + + + 备份数据目录 + + 某些文件系统备份工具在复制过程中,如果它们试图复制的文件发生变化,就会发出警告或错误。对活动数据库进行基础备份时,这种情况是正常的,并不表示出错。不过,你需要确保能够把这类提示与真正的错误区分开来。例如,某些版本的rsync会针对vanished source files返回单独的退出码,你可以编写一个驱动脚本,把这一退出码视为非错误情况。此外,某些版本的 GNU tar在文件被tar复制时如果发生截断,会返回与致命错误无法区分的错误码。幸运的是,GNU tar 1.16 及之后版本在备份过程中如果文件被更改会以 1 退出,而其他错误则以 2 退出。对于 GNU tar 1.23 及之后版本,可以使用警告选项--warning=no-file-changed --warning=no-file-removed隐藏相关警告信息。 + + + + 务必确认你的备份包含数据库集簇目录(例如/usr/local/pgsql/data)下的全部文件。如果你使用的表空间不位于该目录之下,也要记得把它们包括进来(并确保备份把符号链接作为链接归档,否则恢复时会破坏表空间)。 + + + + 不过,备份中应省略集簇pg_xlog/子目录内的文件。这个小调整很值得,因为它能降低恢复时出错的风险。如果pg_xlog/是一个指向集簇目录外某处的符号链接,就很容易做到这一点,而出于性能原因,这本来也是一种常见配置。你也可能想排除postmaster.pidpostmaster.opts,它们记录的是正在运行的postmaster的信息,而不是最终使用此备份的postmaster的信息。(这些文件可能会让pg_ctl感到困惑。) + + + + 通常也最好省略集簇pg_replslot/目录中的文件,以免主库上的复制槽成为备份的一部分。否则,之后用该备份创建备库时,可能导致该备库上的 WAL 文件无限期保留;如果启用了热备反馈,也可能导致主库膨胀,因为使用这些复制槽的客户端仍会连接到并更新主库上的槽,而不是备库上的槽。即使该备份只是用来创建新的主库,复制这些复制槽通常也没有多大意义,因为等新主库上线时,这些槽的内容很可能已经严重过时。 + + + + 备份标签文件包含你提供给pg_start_backup的标签字符串、执行pg_start_backup的时间以及起始 WAL 文件的名称。因此,在发生混淆时,可以查看备份文件内容,精确确定该备份文件来自哪一次备份会话。表空间映射文件包含目录pg_tblspc/中存在的符号链接名称,以及每个符号链接的完整路径。这些文件不仅仅是给你参考;它们的存在及其内容对于系统恢复过程的正确运行至关重要。 + + + + 服务器停止时也可以进行备份。在这种情况下,你显然无法使用pg_start_backuppg_stop_backup,因此只能自己追踪各个备份的身份以及相关 WAL 文件最早需要追溯到哪里。通常最好还是遵循上面的持续归档过程。 + + + + + + 使用持续归档备份进行恢复 + + + 现在,最坏的情况发生了,你需要通过备份进行恢复。步骤如下: + + + + 如果服务器仍在运行,就先停止它。 + + + + + 如果你有足够空间,请把整个集簇数据目录以及所有表空间复制到临时位置,以备后续需要。注意,这一预防措施要求系统有足够空闲空间来保存现有数据库的两份副本。如果空间不够,至少也应保存集簇pg_xlog子目录中的内容,因为其中可能包含系统停机前尚未归档的 WAL 文件。 + + + + + 删除集簇数据目录下以及所有正在使用的表空间根目录下的现有文件和子目录。 + + + + + 从文件系统备份中恢复数据库文件。务必确保它们以正确的所有者(数据库系统用户,而不是root!)和正确的权限恢复。如果使用了表空间,还应验证pg_tblspc/中的符号链接是否已正确恢复。 + + + + + 删除pg_xlog/中现有的所有文件;这些文件来自文件系统备份,因此很可能已经过时而不是最新的。如果你根本没有归档pg_xlog/,那么就以正确权限重新创建它,并注意如果它原先是符号链接,就要重新把它设置成符号链接。 + + + + + 如果你手头还有第 2 步中保存下来的未归档 WAL 段文件,请把它们复制到pg_xlog/中。(最好复制,而不是移动,这样一旦出问题需要重来时,你手里仍然保留着未修改的原始文件。) + + + + 在集簇数据目录中创建恢复命令文件 recovery.conf(参见 )。也可以临时修改 pg_hba.conf,在确认恢复成功之前阻止普通用户连接。 + + + 启动服务器。服务器会进入恢复模式,依次读取所需的已归档 WAL 文件。如果恢复因外部错误而终止,只需重启服务器,它就会继续恢复。恢复过程完成后,服务器会将 recovery.conf 重命名为 recovery.done(防止以后意外重新进入恢复模式),然后开始正常的数据库操作。 + + + + 检查数据库内容,确认你已经恢复到期望状态。如果不是,就回到第 1 步。如果一切正常,就把pg_hba.conf恢复为正常设置,让用户重新连接。 + + + + + + 整个过程的关键,是设置一个恢复配置文件,说明希望如何恢复,以及恢复到什么位置。可以使用 recovery.conf.sample(通常位于安装目录的 share/ 目录中)作为模板。必须在 recovery.conf 中指定 restore_command,它告诉 PostgreSQL 如何获取已归档的 WAL 段文件。与 archive_command 一样,它是一个 shell 命令字符串,可以包含 %f,该标记会被替换为所需日志文件的名称;还可以包含 %p,该标记会被替换为日志文件要复制到的路径名。(路径名相对于当前工作目录,即集簇的数据目录。)使用 %% 可以在命令中嵌入实际的 % 字符。最简单的可用命令类似于: +restore_command = 'cp /mnt/server/archivedir/%f %p' +这会复制先前归档的 WAL 段,来源目录为 /mnt/server/archivedir。当然,也可以使用复杂得多的命令,甚至可以使用要求操作人员挂载相应磁带的 shell 脚本。 + + + 重要的是,该命令在失败时必须返回非零退出状态。系统调用该命令来请求归档中不存在的文件;遇到这种情况时,它必须返回非零值。这不是一种错误情况。例外是,如果该命令被信号终止(用于数据库服务器关闭的SIGTERM除外),或者因 shell 错误(如命令未找到)而失败,那么恢复将中止,服务器也不会启动。 + + + + 被请求的文件并不全都是 WAL 段文件;你还应预期会收到对带有.history后缀文件的请求。另外请注意,%p路径的文件名部分会与%f不同;不要指望它们可以互换使用。 + + + + 在归档中找不到的 WAL 段会转而在pg_xlog/中查找;这使得可以使用最近尚未归档的段。不过,凡是归档中可用的段,都会优先于pg_xlog/中的文件使用。 + + + + 通常,恢复会处理完所有可用的 WAL 段,从而把数据库恢复到当前时间点(或者在可用 WAL 段所允许的情况下尽可能接近当前时间点)。因此,一次正常恢复会以一条file not found消息结束,具体错误文本取决于你选择的restore_command。在恢复开始时,你也可能看到一条针对类似00000001.history文件的错误消息。这同样是正常的,在简单恢复场景中并不表示有问题;相关讨论见。 + + + 如果希望恢复到过去的某个时间点(例如初级 DBA 删除主要事务表之前),只需在 recovery.conf 中指定所需的停止点。这个停止点称为恢复目标,可以通过日期/时间、命名恢复点,或某个特定事务 ID 的完成来指定。撰写本文时,只有日期/时间和命名恢复点这两种方式比较实用,因为还没有工具能帮助你准确确定应使用哪个事务 ID。 + + + + 停止点必须晚于基础备份的结束时间,也就是pg_stop_backup的结束时间。你不能用某次基础备份恢复到该备份仍在进行中的时间点。(若要恢复到这样的时间点,必须回到更早的一次基础备份,再从那里向前重放日志。) + + + + + 如果恢复过程中发现了损坏的 WAL 数据,恢复会在该点停止,服务器也不会启动。在这种情况下,可以从头重新执行恢复,并指定一个位于损坏点之前的恢复目标,使恢复能够正常完成。如果恢复因外部原因失败,例如系统崩溃或 WAL 归档变得不可访问,那么只需重新启动恢复,它几乎会从上次失败的位置继续。恢复重启的工作方式很像正常运行时的检查点:服务器会周期性地把自身状态强制写盘,然后更新pg_control文件,表明已处理过的 WAL 数据无需再次扫描。 + + + + + + 时间线 + + + 时间线 + + + 能够将数据库恢复到过去某个时间点,也会带来一些类似科幻故事中时间旅行和平行宇宙的复杂情况。例如,假设在数据库原来的历史中,你在周二下午 5:15 删除了一张重要的表,直到周三中午才发现错误。你从容地取出备份,将数据库恢复到周二下午 5:14,然后重新投入运行。在数据库宇宙的这段历史中,你从未删除过那张表。但假设你后来发现这样做不太合适,希望回到原来历史中的周三上午某个时刻。如果数据库恢复运行后覆盖了通往该时刻所需的某些 WAL 段文件,就无法回去了。因此,为了避免这种情况,需要区分时间点恢复后产生的一系列 WAL 记录与数据库原来历史中产生的记录。 + + 为解决这一问题,PostgreSQL 引入了时间线的概念。每当归档恢复完成,系统都会创建一条新时间线,用于标识此次恢复之后产生的一系列 WAL 记录。时间线 ID 是 WAL 段文件名的一部分,因此新时间线不会覆盖旧时间线产生的 WAL 数据。实际上,可以归档许多不同的时间线。这个功能看似没什么用,却常常能救急。比如,你不能确定应该恢复到哪个时间点,需要反复尝试时间点恢复,直到找到脱离旧历史的最佳分支点。没有时间线,这个过程很快就会乱得无法管理。有了时间线,就可以恢复到任何先前的状态,包括早先已放弃的时间线分支中的状态。 + + + 每当创建一条新的时间线时,PostgreSQL都会创建一个时间线历史文件,记录它是从哪条时间线、在何时分叉出来的。当从包含多条时间线的归档中恢复时,这些历史文件对于系统选取正确的 WAL 段文件是必需的。因此,它们会像 WAL 段文件一样被归档到 WAL 归档区域。历史文件只是很小的文本文件,因此长期保存它们既便宜也合适(而段文件通常很大)。如果你愿意,还可以在历史文件中加入注释,记录创建这条时间线的方式和原因。当你因实验而积累出一批错综复杂的时间线时,这类注释会特别有价值。 + + + 默认情况下,恢复会沿着制作基础备份时的当前时间线进行。如果希望恢复到某条子时间线(即返回到一次恢复尝试之后产生的某个状态),需要在 recovery.conf 中指定目标时间线 ID。不能恢复到在基础备份之前就已分支出去的时间线。 + + + + 建议和示例 + + + 这里给出一些配置持续归档的建议。 + + + + 独立热备份 + + + 可以利用PostgreSQL的备份设施生成独立热备份。这些备份不能用于时间点恢复,但它们的制作和恢复通常都比pg_dump转储快得多。(它们也比pg_dump转储大得多,因此在某些情况下速度优势可能会被抵消。) + + + + 和基础备份一样,生成独立热备份最简单的方法是使用工具。如果在调用它时包含-X参数,使用该备份所需的全部事务日志都会自动包含在备份中,恢复该备份时也不需要额外动作。 + + + 如果复制备份文件时需要更大的灵活性,也可以使用更低级的流程制作独立热备份。要准备低级独立热备份,将 wal_level 设为 replica 或更高值,将 archive_mode 设为 on,并配置一个 archive_command,使其仅在开关文件存在时执行归档。例如: +archive_command = 'test ! -f /var/lib/pgsql/backup_in_progress || (test ! -f /var/lib/pgsql/archive/%f && cp %p /var/lib/pgsql/archive/%f)' +/var/lib/pgsql/backup_in_progress 存在时,这条命令会执行归档;否则会静默地返回零退出状态(允许 PostgreSQL 回收不需要的 WAL 文件)。 + + 做好上述准备后,就可以使用类似下面的脚本进行备份: +touch /var/lib/pgsql/backup_in_progress +psql -c "select pg_start_backup('hot_backup');" +tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/ +psql -c "select pg_stop_backup();" +rm /var/lib/pgsql/backup_in_progress +tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/ +首先创建开关文件 /var/lib/pgsql/backup_in_progress,以启用已完成 WAL 文件的归档。备份完成后删除该开关文件。随后将已归档的 WAL 文件加入备份,使基础备份与所有必需的 WAL 文件都包含在同一个 tar 文件中。请记得在备份脚本中加入错误处理。 + + + + + 压缩的归档日志 + + 如果担心归档存储空间,可以使用 gzip 压缩归档文件: +archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f' +恢复时则需要使用 gunzip +restore_command = 'gunzip < /mnt/server/archivedir/%f > %p' + + + + + + <varname>archive_command</varname>脚本 + + + 很多人选择使用脚本来定义自己的archive_command,这样postgresql.conf中的配置项就会显得非常简单: + +archive_command = 'local_backup_script.sh "%p" "%f"' + + 只要你希望在归档过程中使用不止一条命令,就建议使用单独的脚本文件。这样一来,所有复杂性都可以在脚本内部管理,而脚本可以使用诸如bashperl之类的常见脚本语言编写。 + + + + 可以在脚本中处理的需求示例包括: + + + + 将数据复制到安全的异地数据存储 + + + + + 把 WAL 文件成批处理,使其每三个小时传输一次,而不是每次只传输一个 + + + + + 与其他备份和恢复软件对接 + + + + + 与监控软件对接以报告错误 + + + + + + + + 使用archive_command脚本时,最好启用。脚本写到stderr的任何消息都会出现在数据库服务器日志中,这样一来,当复杂配置失败时就更容易诊断。 + + + + + + + 注意事项 + + + 在撰写本文时,持续归档技术还存在若干局限。这些问题很可能会在未来版本中得到修复: + + + + + 哈希索引上的操作目前不会写入 WAL 日志,因此重放不会更新这些索引。这意味着任何新插入的行都会被索引忽略,被更新的行会看起来消失了,而被删除的行仍会保留指针。换句话说,如果你修改了一张带有哈希索引的表,那么在备库服务器上会得到不正确的查询结果。恢复完成后,建议在恢复操作结束后对每个这样的索引手工执行。 + + + + + + 如果在进行基础备份时执行了命令,而该CREATE DATABASE所复制的模板数据库又在基础备份尚未结束时被修改,那么恢复时可能会把这些修改也传播到新建数据库中。这当然并不理想。为避免这种风险,最好在进行基础备份时不要修改任何模板数据库。 + + + + + + 命令会以字面绝对路径写入 WAL,因此重放时会按相同的绝对路径创建表空间。如果 WAL 在另一台机器上重放,这可能并不理想。即使 WAL 在同一台机器上、但重放到新的数据目录中,也可能有危险:重放仍会覆盖原表空间的内容。为避免此类潜在陷阱,最佳做法是在创建或删除表空间后重新执行一次基础备份。 + + + + + + + 还应注意,默认的WAL格式相当臃肿,因为它包含许多磁盘页面快照。这些页面快照是为支持崩溃恢复而设计的,因为我们可能需要修复部分写入的磁盘页。根据你的系统硬件和软件情况,部分写入的风险可能小到可以忽略;在这种情况下,可以通过参数关闭页面快照,从而显著减少已归档 WAL 文件的总量。(在这样做之前,请先阅读中的说明和警告。)关闭页面快照并不妨碍把 WAL 用于 PITR 操作。未来一个可能的开发方向,是在full_page_writes开启的情况下,通过去除不必要的页面副本来压缩归档 WAL 数据。在此之前,管理员可以考虑尽可能增大检查点间隔相关参数,以减少 WAL 中包含的页面快照数量。 + + + + + diff --git a/zh/9.6/bgworker.sgml b/zh/9.6/bgworker.sgml new file mode 100644 index 00000000..abe84937 --- /dev/null +++ b/zh/9.6/bgworker.sgml @@ -0,0 +1,138 @@ + + + + 后台工作进程 + + + 后台工作进程 + + + + PostgreSQL可以扩展为在独立进程中运行用户提供的代码。这类进程由postgres启动、停止并监控,因此它们的生命周期能够与服务器状态紧密关联。这些进程可以选择附着到PostgreSQL的共享内存区域以及在内部连接到数据库;它们还可以像普通的客户端连接服务器进程一样串行运行多个事务。此外,通过链接libpq,它们还可以连接到服务器,并表现得像普通客户端应用程序一样。 + + + + + 使用后台工作进程存在相当大的健壮性和安全风险,因为它们是用C语言编写的,对数据具有不受限制的访问权限。希望启用包含后台工作进程的模块的管理员必须极其谨慎。只应允许经过仔细审计的模块运行后台工作进程。 + + + + + 可以通过在shared_preload_libraries中包含模块名,在PostgreSQL启动时初始化后台工作进程。希望运行后台工作进程的模块可以在其_PG_init()函数中调用RegisterBackgroundWorker(BackgroundWorker *worker)来注册它。系统启动并运行后,也可以通过调用RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle)来启动后台工作进程。与只能在 postmaster 进程内调用的RegisterBackgroundWorker不同,RegisterDynamicBackgroundWorker必须由普通后端调用。 + + + 结构体BackgroundWorker的定义如下: +typedef void (*bgworker_main_type)(Datum main_arg); +typedef struct BackgroundWorker +{ + char bgw_name[BGW_MAXLEN]; + int bgw_flags; + BgWorkerStartTime bgw_start_time; + int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */ + bgworker_main_type bgw_main; + char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ + char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ + Datum bgw_main_arg; + char bgw_extra[BGW_EXTRALEN]; + int bgw_notify_pid; +} BackgroundWorker; + + + + bgw_name 是用于日志消息、进程列表及类似场景的字符串。 + + + bgw_flags是一个通过按位或组合的位掩码,表示模块需要的能力。可能的值包括: + + + BGWORKER_SHMEM_ACCESS + + + BGWORKER_SHMEM_ACCESS + 请求共享内存访问。没有共享内存访问权的工作进程不能访问PostgreSQL的任何共享数据结构,如重量级锁或轻量级锁、共享缓冲区,以及工作进程自身可能希望创建和使用的任何自定义数据结构。 + + + + + + BGWORKER_BACKEND_DATABASE_CONNECTION + + + BGWORKER_BACKEND_DATABASE_CONNECTION + 请求建立数据库连接的能力,以便之后运行事务和查询。使用BGWORKER_BACKEND_DATABASE_CONNECTION连接数据库的后台工作进程,还必须使用BGWORKER_SHMEM_ACCESS附着到共享内存,否则工作进程启动将失败。 + + + + + + + + + + bgw_start_time表示postgres应在服务器的哪个状态下启动该进程;它可以是BgWorkerStart_PostmasterStart(在postgres自身完成初始化后立即启动;请求此值的进程不能建立数据库连接)、BgWorkerStart_ConsistentState(在热备达到一致状态后立即启动,允许进程连接数据库并运行只读查询),或BgWorkerStart_RecoveryFinished(在系统进入正常读写状态后立即启动)。请注意,在非热备服务器中,后两个值是等价的。还要注意,此设置只表示何时启动进程;当到达其他状态时,这些进程不会停止。 + + + + bgw_restart_time是以秒计的时间间隔,表示在进程崩溃时,postgres在重新启动该进程之前应等待多久。它可以是任意正值,也可以是BGW_NEVER_RESTART,表示在进程崩溃时不重新启动该进程。 + + + + bgw_main是一个指针,指向进程启动时要运行的函数。该字段只能安全地用于启动核心服务器内部的函数,因为共享库在不同后端进程中可能被加载到不同的起始地址。当库不是通过机制加载时,所有平台上都会出现这种情况。即使使用该机制,在 Windows 上以及使用EXEC_BACKEND时,地址空间布局仍会有变化。因此,此 API 的大多数用户应将此字段设置为 NULL。如果它不为 NULL,则它的优先级高于bgw_library_namebgw_function_name。 + + + + bgw_library_name是一个库的名称,后台工作进程的初始入口点将在该库中查找。该库会由工作进程动态加载,而bgw_function_name将用于标识要调用的函数。如果加载的是核心代码中的函数,则应改设bgw_main。 + + + + bgw_function_name是动态加载库中的一个函数名,用作新后台工作进程的初始入口点。 + + + + bgw_main_arg是传给后台工作进程主函数的Datum参数。无论该函数是通过bgw_main指定,还是通过bgw_library_namebgw_function_name的组合指定,该主函数都应接受一个Datum类型的参数,并返回voidbgw_main_arg会作为该参数传入。此外,全局变量MyBgworkerEntry指向注册时传入的BackgroundWorker结构体的一份副本;检查该结构体可能会对工作进程有所帮助。 + + + + 在 Windows(以及任何定义了EXEC_BACKEND的地方)上,或者在动态后台工作进程中,以引用方式传递Datum并不安全,只能按值传递。如果需要传递参数,最安全的方式是传递一个 int32 或其他较小的值,并将其用作共享内存中分配的数组的索引。如果传递的是cstringtext这样的值,那么该指针在新的后台工作进程中将无效。 + + + + bgw_extra可以包含要传递给后台工作进程的额外数据。与bgw_main_arg不同,这些数据不会作为参数传递给工作进程的主函数,但可以如上所述通过MyBgworkerEntry访问。 + + + + bgw_notify_pid是一个 PostgreSQL 后端进程的 PID,当后台工作进程启动或退出时,postmaster 应向该后端进程发送SIGUSR1。对于在 postmaster 启动时注册的工作进程,或者注册该工作进程的后端不希望等待其启动时,它应为 0。否则,它应初始化为MyProcPid。 + + + 进程一旦开始运行,就可以通过调用 + BackgroundWorkerInitializeConnection(char *dbname, char *username)或 + BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid)连接到数据库。这使得该进程能够通过SPI接口运行事务和查询。如果dbname为 NULL,或dboidInvalidOid,则该会话不会连接到任何特定数据库,但仍可访问共享系统目录。如果username为 NULL,或useroidInvalidOid,则该进程将以initdb期间创建的超级用户身份运行。后台工作进程只能调用这两个函数中的一个,而且只能调用一次。不能切换数据库。 + + + + 当控制流到达bgw_main函数时,信号起初处于阻塞状态,必须由它解除阻塞;这样做是为了在必要时允许该进程自定义其信号处理器。在新进程中,可以调用BackgroundWorkerUnblockSignals解除信号阻塞,并调用BackgroundWorkerBlockSignals重新阻塞信号。 + + + + 如果后台工作进程的bgw_restart_time配置为BGW_NEVER_RESTART,或者它以退出码 0 退出,或者被TerminateBackgroundWorker终止,则 postmaster 会在其退出时自动注销它。否则,它会在bgw_restart_time配置的时间段之后重新启动;如果 postmaster 因某个后端失败而重新初始化集簇,则会立即重新启动它。只需暂时挂起执行的后端应使用可中断的睡眠而不是退出;这可以通过调用WaitLatch()来实现。调用该函数时,要确保设置了WL_POSTMASTER_DEATH标志,并检查返回码,以便在postgres自身终止的紧急情况下及时退出。 + + + + 当使用RegisterDynamicBackgroundWorker函数注册后台工作进程时,执行注册的后端可以获取有关该工作进程状态的信息。希望这样做的后端应将BackgroundWorkerHandle *的地址作为第二个参数传递给RegisterDynamicBackgroundWorker。如果工作进程成功注册,该指针将初始化为一个不透明句柄,随后可以将其传递给GetBackgroundWorkerPid(BackgroundWorkerHandle *, pid_t *)TerminateBackgroundWorker(BackgroundWorkerHandle *)GetBackgroundWorkerPid可用于轮询工作进程的状态:返回值为BGWH_NOT_YET_STARTED表示该工作进程尚未由 postmaster 启动;BGWH_STOPPED表示它已经启动但不再运行;BGWH_STARTED表示它当前正在运行。在最后一种情况下,PID 也会通过第二个参数返回。TerminateBackgroundWorker会使 postmaster 在该工作进程运行时向其发送SIGTERM,并在其不再运行后尽快将其注销。 + + + + 在某些情况下,注册后台工作进程的进程可能希望等待该工作进程启动。这可以通过将bgw_notify_pid初始化为MyProcPid,然后把注册时获得的BackgroundWorkerHandle *传递给WaitForBackgroundWorkerStartup(BackgroundWorkerHandle + *handle, pid_t *)函数来实现。该函数会阻塞,直到 postmaster 已尝试启动该后台工作进程,或者直到 postmaster 死亡。如果后台工作进程正在运行,返回值将是BGWH_STARTED,其 PID 会被写入所提供的地址。否则,返回值将是BGWH_STOPPEDBGWH_POSTMASTER_DIED。 + + + 如果后台工作进程通过服务器编程接口(SPI)使用 NOTIFY 命令发送异步通知,那么应在提交包含该命令的事务后,显式调用 ProcessCompletedNotifies,以便送达通知。如果后台工作进程通过 SPI 使用 LISTEN 注册接收异步通知,该工作进程会将这些通知写入日志,但无法以编程方式拦截并响应这些通知。 + + + src/test/modules/worker_spi模块包含一个可工作的示例,展示了一些有用的技巧。 + + + + 已注册后台工作进程的最大数量受限制。 + + diff --git a/zh/9.6/biblio.sgml b/zh/9.6/biblio.sgml new file mode 100644 index 00000000..82d4599d --- /dev/null +++ b/zh/9.6/biblio.sgml @@ -0,0 +1,587 @@ + + + + 参考书目 + + + 这里列出了一些关于 SQLPostgreSQL 的参考文献与读物。 + + + + 来自最初的 POSTGRES 开发团队的一些白皮书和技术报告,可在加州大学伯克利分校计算机系的 网站 上获取。 + + + + <acronym>SQL</acronym>参考书 + Reference texts for SQL features. + + + The Practical <acronym>SQL</acronym> Handbook + Bowman et al, 2001 + Using SQL Variants + Fourth Edition + + + Judith + Bowman + + + Sandra + Emerson + + + Marcy + Darnovsky + + + 0-201-70309-2 + 2001 + + Addison-Wesley Professional + + + 2001 + + + + + A Guide to the <acronym>SQL</acronym> Standard + Date and Darwen, 1997 + A user's guide to the standard database language SQL + Fourth Edition + + + C. J. + Date + + + Hugh + Darwen + + + 0-201-96426-0 + 1997 + + Addison-Wesley + + + 1997 + Addison-Wesley Longman, Inc. + + + + + An Introduction to Database Systems + Date, 2004 + Eighth Edition + + + C. J. + Date + + + 0-321-19784-4 + 2003 + + Addison-Wesley + + + 2004 + Pearson Education, Inc. + + + + + Fundamentals of Database Systems + Fourth Edition + + + Ramez + Elmasri + + + Shamkant + Navathe + + + 0-321-12226-7 + 2003 + + Addison-Wesley + + + 2004 + + + + + Understanding the New <acronym>SQL</acronym> + Melton and Simon, 1993 + A complete guide + + + Jim + Melton + + + Alan R. + Simon + + + 1-55860-245-3 + 1993 + + Morgan Kaufmann + + + 1993 + Morgan Kaufmann Publishers, Inc. + + + + + Principles of Database and Knowledge-Base Systems + Classical Database Systems + Ullman, 1988 + + + Jeffrey D. + Ullman + + + Volume 1 + + Computer Science Press + + 1988 + + + + + + PostgreSQL相关文档 + This section is for related documentation. + + + Enhancement of the ANSI SQL Implementation of PostgreSQL + Simkovics, 1998 + + + Stefan + Simkovics + + + + + + + 该文讨论了 SQL 的历史和语法,并描述了将 INTERSECTEXCEPT 构造加入 PostgreSQL 的过程。它是作者在维也纳技术大学、在 O. Univ. Prof. Dr. Georg Gottlob 和 Univ. Ass. Mag. Katrin Seyr 支持下完成的硕士学位论文。 + + + + November 29, 1998 + + Department of Information Systems, Vienna University of Technology +
Vienna, Austria
+
+
+ + + The <productname>Postgres95</productname> User Manual + Yu and Chen, 1995 + + + A. + Yu + + + J. + Chen + + + + + The POSTGRES Group + + + + Sept. 5, 1995 + + University of California +
Berkeley, California
+
+
+ + + + <ulink url="https://dsf.berkeley.edu/papers/UCB-MS-zfong.pdf"> + The design and implementation of the <productname>POSTGRES</productname> query optimizer + </ulink> + + Zelaine + Fong + + + University of California, Berkeley, Computer Science Department + + + +
+ + + 会议论文和期刊文章 + This section is for articles and newsletters. + + + + <ulink url="https://arxiv.org/pdf/1208.4179">Serializable Snapshot Isolation in PostgreSQL</ulink> + + + D. + Ports + + + K. + Grittner + + + + + VLDB Conference + August 2012 +
Istanbul, Turkey
+
+
+ + + + <ulink url="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf">A Critique of ANSI SQL Isolation Levels</ulink> + + + H. + Berenson + + + P. + Bernstein + + + J. + Gray + + + J. + Melton + + + E. + O'Neil + + + P. + O'Neil + + + + + ACM-SIGMOD Conference on Management of Data + 1995 年 6 月 +
San Jose, California
+
+
+ + + Partial indexing in POSTGRES: research project + Olson, 1993 + + + Nels + Olson + + + 1993 + UCB Engin T7.49.1993 O676 + + University of California +
Berkeley, California
+
+
+ + + + A Unified Framework for Version Modeling Using Production Rules in a Database System + Ong and Goh, 1990 + + + L. + Ong + + + J. + Goh + + + + + ERL Technical Memorandum M90/33 + April, 1990 + + University of California +
Berkeley, California
+
+
+
+ + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M87-13.pdf"> + The <productname>POSTGRES</productname> data model + </ulink> + Rowe and Stonebraker, 1987 + + + L. + Rowe + + + M. + Stonebraker + + + + + VLDB Conference + Sept. 1987 +
Brighton, England
+
+
+ + + + Generalized Partial Indexes + <ulink url="https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.40.5740">(cached version) + </ulink> + + Seshardri, 1995 + + + P. + Seshadri + + + A. + Swami + + + + + Eleventh International Conference on Data Engineering + 6-10 March 1995 +
Taipeh, Taiwan
+
+ 1995 + Cat. No.95CH35724 + + IEEE Computer Society Press +
Los Alamitos, California
+
+ 420-7 +
+ + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M85-95.pdf"> + The design of <productname>POSTGRES</productname> + </ulink> + Stonebraker and Rowe, 1986 + + + M. + Stonebraker + + + L. + Rowe + + + + + ACM-SIGMOD Conference on Management of Data + May 1986 +
Washington, DC
+
+
+ + + + The design of the <productname>POSTGRES</productname> rules system + Stonebraker, Hanson, Hong, 1987 + + + M. + Stonebraker + + + E. + Hanson + + + C. H. + Hong + + + + + IEEE Conference on Data Engineering + Feb. 1987 +
Los Angeles, California
+
+
+ + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M87-06.pdf"> + The design of the <productname>POSTGRES</productname> storage system + </ulink> + Stonebraker, 1987 + + + M. + Stonebraker + + + + + VLDB Conference + Sept. 1987 +
Brighton, England
+
+
+ + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M89-82.pdf"> + A commentary on the <productname>POSTGRES</productname> rules system + </ulink> + Stonebraker et al, 1989 + + + M. + Stonebraker + + + M. + Hearst + + + S. + Potamianos + + + + + SIGMOD Record 18(3) + Sept. 1989 + + + + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M89-17.pdf"> + The case for partial indexes + </ulink> + Stonebraker, M, 1989b + + + M. + Stonebraker + + + + + SIGMOD Record 18(4) + 4-11 + Dec. 1989 + + + + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M90-34.pdf"> + The implementation of <productname>POSTGRES</productname> + </ulink> + Stonebraker, Rowe, Hirohama, 1990 + + + M. + Stonebraker + + + L. A. + Rowe + + + M. + Hirohama + + + + + Transactions on Knowledge and Data Engineering 2(1) + + IEEE + + March 1990 + + + + + + <ulink url="https://dsf.berkeley.edu/papers/ERL-M90-36.pdf"> + On Rules, Procedures, Caching and Views in Database Systems + </ulink> + Stonebraker et al, ACM, 1990 + + + M. + Stonebraker + + + A. + Jhingran + + + J. + Goh + + + S. + Potamianos + + + + + ACM-SIGMOD Conference on Management of Data + June 1990 + + + +
+
diff --git a/zh/9.6/bki.sgml b/zh/9.6/bki.sgml new file mode 100644 index 00000000..a4a19c17 --- /dev/null +++ b/zh/9.6/bki.sgml @@ -0,0 +1,221 @@ + + + + <acronym>BKI</acronym> 后端接口 + + 后端接口(BKI)文件是使用一种特殊语言编写的脚本,PostgreSQL 后端在引导模式下运行时能够理解这种语言。引导模式允许从零开始创建并填充系统目录,而普通 SQL 命令要求目录已经存在。因此,BKI 文件可以用于最初创建数据库系统。(它们可能也没有其他用途。) + + initdb 在创建新的数据库集簇时,使用 BKI 文件完成部分工作。initdb 使用的输入文件作为构建和安装 PostgreSQL 的一部分,由名为 genbki.pl 的程序创建。该程序读取源码树 src/include/catalog/ 目录中采用特殊格式的 C 头文件。创建的 BKI 文件名为 postgres.bki,通常安装在安装树的 share 子目录中。 + + 相关信息可以在 initdb 的文档中找到。 + + + <acronym>BKI</acronym> 文件格式 + + + 本节描述 PostgreSQL 后端如何解释 BKI 文件。如果手边有一份 postgres.bki 文件作为示例,这段说明会更容易理解。 + + + BKI 输入由一系列命令组成。根据命令语法的不同,每条命令由若干词元组成。词元通常由空白分隔,但如果不会产生歧义,也可以不分隔。没有专门的命令分隔符;语法上不可能属于前一条命令的下一个词元,就会开始一条新命令。(通常为了清晰,你会把新命令写在新的一行。)词元可以是某些关键字、特殊字符(圆括号、逗号等)、数字,或者双引号字符串。所有内容都区分大小写。 + + + 以 # 开头的行会被忽略。 + + + + + + <acronym>BKI</acronym> 命令 + + + + create tablename tableoid bootstrap shared_relation without_oids rowtype_oid oid (name1 = type1 FORCE NOT NULL | FORCE NULL , name2 = type2 FORCE NOT NULL | FORCE NULL , ...) + + + + 创建一个名为 tablename、OID 为 tableoid 的表,其列定义在括号中给出。 + + + + bootstrap.c 直接支持以下列类型:boolbyteachar(1 字节)、nameint2int4regprocregclassregtypetextoidtidxidcidint2vectoroidvector_int4(数组)、_text(数组)、_oid(数组)、_char(数组)以及 _aclitem(数组)。虽然也可以创建包含其他类型列的表,但这必须等到 pg_type 创建完成并填入合适条目之后才能做到。(实际上,这意味着引导目录中只能使用这些列类型,而非引导目录则可以包含任何内置类型。) + + + + 指定 bootstrap 时,表只会在磁盘上创建;不会在 pg_classpg_attribute 等目录中为它写入任何信息。因此,在通过 insert 命令以较为原始的方式补入这些条目之前,普通 SQL 操作无法访问该表。这个选项用于创建 pg_class 等目录自身。 + + + 如果指定了 shared_relation,该表就会作为共享表创建。除非指定 without_oids,否则该表将具有 OID。该表的行类型 OID(即 pg_type 中对应的 OID)还可以通过 rowtype_oid 子句显式指定;如果不指定,就会自动为其生成一个 OID。(如果指定了 bootstrap,那么 rowtype_oid 子句其实没有用,但仍然可以写上,作为文档说明。) + + + + + + open tablename + + + + + 打开名为 tablename 的表,以便插入数据。当前任何已打开的表都会被关闭。 + + + + + + + close tablename + + + + 关闭当前打开的表。可以给出表名进行交叉校验,但并非必须。 + + + + + + insert OID = oid_value ( value1 value2 ... ) + + + + 向当前打开的表插入一行新数据,使用 value1value2 等作为各列的值,使用 oid_value 作为其 OID。如果 oid_value 为零(0)或省略该子句,并且该表具有 OID,则分配下一个可用 OID。 + + NULL 值可以用特殊关键字 _null_ 表示。包含空格的值必须用双引号括起来。 + + + + + + declare unique + index indexname + indexoid + on tablename + using amname + ( opclass1 + name1 + , ... ) + + + + + 在名为 tablename 的表上,使用 amname 访问方法,创建一个名为 indexname、OID 为 indexoid 的索引。要建立索引的字段分别称为 name1name2 等;使用的操作符类分别为 opclass1opclass2 等。该命令会创建索引文件并写入相应的目录条目,但不会初始化索引内容。 + + + + + + + declare toast + toasttableoid + toastindexoid + on tablename + + + + + 为名为 tablename 的表创建一个 TOAST 表。该 TOAST 表的 OID 设为 toasttableoid,其索引的 OID 设为 toastindexoid。与 declare index 一样,索引填充会被延后。 + + + + + + build indices + + + + 填充之前已声明的索引。 + + + + + + + + + 引导 <acronym>BKI</acronym> 文件的结构 + + + 在 open 命令可以使用之前,它所依赖的那些表必须已经存在,并且其中已经有了将要被打开之表对应的条目。(这些最基本的表包括 pg_classpg_attributepg_procpg_type。)为了让这些表本身也能够被填充,带 bootstrap 选项的 create 命令会隐式打开刚创建的表,以便插入数据。 + + + + 同样,declare indexdeclare toast 命令也必须等到它们所需的系统目录已经创建并填充完毕之后才能使用。 + + + + 因此,postgres.bki 文件的结构必须如下: + + + + 对某个关键表执行 create bootstrap + + + + + 用 insert 插入至少足以描述这些关键表的数据 + + + + + close + + + + + 对其他关键表重复上述步骤。 + + + + + 对某个非关键表执行 create(不带 bootstrap) + + + + + open + + + + + 用 insert 插入所需数据 + + + + + close + + + + + 对其他非关键表重复上述步骤。 + + + + + 定义索引和 TOAST 表。 + + + + + build indices + + + + + + + 无疑还存在其他未文档化的顺序依赖。 + + + + + 示例 + + 以下命令序列会创建表test_table,其 OID 为 420,包含两列colacolb,类型分别为int4text,并向表中插入两行: +create test_table 420 (cola = int4, colb = text) +open test_table +insert OID=421 ( 1 "value1" ) +insert OID=422 ( 2 _null_ ) +close test_table + + + + diff --git a/zh/9.6/bloom.sgml b/zh/9.6/bloom.sgml new file mode 100644 index 00000000..13d2a375 --- /dev/null +++ b/zh/9.6/bloom.sgml @@ -0,0 +1,228 @@ + + + + bloom + + + bloom + + + bloom提供了基于布隆过滤器的索引访问方法。 + + + 布隆过滤器是一种空间效率很高的数据结构,用于测试某个元素是否属于某个集合。对于索引访问方法,它允许通过签名快速排除不匹配的元组,而签名的大小则在创建索引时确定。 + + + + 签名是被索引属性的一种有损表示,因此容易产生误报;也就是说,某个元素实际上并不在集合中,却可能被报告为在集合中。因此,索引搜索结果始终都必须使用堆条目中的实际属性值再次检查。更长的签名可以降低误报概率,从而减少无用的堆访问次数,但当然也会使索引变得更大,因此扫描速度更慢。 + + + + 当一张表具有很多属性,而查询又会测试这些属性的任意组合时,这类索引最有用。传统 B-树索引比布隆索引更快,但为了支持所有可能的查询,可能需要许多 B-树索引,而布隆索引只需一个。不过要注意,布隆索引只支持等值查询,而 B-树索引还可以执行不等和范围搜索。 + + + + 参数 + + + bloom索引在其WITH子句中接受下列参数: + + + + + length + + + 按位计的每个签名(索引项)长度。该值会向上圆整为16的倍数。默认值为80位,最大值为4096。 + + + + + + + col1 — col32 + + + 为每个索引列生成的位数。每个参数的名字都对应它所控制的索引列编号。默认值为2位,最大值为4095。对于实际未使用的索引列,其参数会被忽略。 + + + + + + + + 示例 + + + 下面是一个创建布隆索引的示例: + + + +CREATE INDEX bloomidx ON tbloom USING bloom (i1,i2,i3) + WITH (length=80, col1=2, col2=2, col3=4); + + + + 该索引使用长度为 80 位的签名创建,其中属性 i1 和 i2 各映射为 2 位,属性 i3 映射为 4 位。由于lengthcol1col2都采用默认值,因此这些参数指定也可以省略。 + + + + 下面给出一个更完整的布隆索引定义和使用示例,同时与等效的 B-树索引进行比较。布隆索引明显比 B-树索引更小,而且性能可能更好。 + + + +=# CREATE TABLE tbloom AS + SELECT + (random() * 1000000)::int as i1, + (random() * 1000000)::int as i2, + (random() * 1000000)::int as i3, + (random() * 1000000)::int as i4, + (random() * 1000000)::int as i5, + (random() * 1000000)::int as i6 + FROM + generate_series(1,10000000); +SELECT 10000000 + + + 对这个大表进行顺序扫描需要很长时间: +=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; +EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Seq Scan on tbloom (cost=0.00..2137.14 rows=3 width=24) (actual time=19.059..19.060 rows=0 loops=1) + Filter: ((i2 = 898732) AND (i5 = 123451)) + Rows Removed by Filter: 100000 + Planning time: 0.269 ms +(5 rows) + + + + 即使定义了 B-树索引,结果仍然是顺序扫描: +=# CREATE INDEX btreeidx ON tbloom (i1, i2, i3, i4, i5, i6); +CREATE INDEX +=# SELECT pg_size_pretty(pg_relation_size('btreeidx')); + pg_size_pretty +---------------- + 3992 kB +(1 row) +=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Seq Scan on tbloom (cost=0.00..2137.00 rows=2 width=24) (actual time=15.070..15.070 rows=0 loops=1) + Filter: ((i2 = 898732) AND (i5 = 123451)) + Rows Removed by Filter: 100000 + Planning time: 0.130 ms +(5 rows) + + + + 在表上定义 bloom 索引后,处理此类搜索的效果比 B-树更好: +=# CREATE INDEX bloomidx ON tbloom USING bloom (i1, i2, i3, i4, i5, i6); +CREATE INDEX +=# SELECT pg_size_pretty(pg_relation_size('bloomidx')); + pg_size_pretty +---------------- + 1584 kB +(1 row) +=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on tbloom (cost=1792.00..1799.69 rows=2 width=24) (actual time=0.456..0.456 rows=0 loops=1) + Recheck Cond: ((i2 = 898732) AND (i5 = 123451)) + Rows Removed by Index Recheck: 29 + Index Cond: ((i2 = 898732) AND (i5 = 123451)) + Planning time: 0.105 ms +(8 rows) + + + + 现在,B-树搜索的主要问题在于,当搜索条件没有约束索引的前导列时,B-树的效率很低。对 B-树而言,更好的策略是在每一列上创建单独的索引。这样,规划器会选择类似下面的计划: +=# CREATE INDEX btreeidx1 ON tbloom (i1); +CREATE INDEX +=# CREATE INDEX btreeidx2 ON tbloom (i2); +CREATE INDEX +=# CREATE INDEX btreeidx3 ON tbloom (i3); +CREATE INDEX +=# CREATE INDEX btreeidx4 ON tbloom (i4); +CREATE INDEX +=# CREATE INDEX btreeidx5 ON tbloom (i5); +CREATE INDEX +=# CREATE INDEX btreeidx6 ON tbloom (i6); +CREATE INDEX +=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on tbloom (cost=24.34..32.03 rows=2 width=24) (actual time=0.029..0.029 rows=0 loops=1) + Recheck Cond: ((i5 = 123451) AND (i2 = 898732)) + -> BitmapAnd (cost=24.34..24.34 rows=2 width=0) (actual time=0.028..0.028 rows=0 loops=1) + Index Cond: (i5 = 123451) + -> Bitmap Index Scan on btreeidx2 (cost=0.00..12.04 rows=500 width=0) (never executed) + Index Cond: (i2 = 898732) + Planning time: 0.389 ms +(9 rows) +虽然这个查询比使用任意一个单独索引时都快得多,但代价是索引更大。每个单列 B-树索引占用 2 MB,因此总共需要 12 MB,是 bloom 索引所用空间的八倍。 + + + + 操作符类接口 + + + 用于布隆索引的操作符类只需要一个针对被索引数据类型的哈希函数,以及一个用于搜索的等值操作符。下面这个示例展示了 text 数据类型的操作符类定义: + + + +CREATE OPERATOR CLASS text_ops +DEFAULT FOR TYPE text USING bloom AS + OPERATOR 1 =(text, text), + FUNCTION 1 hashtext(text); + + + + + 限制 + + + + + 模块中只包含了 int4text 的操作符类。 + + + + + + 搜索只支持=操作符。不过将来有可能增加对带有并集和交集操作的数组的支持。 + + + + + + bloom访问方法不支持UNIQUE索引。 + + + + + + bloom访问方法不支持搜索NULL值。 + + + + + + + + 作者 + + + Teodor Sigaev teodor@postgrespro.ru,Postgres Professional,俄罗斯莫斯科 + + + + Alexander Korotkov a.korotkov@postgrespro.ru,Postgres Professional,俄罗斯莫斯科 + + + + Oleg Bartunov obartunov@postgrespro.ru,Postgres Professional,俄罗斯莫斯科 + + + + diff --git a/zh/9.6/brin.sgml b/zh/9.6/brin.sgml new file mode 100644 index 00000000..77edec45 --- /dev/null +++ b/zh/9.6/brin.sgml @@ -0,0 +1,696 @@ + + + +BRIN 索引 + + + 索引 + BRIN + + + + 简介 + + + BRIN 是块范围索引(Block Range Index)的缩写。 + BRIN 旨在处理非常大的表,其中某些列与它们在表中的物理位置之间存在某种天然的相关性。 + 块范围是表中物理上相邻的一组页;索引会为每个块范围存储一些摘要信息。 + 例如,一个存储商店销售订单的表可能有一个日期列,表示每个订单的下单日期,而大多数情况下较早订单的条目也会较早出现在表中; + 一个存储邮政编码列的表,则可能会自然地把同一城市的所有邮政编码分组在一起。 + + + + BRIN 索引可以通过常规位图索引扫描来满足查询;对于每个范围,如果索引中存储的摘要信息与查询条件相一致,就会返回该范围内所有页上的全部元组。 + 查询执行器负责重新检查这些元组,并丢弃不匹配查询条件的元组 — 换句话说,这些索引是有损的。 + 由于 BRIN 索引非常小,与顺序扫描相比,扫描索引只会带来很小的额外开销, + 但可以避免扫描那些已知不包含匹配元组的大块表数据。 + + + + BRIN 索引所存储的具体数据,以及它能够满足的具体查询, + 取决于为该索引各列选择的操作符类。 + 例如,具有线性排序顺序的数据类型可以使用在每个块范围内存储最小值和最大值的操作符类; + 几何类型则可能存储该块范围内所有对象的边界框。 + + + + 块范围的大小在创建索引时由 pages_per_range 存储参数决定。 + 索引项的数量等于该关系的页数除以为 pages_per_range 选择的值。 + 因此,该值越小,索引就会越大(因为需要存储更多索引项), + 但与此同时,存储的摘要数据也会更精确,并且在索引扫描期间可以跳过更多数据块。 + + + + 索引维护 + + + 在创建索引时,会扫描所有现有索引页,并为每个范围创建一个摘要索引元组,末尾那个可能不完整的范围也包括在内。 + 随着新页被数据填满,已经完成摘要的页范围会利用新元组中的数据更新其摘要信息。 + 当创建了一个不属于最后一个已摘要范围的新页时,该范围不会自动获得摘要元组; + 这些元组会一直保持未摘要状态,直到稍后调用摘要操作,创建初始摘要。 + 可以使用brin_summarize_new_values(regclass)函数手动调用这一过程,也可以在VACUUM处理该表时自动调用。 + + + + + + + 内置操作符类 + + + 核心 PostgreSQL 发行版包含 + 中所示的 + BRIN 操作符类。 + + + + minmax 操作符类存储该范围内索引列中出现的最小值和最大值。 + inclusion 操作符类存储一个能够包含该范围内索引列值的值。 + + + + 内置 <acronym>BRIN</acronym> 操作符类 + + + + 名称 + 索引数据类型 + 可索引操作符 + + + + + abstime_minmax_ops + abstime + + < + <= + = + >= + > + + + + int8_minmax_ops + bigint + + < + <= + = + >= + > + + + + bit_minmax_ops + bit + + < + <= + = + >= + > + + + + varbit_minmax_ops + bit varying + + < + <= + = + >= + > + + + + box_inclusion_ops + box + << &< && &> >> ~= @> <@ &<| <<| |>> |&> + + + bytea_minmax_ops + bytea + + < + <= + = + >= + > + + + + bpchar_minmax_ops + character + + < + <= + = + >= + > + + + + char_minmax_ops + "char" + + < + <= + = + >= + > + + + + date_minmax_ops + date + + < + <= + = + >= + > + + + + float8_minmax_ops + double precision + + < + <= + = + >= + > + + + + inet_minmax_ops + inet + + < + <= + = + >= + > + + + + network_inclusion_ops + inet + && >>= <<= = >> << + + + int4_minmax_ops + integer + + < + <= + = + >= + > + + + + interval_minmax_ops + interval + + < + <= + = + >= + > + + + + macaddr_minmax_ops + macaddr + + < + <= + = + >= + > + + + + name_minmax_ops + name + + < + <= + = + >= + > + + + + numeric_minmax_ops + numeric + + < + <= + = + >= + > + + + + pg_lsn_minmax_ops + pg_lsn + + < + <= + = + >= + > + + + + oid_minmax_ops + oid + + < + <= + = + >= + > + + + + range_inclusion_ops + 任意范围类型 + << &< && &> >> @> <@ -|- = < <= = > >= + + + float4_minmax_ops + real + + < + <= + = + >= + > + + + + reltime_minmax_ops + reltime + + < + <= + = + >= + > + + + + int2_minmax_ops + smallint + + < + <= + = + >= + > + + + + text_minmax_ops + text + + < + <= + = + >= + > + + + + tid_minmax_ops + tid + + < + <= + = + >= + > + + + + timestamp_minmax_ops + timestamp without time zone + + < + <= + = + >= + > + + + + timestamptz_minmax_ops + timestamp with time zone + + < + <= + = + >= + > + + + + time_minmax_ops + time without time zone + + < + <= + = + >= + > + + + + timetz_minmax_ops + time with time zone + + < + <= + = + >= + > + + + + uuid_minmax_ops + uuid + + < + <= + = + >= + > + + + + +
+
+ + + 可扩展性 + + + BRIN 接口具有较高层次的抽象,访问方法实现者只需实现被访问数据类型的语义。 + BRIN 层本身负责并发、日志记录以及搜索索引结构。 + + + + 要让一种 BRIN 访问方法工作起来,只需实现少数几个用户定义的方法, + 它们定义索引中存储的摘要值的行为,以及这些摘要值与扫描键之间的交互。 + 简言之,BRIN 把可扩展性与通用性、代码重用以及清晰的接口结合在一起。 + + + BRIN中,操作符类必须提供以下四个方法: + + BrinOpcInfo *opcInfo(Oid type_oid) + + 返回索引列摘要数据的内部信息。返回值必须指向一个由 palloc 分配的BrinOpcInfo,其定义如下: +typedef struct BrinOpcInfo +{ + /* 此操作符类在一个索引列中存储的列数 */ + uint16 oi_nstored; + + /* 供操作符类私有使用的不透明指针 */ + void *oi_opaque; + + /* 所存储列的类型缓存条目 */ + TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER]; +} BrinOpcInfo; + + BrinOpcInfo.oi_opaque可供操作符类例程在索引扫描期间于各支持函数之间传递信息。 + + + + + bool consistent(BrinDesc *bdesc, BrinValues *column, + ScanKey key) + + + 返回该 ScanKey 是否与某个范围给定的索引值一致。 + 要使用的属性编号作为扫描键的一部分传入。 + + + + + + bool addValue(BrinDesc *bdesc, BrinValues *column, + Datum newval, bool isnull) + + + 给定一个索引元组和一个被索引值,修改该元组中指定的属性,使其能够额外表示这个新值。 + 如果对该元组做了任何修改,就返回 true。 + + + + + + bool unionTuples(BrinDesc *bdesc, BrinValues *a, + BrinValues *b) + + + 合并两个索引元组。给定两个索引元组,修改第一个元组中指定的属性,使其能够表示这两个元组。 + 第二个元组不会被修改。 + + + + 核心发行版包含两种操作符类支持:minmax 和 inclusion。针对核心数据类型,发行版会酌情附带使用它们的操作符类定义。用户也可以为其他数据类型定义等价的操作符类,而无需编写任何源代码;只要声明适当的系统目录项即可。注意,对操作符策略语义的某些假设嵌入在支持函数的源码中。 + + + 只要为上文所述的四个主要支持函数编写实现,也可以实现语义完全不同的操作符类。 + 注意,不保证跨主版本的向后兼容性:例如,在后续版本中可能需要附加的支持函数。 + + + + 要为实现全序集的数据类型编写操作符类,可以按 + 所示,将 minmax 支持函数与相应操作符一起使用。 + 所有操作符类成员(函数和操作符)都是必需的。 + + + + Minmax 操作符类的函数和支持编号 + + + + 操作符类成员 + 对象 + + + + + 支持函数 1 + 内部函数brin_minmax_opcinfo() + + + 支持函数 2 + 内部函数brin_minmax_add_value() + + + 支持函数 3 + 内部函数brin_minmax_consistent() + + + 支持函数 4 + 内部函数brin_minmax_union() + + + 操作符策略 1 + 小于操作符 + + + 操作符策略 2 + 小于等于操作符 + + + 操作符策略 3 + 等于操作符 + + + 操作符策略 4 + 大于等于操作符 + + + 操作符策略 5 + 大于操作符 + + + +
+ + + 要为一种值能够被包含在另一种类型中的复杂数据类型编写操作符类, + 可以按 所示, + 将 inclusion 支持函数与相应操作符一起使用。 + 它只需要额外一个函数,而且该函数可以用任何语言编写。 + 还可以定义更多函数以提供附加功能。 + 所有操作符都是可选的。某些操作符依赖其他操作符,表中已列出这些依赖关系。 + + + + Inclusion 操作符类的函数和支持编号 + + + + 操作符类成员 + 对象 + 依赖关系 + + + + + 支持函数 1 + 内部函数brin_inclusion_opcinfo() + + + + 支持函数 2 + 内部函数brin_inclusion_add_value() + + + + 支持函数 3 + 内部函数brin_inclusion_consistent() + + + + 支持函数 4 + 内部函数brin_inclusion_union() + + + + 支持函数 11 + 合并两个元素的函数 + + + + 支持函数 12 + 可选函数,检查两个元素是否可以合并 + + + + 支持函数 13 + 可选函数,检查一个元素是否被包含在另一个中 + + + + 支持函数 14 + 可选函数,用于检查元素是否为空 + + + + 操作符策略 1 + 位于左侧的操作符 + 操作符策略 4 + + + 操作符策略 2 + 不延伸到右侧的操作符 + 操作符策略 5 + + + 操作符策略 3 + 重叠操作符 + + + + 操作符策略 4 + 不延伸到左侧的操作符 + 操作符策略 1 + + + 操作符策略 5 + 位于右侧的操作符 + 操作符策略 2 + + + 操作符策略 6、18 + 相同或等于操作符 + 操作符策略 7 + + + 操作符策略 7、13、16、24、25 + 包含或等于操作符 + + + + 操作符策略 8、14、26、27 + 被包含或等于操作符 + 操作符策略 3 + + + 操作符策略 9 + 不延伸到上方的操作符 + 操作符策略 11 + + + 操作符策略 10 + 位于下方的操作符 + 操作符策略 12 + + + 操作符策略 11 + 位于上方的操作符 + 操作符策略 9 + + + 操作符策略 12 + 不延伸到下方的操作符 + 操作符策略 10 + + + 操作符策略 20 + 小于操作符 + 操作符策略 5 + + + 操作符策略 21 + 小于等于操作符 + 操作符策略 5 + + + 操作符策略 22 + 大于操作符 + 操作符策略 1 + + + 操作符策略 23 + 大于等于操作符 + 操作符策略 1 + + + +
+ + + 支持函数编号 1 到 10 保留给 BRIN 内部函数,因此 SQL 层函数从编号 11 开始。 + 支持函数 11 是构建索引所需的主要函数。 + 它应接受两个与操作符类数据类型相同的参数,并返回它们的并集。 + 如果 inclusion 操作符类在定义时使用了 STORAGE 参数, + 则它可以存储具有不同数据类型的并集值。 + 并集函数的返回值应与 STORAGE 数据类型匹配。 + + + 支持函数编号 12 和 14 用于支持内置数据类型中的特殊情形。编号 12 的函数用于支持无法合并的不同族的网络地址。编号 14 的函数用于支持空范围。编号 13 的函数是可选的,但建议提供,以便在把新值传给并集函数之前对其进行检查。由于 BRIN 框架在并集未改变时可以跳过某些操作,因此使用这个函数可以提高索引性能。 + + + minmax 和 inclusion 操作符类都支持跨数据类型操作符,但这样依赖关系会更加复杂。 + minmax 操作符类要求定义一整套两侧参数都具有相同数据类型的操作符。 + 它还允许通过定义额外的操作符集合来支持附加数据类型。 + 如 所示, + inclusion 操作符类的操作符策略依赖于另一种操作符策略,或者依赖于它们自身对应的同名操作符策略。 + 这要求把依赖操作符定义为:左侧参数是 STORAGE 数据类型, + 右侧参数是其他受支持的数据类型。 + minmax 的示例见 float4_minmax_ops,inclusion 的示例见 + box_inclusion_ops。 + +
+
diff --git a/zh/9.6/btree-gin.sgml b/zh/9.6/btree-gin.sgml new file mode 100644 index 00000000..7e418884 --- /dev/null +++ b/zh/9.6/btree-gin.sgml @@ -0,0 +1,40 @@ + + + + btree_gin + + + btree_gin + + + btree_gin提供了 GIN 操作符类,为以下数据类型实现 B-树等价行为:int2int4int8float4float8timestamp with time zonetimestamp without time zonetime with time zonetime without time zonedateintervaloidmoney"char"varchartextbyteabitvarbitmacaddrinetcidr + + + 一般来说,这些操作符类的性能不会优于等价的标准 B-树索引方法,而且它们缺少标准 B-树代码的一项主要特性:强制唯一性的能力。不过,它们对于 GIN 测试以及作为开发其他 GIN 操作符类的基础很有用。此外,对于同时测试一个可由 GIN 建索引的列和一个可由 B-树建索引的列的查询,创建一个使用这些操作符类之一的多列 GIN 索引,可能比创建两个必须通过位图 AND 运算组合的独立索引更高效。 + + + + 用法示例 + + +CREATE TABLE test (a int4); +-- create index +CREATE INDEX testidx ON test USING GIN (a); +-- query +SELECT * FROM test WHERE a < 10; + + + + + + 作者 + + + Teodor Sigaev(teodor@stack.net)和 + Oleg Bartunov(oleg@sai.msu.su)。参阅 + 。 + + + + + diff --git a/zh/9.6/btree-gist.sgml b/zh/9.6/btree-gist.sgml new file mode 100644 index 00000000..d8134863 --- /dev/null +++ b/zh/9.6/btree-gist.sgml @@ -0,0 +1,86 @@ + + + + btree_gist + + + btree_gist + + + + btree_gist提供了 GiST 索引操作符类,可为以下数据类型实现与 B-树等价的行为: + int2, int4, int8, float4, + float8, numeric, timestamp with time zone, + timestamp without time zone, time with time zone, + time without time zone, date, interval, + oid, money, char, + varchar, text, bytea, bit, + varbit, macaddr, inet, 和 cidr。 + + + + 一般来说,这些操作符类的性能不会优于对应的标准 B-树索引方法,而且它们缺少标准 B-树实现的一项主要特性:强制唯一性的能力。不过,正如下文所述,它们提供了一些 B-树索引所不具备的其他特性。另外,当需要多列 GiST 索引,而其中某些列的数据类型只能用 GiST 建立索引、其他列只是简单数据类型时,这些操作符类就很有用。最后,这些操作符类对于 GiST 测试以及作为开发其他 GiST 操作符类的基础也很有用。 + + + 除典型的 B-树搜索操作符之外,btree_gist还为<>不等于)提供索引支持。这在与下文描述的排他约束结合使用时可能很有用。 + + + 此外,对于那些具有自然距离度量的数据类型,btree_gist定义了距离操作符<->,并为使用该操作符的最近邻搜索提供 GiST 索引支持。为以下类型提供了距离操作符:int2int4int8float4、 + float8timestamp with time zone、 + timestamp without time zone、 + time without time zonedateinterval、 + oidmoney。 + + + + 用法示例 + + + 一个使用btree_gist代替btree的简单示例: + + + +CREATE TABLE test (a int4); +-- create index +CREATE INDEX testidx ON test USING GIST (a); +-- query +SELECT * FROM test WHERE a < 10; +-- nearest-neighbor search: find the ten entries closest to "42" +SELECT *, a <-> 42 AS dist FROM test ORDER BY a <-> 42 LIMIT 10; + + + 使用排他约束来强制执行这样一条规则:动物园中的一个笼子只能容纳一种动物: + + +=> CREATE TABLE zoo ( + cage INTEGER, + animal TEXT, + EXCLUDE USING GIST (cage WITH =, animal WITH <>) +); + +=> INSERT INTO zoo VALUES(123, 'zebra'); +INSERT 0 1 +=> INSERT INTO zoo VALUES(123, 'zebra'); +INSERT 0 1 +=> INSERT INTO zoo VALUES(123, 'lion'); +ERROR: conflicting key value violates exclusion constraint "zoo_cage_animal_excl" +DETAIL: Key (cage, animal)=(123, lion) conflicts with existing key (cage, animal)=(123, zebra). +=> INSERT INTO zoo VALUES(124, 'lion'); +INSERT 0 1 + + + + + + 作者 + + + Teodor Sigaev(teodor@stack.net)、 + Oleg Bartunov(oleg@sai.msu.su)和 + Janko Richter(jankorichter@yahoo.de)。更多信息见 + 。 + + + + + diff --git a/zh/9.6/catalogs.sgml b/zh/9.6/catalogs.sgml new file mode 100644 index 00000000..71bd248a --- /dev/null +++ b/zh/9.6/catalogs.sgml @@ -0,0 +1,9905 @@ + + + + + 系统目录 + + + 系统目录是关系型数据库管理系统存放模式元数据的地方,例如表和列的信息以及内部记账信息。PostgreSQL 的系统目录是普通表。你可以删除并重建这些表、增加列、插入和更新数值,并借此把系统严重搞坏。通常,不应手工修改系统目录,通常都有 SQL 命令可用于完成这些操作。(例如,CREATE DATABASE 会向 pg_database 目录中插入一行 — 并且实际上会在磁盘上创建该数据库。)对于某些特别深奥的操作,确实存在少数例外,但随着时间推移,其中很多也已经可以通过 SQL 命令完成,因此直接操纵系统目录的需求正越来越少。 + + + + 概述 + + + 列出了系统目录。每个目录更详细的文档见后文。 + + + + 大多数系统目录在创建数据库时都会从模板数据库复制过来,因此之后是数据库专有的。少数目录在物理上由一个集簇中的所有数据库共享;这些目录会在各自的说明中注明。 + + + + 系统目录 + + + + + 目录名 + 用途 + + + + + + pg_aggregate + 聚合函数 + + + + pg_am + 索引访问方法 + + + + pg_amop + 访问方法操作符 + + + + pg_amproc + 访问方法支持函数 + + + + pg_attrdef + 列默认值 + + + + pg_attribute + 表列(属性) + + + + pg_authid + 授权标识符(角色) + + + + pg_auth_members + 授权标识符成员关系 + + + + pg_cast + 转换(数据类型转换) + + + + pg_class + 表、索引、序列、视图 (关系 + + + + pg_collation + 排序规则(区域设置信息) + + + + pg_constraint + 检查约束、唯一约束、主键约束、外键约束 + + + + pg_conversion + 编码转换信息 + + + + pg_database + 本数据库集簇中的数据库 + + + + pg_db_role_setting + 每角色和每数据库的设置 + + + + pg_default_acl + 对象类型的默认权限 + + + + pg_depend + 数据库对象间的依赖 + + + + pg_description + 数据库对象上的描述或注释 + + + + pg_enum + 枚举标签和值定义 + + + + pg_event_trigger + 事件触发器 + + + + pg_extension + 已安装扩展 + + + + pg_foreign_data_wrapper + 外部数据包装器定义 + + + + pg_foreign_server + 外部服务器定义 + + + + pg_foreign_table + 外部表信息 + + + + pg_index + 索引信息 + + + + pg_inherits + 表继承层次 + + + + pg_init_privs + 对象初始权限 + + + + pg_language + 编写函数的语言 + + + + pg_largeobject + 大对象的数据页 + + + + pg_largeobject_metadata + 大对象的元数据 + + + + pg_namespace + 模式 + + + + pg_opclass + 访问方法操作符类 + + + + pg_operator + 操作符 + + + + pg_opfamily + 访问方法操作符族 + + + + + + pg_pltemplate + 过程语言的模板数据 + + + + pg_policy + 行安全策略 + + + + pg_proc + 函数和过程 + + + + + + + + pg_range + 范围类型的信息 + + + + pg_replication_origin + 已注册的复制源 + + + + pg_rewrite + 查询重写规则 + + + + pg_seclabel + 数据库对象上的安全标签 + + + + + + pg_shdepend + 共享对象上的依赖 + + + + pg_shdescription + 共享对象上的注释 + + + + pg_shseclabel + 共享数据库对象上的安全标签 + + + + pg_statistic + 规划器统计 + + + + + + + + + + pg_tablespace + 本数据库集簇内的表空间 + + + + pg_transform + 转换(将数据类型转换为过程语言需要的形式) + + + + pg_trigger + 触发器 + + + + pg_ts_config + 文本检索配置 + + + + pg_ts_config_map + 文本检索配置的词元映射 + + + + pg_ts_dict + 文本检索词典 + + + + pg_ts_parser + 文本检索解析器 + + + + pg_ts_template + 文本检索模板 + + + + pg_type + 数据类型 + + + + pg_user_mapping + 将用户映射到外部服务器 + + + +
+
+ + + + <structname>pg_aggregate</structname> + + + pg_aggregate + + + + 目录pg_aggregate存储关于聚合函数的信息。 + 聚合函数是对一组值(典型的是每个匹配查询条件的行中的同一个列的值)进行操作的函数,它返回从这些值中计算出的单个值。 + 典型的聚合函数是 sumcountmax。 + pg_aggregate里的每个项都是一个pg_proc项的扩展。 + pg_proc项记载该聚合的名字、输入和输出数据类型,以及其他一些和普通函数类似的信息。 + + + + <structname>pg_aggregate</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + aggfnoid + regproc + pg_proc.oid + + 聚合函数的pg_proc OID + + + + aggkind + char + + + 聚合种类: + n 表示普通聚合, + o 表示有序集聚合,或 + h 表示假想集聚合 + + + + aggnumdirectargs + int2 + + + 有序集或假想集聚合的直接(非聚合)参数个数,其中一个可变参数数组算作一个参数。 + 如果它等于pronargs,则该聚合必定是可变参数的,并且该可变参数数组既描述聚合参数也描述最终的直接参数。 + 对普通聚合总为零。 + + + + aggtransfn + regproc + pg_proc.oid + + 转移函数 + + + + aggfinalfn + regproc + pg_proc.oid + + 最终函数(如果没有就为零) + + + + aggcombinefn + regproc + pg_proc.oid + + 合并函数(如果没有就为零) + + + + aggserialfn + regproc + pg_proc.oid + + 序列化函数(如果没有就为零) + + + + aggdeserialfn + regproc + pg_proc.oid + + 反序列化函数(如果没有就为零) + + + + aggmtransfn + regproc + pg_proc.oid + + 用于移动聚合模式的前向状态转移函数(如果没有就为零) + + + + aggminvtransfn + regproc + pg_proc.oid + + 用于移动聚合模式的逆向状态转移函数(如果没有就为零) + + + + aggmfinalfn + regproc + pg_proc.oid + + 用于移动聚合模式的最终函数(如果没有就为零) + + + + aggfinalextra + bool + + + 若为真,则会向aggfinalfn传递额外的虚拟参数 + + + + aggmfinalextra + bool + + + 若为真,则会向aggmfinalfn传递额外的虚拟参数 + + + + aggsortop + oid + pg_operator.oid + + 相关联的排序操作符(如果没有则为0) + + + + aggtranstype + oid + pg_type.oid + + 聚合函数的内部转移(状态)数据的数据类型 + + + + aggtransspace + int4 + + + 转移状态数据的近似平均尺寸(字节),或者为零表示使用一个默认估算值 + + + + aggmtranstype + oid + pg_type.oid + + 聚合函数在移动聚合模式下内部转移(状态)数据的数据类型(如果没有则为零) + + + + aggmtransspace + int4 + + + 移动聚合模式的转移状态数据的近似平均尺寸(字节),或者为零表示使用默认估算值 + + + + agginitval + text + + + 转移状态的初始值。这是一个文本字段,包含初始值的外部字符串表示形式。如果此字段为空,则转移状态值从空值开始。 + + + + aggminitval + text + + + 用于移动聚合模式的转移状态初值。这是一个文本域,它包含了以其文本字符串形式表达的初值。 + 如果这个域为空,则转移状态值从空值开始。 + + + + +
+ + + 新的聚合函数可通过命令注册。 + 更多关于编写聚合函数以及转移函数的含义等信息请参见。 + + +
+ + + + <structname>pg_am</structname> + + + pg_am + + + + 目录pg_am存储关于关系访问方法的信息。系统支持的每种访问方法在这个目录中都有一行。目前只有索引拥有访问方法。索引访问方法的需求在中详细讨论。 + + + + <structname>pg_am</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + amname + name + + + 访问方法的名字 + + + + + amhandler + regproc + pg_proc.oid + + 负责提供有关该访问方法信息的处理器函数的 OID + + + + + amtype + char + + 目前总是i,表示索引访问方法;将来可能允许其他值 + + + +
+ + + + 在PostgreSQL 9.6 之前,pg_am包含很多额外的列以表示索引访问方法的性质。那些数据现在只有在 C 代码级别才是直接可见的。不过,系统中增加了pg_index_column_has_property()和一些相关函数来允许 SQL 查询检查索引访问方法的性质,请见。 + + + +
+ + + + <structname>pg_amop</structname> + + + pg_amop + + + + 目录pg_amop存储关于与访问方法操作符族相关的操作符信息。对于操作符族中的每个成员操作符,此目录中都有一行。一个成员可以是搜索操作符,也可以是排序操作符。一个操作符可以出现在多个族中,但在同一个族中既不能出现在多个搜索位置,也不能出现在多个排序位置(虽然不太可能,但允许一个操作符同时用于搜索和排序)。 + + + + <structname>pg_amop</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + amopfamily + oid + pg_opfamily.oid + + 此项所属的操作符族 + + + + + amoplefttype + oid + pg_type.oid + + 操作符的左手输入数据类型 + + + + + amoprighttype + oid + pg_type.oid + + 操作符的右手输入数据类型 + + + + + amopstrategy + int2 + + + 操作符策略号 + + + + + amoppurpose + char + + + 操作符目的,s表示搜索,o表示排序 + + + + + amopopr + oid + pg_operator.oid + + 操作符的OID + + + + + amopmethod + oid + pg_am.oid + + 此操作符族所属的索引访问方法 + + + + + amopsortfamily + oid + pg_opfamily.oid + + 如果是排序操作符,则此项按这个 B-树操作符族排序;如果是搜索操作符,则为零 + + + + + +
+ + + 一个搜索操作符项表示,该操作符族上的索引可以用来查找满足如下条件的所有行: + WHERE + indexed_column + operator + constant。 + 显然,这样的操作符必须返回boolean,并且它的左输入类型必须匹配索引列的数据类型。 + + + + 一个排序操作符项表示,该操作符族上的索引可以被扫描,以按如下顺序返回行: + ORDER BY + indexed_column + operator + constant。 + 这样的操作符可以返回任何可排序的数据类型,不过它的左输入类型同样必须匹配索引列的数据类型。 + ORDER BY 的准确语义由amopsortfamily列指定,它必须引用一个适用于该操作符结果类型的 B-树操作符族。 + + + + + 目前,排序操作符的排序顺序被假定为其所引用操作符族的默认值,即ASC NULLS LAST。将来可能会通过增加额外的列来显式指定排序选项,从而放宽这一假设。 + + + + + 一个项的amopmethod必须和其所属操作符族的opfmethod相匹配(这里包括amopmethod是一个为了性能原因而故意对目录结构做的反规范化)。 + 此外,amoplefttypeamoprighttype也必须匹配被引用的pg_operator项的oprleftoprright域。 + + +
+ + + + <structname>pg_amproc</structname> + + + pg_amproc + + + + 目录pg_amproc存储关于访问方法操作符族相关的支持函数。属于一个操作符族的每一个支持函数在这个目录中都有一行。 + + + + <structname>pg_amproc</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + amprocfamily + oid + pg_opfamily.oid + + 此项所属的操作符族 + + + + + amproclefttype + oid + pg_type.oid + + 相关操作符的左手输入数据类型 + + + + + amprocrighttype + oid + pg_type.oid + + 相关操作符的右手输入数据类型 + + + + + amprocnum + int2 + + + 支持函数编号 + + + + + amproc + regproc + pg_proc.oid + + 函数的OID + + + + + +
+ + + amproclefttypeamprocrighttype列的通常解释是它们标识了一个特定支持函数所支持的操作符的左右输入类型。对于某些访问方法它们和支持函数本身的输入数据类型相匹配,而对其他的则不会匹配。对于一个索引有一个默认支持函数的概念,这些支持函数的amproclefttypeamprocrighttype都等于索引操作符类的opcintype。 + + +
+ + + + <structname>pg_attrdef</structname> + + + pg_attrdef + + + + 目录pg_attrdef存储列默认值。列的主要信息存储在pg_attribute中(见下文)。 + 只有在创建表或添加列时显式指定默认值的列,才会在这里有一项。 + + + + <structname>pg_attrdef</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + adrelid + oid + pg_class.oid + + 该列所属的表 + + + + + adnum + int2 + pg_attribute.attnum + + 列的编号 + + + + + adbin + pg_node_tree + + 列默认值的内部表示 + + + + adsrc + text + + 适合人阅读的默认值表示 + + + +
+ + + adsrc字段是历史遗留,最好不要使用,因为它不会跟踪可能影响默认值表示的外部变化。 + 显示默认值时,更好的方法是反向编译adbin字段(例如使用pg_get_expr)。 + + +
+ + + + <structname>pg_attribute</structname> + + + pg_attribute + + + + 目录pg_attribute存储关于表列的信息。数据库中每个表的每一列,在pg_attribute中都恰好有一行。 + (其中也包括索引的属性项,实际上,凡是在pg_class中有项的对象,都有对应的属性项。) + + + + 术语属性等同于列,这里使用它只是出于历史原因。 + + + + <structname>pg_attribute</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + attrelid + oid + pg_class.oid + + 此列所属的表 + + + + + attname + name + + + 列名称 + + + + + atttypid + oid + pg_type.oid + + 此列的数据类型 + + + + + attstattarget + int4 + + + attstattarget 控制 + 为该列收集统计信息时的细节级别。 + 零值表示不应收集统计信息。 + 负值表示使用系统默认统计目标。 + 正值的确切含义依赖于数据类型。 + 对于标量数据类型,attstattarget 既是要收集的高频值目标个数,也是要创建的直方图桶目标个数。 + + + + + attlen + int2 + + + 该列数据类型的pg_type.typlen副本 + + + + + attnum + int2 + + + 列的编号。一般列从1开始向上编号。系统列,如oid,则拥有(任意)负值编号。 + + + + + attndims + int4 + + + 如果该列是数组类型,则为维数;否则为 0。 + (目前并不会强制检查数组维数,因此任何非零值实际上都只意味着这是一个数组。) + + + + + attcacheoff + int4 + + 在存储中总是 -1,但加载到内存中的行描述符后,此值可能被更新,用于缓存该属性在行内的偏移量 + + + + atttypmod + int4 + + + atttypmod记录了在表创建时提供的类型相关数据(例如一个varchar列的最大长度)。 + 它会被传递给类型相关的输入函数和长度强制转换函数。对于那些不需要atttypmod的类型,这个值通常为 -1。 + + + + + attbyval + bool + + + 该列类型的pg_type.typbyval的一个拷贝 + + + + + attstorage + char + + + 通常是该列类型的pg_type.typstorage的一个拷贝。 + 对于可TOAST的数据类型,这可以在列创建后被修改以控制存储策略。 + + + + + attalign + char + + + 该列类型的 pg_type.typalign 的一个拷贝 + + + + + attnotnull + bool + + + 这代表一个非空约束 + + + + + atthasdef + bool + + 该列有一个默认值,在此情况下pg_attrdef目录中会有一个对应项来真正定义该值。 + + + + + + attisdropped + bool + + + 该列被删除且不再有效。一个删除的列仍然物理存在于表中,但是会被解析器忽略并因此无法通过SQL访问。 + + + + + attislocal + bool + + + 该列是由关系本地定义的。注意一个列可以同时是本地定义和继承的。 + + + + + attinhcount + int4 + + + 该列直接祖先的数量。祖先数量非零的列不能被删除,也不能被重命名。 + + + + + attcollation + oid + pg_collation.oid + + 该列定义的排序规则;如果该列的数据类型不支持排序规则,则为零。 + + + + + attacl + aclitem[] + + + 列级访问权限,如果此列上已有特别授予的权限 + + + + + attoptions + text[] + + + 属性级选项,以keyword=value形式的字符串 + + + + + attfdwoptions + text[] + + + 属性级的外部数据包装器选项,以keyword=value形式的字符串 + + + + + +
+ + + 在被删除列的 pg_attribute 条目中,atttypid 被重置为零,但 attlen 以及其他从 pg_type 复制的字段仍然有效。这种安排用于应对被删除列的数据类型后来也被删除、因而不再有相应 pg_type 行的情况。attlen 和其他字段可用于解释表中一行的内容。 + +
+ + + + <structname>pg_authid</structname> + + + pg_authid + + + + 目录pg_authid包含关于数据库授权标识符(角色)的信息。角色涵盖了用户这两个概念。用户本质上只是设置了rolcanlogin标志的角色。任何角色(无论是否设置了rolcanlogin)都可以拥有其他角色作为成员;参见pg_auth_members。 + + + + 由于这个目录包含密码,它不应公开可读。pg_roles是建立在pg_authid之上的公开可读视图,它会隐藏密码字段。 + + + + 包含关于用户和权限管理的详细信息。 + + + + 由于用户标识符是集簇范围的,pg_authid在一个集簇的所有数据库之间共享:在一个集簇中只有一份pg_authid拷贝,而不是每个数据库一份。 + + + + <structname>pg_authid</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + + + oid + oid + 行标识符(隐藏属性,必须显式选择) + + + + rolname + name + + 角色名 + + + + + rolsuper + bool + + 角色有超级用户权限 + + + + + rolinherit + bool + + 该角色是否会自动继承其所属其他角色的权限 + + + + + rolcreaterole + bool + + 角色能创建更多角色 + + + + + rolcreatedb + bool + + 角色能创建数据库 + + + + + rolcanlogin + bool + + 角色是否能登录。即该角色是否能够作为初始会话授权标识符 + + + + + rolreplication + bool + + 角色是一个复制角色。复制角色可以启动复制连接并且创建和删除复制槽。 + + + + + rolbypassrls + bool + + 角色是否可以绕过所有的行级安全性策略,详见。 + + + + + rolconnlimit + int4 + + 对于可以登录的角色,本列设置该角色能够建立的最大并发连接数。-1 表示无限制。 + + + + + rolpassword + text + 密码(可能已加密);如果未设置则为空值。如果密码已加密,这一列将以字符串md5开头,后面跟一个 32 字符的十六进制 MD5 哈希值。该 MD5 哈希是对用户密码与其用户名拼接后计算得到的。例如,如果用户joe的密码是xyzzyPostgreSQL将存储xyzzyjoe的 md5 哈希。不符合该格式的密码被假定为未加密。 + + + + rolvaliduntil + timestamptz + + 密码过期时间(仅用于密码认证);不设过期时间时为空值 + + + + +
+ + +
+ + + + <structname>pg_auth_members</structname> + + + pg_auth_members + + + + 目录pg_auth_members展示了角色之间的成员关系。允许任何无环的关系集合。 + + + + 由于用户标识符是集簇范围的,pg_auth_members在一个集簇的所有数据库之间共享:在一个集簇中只有一份pg_auth_members拷贝,而不是每个数据库一份。 + + + + <structname>pg_auth_members</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + roleid + oid + pg_authid.oid + + 拥有成员的角色的ID + + + + + member + oid + pg_authid.oid + + roleid的成员角色的ID + + + + + grantor + oid + pg_authid.oid + + 授权此成员关系的角色的ID + + + + + admin_option + bool + + + 如果member能够将roleid的成员资格授予其他角色,则为真 + + + + +
+ +
+ + + + <structname>pg_cast</structname> + + + pg_cast + + + + 目录pg_cast存储数据类型转换路径,包括内置的和用户定义的类型。 + + + + 需要注意的是,pg_cast并不表示系统知道如何执行的所有类型转换,它只包括那些不能从某些通用规则推导出的转换。例如,一个域及其基础类型之间的转换并未显式地在pg_cast中展示。另一个重要的例外是自动的基于 I/O 的类型转换,它们通过数据类型自己的 I/O 函数来转换成(或者转换自)text或其他字符串类型,这些转换也没有显式地在pg_cast中表示。 + + + + <structname>pg_cast</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + castsource + oid + pg_type.oid + + 源数据类型的OID + + + + + casttarget + oid + pg_type.oid + + 目标数据类型的OID + + + + + castfunc + oid + pg_proc.oid + + 执行该转换的函数的OID。如果该转换方法不需要一个函数则存储0。 + + + + + castcontext + char + + + 指示该转换能被调用的环境。 + e表示仅能作为一个显式转换(使用CAST或::语法)。 + a表示在赋值给目标列时隐式调用, 和显式调用一样。 + i表示在表达式中隐式调用,和其他转换一样。 + + + + castmethod + char + + + 指示转换如何被执行。 + f表明使用castfunc中指定的函数。 + i表明使用输入/输出函数。 + b表明该类型是二进制可强制转换的,因此不需要转换。 + + + + +
+ + + 在pg_cast里列出的类型转换函数必须总是以转换的源类型作为它的第一个参数类型, 并且返回转换的目标类型作为它的结果类型。一个类型转换函数最多有三个参数。 如果出现了第二个参数,必须是integer类型;它接受与目标类型关联的修饰词, 如果没有,就是 -1。如果出现了第三个参数,那么必须是boolean类型; 如果该类型转换是一种明确的转换,那么它接受true,否则接受false。 + + + + 在pg_cast里创建一条源类型和目标类型相同的记录是合理的, 只要相关联的函数接受多过一个参数。这样的记录代表长度转换函数, 它们把该类型的值转换为对特定的类型合法的值。 + + + + 如果一个pg_cast的项有着不同的原类型和目标类型, 并且有一个接收多于一个参数的函数,那么它会在一个步骤中完成从一种类型到另外一种类型的转换并应用一个长度转换。如果没有这样的项,使用一个类型修改器的转换涉及两个步骤, 一个是在数据类型之间转换,另外一个是应用修改器。 + +
+ + + <structname>pg_class</structname> + + + pg_class + + + + 目录pg_class记录表,以及几乎所有其他具有列或在其他方面与表类似的对象。这包括索引(但请参见pg_index)、序列、视图、物化视图、复合类型和TOAST表;请参见relkind。 + 在下面,当我们指的是所有这些类型的对象时,我们称之为关系(relations)。并非所有列对所有关系类型都有意义。 + + + + <structname>pg_class</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + relname + name + + + 表、索引、视图等的名字 + + + + + relnamespace + oid + pg_namespace.oid + + 包含该关系的名字空间的OID + + + + + reltype + oid + pg_type.oid + 与此表的行类型对应的数据类型的 OID(如有);索引没有pg_type项,此值为零 + + + + reloftype + oid + pg_type.oid + + 对于类型化表,为底层复合类型的 OID;对于其他所有关系则为零 + + + + + relowner + oid + pg_authid.oid + + 关系的拥有者 + + + + + relam + oid + pg_am.oid + 如果这是索引,则为使用的访问方法(B-树、hash 等) + + + + relfilenode + oid + + + 该关系的磁盘文件名;零表示这是一个映射关系,其磁盘文件名取决于底层状态 + + + + + reltablespace + oid + pg_tablespace.oid + + 存储此关系的表空间。 + 如果为零,则暗示使用数据库的默认表空间。 + (如果关系没有磁盘文件,则没有意义。) + + + + + relpages + int4 + + + 该表磁盘表示的尺寸,以页面计(页面尺寸为BLCKSZ)。这只是一个由规划器使用的估计值。 + 它被VACUUMANALYZE以及一些DDL命令(如CREATE INDEX)所更新。 + + + + + reltuples + float4 + + + 表中的行数。这只是一个由规划器使用的估计值。 + 它被VACUUMANALYZE以及一些DDL命令(如CREATE INDEX)所更新。 + + + + + relallvisible + int4 + + + 在表的可见性映射表中被标记为全可见的页数。这只是一个由规划器使用的估计值。 + 它被VACUUMANALYZE以及一些DDL命令,如CREATE INDEX所更新。 + + + + + reltoastrelid + oid + pg_class.oid + + 与该表相关联的TOAST表的OID,如果没有则为零。TOAST表将大属性线外存储在一个二级表中。 + + + + + relhasindex + bool + + + 如果这是一个表并且其上建有(或最近建有)索引则为真 + + + + + relisshared + bool + + + 如果该表在集簇中的所有数据库间共享则为真。只有某些系统目录(如pg_database)是共享的。 + + + + + relpersistence + char + + + p = 永久表,u = 不记录 WAL 的表, + t = 临时表 + + + + + relkind + char + + + r = 普通表,i = 索引, + S = 序列,v = 视图, + m = 物化视图, + c = 复合类型,t = TOAST 表, + f = 外部表 + + + + + relnatts + int2 + + + 关系中用户列的数量(不计系统列)。在pg_attribute中必须有这么多对应项。 + 另请参阅pg_attribute.attnum。 + + + + + relchecks + int2 + + + 表上CHECK约束的数目,参见pg_constraint目录 + + + + + relhasoids + bool + + 如果为此关系的每一行生成 OID,则为真 + + + + relhaspkey + bool + + 如果表有(或曾经有过)主键,则为真 + + + + relhasrules + bool + + + 如果表有(或曾有)规则则为真,参见pg_rewrite目录 + + + + + relhastriggers + bool + + + 如果表有(或曾有)触发器则为真,参见 + pg_trigger目录 + + + + + relhassubclass + bool + + + 如果表有(或曾经有过)任何继承子类,则为真 + + + + + relrowsecurity + bool + + + 如果表上启用了行级安全性则为真,参见 + pg_policy目录 + + + + + relforcerowsecurity + bool + + + 如果行级安全性(启用时)也适用于表拥有者则为真,参见 + pg_policy目录 + + + + + relispopulated + bool + + + 如果关系已被填充则为真(对于所有关系该列都为真,但对于某些物化视图却不是) + + + + + relreplident + char + + + 用于形成行复制标识的列: + d = 默认(主键,如果存在), + n = 无, + f = 所有列, + i = 设置了indisreplident的索引(如果该索引已被删除,则效果等同于无) + + + + + + + relfrozenxid + xid + + + 在此之前的所有事务ID在表中已经被替换为一个永久的(冻结的)事务ID。 + 这用于跟踪表是否需要被清理,以便阻止事务ID回卷或者允许pg_clog被收缩。 + 如果该关系不是一个表则为0(InvalidTransactionId)。 + + + + + relminmxid + xid + + + 在此之前的所有多事务ID在表中已经被替换为一个事务ID。这被用于跟踪表是否需要被清理,以阻止 + 多事务ID回卷或者允许pg_multixact被收缩。如果关系不是一个表则 + 为0(InvalidMultiXactId)。 + + + + + relacl + aclitem[] + + 访问权限,详见 + + + + reloptions + text[] + + + 访问方法相关的选项,以keyword=value字符串形式 + + + + + + +
+ + + pg_class中的一些布尔标志采用延迟维护:当条件成立时,保证它们为真;但当条件不再成立时,可能不会立即将它们重置为假。 + 例如,relhasindexCREATE INDEX设置,但它从不会被DROP INDEX清除。 + 作为替代,VACUUM会在找到无索引表后清除其relhasindex。 + 这种安排避免了竞争条件并且提高了并发性。 + +
+ + + <structname>pg_collation</structname> + + + pg_collation + + + + 目录pg_collation描述可用的排序规则,其本质上是从 SQL 名字到操作系统区域设置类别的映射。更多信息参见。 + + + + <structname>pg_collation</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + collname + name + + + 排序规则名字(在每一个名字空间和编码中唯一) + + + + + collnamespace + oid + pg_namespace.oid + + 包含该排序规则的名字空间的OID + + + + + collowner + oid + pg_authid.oid + + 排序规则的拥有者 + + + + + + + collencoding + int4 + + + 该排序规则可应用的编码,或以-1表示它可用于任何编码 + + + + + collcollate + name + + + 此排序规则对象的 LC_COLLATE + + + + + collctype + name + + + 此排序规则对象的 LC_CTYPE + + + + + + +
+ + + 注意在这个目录中的唯一键是(collname、 + collencodingcollnamespace), 不仅仅是(collnamecollnamespace)。 + PostgreSQL 通常会忽略所有 collencoding 既不等于当前数据库编码、也不等于 -1 的排序规则,并且禁止创建与 collencoding = -1 的项同名的新项。因此,使用限定的 SQL 名字(schema.name)来标识一个排序规则已经足够,即使按目录定义它并不唯一。之所以将该目录定义成这样,是因为 initdb 在集簇初始化时会用系统上所有可用的区域设置填充它,因此它必须能够容纳集簇中未来可能使用到的所有编码的项。 + + + + 在template0数据库中,创建与数据库编码不匹配的排序规则可能很有用,因为它们可以匹配之后从template0克隆出的数据库的编码。目前这必须手工完成。 + +
+ + + <structname>pg_constraint</structname> + + + pg_constraint + + + + 目录pg_constraint存储表上的检查约束、主键约束、唯一约束、外键约束和排他约束。 + (列约束不会被特殊对待。每一个列约束都等价于某种表约束。) + 非空约束在pg_attribute目录中表示,而不是在这里。 + + + + 用户定义的约束触发器(使用CREATE CONSTRAINT TRIGGER创建)也会在这个表中产生一项。 + + + + 域上的检查约束也存储在这里。 + + + + <structname>pg_constraint</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + conname + name + + + 约束名字(不需要唯一!) + + + + + connamespace + oid + pg_namespace.oid + + 包含此约束的名字空间的OID + + + + + contype + char + + + c = 检查约束, + f = 外键约束, + p = 主键约束, + u = 唯一约束, + t = 约束触发器, + x = 排他约束 + + + + + condeferrable + bool + + + 该约束是否能被延迟? + + + + + condeferred + bool + + + 该约束是否默认被延迟? + + + + + convalidated + bool + + + 该约束是否已经验证?目前只有外键和 CHECK 约束的此值可以为假 + + + + + conrelid + oid + pg_class.oid + + 该约束所在的表,如果不是表约束则为零 + + + + + contypid + oid + pg_type.oid + + 该约束所在的域,如果不是域约束则为0 + + + + + conindid + oid + pg_class.oid + + 如果该约束是唯一、主键、外键或排他约束,此列表示支持此约束的索引,否则为零 + + + + + confrelid + oid + pg_class.oid + + 如果此约束是一个外键约束,此列为被引用的表,否则为零 + + + + + confupdtype + char + + + 外键更新动作代码: + a = 无动作, + r = 限制, + c = 级联, + n = 置空, + d = 置为默认值 + + + + + confdeltype + char + + + 外键删除动作代码: + a = 无动作, + r = 限制, + c = 级联, + n = 置空, + d = 置为默认值 + + + + + confmatchtype + char + + + 外键匹配类型: + f = 完全, + p = 部分, + s = 简单 + + + + + conislocal + bool + + + 此约束是定义在关系本地。注意一个约束可以同时是本地定义和继承。 + + + + + coninhcount + int4 + + + 该约束直接继承自多少个祖先。祖先数量非零的约束不能被删除,也不能被重命名。 + + + + + connoinherit + bool + + + 为真表示此约束被定义在关系本地。它是一个不可继承约束。 + + + + + conkey + int2[] + pg_attribute.attnum + + 如果是一个表约束(包括外键但不包括约束触发器),此列是被约束列的列表 + + + + + confkey + int2[] + pg_attribute.attnum + + 如果是一个外键,此列是被引用列的列表 + + + + + conpfeqop + oid[] + pg_operator.oid + + 如果是一个外键,此列是用于PK = FK比较的等值操作符的列表 + + + + + conppeqop + oid[] + pg_operator.oid + + 如果是一个外键,此列是用于PK = PK比较的等值操作符的列表 + + + + + conffeqop + oid[] + pg_operator.oid + + 如果是一个外键,此列是用于FK = FK比较的等值操作符的列表 + + + + + conexclop + oid[] + pg_operator.oid + 如果是排他约束,则列出每列的排他操作符 + + + + conbin + pg_node_tree + + + 如果是一个检查约束,此列是表达式的一个内部表示 + + + + + consrc + text + + 如果是检查约束,则为适合人阅读的表达式表示 + + + +
+ + + 对于排他约束,conkey仅对作为简单列引用的约束元素有用。 + 对于其他情况,conkey中会出现一个 0,必须查阅关联索引来确定被约束的表达式。 + (因此,conkey与该索引的 pg_index.indkey具有相同的内容。) + + + + + 被引用对象发生变化时,consrc不会更新;例如,它不会跟踪列重命名。 + 最好使用pg_get_constraintdef()提取检查约束的定义,而不是依赖这个字段。 + + + + + + pg_class.relchecks需要和每个关系在此目录中的检查约束数量保持一致。 + + +
+ + + + <structname>pg_conversion</structname> + + + pg_conversion + + + + 目录pg_conversion描述编码转换函数。更多信息参见。 + + + + <structname>pg_conversion</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + conname + name + + + 转换的名字(在一个名字空间内唯一) + + + + + connamespace + oid + pg_namespace.oid + + 包含此转换的名字空间的OID + + + + + conowner + oid + pg_authid.oid + + 转换的拥有者 + + + + + conforencoding + int4 + + + 源编码ID + + + + + contoencoding + int4 + + + 目标编码ID + + + + + conproc + regproc + pg_proc.oid + + 转换函数 + + + + + condefault + bool + + + 如果这是默认转换则为真 + + + + + +
+ +
+ + + <structname>pg_database</structname> + + + pg_database + + + + 目录pg_database存储有关可用数据库的信息。 + 数据库通过命令创建。 + 更多关于其参数的信息请查阅。 + + + + 和大部分系统目录不同,pg_database是在集簇的所有数据库之间共享的:在一个集簇中只有一份pg_database拷贝,而不是每个数据库一份。 + + + + <structname>pg_database</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + datname + name + + + 数据库名称 + + + + + datdba + oid + pg_authid.oid + + 数据库的拥有者,通常是创建它的用户 + + + + + encoding + int4 + + + 数据库的字符编码 + (pg_encoding_to_char()可以将这个数字翻译为编码名称) + + + + + datcollate + name + + + 此数据库的LC_COLLATE + + + + + datctype + name + + + 此数据库的LC_CTYPE + + + + + datistemplate + bool + + + 如果为真,则此数据库可被任何具有CREATEDB权限的用户克隆; + 如果为假,则只有超级用户或者该数据库的属主能够克隆它。 + + + + + datallowconn + bool + + + 如果为假则没有人能连接到这个数据库。这可以用来保护template0数据库不被修改。 + + + + + datconnlimit + int4 + + + 设置可以连接到此数据库的最大并发连接数。-1表示没有限制。 + + + + + datlastsysoid + oid + + + 数据库中最后一个系统 OID; + 对 pg_dump 尤其有用 + + + + + datfrozenxid + xid + + + 在此事务 ID 之前的所有事务 ID,都已经在该数据库中被替换为永久的(冻结的)事务 ID。 + 它用于跟踪数据库是否需要清理,以防止事务 ID 回卷,或者允许pg_clog收缩。 + 它等于该数据库中所有表的pg_class.relfrozenxid值的最小值。 + + + + + datminmxid + xid + + + 在此多事务 ID 之前的所有多事务 ID,都已经在该数据库中被替换为事务 ID。 + 它用于跟踪数据库是否需要清理,以防止多事务 ID 回卷,或者允许pg_multixact收缩。 + 它等于该数据库中所有表的pg_class.relminmxid值的最小值。 + + + + + dattablespace + oid + pg_tablespace.oid + + 此数据库的默认表空间。 + 在此数据库中,所有pg_class.reltablespace为0的表都将被存储在这个表空间中;尤其是非共享系统目录都会在其中。 + + + + + datacl + aclitem[] + + 访问权限,详见 + + + +
+
+ + + + <structname>pg_db_role_setting</structname> + + + pg_db_role_setting + + + + 目录 pg_db_role_setting为每个角色与数据库组合记录运行时配置变量的默认值。 + + + + 和大部分系统目录不同,pg_db_role_setting是在集簇的所有数据库之间共享的:在一个集簇中只有一份pg_db_role_setting拷贝,而不是每个数据库一份。 + + + + <structname>pg_db_role_setting</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + setdatabase + oid + pg_database.oid + + 此设置适用的数据库 OID;如果不与特定数据库相关,则为零 + + + + + setrole + oid + pg_authid.oid + + 此设置适用的角色 OID;如果不与特定角色相关,则为零 + + + + + setconfig + text[] + + + 运行时配置变量的默认值 + + + + +
+
+ + + + <structname>pg_default_acl</structname> + + + pg_default_acl + + + + 目录pg_default_acl存储要被分配给新创建对象的初始权限。 + + + + <structname>pg_default_acl</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + defaclrole + oid + pg_authid.oid + + 与此项相关的角色的OID + + + + + defaclnamespace + oid + pg_namespace.oid + + 与此项相关的名字空间的OID,如果没有则为零 + + + + + defaclobjtype + char + + + 本条目的对象类型: + r = 关系(表、视图), + S = 序列, + f = 函数, + T = 类型 + + + + + defaclacl + aclitem[] + + + 此类对象在创建时应具有的访问权限 + + + + +
+ + + 一个pg_default_acl项表示,要赋给属于指定用户的对象的初始权限。 + 目前有两类项:defaclnamespace = 0 的全局项,以及引用特定模式的按模式项。 + 如果存在全局项,则它会覆盖该对象类型通常写死的默认权限。 + 如果存在按模式项,则表示要将这些权限附加到全局默认权限或写死的默认权限之上。 + + + + 注意,当另一个目录中的 ACL 项为 NULL 时,它表示该对象采用写死的默认权限, + 而不是 当前 pg_default_acl 中可能存在的内容。 + pg_default_acl 只会在对象创建期间被查阅。 + + +
+ + + + <structname>pg_depend</structname> + + + pg_depend + + + + 目录pg_depend记录数据库对象之间的依赖关系。这些信息允许DROP命令查找必须被DROP CASCADE删除的其他对象,或者在DROP RESTRICT情况下阻止删除。 + + + + 另请参阅pg_shdepend,它对在一个数据库集簇中共享的对象之间的依赖提供了相似的功能。 + + + + <structname>pg_depend</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + classid + oid + pg_class.oid + + 依赖对象所在系统目录的OID + + + + + objid + oid + 任意 OID 列 + + 特定依赖对象的OID + + + + + objsubid + int4 + + + 对于一个表列,这里是列号(objidclassid指表本身)。对于所有其他对象类型,此列为0。 + + + + + refclassid + oid + pg_class.oid + + 被引用对象所在的系统目录的OID + + + + + refobjid + oid + 任意 OID 列 + + 指定被引用对象的OID + + + + + refobjsubid + int4 + + + 对于一个表列,这里是列号(refobjidrefclassid指表本身)。对于所有其他对象类型,此列为0。 + + + + + deptype + char + + + 定义此依赖关系语义的一个代码,见文本 + + + + + +
+ + 在所有情况下,一条pg_depend记录都表示:如果不同时删除依赖对象,就不能删除被引用对象。不过,还存在若干由deptype标识的子类型: + + DEPENDENCY_NORMALn + + + 两个独立创建的对象之间的正常关系。依赖对象可以被删除而不影响被引用对象。 + 被引用对象只能通过指定CASCADE来删除,这样依赖对象也会被删除。 + 例如:表列对其数据类型有一个正常依赖关系。 + + + + + + DEPENDENCY_AUTO (a) + + + 依赖对象可以与被引用对象分开删除,并且在被引用对象删除时应自动删除(无论使用RESTRICT还是CASCADE模式)。 + 例如,表上的命名约束会自动依赖于该表,因此删除表时该约束也会消失。 + + + + + + DEPENDENCY_INTERNALi + + + 依赖对象作为被引用对象创建过程的一部分而创建,实际上只是其内部实现的一部分。 + 对依赖对象执行DROP会被直接禁止(我们会告诉用户改为对被引用对象执行DROP)。 + 对被引用对象执行DROP会传播到依赖对象,使其被删除,无论是否指定CASCADE。 + 例如,为实施外键约束而创建的触发器会在内部依赖于该约束的pg_constraint项。 + + + + + + DEPENDENCY_EXTENSIONe + + + 依赖对象是作为被引用对象的扩展的一个成员(参见pg_extension)。 + 只能通过对被引用对象执行DROP EXTENSION来删除依赖对象。 + 在功能上,这种依赖类型与内部依赖相同,但为了清晰和简化pg_dump而单独区分。 + + + + + + DEPENDENCY_AUTO_EXTENSIONx + + + 依赖对象不是作为被引用对象的扩展的成员(因此 pg_dump 不应忽略它),但它无法在没有该扩展的情况下运行, + 所以删除扩展时也应删除它。依赖对象也可以单独删除。 + + + + + + DEPENDENCY_PIN (p) + + + 不存在依赖对象;这种项表示系统本身依赖于被引用对象,因此该对象绝不能删除。 + 这种项只由initdb创建。用于表示依赖对象的各列包含零。 + + + + 未来可能需要其他依赖关系的变种。 + +
+ + + + <structname>pg_description</structname> + + + pg_description + + + + 目录pg_description存储对每一个数据库对象可选的描述(注释)。 + 描述可以通过操作,并可使用psql\d命令查看。 + 在pg_description的初始内容中提供了很多内置系统对象的描述。 + + + + 参见pg_shdescription,它对在一个数据库集簇中共享的对象的描述提供了相似的功能。 + + + + <structname>pg_description</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + objoid + oid + 任意 OID 列 + + 描述所属对象的OID + + + + + classoid + oid + pg_class.oid + + 该对象所在的系统目录的 OID + + + + + objsubid + int4 + + + 对于一个表列上的一个注释,这里是列号(objoidclassoid指表本身)。对所有其他对象类型,此列为0。 + + + + + description + text + + + 作为该对象描述的任意文本 + + + + +
+ +
+ + + + <structname>pg_enum</structname> + + + pg_enum + + + + pg_enum目录包含每一个枚举类型的项,其中包括了值和标签。一个给定枚举值的内部表示实际上是它在pg_enum中的相关行的OID。 + + + + <structname>pg_enum</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + enumtypid + oid + pg_type.oid + + 包含此枚举值的pg_type项的OID + + + + + enumsortorder + float4 + + + 此枚举值在其枚举类型中的排序位置 + + + + + enumlabel + name + + + 此枚举值的文本标签 + + + + +
+ + + pg_enum行的OID值遵循一种特殊的规则:即OID的数值被保证按照其枚举类型的排序顺序进行排序。即如果两个偶数OID属于同一枚举类型,较小的OID必然具有较小的enumsortorder值。奇数OID值不需要遵循排序顺序。这种规则使得枚举比较例程在很多常见情况下可以避免系统目录查找。创建和修改枚举类型的例程将尝试尽可能地为枚举值分配偶数OID。 + + + + 当一个枚举类型被创建后,其成员会被分配排序位置 1..n。但后来增加的成员可能会被赋予负值或分数值的enumsortorder。对这些值的唯一要求是它们必须排序正确并且保持唯一。 + +
+ + + + <structname>pg_event_trigger</structname> + + + pg_event_trigger + + + + 目录pg_event_trigger存储事件触发器。更多信息参见。 + + + + <structname>pg_event_trigger</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + evtname + name + + + 触发器名(必须唯一) + + + + + evtevent + name + + + 此触发器触发的事件的标识符 + + + + + evtowner + oid + pg_authid.oid + + 事件触发器的拥有者 + + + + + evtfoid + oid + pg_proc.oid + + 将被调用的函数 + + + + + evtenabled + char + + + 控制事件触发器触发的模式。 + O = 触发器在origin和local模式触发, + D = 触发器被禁用, + R = 触发器在replica模式触发, + A = 触发器总是触发。 + + + + + evttags + text[] + + + 此触发器将触发的命令标签。如果为空,此触发器的触发不受命令标签的限制。 + + + + +
+
+ + + + <structname>pg_extension</structname> + + + pg_extension + + + + 目录pg_extension存储有关已安装扩展的信息。有关扩展的细节请参见。 + + + + <structname>pg_extension</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + extname + name + + + 扩展的名字 + + + + + extowner + oid + pg_authid.oid + + 扩展的拥有者 + + + + + extnamespace + oid + pg_namespace.oid + + 包含此扩展的导出对象的模式 + + + + + extrelocatable + bool + + + 如果扩展可被重定位到另一个模式则为真 + + + + + extversion + text + + + 扩展的版本名字 + + + + + extconfig + oid[] + pg_class.oid + + 扩展的配置表的regclass项的OID数组,如果没有配置表则为NULL + + + + + extcondition + text[] + + + 扩展的配置表的WHERE子句过滤条件的数组,如果没有则为NULL + + + + + +
+ + + 注意和大部分具有一个namespace列的模式不同,extnamespace不是用来表示扩展属于该模式。扩展的名字从不用模式进行限定。extnamespace表明该模式包含了该扩展的大部分或全部对象。如果extrelocatable为真,则该模式事实上必须包含属于此扩展的全部模式限定的对象。 + +
+ + + + <structname>pg_foreign_data_wrapper</structname> + + + pg_foreign_data_wrapper + + + + 目录pg_foreign_data_wrapper存储外部数据包装器定义。外部数据包装器是一种访问位于外部服务器上数据的机制。 + + + + <structname>pg_foreign_data_wrapper</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + fdwname + name + + + 外部数据包装器的名字 + + + + + fdwowner + oid + pg_authid.oid + + 外部数据包装器的拥有者 + + + + + fdwhandler + oid + pg_proc.oid + + 指向一个负责为外部数据包装器提供执行例程的处理函数;如果没有提供处理函数则为零 + + + + + fdwvalidator + oid + pg_proc.oid + + 指向一个负责检查传给外部数据包装器的选项有效性的验证函数,包括外部服务器选项以及使用该外部数据包装器的用户映射;如果没有提供验证函数则为零 + + + + + fdwacl + aclitem[] + + 访问权限,详见 + + + + fdwoptions + text[] + + + 外部数据包装器特定选项,以keyword=value字符串形式 + + + + +
+
+ + + + <structname>pg_foreign_server</structname> + + + pg_foreign_server + + + + 目录pg_foreign_server存储外部服务器定义。外部服务器定义了外部数据的来源,例如一个远程服务器。外部服务器通过外部数据包装器来访问。 + + + + <structname>pg_foreign_server</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + srvname + name + + + 外部服务器的名字 + + + + + srvowner + oid + pg_authid.oid + + 外部服务器的拥有者 + + + + + srvfdw + oid + pg_foreign_data_wrapper.oid + + 此外部服务器的外部数据包装器的OID + + + + + srvtype + text + + + 服务器的类型(可选) + + + + + srvversion + text + + + 服务器的版本(可选) + + + + + srvacl + aclitem[] + + 访问权限,详见 + + + + srvoptions + text[] + + + 外部服务器特定选项,以keyword=value字符串形式 + + + + +
+
+ + + + <structname>pg_foreign_table</structname> + + + pg_foreign_table + + + + 目录pg_foreign_table包含关于外部表的辅助信息。 + 一个外部表和普通表一样,主要由一个pg_class项表示。 + 它的pg_foreign_table项包含外部表所特有的信息。 + + + + <structname>pg_foreign_table</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + ftrelid + oid + pg_class.oid + + 外部表的pg_class项的OID + + + + + ftserver + oid + pg_foreign_server.oid + + 外部表所在的外部服务器的OID + + + + + ftoptions + text[] + + + 外部表选项,以keyword=value字符串形式 + + + + +
+
+ + + + <structname>pg_index</structname> + + + pg_index + + + + 目录pg_index包含关于索引的部分信息。 + 其他信息大部分在pg_class中。 + + + + <structname>pg_index</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + indexrelid + oid + pg_class.oid + + 此索引的pg_class项的OID + + + + + indrelid + oid + pg_class.oid + + 此索引所对应的基表的pg_class项的OID + + + + + indnatts + int2 + + + 索引中的总列数(与pg_class.relnatts重复) + + + + + indisunique + bool + + + 如为真, 这是唯一索引 + + + + + indisprimary + bool + + + 如为真,表示索引为表的主键(如果此列为真,indisunique也总是为真) + + + + + indisexclusion + bool + + + 如为真,此索引支持一个排他约束 + + + + + indimmediate + bool + + + 如为真,唯一性检查在插入时立即被执行(如果indisunique为假,此列无关) + + + + + indisclustered + bool + + + 如果为真,表示表最后以此索引进行了聚簇 + + + + + indisvalid + bool + + + 如果为真,此索引当前可以用于查询。 + 为假表示此索引可能不完整:它肯定还在被INSERT/UPDATE操作所修改,但它不能安全地被用于查询。 + 如果索引是唯一索引,唯一性属性也不能被保证。 + + + + + indcheckxmin + bool + + + 如果为真,查询必须不使用该索引,直到这个pg_index行的xmin + 低于其TransactionXmin事件视界,因为表可能 + 包含损坏的HOT链(这其中包含了他们可以看到的不兼容行) + + + + + indisready + bool + + + 如果为真,表示此索引当前可以用于插入。 + 为假表示索引必须被INSERT/UPDATE操作忽略。 + + + + + indislive + bool + + + 如果为假,索引正处于被删除过程中,并且必须被所有处理忽略(包括HOT安全的决策) + + + + + indisreplident + bool + + + 如果为真,这个索引被选择为使用ALTER TABLE ... REPLICA IDENTITY USING INDEX ...replica identity + + + + + indkey + int2vector + pg_attribute.attnum + + 这是一个包含indnatts值的数组,用于指示该索引为哪些表列建立索引。 + 例如,1 3的值表示第一个和第三个表列组成索引键。 + 此数组中的零表示相应的索引属性是表列上的表达式,而不是简单的列引用。 + + + + + indcollation + oidvector + pg_collation.oid + + 对于索引键中的每一列,这包含要用于该索引的排序规则的OID。 + + + + + indclass + oidvector + pg_opclass.oid + + 对于索引键中的每一列,这里包含了要使用的操作符类的OID。详见pg_opclass。 + + + + + indoption + int2vector + + + 这是一个indnatts值的数组,用于存储每列的标志位。位的意义由索引的访问方法定义。 + + + + + indexprs + pg_node_tree + + + 非简单列引用索引属性的表达式树(以nodeToString()形式)。对于indkey中每一个为0的项,这个列表中都有一个元素。如果所有的索引属性都是简单引用,此列为空。 + + + + + indpred + pg_node_tree + + + 部分索引谓词的表达式树(以nodeToString()形式)。如果不是部分索引,此列为空。 + + + + +
+ +
+ + + + <structname>pg_inherits</structname> + + + pg_inherits + + + + 目录pg_inherits记录有关表继承层次的信息。 + 数据库中每个直接子表在这里都有一项。(非直接继承可以通过顺着项构成的链来确定。) + + + + <structname>pg_inherits</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + inhrelid + oid + pg_class.oid + + 子表的OID + + + + + inhparent + oid + pg_class.oid + + 父表的OID + + + + + inhseqno + int4 + + + 如果一个孩子表有多于一个直接父表(多继承),这个数字说明了继承列被排列的顺序。计数从1开始。 + + + + +
+ +
+ + + <structname>pg_init_privs</structname> + + + pg_init_privs + + + + 目录pg_init_privs记录系统中对象的初始权限。数据库中每个具有非默认(非 NULL)初始权限集合的对象都在其中有一个条目。 + + + + 对象可以在系统初始化(initdb)时获得其初始权限,也可以在CREATE EXTENSION期间创建该对象,并在扩展脚本中用GRANT设置对象的初始权限。 + 注意,系统会自动处理扩展脚本执行期间对权限的记录;扩展作者只需要在脚本中使用GRANTREVOKE语句,权限就会被记录下来。 + privtype列表示初始权限是由initdb设置,还是在某次CREATE EXTENSION命令期间设置。 + + + + 对于初始权限由initdb设置的对象,其条目中的privtype'i';对于初始权限由CREATE EXTENSION设置的对象,其条目中的privtype'e'。 + + + + <structname>pg_init_privs</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + objoid + oid + 任意 OID 列 + + 指定对象的 OID + + + + + classoid + oid + pg_class.oid + + 对象所在的系统目录的 OID + + + + + objsubid + int4 + + + 对于一个表列,这里是列编号(objoid和classoid指向表本身)。对于所有其他对象类型,这列为零。 + + + + + privtype + char + + + 定义此对象初始权限类型的代码,见文字说明 + + + + + initprivs + aclitem[] + + 初始访问权限,详见 + + + + +
+ +
+ + + + <structname>pg_language</structname> + + + pg_language + + + + 目录pg_language注册了可用于编写函数或存储过程的语言。 + 更多关于语言处理器的信息请参阅。 + + + + <structname>pg_language</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + lanname + name + + + 语言的名字 + + + + + lanowner + oid + pg_authid.oid + + 语言的拥有者 + + + + + lanispl + bool + + + 内部语言为假(如SQL),用户定义语言为真。当前,pg_dump仍然使用这个列来决定要转储哪些语言,但在未来这可能会被一种不同的机制所取代。 + + + + + lanpltrusted + bool + + + 为真表示这是一种可信的语言,即它被相信不会向普通SQL执行环境之外的任何东西授予权限。只有超级用户可以在非可信语言中创建函数。 + + + + + lanplcallfoid + oid + pg_proc.oid + + 对于非内部语言,此列引用语言处理器,它是一个特殊函数负责执行所有用这种语言编写的函数。 + + + + + laninline + oid + pg_proc.oid + + 此列引用一个负责执行内联匿名代码块的函数( 块)。如果不支持内联块则为0。 + + + + + lanvalidator + oid + pg_proc.oid + + 此列引用一个负责在函数创建时对其进行语法和可用性检查的语言验证函数。如果没有提供验证器则为0。 + + + + + lanacl + aclitem[] + + 访问权限,详见 + + + +
+ +
+ + + + <structname>pg_largeobject</structname> + + + pg_largeobject + + + + 目录pg_largeobject保存构成大对象的数据。一个大对象在被创建时会被分配一个OID。每个大对象被分解成段或,以便小到可以被方便地作为行存储在pg_largeobject中。每页中的数据量被定义为LOBLKSIZE(目前是BLCKSZ/4或是2 kB)。 + + + + 在PostgreSQL 9.0 之前,大对象没有相关的权限结构。因此,pg_largeobject曾是公开可读的,并且可以用来获得系统中所有大对象的 OID(和内容)。但现在已经不是这样了;可使用pg_largeobject_metadata来获得大对象 OID 的列表。 + + + + <structname>pg_largeobject</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + loid + oid + pg_largeobject_metadata.oid + + 包含此页的大对象的标识符 + + + + + pageno + int4 + + + 此页在它所属大对象中的页号(从0开始计) + + + + + data + bytea + + + 实际存储在大对象中的数据。它从不会超过LOBLKSIZE字节,也可能更少。 + + + + +
+ + + pg_largeobject的每一行保存一个大对象的一个页的数据,从对象内部的字节偏移量(pageno * LOBLKSIZE)开始。现在的实现允许稀疏存储:页面可能丢失,并且可能比LOBLKSIZE字节短(即便不是最后一页)。一个大对象中丢失的区域会被读出为0。 + + +
+ + + <structname>pg_largeobject_metadata</structname> + + + pg_largeobject_metadata + + + + 目录pg_largeobject_metadata保持着与大对象有关的元数据。真正的大对象数据被存储在pg_largeobject中。 + + + + <structname>pg_largeobject_metadata</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + lomowner + oid + pg_authid.oid + + 大对象的拥有者 + + + + + lomacl + aclitem[] + + 访问权限,详见 + + + + +
+
+ + + + <structname>pg_namespace</structname> + + + pg_namespace + + + + 目录pg_namespace存储名字空间。名字空间是SQL模式之下的结构:每个名字空间拥有一个独立的表、类型等的集合,且其中没有名字冲突。 + + + + <structname>pg_namespace</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + nspname + name + + + 名字空间的名字 + + + + + nspowner + oid + pg_authid.oid + + 名字空间的拥有者 + + + + + nspacl + aclitem[] + + 访问权限,详见 + + + +
+ +
+ + + + <structname>pg_opclass</structname> + + + pg_opclass + + + + 目录pg_opclass定义索引访问方法的操作符类。每一个操作符类定义了一种特定数据类型和一种特定索引访问方法的索引列的语义。一个操作符类实际上指定了一个特定的操作符族可以用于一个特定可索引列数据类型。该族中可用于索引列的操作符能够接受该列的数据类型作为它们的左输入。 + + + + 操作符类详见。 + + + + <structname>pg_opclass</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + opcmethod + oid + pg_am.oid + + 操作符类所属的索引访问方法 + + + + + opcname + name + + + 操作符类的名称 + + + + + opcnamespace + oid + pg_namespace.oid + + 操作符类所属的名字空间 + + + + + opcowner + oid + pg_authid.oid + + 操作符类的拥有者 + + + + + opcfamily + oid + pg_opfamily.oid + + 包含此操作符类的操作符族 + + + + + opcintype + oid + pg_type.oid + + 操作符类索引的数据类型 + + + + + opcdefault + bool + + + 如果此操作符类为opcintype的默认值则为真 + + + + + opckeytype + oid + pg_type.oid + + 存储在索引中的数据的类型,如果值为0表示与opcintype相同 + + + + + +
+ + + 一个操作符类的opcmethod必须匹配包含它的操作符族的opfmethod。 + 而且,对于任何给定的opcmethodopcintype组合,只有不超过一个pg_opclass行的opcdefault值为真。 + + +
+ + + + <structname>pg_operator</structname> + + + pg_operator + + + + 目录pg_operator存储关于操作符的信息。详见。 + + + + <structname>pg_operator</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + oprname + name + + + 操作符的名称 + + + + + oprnamespace + oid + pg_namespace.oid + + 操作符所属的名字空间的OID + + + + + oprowner + oid + pg_authid.oid + + 操作符的拥有者 + + + + + oprkind + char + + b = 中缀(双目),l = 前缀(左目),r = 后缀(右目 + + + + oprcanmerge + bool + + + 该操作符支持归并连接 + + + + + oprcanhash + bool + + + 该操作符支持哈希连接 + + + + + oprleft + oid + pg_type.oid + + 左操作数类型 + + + + + oprright + oid + pg_type.oid + + 右操作数类型 + + + + + oprresult + oid + pg_type.oid + + 结果类型 + + + + + oprcom + oid + pg_operator.oid + + 该操作符的交换子(如有) + + + + + oprnegate + oid + pg_operator.oid + + 该操作符的否定(如有) + + + + + oprcode + regproc + pg_proc.oid + + 实现该操作符的函数 + + + + + oprrest + regproc + pg_proc.oid + + 该操作符的限制选择率估算函数 + + + + + oprjoin + regproc + pg_proc.oid + + 该操作符的连接选择率估算函数 + + + + +
+ + 未使用的列包含零。例如,前缀操作符的oprleft为零。 + +
+ + + + <structname>pg_opfamily</structname> + + + pg_opfamily + + + + 目录pg_opfamily定义了操作符族。每一个操作符族是操作符和相关支持例程的集合,支持例程用于实现一个特定索引访问方法的语义。此外,按照访问方法指定的某种方式,一个族内的操作符都是兼容的。操作符族概念允许在索引中使用跨数据类型操作符,并可以使用访问方法语义的知识推导出。 + + + + 操作符族最终在中描述。 + + + + <structname>pg_opfamily</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + opfmethod + oid + pg_am.oid + + 操作符族适用的索引访问方法 + + + + + opfname + name + + + 操作符族的名字 + + + + + opfnamespace + oid + pg_namespace.oid + + 操作符族所属的名字空间 + + + + + opfowner + oid + pg_authid.oid + + 操作符族的拥有者 + + + + + +
+ + + 定义操作符族的主要信息不在它的pg_opfamily行,而是在相关的pg_amoppg_amprocpg_opclass行中。 + + +
+ + + + + + <structname>pg_pltemplate</structname> + + + pg_pltemplate + + + + 目录pg_pltemplate存储过程语言的模板信息。 + 有了语言模板,就可以用简单的CREATE LANGUAGE命令在特定数据库中创建该语言,无需指定实现细节。 + + + + 与大多数系统目录不同,pg_pltemplate由一个集簇中的所有数据库共享: + 每个集簇只有一份pg_pltemplate,而不是每个数据库各有一份。 + 这样,每个数据库都可以在需要时访问这些信息。 + + + + <structname>pg_pltemplate</structname>列 + + + + + 名称 + 类型 + + 描述 + + + + + + + tmplname + name + 此模板所对应的语言名称 + + + + tmpltrusted + boolean + 如果该语言被视为可信语言,则为真 + + + + tmpldbacreate + boolean + 如果数据库拥有者可以创建该语言,则为真 + + + + tmplhandler + text + 调用处理器函数的名称 + + + + tmplinline + text + 匿名块处理器函数的名称;如果没有则为空 + + + + tmplvalidator + text + 验证器函数的名称;如果没有则为空 + + + + tmpllibrary + text + 实现该语言的共享库的路径 + + + + tmplacl + aclitem[] + 模板的访问权限(实际上未使用) + + + + +
+ + + 目前没有用于操纵过程语言模板的命令;要更改内置信息,超级用户必须使用普通的INSERTDELETEUPDATE命令修改此表。 + + + + + pg_pltemplate很可能会在PostgreSQL的某个未来版本中移除, + 改为将这些过程语言的信息保存在各自的扩展安装脚本中。 + + + +
+ + + + <structname>pg_policy</structname> + + + pg_policy + + + + 目录pg_policy存储着表的行级安全性策略。 + 一个策略包括它适用于的命令种类(可能适用于所有命令)、它适用于的角色、 + 被作为安全屏障条件增加到包括该表的查询的表达式以及被作为 + WITH CHECK选项增加到尝试向表增加新纪录的查询的表达式。 + + + + + <structname>pg_policy</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + polname + name + + + 策略的名称 + + + + + polrelid + oid + pg_class.oid + + 策略适用的表 + + + + + polcmd + char + + + 策略适用的命令类型: + r 表示 SELECT, + a 表示 INSERT, + w 表示 UPDATE, + d 表示 DELETE, + *表示所有命令类型 + + + + + + + polroles + oid[] + pg_authid.oid + + 策略适用的角色 + + + + + polqual + pg_node_tree + + + 被作为安全屏障条件增加到使用该表的查询的表达式树 + + + + + polwithcheck + pg_node_tree + + + 被作为 WITH CHECK 条件增加到尝试向表增加行的查询的表达式树 + + + + + +
+ + + + 存储在pg_policy中的策略只有在它们所适用的表的pg_class.relrowsecurity被设置时才起作用。 + + + +
+ + + <structname>pg_proc</structname> + + + pg_proc + + + + 目录pg_proc存放有关函数(或过程)的信息。 + 更多信息请参见。 + + + + 该表同时包含聚合函数和普通函数的数据。如果proisagg为真, + 在pg_aggregate中应该有一个相匹配的行。 + + + + <structname>pg_proc</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + proname + name + + + 函数的名字 + + + + + pronamespace + oid + pg_namespace.oid + + 函数所属的名字空间的OID + + + + + proowner + oid + pg_authid.oid + + 函数的拥有者 + + + + + prolang + oid + pg_language.oid + + 实现语言或该函数的调用接口 + + + + + procost + float4 + + + 估计的执行代价(以为单位),如果proretset为真,这是每行返回的代价 + + + + + prorows + float4 + + + 估计的结果行数量(如果proretset为假,该值为0) + + + + + provariadic + oid + pg_type.oid + + 可变数组参数的元素的数据类型,如果函数没有可变参数则为0 + + + + + protransform + regproc + pg_proc.oid + 对该函数的调用可以由此处的另一个函数简化(参见 + + + + proisagg + bool + + 该函数是聚合函数 + + + + proiswindow + bool + + 该函数是窗口函数 + + + + prosecdef + bool + + + 函数是一个安全性定义者(即,一个setuid函数) + + + + + proleakproof + bool + + + 该函数没有副作用。除返回值外,不会泄露任何关于参数的信息。任何可能因参数值而报错的函数都不是 leakproof。 + + + + + proisstrict + bool + + + 当任一调用参数为 NULL 时,函数是否返回空值。在这种情况下,函数实际上根本不会被调用。非strict函数必须准备好处理空值输入。 + + + + + proretset + bool + + + 函数是否返回一个集合(即,指定数据类型的多个值) + + + + + provolatile + char + + + provolatile说明函数是仅仅只依赖于它的输入参数,还是会被外部因素影响。 + 值i表示不变的函数,它对于相同的输入总是输出相同的结果。 + 值s表示稳定的函数,它的结果(对于固定输入)在一次扫描内不会变化。 + 值v表示不稳定的函数,它的结果在任何时候都可能变化(使用v也表示函数具有副作用,因此对它们的调用无法得到优化) + + + + + proparallel + char + + + proparallel说明该函数在并行模式下是否能安全地运行。 + 对于能在并行模式下不受限制安全运行的函数,这列是s。 + 对于可以在并行模式下运行但是只限于由并行分组的领导者执行的函数,这列是r。 + 对于在并行模式中不安全的函数,这列是u,这种函数的存在会强制一个顺序执行计划。 + + + + + pronargs + int2 + + + 输入参数的个数 + + + + + pronargdefaults + int2 + + + 具有默认值的参数个数 + + + + + prorettype + oid + pg_type.oid + + 返回值的数据类型 + + + + + proargtypes + oidvector + pg_type.oid + + 一个函数参数的数据类型的数组。 + 这只包括输入参数(含INOUTVARIADIC参数),因此也表现了函数的调用签名。 + + + + + proallargtypes + oid[] + pg_type.oid + + 一个函数参数的数据类型的数组。 + 这包括所有参数(含OUTINOUT参数)。 + 但是,如果所有参数都是IN参数,这个域将为空。 + 注意下标是从1开始 ,然而由于历史原因proargtypes的下标是从0开始。 + + + + + proargmodes + char[] + + + 一个函数参数的模式的数组。这里包括: + i表示IN参数 , + o表示OUT参数, + b表示INOUT参数, + v表示VARIADIC参数, + t表示TABLE参数。 + 如果所有的参数都是IN参数,这个域为空。 + 注意这里的下标对应着proallargtypes而不是proargtypes中的位置。 + + + + + proargnames + text[] + + + 一个函数参数的名字的数组。没有名字的参数在数组中设置为空字符串。如果没有一个参数有名字,这个域为空。 + 注意这里的下标对应着proallargtypes而不是proargtypes中的位置。 + + + + + proargdefaults + pg_node_tree + + + 默认值的表达式树(按照nodeToString()的表现方式)。 + 这是一个pronargdefaults元素的列表,对应于最后N个input参数(即最后N个proargtypes位置)。 + 如果没有一个参数具有默认值,这个域为空。 + + + + + protrftypes + oid[] + + 要应用转换的数据类型 OID。 + + + + prosrc + text + + + 这个字段告诉函数处理器如何调用该函数。它可能是针对解释型语言的真实源码、一个链接符号、一个文件名,或任何其他取决于实现语言/调用约定的内容。 + + + + + probin + text + + + 关于如何调用函数的附加信息。其解释是与语言相关的。 + + + + + proconfig + text[] + + + 函数对于运行时配置变量的本地设置值 + + + + + proacl + aclitem[] + + 访问权限,详见 + + + +
+ + + 对于编译好的函数,包括内置函数和动态装载函数,prosrc包含函数的 C 语言名字(链接符号)。 + 所有其他已知的语言类型,prosrc包含函数的源码文本。 + 除了对于动态载入的C函数之外,probin不使用,对于动态载入的C函数,它给定了包含该函数的共享库文件的名称。 + + +
+ + + + + <structname>pg_range</structname> + + + pg_range + + + + 目录pg_range存储关于范围类型的信息。它是类型在pg_type中项的补充。 + + + + <structname>pg_range</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + rngtypid + oid + pg_type.oid + + 范围类型的OID + + + + + rngsubtype + oid + pg_type.oid + + 该范围类型的元素类型(子类型)的OID + + + + + rngcollation + oid + pg_collation.oid + + 用于范围比较的排序规则的OID,如果没有则为零 + + + + + rngsubopc + oid + pg_opclass.oid + + 用于范围比较的子类型的操作符类的OID + + + + + rngcanonical + regproc + pg_proc.oid + + 将一个范围值转换为规范形式的函数的OID,如果没有则为零 + + + + + rngsubdiff + regproc + pg_proc.oid + + 返回两个元素值之差并以 double precision 表示的函数的 OID;如果没有则为零 + + + + +
+ + + rngsubopc(如果元素类型支持排序规则,则还包括 + rngcollation)决定范围类型所用的排序顺序。 + rngcanonical用于元素类型为离散类型的情况。 + rngsubdiff是可选的,但应当提供它, + 以提高范围类型上的 GiST 索引性能。 + + +
+ + + <structname>pg_replication_origin</structname> + + + pg_replication_origin + + + +pg_replication_origin目录包含所有已创建的复制源。更多复制源的信息请见。 + + + + + <structname>pg_replication_origin</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + roident + Oid + + + 一个集簇范围内唯一的复制源标识符。应该绝不会脱离系统。 + + + + + roname + text + + + 外部的由用户定义的复制源名称。 + + + + +
+
+ + + <structname>pg_rewrite</structname> + + + pg_rewrite + + + + 目录pg_rewrite存储对于表和视图的重写规则。 + + + + <structname>pg_rewrite</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + rulename + name + + + 规则名称 + + + + + ev_class + oid + pg_class.oid + + 使用该规则的表 + + + + + ev_type + char + + + 使用该规则的事件类型: 1 = SELECT, 2 = UPDATE, 3 = INSERT, 4 = DELETE + + + + + ev_enabled + char + + + 控制在哪种模式中触发该规则。 + O = 规则在origin和local模式触发, + D = 规则被禁用, + R = 规则在replica模式触发, + A = 规则总是被触发。 + + + + + is_instead + bool + + + 为真表示是一个INSTEAD规则 + + + + + ev_qual + pg_node_tree + + + 规则条件的表达式树(以 nodeToString() 表示) + + + + + ev_action + pg_node_tree + + + 规则动作的查询树(以 nodeToString() 表示) + + + + +
+ + + + 如果一个表在这个目录中有任何规则,pg_class.relhasrules必须为真。 + + + +
+ + + <structname>pg_seclabel</structname> + + + pg_seclabel + + + + 目录pg_seclabel存储数据库对象上的安全标签。 + 安全标签可以通过命令操纵。 + 简单的查看安全标签方法请见。 + + + + 同时请见pg_shseclabel,它对集簇共享的数据库对象的安全标签执行相似的功能。 + + + + <structname>pg_seclabel</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + objoid + oid + 任意 OID 列 + + 该安全标签依附的对象的OID + + + + + classoid + oid + pg_class.oid + + 该对象所在的系统目录的 OID + + + + + objsubid + int4 + + + 对于一个在表列上的安全标签,这将是列号(objoidclassoid指表本身)。对于所有其他对象类型,本列为0。 + + + + + provider + text + + + 与该标签相关的标签提供者。 + + + + + label + text + + + 应用于该对象的安全标签。 + + + + +
+
+ + + + <structname>pg_shdepend</structname> + + + pg_shdepend + + + + 目录pg_shdepend记录数据库对象和共享对象之间的依赖关系,例如角色。这些信息使得PostgreSQL可以确保对象在被删除时没有被其他对象引用。 + + + + 另请参阅pg_depend,它对单个数据库中对象之间的依赖提供了相似的功能。 + + + 与大部分其他系统目录不同,pg_shdepend在整个集簇的所有数据库之间共享:在每一个集簇中只有一个pg_shdepend的拷贝,而不是每个数据库一份。 + + + + <structname>pg_shdepend</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + dbid + oid + pg_database.oid + + 依赖对象所在数据库的OID,对于共享对象为零 + + + + + classid + oid + pg_class.oid + + 依赖对象所在系统目录的OID + + + + + objid + oid + 任意 OID 列 + + 特定依赖对象的OID + + + + + objsubid + int4 + + + 对于一个表列,这将是列号(objidclassid指向表本身)。对于所有其他对象类型,该列值为0。 + + + + + refclassid + oid + pg_class.oid + + 被引用对象所在的系统目录的OID(必须是一个共享的目录) + + + + + refobjid + oid + 任意 OID 列 + + 指定被引用对象的OID + + + + + deptype + char + + + 定义此依赖关系语义的一个代码,见文本 + + + + + +
+ + 在所有情况下,一条pg_shdepend记录都表示:如果不同时删除依赖对象,就不能删除被引用对象。不过,还存在若干由deptype标识的子类型: + + SHARED_DEPENDENCY_OWNER (o) + + + 被引用对象(必须是一个角色)是依赖对象的拥有者。 + + + + + + SHARED_DEPENDENCY_ACL (a) + + + 被引用对象(必须是角色)出现在依赖对象的 + ACL(访问控制列表,即权限列表) 中。(不会为对象拥有者创建 SHARED_DEPENDENCY_ACL 项,因为对象拥有者无论如何都会有一个 SHARED_DEPENDENCY_OWNER 项。) + + + + + + SHARED_DEPENDENCY_POLICY (r) + + + 被引用对象(必须是一个角色)被提及为依赖策略对象的目标。 + + + + + + SHARED_DEPENDENCY_PIN (p) + + + 不存在依赖对象;这种项表示系统本身依赖于被引用对象,因此该对象绝不能删除。 + 这种项只由initdb创建。用于表示依赖对象的各列包含零。 + + + + 将来可能还需要其他依赖类型。特别要注意的是,当前定义只支持把角色作为被引用对象。 + +
+ + + <structname>pg_shdescription</structname> + + + pg_shdescription + + + + 目录pg_shdescription存储共享数据库对象的可选描述(注释)。 + 这些描述可以通过命令设置或修改,并且可以使用psql\d命令查看。 + + + + 另请参阅pg_description,它对单个数据库内对象的描述提供了相似的功能。 + + + + 与大部分其他系统目录不同,pg_shdescription在整个集簇的所有数据库之间共享:在每一个集簇中只有一个pg_shdescription的拷贝,而不是每个数据库一份。 + + + + <structname>pg_shdescription</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + objoid + oid + 任意 OID 列 + + 描述所属对象的OID + + + + + classoid + oid + pg_class.oid + + 该对象所在的系统目录的 OID + + + + + description + text + + + 作为该对象描述的任意文本 + + + + +
+ +
+ + + <structname>pg_shseclabel</structname> + + + pg_shseclabel + + + + 目录pg_shseclabel存储共享数据库对象上的安全标签。 + 安全标签可以通过命令操纵。 + 更简单的查看安全标签的方式请见。 + + + + 另请参阅pg_seclabel,它对单个数据库中对象的安全标签提供了相似的功能。 + + + + 与大部分其他系统目录不同,pg_shseclabel在整个集簇的所有数据库之间共享:在每一个集簇中只有一个pg_shseclabel的拷贝,而不是每个数据库一份。 + + + + <structname>pg_shseclabel</structname> 列 + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + objoid + oid + 任意 OID 列 + + 该安全标签依附的对象的OID + + + + classoid + oid + pg_class.oid + + 该对象所在的系统目录的 OID + + + + provider + text + + + 与该标签相关的标签提供者。 + + + + label + text + + + 应用于该对象的安全标签。 + + + + +
+
+ + + <structname>pg_statistic</structname> + + + pg_statistic + + + + 目录pg_statistic存储有关数据库内容的统计数据。 + 其中的项由创建,查询规划器会使用这些数据来进行查询规划。 + 注意所有的统计数据天然就是近似的,即使它刚刚被更新。 + + + + 通常,每个已分析的表列都有一个条目,其中stainherit = false。 + 如果表具有继承子表,则还会创建第二个条目,其中stainherit = true。 + 此行表示继承树上列的统计信息,即你可以通过SELECT column FROM table*看到的数据的统计信息, + 而stainherit = false行表示SELECT column FROM ONLY table的结果。 + + + + pg_statistic也存储关于索引表达式值的统计数据,就好像它们是真正的数据列,但在这种情况中starelid指索引。对一个普通非表达式索引列不会创建项,因为它将是底层表列的项的冗余。当前,索引表达式的项都具有stainherit = false。 + + + + 由于不同种类的数据可能适合不同种类的统计信息,pg_statistic 在设计上尽量不对所存储的统计信息种类作出假定。只有极为通用的统计信息(例如空值情况)才在 pg_statistic 中有专用的列。其余统计信息都存储在槽位中。每个槽位都是一组相关的列,其内容由其中一列的代码编号来标识。更多信息见 src/include/catalog/pg_statistic.h。 + + + + pg_statistic不应公开可读,因为即使是表内容的统计信息也可能被认为是敏感的(例如,一个工资列的最大值和最小值就可能非常引人关注)。pg_stats是建立在pg_statistic之上的公开可读视图,它只会显示当前用户可读取的表的信息。 + + + + <structname>pg_statistic</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + starelid + oid + pg_class.oid + + 被描述列所属的表或索引 + + + + + staattnum + int2 + pg_attribute.attnum + + 被描述列的编号 + + + + + stainherit + bool + + + 如果为真,则统计信息包括子表中的值,而不仅仅是指定关系中的值 + + + + + stanullfrac + float4 + + + 列中空值所占的比例 + + + + + stawidth + int4 + + + 非空项的平均存储宽度,以字节计 + + + + + stadistinct + float4 + + + 列中不同非空数据值的数量。一个大于零的值是不同值的真正数目。 + 一个小于零的值是表中行数的乘数的负值;例如,对于一个约 80% 的值为非空且每个非空值平均出现两次的列,可以表示为stadistinct = -0.4。一个0值表示不同值的数目未知。 + + + + + stakindN + int2 + + + 一个代码,它表示存储在该pg_statistic行中第N槽位的统计类型。 + + + + + staopN + oid + pg_operator.oid + + 一个用于生成这些存储在第N槽位的统计信息的操作符。 + 例如,直方图槽位会使用<操作符,该操作符定义了这些数据的排序顺序。 + + + + + stanumbersN + float4[] + + + 第N槽位中相应种类的数值统计信息;如果该槽位种类不涉及数值,则为 NULL + + + + + stavaluesN + anyarray + + + 第N槽位的类型的列值,如果该槽位类型不存储任何数据值则为 NULL。 + 每个数组的元素值实际上都是指定列的数据类型或者是一个相关类型(如数组元素类型), 因此,无法把这些列的类型定义得比anyarray更具体。 + + + + +
+ +
+ + + + + + <structname>pg_tablespace</structname> + + + pg_tablespace + + + + 目录pg_tablespace存储关于可用表空间的信息。表可以被放置在特定表空间中以实现磁盘布局的管理。 + + + + 与大部分其他系统目录不同,pg_tablespace在整个集簇的所有数据库之间共享:在每一个集簇中只有一个pg_tablespace的拷贝,而不是每个数据库一份。 + + + + <structname>pg_tablespace</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + spcname + name + + + 表空间名 + + + + + spcowner + oid + pg_authid.oid + + 表空间的拥有者,通常是创建它的用户 + + + + + spcacl + aclitem[] + + 访问权限,详见 + + + + spcoptions + text[] + + + 表空间级别的选项,形如keyword=value的字符串 + + + + +
+
+ + + + <structname>pg_transform</structname> + + + pg_transform + + + + 目录pg_transform存储有关转换的信息,转换是 + 一种让数据类型适应过程语言的机制。详见。 + + + + <structname>pg_transform</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + trftype + oid + pg_type.oid + + 这个转换所针对的数据类型的 OID + + + + + trflang + oid + pg_language.oid + + 这个转换所针对的语言的 OID + + + + + trffromsql + regproc + pg_proc.oid + + 一个函数的 OID,该函数用来将数据类型转换为过程语言的输入(例如函数参数)。 + 如果不支持此操作,这里存储零。 + + + + + trftosql + regproc + pg_proc.oid + + 一个函数的 OID,该函数被用来转换过程语言的输出(例如返回值)为该数据类型。 + 如果不支持此操作,这里存储零。 + + + + +
+
+ + + + <structname>pg_trigger</structname> + + + pg_trigger + + + + 目录pg_trigger存储表和视图上的触发器。详见。 + + + + <structname>pg_trigger</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + tgrelid + oid + pg_class.oid + + 触发器所在的表 + + + + + tgname + name + + + 触发器名(在同一个表的触发器中必须唯一) + + + + + tgfoid + oid + pg_proc.oid + + 要被触发器调用的函数 + + + + + tgtype + int2 + + + 标识触发器触发条件的位掩码 + + + + + tgenabled + char + + + 控制触发器在模式中的触发。 + O = 触发器在origin和local模式触发, + D = 触发器被禁用, + R = 触发器在replica模式触发, + A = 触发器总是触发。 + + + + + tgisinternal + bool + + + 为真表示触发器是内部生成的(通常是为了强制由tgconstraint指定的约束) + + + + + tgconstrrelid + oid + pg_class.oid + + 被一个引用完整性约束引用的表 + + + + + tgconstrindid + oid + pg_class.oid + + 支持一个唯一、主键、引用完整性约束或者排他约束的索引 + + + + + tgconstraint + oid + pg_constraint.oid + + 与触发器相关的pg_constraint项(如有) + + + + + tgdeferrable + bool + + + 如果约束触发器可延迟则为真 + + + + + tginitdeferred + bool + + + 如果约束触发器初始处于延迟状态则为真 + + + + + tgnargs + int2 + + + 传递给触发器函数的参数字符串个数 + + + + + tgattr + int2vector + pg_attribute.attnum + + 如果触发器是列限定的,这里存放列号;否则这是一个空数组 + + + + + tgargs + bytea + + + 传递给触发器的参数字符串,每一个都以NULL结尾 + + + + + tgqual + pg_node_tree + + + 触发器WHEN条件的表达式树(以nodeToString()表示);如果没有则为空 + + + + + + + + +
+ + + 当前,列限定触发器只支持UPDATE事件,因此tgattr只用于这种事件类型。tgtype也可以包含用于其他事件类型的位,但那些事件类型对应的是表级触发器,并且会忽略tgattr。 + + + + + 当tgconstraint非零时,tgconstrrelidtgconstrindidtgdeferrabletginitdeferred与被引用的pg_constraint 项有很大的冗余。 + 但是,存在将一个不可延迟触发器关联到一个可延迟约束的可能性:外键约束可以有一些可延迟和一些不可延迟触发器。 + + + + + + 如果一个关系在本目录中拥有任何触发器,其pg_class.relhastriggers必须为真。 + + + +
+ + + + <structname>pg_ts_config</structname> + + + pg_ts_config + + + + pg_ts_config系统目录包含表示文本检索配置的条目。一个配置指定某个特定的文本检索解析器,以及针对该解析器每种输出词元类型所定义的词典列表。解析器记录在pg_ts_config条目中,而词元到词典的映射则由pg_ts_config_map中的辅助项定义。 + + + + PostgreSQL的文本检索特性在中有更详尽的描述。 + + + + <structname>pg_ts_config</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + cfgname + name + + + 文本检索配置名 + + + + + cfgnamespace + oid + pg_namespace.oid + + 包含该配置的名字空间的OID + + + + + cfgowner + oid + pg_authid.oid + + 配置的拥有者 + + + + + cfgparser + oid + pg_ts_parser.oid + + 该配置的文本检索解析器的 OID + + + + +
+
+ + + + <structname>pg_ts_config_map</structname> + + + pg_ts_config_map + + + + pg_ts_config_map 系统目录中的条目说明了,对于每个文本检索配置所用解析器的每种输出词元类型,应当查询哪些文本检索词典以及查询的顺序。 + + + + PostgreSQL的文本检索特性在中有更详尽的描述。 + + + + <structname>pg_ts_config_map</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + mapcfg + oid + pg_ts_config.oid + + 拥有该映射项的pg_ts_config项的OID + + + + + maptokentype + integer + + + 一种由配置的解析器送出的词元类型 + + + + + mapseqno + integer + + + 查询该项的顺序(mapseqno值小的优先) + + + + + mapdict + oid + pg_ts_dict.oid + + 要查询的文本检索词典的 OID + + + + +
+
+ + + + <structname>pg_ts_dict</structname> + + + pg_ts_dict + + + + pg_ts_dict系统目录包含定义文本检索词典的项。一个词典依赖于一个文本检索模板,它指定了所有需要的实现函数,词典本身则为模板支持的用户可设置参数提供值。这种分工允许普通用户创建词典。参数由一个文本串dictinitoption定义,其格式和意义随着模板而变化。 + + + + PostgreSQL的文本检索特性在中有更详尽的描述。 + + + + <structname>pg_ts_dict</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + dictname + name + + + 文本检索词典名 + + + + + dictnamespace + oid + pg_namespace.oid + + 包含该词典的名字空间的 OID + + + + + dictowner + oid + pg_authid.oid + + 词典的拥有者 + + + + + dicttemplate + oid + pg_ts_template.oid + + 该词典的文本检索模板的 OID + + + + + dictinitoption + text + + + 模板的初始化选项字符串 + + + + +
+
+ + + + <structname>pg_ts_parser</structname> + + + pg_ts_parser + + + + pg_ts_parser系统目录包含定义文本检索解析器的项。一个解析器负责将输入文本分割成词位并为每一个词位分配一个词元类型。由于一个解析器必须用 C 语言级别的函数实现,创建新解析器的工作只限于数据库的超级用户。 + + + + PostgreSQL的文本检索特性在中有更详尽的描述。 + + + + <structname>pg_ts_parser</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + prsname + name + + + 文本检索解析器的名字 + + + + + prsnamespace + oid + pg_namespace.oid + + 包含此解析器的名字空间的 OID + + + + + prsstart + regproc + pg_proc.oid + + 解析器启动函数的 OID + + + + + prstoken + regproc + pg_proc.oid + + 解析器的下一词元函数的 OID + + + + + prsend + regproc + pg_proc.oid + + 解析器的关闭函数的 OID + + + + + prsheadline + regproc + pg_proc.oid + + 解析器的 headline 函数的 OID + + + + + prslextype + regproc + pg_proc.oid + + 解析器的 lextype 函数的 OID + + + + +
+
+ + + + <structname>pg_ts_template</structname> + + + pg_ts_template + + + + pg_ts_template系统目录包含定义文本检索模板的项。一个模板是一类文本检索词典的实现骨架。由于一个模板必须用 C 语言级别的函数实现,新模板的创建只限于数据库超级用户。 + + + + PostgreSQL的文本检索特性在中有更详尽的描述。 + + + + <structname>pg_ts_template</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + tmplname + name + + + 文本检索模板的名字 + + + + + tmplnamespace + oid + pg_namespace.oid + + 包含此模板的名字空间的OID + + + + + tmplinit + regproc + pg_proc.oid + + 模板的初始化函数的OID + + + + + tmpllexize + regproc + pg_proc.oid + + 模板的词汇化函数的OID + + + + +
+
+ + + + <structname>pg_type</structname> + + + pg_type + + + + 目录pg_type存储有关数据类型的信息。 + 基础类型和枚举类型(标量类型)使用创建,而域使用创建。 + 数据库中的每一个表都会有一个自动创建的复合类型,用于表示表的行结构。 + 也可以使用CREATE TYPE AS创建复合类型。 + + + + <structname>pg_type</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + typname + name + + + 数据类型的名字 + + + + + typnamespace + oid + pg_namespace.oid + + 包含此类型的名字空间的OID + + + + + typowner + oid + pg_authid.oid + + 类型的拥有者 + + + + + typlen + int2 + + + 对于一个固定尺寸的类型,typlen是该类型内部表示的字节数。 + 对于一个变长类型,typlen为负值。 + -1表示一个varlena类型(具有长度字),-2表示一个以空值结尾的C字符串。 + + + + + typbyval + bool + + + typbyval判断内部例程传递这个类型的数值时是通过传值还是传引用。 + 如果typlen不是1、2或4(或者在Datum为8字节的机器上为8),因此typbyval最好是假。 + 变长类型总是传引用。注意即使长度允许传值, typbyval也可以为假。 + + + + + typtype + char + + + typtype可以是: + b表示一个基础类型, + c表示一个复合类型(例如一个表的行类型), + d表示一个域, + e表示一个枚举类型, + p表示一个伪类型, + r表示一个范围类型。 + 另请参阅typrelidtypbasetype。 + + + + + typcategory + char + + + typcategory是一种任意的数据类型分类,它被分析器用来决定哪种隐式转换更好。参见。 + + + + + typispreferred + bool + + + 如果此类型在它的typcategory中是一个更好的转换目标,此列为真 + + + + + typisdefined + bool + + + 如果此类型已被定义则为真,如果此类型只是一个表示还未定义类型的占位符则为假。 + 当typisdefined为假,除了类型名字、名字空间和OID之外什么都不能被依赖。 + + + + + typdelim + char + + + 在分析数组输入时,分隔两个此类型值的字符。注意该分隔符是与数组元素数据类型相关联的, 而不是和数组的数据类型关联。 + + + + + typrelid + oid + pg_class.oid + + 如果这是一个复合类型(见typtype),那么此列指向pg_class中定义对应表的项 + (对于自由存在的复合类型,pg_class项并不表示一个表,但不管怎样该类型的pg_attribute项需要链接到它)。 + 对非复合类型此列为零。 + + + + + typelem + oid + pg_type.oid + + 如果typelem不为 0,则它标识pg_type中的另一行。 + 此时可以像数组一样对当前类型使用下标,得到typelem类型的值。 + 真正的数组类型是变长的(typlen = -1), + 但某些定长类型(typlen > 0)的typelem也非零, + 例如namepoint。 + 如果定长类型具有typelem,则其内部表示必须是若干个typelem数据类型的值,不含其他数据。 + 变长数组类型的首部由数组子例程定义。 + + + + + typarray + oid + pg_type.oid + + 如果typarray不是零,则它标识pg_type中的另一行,这一行是一个将此类型作为元素的真的数组类型 + + + + + typinput + regproc + pg_proc.oid + + 输入转换函数(文本格式) + + + + + typoutput + regproc + pg_proc.oid + + 输出转换函数(文本格式) + + + + + typreceive + regproc + pg_proc.oid + + 输入转换函数(二进制格式),如果没有则为零 + + + + + typsend + regproc + pg_proc.oid + + 输出转换函数(二进制格式),如果没有则为零 + + + + + typmodin + regproc + pg_proc.oid + + 类型修改器输入函数,如果类型没有提供修改器则为零 + + + + + typmodout + regproc + pg_proc.oid + + 类型修改器输出函数,或为零以使用标准格式 + + + + + typanalyze + regproc + pg_proc.oid + + 自定义ANALYZE函数,零表示使用标准函数 + + + + + typalign + char + + + typalign表示存储此类型值时所要求的对齐方式。 + 它适用于磁盘存储,以及该值在 PostgreSQL 内部的大多数表示形式。 + 当多个值连续存放时,例如在磁盘上完整行的表示中,这种类型的数据前会插入填充,使其从指定边界开始。 + 对齐参考点是该序列中第一个数据的起始位置。 + + + 可能的值为: + + c = char对齐,即不需要对齐。 + + + s = short对齐(在大部分机器上为2字节)。 + + + i = int对齐(在大部分机器上为4字节)。 + + + d = double对齐(在很多机器上为8字节,但绝不是全部)。 + + + + + 对于系统表中使用的类型,关键的是在pg_type定义的大小和对齐与编译器在表示表行的结构体中布局列的方式一致。 + + + + + + typstorage + char + + + typstorage用于指示 varlena 类型(即typlen = -1)是否支持 TOAST,以及这种类型的属性应采用什么默认策略。可能的值为: + + p:值必须始终以普通方式存储。 + + + e:值可以存储在一个二级关系中(如果关系有这样的关系,参见pg_class.reltoastrelid)。 + + + m:值可以被压缩并内联存储。 + + + x:值可以被压缩并内联存储,或存储在二级存储中。 + + 注意,m列也可以被移动到二级存储,但只能作为最后手段(ex列会先被移动)。 + + + + typnotnull + bool + + + typnotnull表示类型上的一个非空约束。只用于域。 + + + + + typbasetype + oid + pg_type.oid + + 如果这是一个域(见typtype),则typbasetype标识该域所基于的类型。如果此类型不是域,则为 0。 + + + + + typtypmod + int4 + + + 域使用typtypmod来记录被应用于它们基础类型的typmod(如果基础类型不使用typmod,则为-1)。如果此类型不是一个域则为-1。 + + + + + typndims + int4 + + + 对于基于数组的域,typndims是数组维度数(即,typbasetype是一个数组类型)。对于不是基于数组的域的类型,此列为零。 + + + + + typcollation + oid + pg_collation.oid + + typcollation指定此类型的排序规则。如果类型不支持排序规则,此列为零。 + 支持排序规则的基础类型在这里会有DEFAULT_COLLATION_OID。 + 基于支持排序规则的类型的域,如果为其指定了排序规则 OID,则它的排序规则 OID 可以不同于其基础类型的排序规则 OID。 + + + + + typdefaultbin + pg_node_tree + + + 如果typdefaultbin非空,那么它就是该类型默认表达式的nodeToString()表示形式。此列只用于域。 + + + + + typdefault + text + + + 如果某类型没有相关默认值,那么typdefault为空。如果typdefaultbin不为空,那么typdefault必须包含由typdefaultbin表示的默认表达式的人类可读版本。 + 如果typdefaultbin为空而typdefault不为空,则typdefault是该类型默认值的外部表示形式,它可以被交给该类型的输入转换器来产生一个常量。 + + + + + typacl + aclitem[] + + 访问权限,详见 + + + +
+ + + 列出了typcategory的系统定义值。任何未来对此列表的增加都将是大写ASCII字母。所有其他ASCII字符都保留给用户定义的类别。 + + + + <structfield>typcategory</structfield> Codes + + + + + 编码 + 类别 + + + + + + A + 数组类型 + + + B + 布尔类型 + + + C + 复合类型 + + + D + 日期/时间类型 + + + E + 枚举类型 + + + G + 几何类型 + + + I + 网络地址类型 + + + N + 数字类型 + + + P + 伪类型 + + + R + 范围类型 + + + S + 字符串类型 + + + T + 时间间隔类型 + + + U + 用户定义类型 + + + V + 位字符串类型 + + + X + unknown 类型 + + + +
+ +
+ + + + <structname>pg_user_mapping</structname> + + + pg_user_mapping + + + + 目录pg_user_mapping存储从本地用户到远程的映射。对这个目录的访问对普通用户有限制,可使用视图pg_user_mappings替代。 + + + + <structname>pg_user_mapping</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + oid + oid + + 行标识符(隐藏属性,必须显式选择) + + + + umuser + oid + pg_authid.oid + + 将要被映射的本地角色的OID,如果用户映射是公共的则为零 + + + + + umserver + oid + pg_foreign_server.oid + + 包含此映射的外部服务器的OID + + + + + umoptions + text[] + + + 用户映射专用的选项,以keyword=value字符串形式 + + + + +
+
+ + + + 系统视图 + + + 除了系统目录之外,PostgreSQL还提供了一些内置视图。一些系统视图提供了对系统目录中一些常用查询的便捷访问。其他视图则提供了对内部服务器状态的访问。 + + + + 信息模式()提供了一组与系统视图功能重叠的替代视图。由于信息模式是SQL标准,而这里描述的视图是PostgreSQL特有的,所以如果信息模式提供了所需的全部信息,通常最好使用信息模式。 + + + + 列出了这里描述的系统视图。每个视图的更详细文档见下文。还有一些额外的视图提供了对统计信息收集器结果的访问;它们在中描述。 + + + + 除非另有说明,否则这里描述的所有视图都是只读的。 + + + + 系统视图 + + + + + 视图名称 + 用途 + + + + + + pg_available_extensions + 可用的扩展 + + + + pg_available_extension_versions + 扩展的可用版本 + + + + pg_config + 编译时配置参数 + + + + pg_cursors + 打开的游标 + + + + pg_file_settings + 配置文件内容摘要 + + + + pg_group + 数据库用户组 + + + + + + pg_indexes + 索引 + + + + pg_locks + 当前持有或等待的锁 + + + + pg_matviews + 物化视图 + + + + pg_policies + 策略 + + + + pg_prepared_statements + 预备语句 + + + + pg_prepared_xacts + 预备事务 + + + + + + pg_replication_origin_status + 有关复制源的信息,包括复制进度 + + + + pg_replication_slots + 复制槽信息 + + + + pg_roles + 数据库角色 + + + + pg_rules + 规则 + + + + pg_seclabels + 安全标签 + + + + + + pg_settings + 参数设置 + + + + pg_shadow + 数据库用户 + + + + pg_stats + 规划器统计信息 + + + + pg_tables + + + + + pg_timezone_abbrevs + 时区简写 + + + + pg_timezone_names + 时区名称 + + + + pg_user + 数据库用户 + + + + pg_user_mappings + 用户映射 + + + + pg_views + 视图 + + + + +
+
+ + + <structname>pg_available_extensions</structname> + + + pg_available_extensions + + + + pg_available_extensions视图列出了可供安装的扩展。 + 另请参阅pg_extension目录,显示当前已安装的扩展。 + + + + <structname>pg_available_extensions</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + + name + name + + 扩展名 + + + + + default_version + text + + 默认版本的名称,如果没有指定则为NULL + + + + + installed_version + text + + 当前已安装的扩展版本,如果没有安装则为NULL + + + + + comment + text + + 来自于扩展的控制文件的注释字符串 + + + + +
+ + + pg_available_extensions视图是只读的。 + +
+ + + <structname>pg_available_extension_versions</structname> + + + pg_available_extension_versions + + + + pg_available_extension_versions视图列出了可供安装的特定扩展版本。 + 另请参阅pg_extension目录,显示当前已安装的扩展。 + + + + <structname>pg_available_extension_versions</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + + name + name + + 扩展名 + + + + + version + text + + 版本名 + + + + + installed + bool + + 如果此版本的扩展当前已安装则为真 + + + + + superuser + bool + 如果只有超级用户可以安装此扩展,则为真 + + + + relocatable + bool + + 如果扩展能被重定位到另一个模式则为真 + + + + + schema + name + + 此扩展必须被安装到的模式名,如果此扩展是部分或者全部可以重定位的,此列为NULL + + + + + requires + name[] + + 先决条件扩展的名称,如果没有则为NULL + + + + + comment + text + + 来自于扩展的控制文件的注释字符串 + + + + +
+ + + pg_available_extension_versions视图是只读的。 + +
+ + + <structname>pg_config</structname> + + + pg_config + + + + 视图pg_config描述了当前安装版本的PostgreSQL的编译时配置参数。 + 例如,它可供希望与PostgreSQL进行接口的软件包使用,以便找到所需的头文件和库。 + 它提供与PostgreSQL客户端应用程序相同的基本信息。 + + + + 默认情况下,pg_config视图只能被超级用户读取。 + + + + <structname>pg_config</structname> 列 + + + + 名称 + 类型 + + 描述 + + + + + + + name + text + + 参数名 + + + + + setting + text + + 参数值 + + + + +
+ +
+ + + <structname>pg_cursors</structname> + + + pg_cursors + + + pg_cursors视图列出当前可用的游标。游标可以通过几种方式定义: + + + 通过SQL中的语句 + + + + + + 通过前端/后端协议中的Bind消息,如中所述 + + + + + + 通过服务器编程接口(SPI),如中所述 + + + pg_cursors视图会显示通过上述任意方式创建的游标。游标仅在定义它的事务持续期间存在,除非将其声明为WITH HOLD。因此,不可保持的游标只会在此视图中保留到创建它的事务结束为止。 + + 游标在内部用于实现PostgreSQL的某些组件,如过程语言。因此,pg_cursors视图可能包含用户未明确创建的游标。 + + + + + + <structname>pg_cursors</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + + name + text + + 游标名 + + + + + statement + text + + 用于声明此游标的原始查询字符串 + + + + + is_holdable + boolean + + 如果游标是可保持游标(即,在声明该游标的事务提交后仍可访问)则为true,否则为false + + + + + is_binary + boolean + + 如果游标被声明为 BINARY,则为 true;否则为 false + + + + + is_scrollable + boolean + + 如果游标是可滚动的(即,允许以一种非顺序的方式检索行)则为true,否则为false + + + + + creation_time + timestamptz + + 游标被声明的时间 + + + + +
+ + + pg_cursors视图是只读的。 + + +
+ + + <structname>pg_file_settings</structname> + + + pg_file_settings + + + + 视图pg_file_settings提供了服务器配置文件的内容摘要。 + 每个name = value条目在文件中出现时,此视图中会出现一行, + 并附有标注,指示该值是否能够成功应用。还可能出现额外的行,用于表示与name = value条目无关的问题, + 例如文件中的语法错误。 + + + + 这个视图对于检查计划中的配置文件变更是否有效,或者诊断之前的故障很有帮助。 + 请注意,这个视图报告的是文件的当前内容,而不是服务器上最后应用的内容。 + (通常可以通过pg_settings + 视图确定最后一次由服务器应用的内容。) + + + + 默认情况下,pg_file_settings视图只能被超级用户读取。 + + + + <structname>pg_file_settings</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + sourcefile + text + + 配置文件的完整路径名 + + + + sourceline + integer + + 该项在配置文件中出现的行号 + + + + seqno + integer + + 项被处理的顺序(1..n) + + + + name + text + + 配置参数名 + + + + setting + text + + 将赋给该参数的值 + + + + applied + boolean + + 如果该值能够成功应用则为真 + + + + error + text + + 如果非空,则为说明该条目为何不能应用的错误消息 + + + + +
+ + + 如果配置文件包含语法错误或无效的参数名称,服务器将不会尝试应用其中的任何设置,因此所有applied字段将读取为false。 + 在这种情况下,将会有一个或多个带有非空error字段的行,指示问题所在。否则,将尝试应用各个设置。 + 如果无法应用某个单独的设置(例如,无效值,或者在服务器启动后无法更改设置),则error字段中将有适当的消息。 + 另一种导致条目的applied = false 的方式是被后续相同参数名称的条目覆盖;这种情况不被视为错误,因此error字段中不会出现任何内容。 + + + + 查看以获取有关更改运行时参数的各种方法的更多信息。 + + +
+ + + <structname>pg_group</structname> + + + pg_group + + + + 视图pg_group存在是为了向后兼容性:它模拟了在PostgreSQL版本8.1之前存在的目录。 + 它显示了所有被标记为非rolcanlogin的角色的名称和成员,这是对被用作组的角色集合的近似。 + + + + <structname>pg_group</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + groname + name + pg_authid.rolname + + 组的名称 + + + + + grosysid + oid + pg_authid.oid + + 组ID + + + + + grolist + oid[] + pg_authid.oid + + 包含此组中角色ID的一个数组 + + + + +
+ +
+ + + + <structname>pg_indexes</structname> + + + pg_indexes + + + + 视图pg_indexes提供了有关数据库中每个索引的有用信息。 + + + + <structname>pg_indexes</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含表和索引的模式名 + + + + tablename + name + pg_class.relname + + 此索引的基表名称 + + + + indexname + name + pg_class.relname + + 索引名 + + + + tablespace + name + pg_tablespace.spcname + + 包含索引的表空间名(如果是数据库的默认值则为空) + + + + indexdef + text + + + 索引定义(重建出的 CREATE INDEX 命令) + + + + +
+ +
+ + + <structname>pg_locks</structname> + + + pg_locks + + + + 视图pg_locks提供了对数据库服务器中活动进程持有的锁的信息的访问。有关锁定的更多讨论,请参见。 + + + + pg_locks包含每个活动的可锁定对象、请求的锁模式和相关进程的一行。 + 因此,如果多个进程正在持有或等待锁定它,同一可锁定对象可能会出现多次。 + 但是,当前没有任何锁定的对象将不会出现。 + + + + 有几种不同类型的可锁定对象: + 整个关系(例如,表),关系的单个页面, + 关系的单个元组, + 事务ID(虚拟和永久ID均包括), + 以及一般的数据库对象(由类OID和对象OID标识, + 与pg_description或 + pg_depend中的方式相同)。 + 此外,扩展关系的权利被表示为单独的可锁定对象,更新 + pg_database.datfrozenxid的权利也是如此。 + 此外,还可以对具有用户定义含义的数字施加咨询锁。 + + + + <structname>pg_locks</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + locktype + text + + 可锁定对象的类型:relationextendfrozenidpagetupletransactionidvirtualxidobjectuserlockadvisory + + + database + oid + pg_database.oid + + 锁目标存在的数据库的OID,如果目标是一个共享对象则为0,如果目标是一个事务ID则为 NULL + + + + relation + oid + pg_class.oid + + 作为锁目标的关系的OID,如果目标既不是关系,也不是关系的一部分则此列为 NULL + + + + page + integer + + + 作为锁目标的页在关系中的页号,如果目标不是一个关系页或元组则此列为 NULL + + + + tuple + smallint + + + 作为锁目标的元组在页中的元组号,如果目标不是一个元组则此列为 NULL + + + + virtualxid + text + + + 锁定目标事务的虚拟 ID,如果目标不是虚拟事务 ID,则为 NULL + + + + transactionid + xid + + + 锁定目标事务的标识,如果目标不是事务 ID,则为 NULL + + + + classid + oid + pg_class.oid + + 包含锁目标的系统目录的OID,如果目标不是一个普通数据库对象则此列为 NULL + + + + objid + oid + 任意 OID 列 + + 锁目标在它的系统目录中的OID,如果目标不是一个普通数据库对象则为 NULL + + + + objsubid + smallint + + + 锁的目标列号(classidobjid指表本身),如果目标是某种其他普通数据库对象则此列为0,如果目标不是一个普通数据库对象则此列为 NULL + + + + virtualtransaction + text + + + 保持这个锁或者正在等待这个锁的事务的虚拟ID + + + + pid + integer + + + 保持这个锁或者正在等待这个锁的服务器进程的PID,如果此锁被一个预备事务所持有则此列为 NULL + + + + mode + text + + + 此进程已持有或者希望持有的锁模式的名称(参见) + + + + granted + boolean + + + 如果锁已授予则为真,如果锁被等待则为假 + + + + fastpath + boolean + + + 如果锁通过快速路径获得则为真,通过主锁表获得则为假 + + + + +
+ + + 在表示由指定进程持有的锁的行中,granted 为真。为假表示该进程当前正在等待获取此锁, + 这意味着至少有一个其他进程正在同一可锁定对象上持有或等待获取冲突的锁模式。等待的进程会休眠,直到另一个锁被释放 + (或检测到死锁情况)。单个进程一次最多只能等待获取一个锁。 + + + + 在执行事务期间,服务器进程会对事务的虚拟事务ID持有独占锁。如果为事务分配了永久ID + (通常仅在事务改变数据库状态时才会发生),它还会对事务的永久事务ID持有独占锁直到事务结束。 + 当一个进程发现有必要等待另一个事务结束时,它会尝试获取另一个事务ID(根据情况是虚拟ID还是永久ID) + 的共享锁。只有当另一个事务终止并释放其锁时,这才会成功。 + + + + 虽然元组是一种可锁定的对象类型,但关于行级锁的信息存储在磁盘上,而不是内存中,因此行级锁通常不会出现在此视图中。 + 如果一个进程正在等待行级锁,它通常会出现在视图中,等待当前持有该行锁的永久事务ID。 + + + + 咨询锁可以在由单个 bigint 值或两个整数值组成的键上获取。 + 一个bigint键在classid列中显示其高位半部分, + 在objid列中显示其低位半部分,并且objsubid等于1。 + 可以使用表达式(classid::bigint << 32) | objid::bigint重新组装原始bigint值。 + 整数键在classid列中显示第一个键,在objid列中显示第二个键, + 并且 objsubid 等于 2。键的实际含义由用户自行决定。咨询锁在每个数据库内都是本地的, + 因此 database 列对于咨询锁是有意义的。 + + + + pg_locks提供了集簇中所有锁的全局视图,不仅包括与当前数据库相关的锁。 + 虽然它的relation列可以与pg_class.oid 连接来识别被锁定的关系,但这仅对当前数据库中的关系有效(即 database 列为当前数据库的 OID 或零的那些关系)。 + + + + pid列可以与 + + pg_stat_activity + 视图的pid列进行连接,以获取有关持有或等待每个锁的会话的更多信息, + 例如 + +SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa + ON pl.pid = psa.pid; + + 另外,如果您正在使用预备事务,virtualtransaction列可以与 + pg_prepared_xacts + 视图的transaction列进行连接,以获取有关持有锁的预备事务的更多信息。 + (预备事务永远不会等待锁,但它继续持有其在运行时获取的锁。) + 例如: + +SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx + ON pl.virtualtransaction = '-1/' || ppx.transaction; + + + + + 尽管可以通过将pg_locks与自身连接来获取关于哪些进程阻塞了哪些其他进程的信息, + 但在细节上很难做到准确。这样的查询将不得不编码关于哪些锁模式与哪些其他锁模式冲突的知识。更糟糕的是, + pg_locks视图不公开关于哪些进程在锁等待队列中领先于哪些其他进程的信息, + 也不公开关于哪些进程是代表哪些其他客户端会话运行的并行工作者的信息。最好使用pg_blocking_pids()函数 + (参见)来识别等待进程被哪些进程阻塞。 + + + + pg_locks视图显示来自常规锁管理器和谓词锁管理器的数据,这两个是独立的系统; + 此外,常规锁管理器将其锁分为常规锁和快速路径锁。 + 不能保证这些数据完全一致。 + 当查询该视图时, + 快速路径锁的数据(具有fastpath = true) + 从每个后端逐个收集,而不会冻结整个锁管理器的状态,因此在收集信息时可能会发生锁的获取或释放。 + 但请注意,这些锁已知不会与当前放置的任何其他锁发生冲突。 + 在查询所有后端的快速路径锁后,剩余的常规锁管理器将作为一个单元被锁定,并且所有剩余锁的一致快照将作为一个原子操作收集。 + 解锁常规锁管理器后,谓词锁管理器类似地被锁定,并且所有谓词锁将作为一个原子操作收集。 + 因此,除了快速路径锁外,每个锁管理器将提供一致的结果集,但由于我们不同时锁定两个锁管理器,因此在询问常规锁管理器后和在询问谓词锁管理器前,可能会发生锁的获取或释放。 + + + + 如果这个视图被非常频繁地访问,锁定常规和(或)谓词锁管理器可能会对数据库性能产生一些影响。 + 锁仅在获取来自锁管理器的数据所需的最短时间内保持,但这并不能完全消除性能影响的可能性。 + + +
+ + + <structname>pg_matviews</structname> + + + pg_matviews + + + + materialized views + + + + 视图pg_matviews提供了对数据库中每个物化视图的有用信息的访问。 + + + + <structname>pg_matviews</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含物化视图的模式名称 + + + + matviewname + name + pg_class.relname + + 物化视图名称 + + + + matviewowner + name + pg_authid.rolname + + 物化视图拥有者名称 + + + + tablespace + name + pg_tablespace.spcname + + 包含物化视图的表空间名(如使用数据库默认表空间则为空) + + + + hasindexes + boolean + + + 如果物化视图有(或者最近有过)任何索引,则此列为真 + + + + ispopulated + boolean + + + 如果物化视图当前已被填充,则此列为真 + + + + definition + text + + + 物化视图的定义(一个重建的SELECT查询) + + + + +
+ +
+ + + <structname>pg_policies</structname> + + + pg_policies + + + + 视图pg_policies提供了有关数据库中每个行级安全策略的有用信息。 + + + + <structname>pg_policies</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含策略所在表的模式的名称 + + + + tablename + name + pg_class.relname + + 策略所在表的名称 + + + + policyname + name + pg_policy.polname + + 策略名称 + + + + + roles + name[] + + + 这个策略适用的角色 + + + + cmd + text + + + 这个策略适用的命令类型 + + + + qual + text + + + 作为这个策略适用的查询的安全屏障条件增加的表达式 + + + + with_check + text + + + 作为尝试向该表增加行的查询的 WITH CHECK 条件增加的表达式 + + + + +
+ +
+ + + <structname>pg_prepared_statements</structname> + + + pg_prepared_statements + + + + pg_prepared_statements 视图显示当前会话中所有可用的预备语句。 + 关于预备语句的更多信息,参见 。 + + + + pg_prepared_statements 为每个预备语句包含一行。 + 创建新的预备语句时,会向该视图添加一行;释放预备语句时(例如通过 + 命令)则会移除该行。 + + + + <structname>pg_prepared_statements</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + name + text + + 预备语句的标识符 + + + + statement + text + + 客户端提交用于创建此预备语句的查询语句。对于通过SQL创建的预备语句,这里是由客户端提交的PREPARE语句。 + 对于通过前端/后端协议创建的预备语句,这里是预备语句本身的文本。 + + + + prepare_time + timestamptz + + 预备语句被创建的时间 + + + + parameter_types + regtype[] + + 预备语句期望的参数类型,以一个regtype数组的形式。这个数组中一个元素所对应的OID可通过将regtype值转换为oid获得。 + + + + from_sql + boolean + + 如果预备语句通过SQL命令PREPARE创建,则为true;如果预备语句通过前端/后端协议创建,则为false + + + + +
+ + + pg_prepared_statements视图是只读的。 + +
+ + + <structname>pg_prepared_xacts</structname> + + + pg_prepared_xacts + + + + 视图pg_prepared_xacts显示当前准备进行两阶段提交的事务的信息 + (详见)。 + + + + pg_prepared_xacts 为每个预备事务包含一行。事务提交或回滚时,对应条目会被移除。 + + + + <structname>pg_prepared_xacts</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + transaction + xid + + + 预备事务的数字事务 ID + + + + gid + text + + + 分配给事务的全局标识符 + + + + prepared + timestamp with time zone + + + 此事务为提交准备好的时间 + + + + owner + name + pg_authid.rolname + + 执行此事务的用户名 + + + + database + name + pg_database.datname + + 执行此事务所在数据库的名称 + + + + +
+ + + 当访问pg_prepared_xacts视图时,内部事务管理器数据结构会被暂时锁定,并为视图制作一份副本用以显示。 + 这确保视图生成一致的结果集,同时不会不必要地阻塞正常操作。尽管如此,如果频繁访问该视图,可能会对数据库性能产生一些影响。 + + +
+ + + + <structname>pg_replication_origin_status</structname> + + + pg_replication_origin_status + + + + pg_replication_origin_status视图包含关于某个源的重放进度信息。 + 有关复制源的更多信息,请参见。 + + + + + <structname>pg_replication_origin_status</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + local_id + Oid + pg_replication_origin.roident + + 内部的节点标识符 + + + + + external_id + text + pg_replication_origin.roname + + 外部的节点标识符 + + + + + remote_lsn + pg_lsn + + + 源节点的 LSN,到这个位置的数据都已经被复制。 + + + + + local_lsn + pg_lsn + + + 这个节点的 LSN,remote_lsn已经被复制到这里。使用异步提交时,在将数据持久化到磁盘前用它来刷入提交记录。 + + + + +
+
+ + + <structname>pg_replication_slots</structname> + + + pg_replication_slots + + + + pg_replication_slots视图提供当前数据库集簇中所有复制槽及其当前状态的列表。 + + + + 有关复制槽的更多信息,请参见。 + + + + + <structname>pg_replication_slots</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + slot_name + name + + + 一个集簇范围内唯一的复制槽标识符 + + + + + plugin + name + + + 包含该逻辑槽所用输出插件的共享对象的基础名称;物理槽为 NULL。 + + + + + slot_type + text + + 槽类型——physicallogical + + + + datoid + oid + pg_database.oid + + 与该槽关联的数据库 OID,或为 NULL。只有逻辑槽才有关联数据库。 + + + + + database + text + pg_database.datname + + 与该槽关联的数据库名称,或为 NULL。只有逻辑槽才有关联数据库。 + + + + + + + active + boolean + + + 如果该槽当前正在使用中则为真 + + + + + active_pid + integer + + + 如果该槽当前正在使用中,则为使用该槽的会话的进程 ID;不活动时为 NULL。 + + + + + xmin + xid + + + 该槽要求数据库保留的最旧事务。VACUUM 不能移除由任何更晚事务删除的元组。 + + + + + catalog_xmin + xid + + + 该槽要求数据库保留的、影响系统目录的最旧事务。VACUUM 不能移除由任何更晚事务删除的目录元组。 + + + + + restart_lsn + pg_lsn + + 此槽的消费者可能仍然需要的最早 WAL 地址(LSN),因此检查点期间不会自动移除这些 WAL。 + + + + confirmed_flush_lsn + pg_lsn + + + 逻辑槽消费者已确认接收的数据所到达的地址(LSN)。早于此地址的数据将不再可用。物理槽为 NULL。 + + + + + +
+
+ + + <structname>pg_roles</structname> + + + pg_roles + + + + 视图pg_roles提供数据库角色的信息。它本质上是 + pg_authid 的公开可读视图,并隐藏密码字段。 + + + 此视图显式显示底层表的 OID 列,因为与其他目录连接时需要此列。 + + + <structname>pg_roles</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + rolname + name + + + 角色名 + + + + + rolsuper + bool + + + 角色有超级用户权限 + + + + + rolinherit + bool + + + 该角色是否会自动继承其所属角色的权限 + + + + + rolcreaterole + bool + + + 角色能创建更多角色 + + + + + rolcreatedb + bool + + + 角色能创建数据库 + + + + + rolcanlogin + bool + + + 角色是否可登录。也就是说,该角色能否被用作初始会话授权标识符 + + + + + rolreplication + bool + + + 角色是一个复制角色。复制角色可以启动复制连接并且创建和删除复制槽。 + + + + + rolconnlimit + int4 + + + 对于可以登录的角色,本列设置该角色能够建立的最大并发连接数。-1 表示无限制。 + + + + + rolpassword + text + + + 不是密码(始终显示为 ********) + + + + + rolvaliduntil + timestamptz + + + 密码过期时间(仅用于密码认证);不设过期时间时为空值 + + + + + rolbypassrls + bool + + + 角色是否可以绕过所有的行级安全性策略,详见。 + + + + + rolconfig + text[] + + + 运行时配置变量的角色特定默认值 + + + + + oid + oid + pg_authid.oid + + 角色的ID + + + + +
+ +
+ + + <structname>pg_rules</structname> + + + pg_rules + + + + 视图pg_rules提供了有关查询重写规则的有用信息。 + + + + <structname>pg_rules</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含表的模式名称 + + + + tablename + name + pg_class.relname + + 规则适用的表名 + + + + rulename + name + pg_rewrite.rulename + + 规则名 + + + + definition + text + + + 规则定义(重建的创建命令) + + + + +
+ + + 视图pg_rules排除了视图和物化视图的ON SELECT规则; + 这些规则可以在pg_views和 + pg_matviews中看到。 + + +
+ + + <structname>pg_seclabels</structname> + + + pg_seclabels + + + + 视图pg_seclabels提供有关安全标签的信息。它是 + pg_seclabel目录的更易查询版本。 + + + + <structname>pg_seclabels</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + objoid + oid + 任意 OID 列 + + 该安全标签依附的对象的OID + + + + classoid + oid + pg_class.oid + + 该对象所在系统目录的 OID + + + + objsubid + int4 + + + 对于一个在表列上的安全标签,这将是列号(objoidclassoid指表本身)。对于所有其他对象类型,本列为0。 + + + + objtype + text + + + 此标签应用的对象类型,以文本方式。 + + + + objnamespace + oid + pg_namespace.oid + + 如果适用,为此对象的命名空间 OID;否则为空。 + + + + objname + text + + + 此标签应用的对象名,以文本形式。 + + + + provider + text + pg_seclabel.provider + + 与此标签相关的标签提供者。 + + + + label + text + pg_seclabel.label + + 应用于此对象的安全标签。 + + + + +
+
+ + + + <structname>pg_settings</structname> + + + pg_settings + + + + 视图pg_settings提供对服务器运行时参数的访问。 + 它实质上是命令的替代接口。 + 它还提供了一些关于每个参数的事实,这些事实无法直接从SHOW中获取,例如最小值和最大值。 + + + + <structname>pg_settings</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + name + text + + 运行时配置参数名 + + + + setting + text + + 参数的当前值 + + + + unit + text + + 参数的隐式单元 + + + + category + text + + 参数的逻辑组 + + + + short_desc + text + + 参数的简短描述 + + + + extra_desc + text + + 附加的参数的详细描述 + + + + context + text + + 要求设置此参数值的上下文 (参见下方) + + + + vartype + text + + 参数类型 (bool, enum, + integer, real,或 string) + + + + source + text + + 当前参数值的来源 + + + + min_val + text + + 参数的最小允许值(对非数值参数为空) + + + + max_val + text + + 参数的最大允许值(对非数值参数为空) + + + + enumvals + text[] + + 枚举参数的允许值(对非枚举参数为空) + + + + boot_val + text + + 如果该参数未以其他方式设置,则为服务器启动时假定的参数值 + + + + reset_val + text + RESET在当前会话中会将此参数重置到的值 + + + sourcefile + text + + 设置当前值的配置文件(对于来自配置文件以外来源的值,或者由非超级用户查看时,为 NULL);在配置文件中使用 include 指令时这很有帮助 + + + + sourceline + integer + 设置当前值的配置文件中的行号(对于来自配置文件以外来源的值,或者由非超级用户查看时,为 NULL) + + + pending_restart + boolean + + 如果配置文件中修改了该值但需要重启,则为true,否则为false + + + + +
+ + + 有几种可能的context值。 + 按照更改设置的难度递减的顺序,它们是: + + + + + + internal + + + 这些设置不能直接更改;它们反映了内部确定的值。其中一些可能可通过使用不同的配置选项重新构建服务器,或通过更改提供给initdb的选项来进行调整。 + + + + + + postmaster + + + 这些设置只能在服务器启动时应用,因此任何更改都需要重启服务器。这些设置的值通常存储在 + postgresql.conf 文件中,或者在服务器启动时通过命令行传递。当然,任何较低 + context 类型的设置也都可以在服务器启动时设置。 + + + + + + sighup + + + 可以在 postgresql.conf 中更改这些设置,而无需重启服务器。 + 向 postmaster 发送 SIGHUP 信号,会使其重新读取 postgresql.conf 并应用这些更改。 + postmaster 还会将 SIGHUP 信号转发给其子进程,以便它们都采用新值。 + + + + + + superuser-backend + + + 可以在 postgresql.conf 中更改这些设置,而无需重启服务器。 + 也可以在连接请求报文中为特定会话设置这些值(例如,通过 libpqPGOPTIONS + 环境变量),但前提是连接用户是超级用户。 + 然而,这些设置在会话启动后永远不会更改。 + 如果在 postgresql.conf 中更改它们,请向 postmaster 发送 + SIGHUP 信号,使其重新读取 postgresql.conf。新值只会 + 影响随后启动的会话。 + + + + + + backend + + + 可以在 postgresql.conf 中更改这些设置,而无需重启服务器。 + 也可以在连接请求报文中为特定会话设置这些值(例如,通过 libpqPGOPTIONS + 环境变量);任何用户都可以为其会话进行此类更改。 + 但是,在会话启动后,这些设置永远不会更改。 + 如果在 postgresql.conf 中更改它们,请向 postmaster 发送 + SIGHUP 信号,使其重新读取 postgresql.conf。新值只会 + 影响随后启动的会话。 + + + + + + superuser + + + 这些设置可以从postgresql.conf中设置, + 或者通过SET命令在会话中设置;但只有超级用户 + 可以通过SET更改它们。 + 如果没有使用SET建立会话本地值,那么在postgresql.conf中的更改也会影响到现有会话。 + + + + + + user + + + 这些设置可以从postgresql.conf中设置, + 或通过SET命令在会话中设置。任何用户都可以更改其会话本地值。 + 如果没有使用SET建立会话本地值,那么在postgresql.conf中的更改也会影响到现有会话。 + + + + + + 有关更改这些参数的各种方法,详见 + + + pg_settings视图不能插入或删除,但可以更新。对 pg_settings 的某一行执行 + UPDATE,等同于对该命名参数执行 命令。更改只影响当前会话所用的值。 + 如果在之后被中止的事务中发出了 UPDATE,那么事务回滚时 UPDATE 命令的效果也会消失。 + 一旦外围事务提交,这些效果会持续到会话结束,除非被另一个 UPDATESET 覆盖。 + + +
+ + + <structname>pg_shadow</structname> + + + pg_shadow + + + + 视图pg_shadow存在是为了向后兼容性:它模拟了在PostgreSQL 8.1版本之前存在的目录。 + 它显示了在pg_authid中标记为rolcanlogin的所有角色的属性。 + + + + 这个名称源于该表不应向公众开放读取,因为它包含密码。 + pg_user + 是 pg_shadow 的公开可读视图,并隐藏密码字段。 + + + + <structname>pg_shadow</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + usename + name + pg_authid.rolname + + 用户名 + + + + + usesysid + oid + pg_authid.oid + + 用户的ID + + + + + usecreatedb + bool + + + 用户可创建数据库 + + + + + usesuper + bool + + + 用户为一个超级用户 + + + + + userepl + bool + + + 用户可开启流复制并将系统设置或者取消备份模式。 + + + + + usebypassrls + bool + + + 用户能否绕过所有的行级安全性策略,详见。 + + + + + passwd + text + + 密码(可能已加密);如果未设置则为空值。关于加密密码的存储方式,详见pg_authid + + + + valuntil + abstime + + + 密码过期时间(仅用于密码认证) + + + + + useconfig + text[] + + + 运行时配置变量的会话默认值 + + + + +
+ +
+ + + <structname>pg_stats</structname> + + + pg_stats + + + + 视图pg_stats提供对存储在pg_statistic目录中信息的访问。 + 此视图仅允许访问用户具有读取权限的表对应的pg_statistic行, + 因此可以安全地允许对此视图进行公共读取访问。 + + + + pg_stats也旨在以比底层目录更易读的格式呈现信息— + 但其模式必须在为pg_statistic定义新的槽类型时进行扩展。 + + + + <structname>pg_stats</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含表的模式名称 + + + + + tablename + name + pg_class.relname + + 表的名称 + + + + + attname + name + pg_attribute.attname + + 被此行描述的列名 + + + + + inherited + bool + + + 如果为true,则此行包括来自子表的值,而不仅仅是指定表中的值 + + + + + null_frac + real + + + 列项中为空的比例 + + + + + avg_width + integer + + + 列的条目的平均字节宽度 + + + + + n_distinct + real + + + 如果大于零,表示列中可区分值的估计个数。如果小于零,是可区分值个数除以行数的负值(当ANALYZE认为可区分值的数量会随着表增长而增加时采用负值的形式,而如果认为列具有固定数量的可选值时采用正值的形式)。 + 例如,-1表示一个唯一列,即其中可区分值的个数等于行数。 + + + + + most_common_vals + anyarray + + + 列中高频值的一个列表(如果没有任何一个值看起来比其他值更常用,此列为空) + + + + + most_common_freqs + real[] + + + 高频值的频率列表,即每一个高频值的出现次数除以总行数(如果most_common_vals为空,则此列为空) + + + + + histogram_bounds + anyarray + + + 将列值划分成大小接近的组的值列表。如果存在most_common_vals,其中的值会被直方图计算所忽略(如果列类型没有一个<操作符或者most_common_vals等于整个值集合,则此列为空) + + + + + correlation + real + + + 物理行顺序和列值逻辑顺序之间的统计关联。其范围从-1到+1。当值接近-1或+1时,在列上的一个索引扫描被认为比值接近0时的代价更低,因为这种情况减少了对磁盘的随机访问(如果列数据类型不具有一个<操作符,则此列为空) + + + + + most_common_elems + anyarray + + + 在列值中,最经常出现的非空元素列表(对标量类型为空) + + + + + most_common_elem_freqs + real[] + + + 最常用元素值的频度列表,即含有至少一个给定值实例的行的分数。 + 在每个元素的频度之后有二至三个附加值,它们是每个元素频度的最小和最大值,以及可选的空元素的频度(如果most_common_elems为空,则此列为空) + + + + + elem_count_histogram + real[] + + + 在列值中可区分非空元素值计数的一个直方图,后面跟随可区分非空元素的平均数(对于标量类型为空) + + + + +
+ + + 数组字段中的条目最大数量可以通过逐列控制,使用ALTER + TABLE SET STATISTICS + 命令,或者通过设置 + 运行时参数来全局控制。 + + +
+ + + <structname>pg_tables</structname> + + + pg_tables + + + + 视图pg_tables提供了对数据库中每个表的有用信息的访问。 + + + + <structname>pg_tables</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含表的模式名称 + + + + tablename + name + pg_class.relname + + 表的名称 + + + + tableowner + name + pg_authid.rolname + + 表拥有者名称 + + + + tablespace + name + pg_tablespace.spcname + + 包含该表的表空间名称(如果使用数据库的默认表空间,此列为空) + + + + hasindexes + boolean + pg_class.relhasindex + + 如果表有(或最近有过)任何索引,此列为真 + + + + hasrules + boolean + pg_class.relhasrules + + 如果表有(或曾经有过)规则,此列为真 + + + + hastriggers + boolean + pg_class.relhastriggers + + 如果表有(或者曾经有过)触发器,此列为真 + + + + rowsecurity + boolean + pg_class.relrowsecurity + + 如果表上启用了行安全性则为真 + + + + +
+ +
+ + + <structname>pg_timezone_abbrevs</structname> + + + pg_timezone_abbrevs + + + 视图pg_timezone_abbrevs提供当前被日期时间输入例程识别的时区缩写列表。当修改运行时参数时,该视图的内容会发生变化。 + + + <structname>pg_timezone_abbrevs</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + abbrev + text + + 时区缩写 + + + + utc_offset + interval + + 相对于UTC的偏移(正值表示格林威治东部) + + + + is_dst + boolean + + 如果这是一个夏令时缩写,则为真 + + + + +
+ + + 虽然大多数时区缩写代表与UTC的固定偏移量, + 但也有一些在历史上变化过(有关更多信息,请参见)。 + 在这种情况下,此视图呈现它们当前的含义。 + + +
+ + + <structname>pg_timezone_names</structname> + + + pg_timezone_names + + + + 视图pg_timezone_names提供了一个由SET TIMEZONE识别的时区名称列表, + 以及它们的相关缩写、UTC偏移和夏令时状态。 + (从技术上讲,PostgreSQL不使用UTC,因为不处理闰秒。) + 与在pg_timezone_abbrevs中显示的缩写不同, + 这些名称中的许多暗示了一组夏令时转换日期规则。 + 因此,相关信息在本地夏令时边界上发生变化。 + 显示的信息是基于CURRENT_TIMESTAMP的当前值计算的。 + + + + <structname>pg_timezone_names</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + name + text + + 时区名 + + + + abbrev + text + + 时区缩写 + + + + utc_offset + interval + + 相对于UTC的偏移(正值表示格林威治东部) + + + + is_dst + boolean + + 如果当前保持为夏令时则为真 + + + + +
+ +
+ + + <structname>pg_user</structname> + + + pg_user + + + + 视图pg_user提供数据库用户的信息。它本质上是 + pg_shadow 的公开可读视图,并隐藏密码字段。 + + + + <structname>pg_user</structname> 列 + + + + + 名称 + 类型 + + 描述 + + + + + + usename + name + + 用户名 + + + + + usesysid + oid + + 用户的ID + + + + + usecreatedb + bool + + 用户可创建数据库 + + + + + usesuper + bool + + 用户为一个超级用户 + + + + + userepl + bool + + 用户可开启流复制并将系统设置或者取消备份模式。 + + + + + usebypassrls + bool + + 用户能否绕过所有的行级安全性策略,详见。 + + + + + passwd + text + + 不是密码(始终显示为 ********) + + + + + valuntil + abstime + + 密码过期时间(仅用于密码认证) + + + + + useconfig + text[] + + 运行时配置变量的会话默认值 + + + + +
+ +
+ + + <structname>pg_user_mappings</structname> + + + pg_user_mappings + + + + 视图pg_user_mappings提供用户映射的信息。它本质上是 + pg_user_mapping 的公开可读视图, + 但如果用户没有使用相应选项的权限,则会省略选项字段。 + + + + <structname>pg_user_mappings</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + umid + oid + pg_user_mapping.oid + + 用户映射的OID + + + + + srvid + oid + pg_foreign_server.oid + + 包含该映射的外部服务器的OID + + + + + srvname + name + pg_foreign_server.srvname + + 外部服务器名 + + + + + umuser + oid + pg_authid.oid + + 将要被映射的本地角色的OID,如果用户映射是公共的则为零 + + + + + usename + name + + + 将被映射的本地用户名 + + + + + umoptions + text[] + + + 用户映射特定的选项,以keyword=value字符串形式 + + + + +
+ + 为了保护作为用户映射选项存储的密码信息,umoptions列会显示为空值,除非符合以下条件之一: + + + 当前用户是被映射的用户,并拥有服务器或在其上拥有USAGE权限 + + + + + 当前用户是服务器所有者,映射是为PUBLIC而进行的 + + + + + 当前用户是超级用户 + + + + + +
+ + + + <structname>pg_views</structname> + + + pg_views + + + + 视图pg_views提供了对数据库中每个视图的有用信息的访问。 + + + + <structname>pg_views</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + schemaname + name + pg_namespace.nspname + + 包含视图的模式名 + + + + viewname + name + pg_class.relname + + 视图名称 + + + + viewowner + name + pg_authid.rolname + + 视图拥有者名称 + + + + definition + text + + + 视图定义(一个重建的SELECT查询) + + + + +
+ +
+ +
diff --git a/zh/9.6/charset.sgml b/zh/9.6/charset.sgml new file mode 100644 index 00000000..ee9a3bb1 --- /dev/null +++ b/zh/9.6/charset.sgml @@ -0,0 +1,1338 @@ + + + + 本地化 + + + 本章从管理员的角度介绍可用的本地化特性。 + PostgreSQL支持两类本地化机制: + + + + + 利用操作系统的区域设置(locale)特性,提供与区域设置相关的排序顺序、 + 数字格式、翻译后的消息以及其他方面的支持。详见。 + + + + + + 提供多种字符集,以支持存储各种语言的文本,并提供客户端与服务器之间的 + 字符集转换。详见。 + + + + + + + + 区域设置支持 + + 区域设置 + + + 区域设置支持是指应用程序在字母表、排序、数字格式等 + 方面尊重文化偏好。PostgreSQL使用服务器操作系统 + 提供的标准 ISO C 和POSIX区域设置机制。更多信息请参考 + 系统文档。 + + + + 概述 + + + 在使用initdb创建数据库集簇时,区域设置支持会自动 + 初始化。默认情况下,initdb会使用其执行环境中的区域 + 设置来初始化数据库集簇,因此如果你的系统已经设置为数据库集簇想要使用的 + 区域设置,就无需额外操作。如果想使用不同的区域设置(或者不确定系统当前 + 设置了哪种区域设置),可以通过指定选项,精确 + 告诉initdb使用哪种区域设置。例如: + +initdb --locale=sv_SE + + + + + 这个 Unix 系统上的示例把区域设置为在瑞典(SE)使用的 + 瑞典语(sv)。其他可能的值包括en_US + (美国英语)和fr_CA(加拿大法语)。如果一个区域设置 + 可以使用多于一种字符集,则说明可以采用 + language_territory.codeset这样的形式。例如, + fr_BE.UTF-8表示在比利时(BE)使用、采用 + UTF-8字符集编码的法语(fr)。 + + + + 系统上有哪些区域设置可用,以及它们以什么名称出现,取决于操作系统供应商 + 提供了什么以及实际安装了什么。在大多数 Unix 系统上,命令 + locale -a会给出所有可用区域设置的列表。Windows 使用 + 更冗长的区域设置名称,例如German_Germany或 + Swedish_Sweden.1252,但原则是相同的。 + + + 有时混合多个区域设置的规则会很有用,例如使用英语排序规则但使用西班牙语消息。为此,存在一组只控制本地化规则某些方面的区域设置子类别: LC_COLLATE 字符串排序顺序 LC_CTYPE 字符分类(什么算字母?它的大写等价形式是什么?) LC_MESSAGES 消息的语言 LC_MONETARY 货币数量使用的格式 LC_NUMERIC 数字的格式 LC_TIME 日期和时间的格式 这些类别名会转换成initdb选项名,用于覆盖特定类别的区域设置选择。例如,要将区域设置设为加拿大法语,但对货币格式采用美国规则,可以使用initdb --locale=fr_CA --lc-monetary=en_US + + + 如果希望系统表现得像没有区域设置支持一样,可使用特殊区域设置名 + C,或等效的POSIX。 + + + + 某些区域设置类别的值必须在创建数据库时固定下来。你可以为不同数据库使用 + 不同设置,但数据库一旦创建,这些类别的值就不能再更改。 + LC_COLLATELC_CTYPE就属于这类 + 类别。它们会影响索引的排序顺序,因此必须保持固定,否则文本列上的索引会 + 损坏。(不过,可以通过排序规则缓解这一限制,参见。)这些类别的默认值在运行initdb + 时确定,创建新数据库时会使用这些值,除非在CREATE DATABASE + 命令中另行指定。 + + + + 其他区域设置类别可以在任何时候通过设置与其同名的服务器配置参数来更改 + (详见)。initdb + 选择的值实际上只是写入配置文件postgresql.conf中, + 作为服务器启动时的默认值。如果从postgresql.conf中 + 删除这些赋值,服务器就会从其执行环境继承相应设置。 + + + + 请注意,服务器的区域设置行为由服务器看到的环境变量决定,而不是由任何客 + 户端的环境决定。因此,务必在启动服务器前配置好正确的区域设置。由此带来 + 的一个结果是,如果客户端和服务器使用不同的区域设置,消息可能会因其来源 + 不同而显示为不同语言。 + + + + + 这里所说的从执行环境继承区域设置,在大多数操作系统上是指:对于某个给 + 定的区域设置类别,例如排序规则,会按以下顺序检查环境变量,直到找到一个 + 已设置的变量为止:LC_ALL、 + LC_COLLATE(或相应类别对应的变量)、LANG。 + 如果这些环境变量都没有设置,则区域设置默认为C。 + + + + 某些消息本地化库还会查看环境变量LANGUAGE,它会覆盖所有 + 其他用于设置消息的语言的区域设置。如果有疑问,请参考操作系统文档,尤其是 + 关于gettext的文档。 + + + + + 若要让消息可以翻译为用户偏好的语言,构建时必须启用NLS + (configure --enable-nls)。所有其他区域设置支持都会 + 自动内置。 + + + + + 行为 + + + 区域设置会影响下列 SQL 特性: + + + + + 在对文本数据使用ORDER BY或标准比较操作符的查询 + 中的排序顺序 + ORDER BY与区域设置 + + + + + + upperlower和 + initcap函数 + upper与区域设置 + lower与区域设置 + + + + + + 模式匹配操作符(LIKESIMILAR TO + 以及 POSIX 风格正则表达式);区域设置既会影响大小写不敏感匹配,也会 + 影响字符类正则表达式中的字符分类 + LIKE与区域设置 + 正则表达式与区域设置 + + + + + + to_char函数族 + to_char与区域设置 + + + + + + 对LIKE子句使用索引的能力 + + + + + + + 在PostgreSQL中使用C或 + POSIX之外的区域设置,缺点是会影响性能。它会减慢字 + 符处理速度,并阻止普通索引用于LIKE。因此,只有在确 + 有需要时才应使用区域设置。 + + + + 为了让PostgreSQL在非 C 区域设置下也能对 + LIKE子句使用索引,存在若干自定义操作符类可供使用。 + 它们允许创建执行严格逐字符比较、忽略区域设置比较规则的索引。详见。另一种方法是创建使用C + 排序规则的索引,如所述。 + + + + + 问题 + + + 如果区域设置支持没有像上面说明的那样工作,请检查操作系统中的区域设置支 + 持是否已经正确配置。要检查系统中安装了哪些区域设置,可以使用 + locale -a命令(如果操作系统提供该命令)。 + + + + 请确认PostgreSQL实际使用的区域设置就是你预 + 期的区域设置。LC_COLLATELC_CTYPE是在创 + 建数据库时确定的,除非重新创建数据库,否则不能更改。其他区域设置,包括 + LC_MESSAGESLC_MONETARY,最初由启动服 + 务器时的环境决定,但可以在运行中动态修改。你可以使用SHOW + 命令检查当前生效的区域设置。 + + + + 源码发布包中的src/test/locale目录包含 + PostgreSQL区域设置支持的测试套件。 + + + + 那些通过解析错误消息文本来处理服务器端错误的客户端应用,显然会遇到问 + 题,因为服务器消息可能使用不同语言。这类应用的作者应改用错误代码机制。 + + + + 维护消息翻译目录需要许多志愿者持续投入,他们希望 + PostgreSQL能够良好地支持他们偏好的语言。如 + 果你所用语言的消息目前还不可用,或尚未完全翻译,我们将非常感谢你的协 + 助。如果你愿意帮忙,请参考,或向开发者邮件列表写 + 信。 + + + + + + + 排序规则支持 + + 排序规则 + + + 排序规则特性允许为每一列甚至每一次操作指定数据的排序顺序和字符分类行为。 + 这缓解了数据库的LC_COLLATELC_CTYPE + 设置在创建之后无法更改这一限制。 + + + + 概念 + + + 从概念上讲,每个支持排序规则的数据类型的表达式都有一个排序规则。 + (内置的支持排序规则的数据类型包括textvarcharchar。 + 用户定义的基础类型也可以标记为支持排序规则,当然,建立在支持排序规则的数据类型之上的 + 域也支持排序规则。) + 如果表达式是列引用,则该表达式的排序规则就是该列定义的排序规则。如果表 + 达式是常量,则其排序规则就是该常量数据类型的默认排序规则。更复杂表达式 + 的排序规则则按下文所述,从其输入表达式的排序规则推导出来。 + + + + 表达式的排序规则可以是默认排序规则,这表示为数据库定义的 + 区域设置。表达式的排序规则也可能是不确定的。在这种情况下,排序操作以及 + 其他需要知道排序规则的操作都会失败。 + + + + 当数据库系统必须执行排序或字符分类时,它会使用输入表达式的排序规则。这 + 例如发生在ORDER BY子句以及函数或操作符调用(如 + <)中。应用于ORDER BY子句的 + 排序规则就是排序键的排序规则。应用于函数或操作符调用的排序规则则按下文 + 所述,从参数中推导出来。除了比较操作符之外,在大小写之间进行转换的函数 + (例如lowerupper和 + initcap)、模式匹配操作符,以及to_char + 及相关函数,也都会考虑排序规则。 + + + + 对于函数或操作符调用,通过检查参数排序规则推导出的排序规则,会在运行时 + 用于执行指定操作。如果该函数或操作符调用的结果属于支持排序规则的数据类型,那么 + 在解析时它也会被用作该函数或操作符表达式的已定义排序规则,以便在外围表 + 达式需要知道其排序规则时使用。 + + + + 表达式的排序规则派生可以是显式的,也可以是隐式 + 的。这一区别会影响当一个表达式中出现多个不同排序规则时,系统如何把它们 + 组合起来。使用COLLATE子句时,会发生显式排序规则派 + 生;其他所有排序规则派生都是隐式的。当需要组合多个排序规则时,例如在函 + 数调用中,将使用以下规则: + + + + + 如果任一输入表达式具有显式排序规则派生,那么输入表达式中所有显式派生 + 的排序规则都必须相同,否则会报错。如果存在显式派生的排序规则,那么排 + 序规则组合的结果就是该排序规则。 + + + + + + 否则,所有输入表达式都必须具有相同的隐式排序规则派生,或者为默认排序 + 规则。如果存在任何非默认排序规则,那么排序规则组合的结果就是该排序规 + 则;否则,结果就是默认排序规则。 + + + + + + 如果输入表达式之间存在相互冲突的非默认隐式排序规则,则该组合会被视为 + 具有不确定排序规则。除非被调用的特定函数确实需要知道它应当使用哪个排 + 序规则,否则这并不是错误。如果它确实需要,就会在运行时抛出错误。 + + + + + 例如,考虑如下表定义: + +CREATE TABLE test1 ( + a text COLLATE "de_DE", + b text COLLATE "es_ES", + ... +); + + + 那么在 + +SELECT a < 'foo' FROM test1; + + 中,<比较会按de_DE规则进行, + 因为该表达式组合了一个隐式派生的排序规则和默认排序规则。但在 + +SELECT a < ('foo' COLLATE "fr_FR") FROM test1; + + 中,比较会按fr_FR规则进行,因为显式排序规则派生覆盖 + 了隐式派生。进一步,给定 + +SELECT a < b FROM test1; + + 解析器无法确定应当应用哪个排序规则,因为a列 + 和b列具有冲突的隐式排序规则。由于 + <操作符确实需要知道要使用哪个排序规则,因此这会 + 导致一个错误。该错误可以通过给任一输入表达式附加显式排序规则说明符来解 + 决,例如: + +SELECT a < b COLLATE "de_DE" FROM test1; + + 或者等价地: + +SELECT a COLLATE "de_DE" < b FROM test1; + + 另一方面,结构相似的情况 + +SELECT a || b FROM test1; + + 不会导致错误,因为||操作符不关心排序规则:无论使用 + 什么排序规则,其结果都相同。 + + + + 如果函数或操作符返回的是支持排序规则的数据类型,那么分配给该函数或操作符组合输 + 入表达式的排序规则,也被认为适用于其结果。因此,在 + +SELECT * FROM test1 ORDER BY a || 'foo'; + + 中,排序将按de_DE规则进行。但这个查询: + +SELECT * FROM test1 ORDER BY a || b; + + 会报错,因为即使||操作符本身不需要知道排序规则, + ORDER BY子句仍然需要。与之前一样,可以通过显式排序 + 规则说明符来解决冲突: + +SELECT * FROM test1 ORDER BY a || b COLLATE "fr_FR"; + + + + + + 管理排序规则 + + + 排序规则是一个 SQL 模式对象,它把某个 SQL 名称映射到操作系统的区域设置。 + 具体来说,它对应于LC_COLLATELC_CTYPE + 的一种组合。(顾名思义,排序规则的主要用途是设置控制排序顺序的 + LC_COLLATE。但在实践中,很少有必要让 + LC_CTYPELC_COLLATE不同,因此把 + 二者归为一个概念,比再建立一套为每个表达式设置LC_CTYPE + 的机制更方便。)此外,排序规则还与某种字符集编码绑定(见)。 + 同一个排序规则名称可能会在不同编码中出现。 + + + 所有平台都提供名为 defaultCPOSIX 的排序规则。根据操作系统支持情况,还可能提供其他排序规则。default 排序规则选择创建数据库时指定的 LC_COLLATELC_CTYPE 值。CPOSIX 排序规则都采用传统 C行为,仅将 ASCII 字母 AZ 视为字母,并严格按字符编码的字节值排序。 + + + 如果操作系统支持在单个程序中使用多个区域设置(newlocale + 及相关函数),那么在初始化数据库集簇时, + initdb会根据当时在操作系统中发现的所有区域设置,用 + 排序规则填充系统目录pg_collation。例如,操作系统可能提供一个名为de_DE.utf8的区域设 + 置。initdb随后会为UTF8编码创建 + 一个名为de_DE.utf8的排序规则,其 + LC_COLLATELC_CTYPE都设置为 + de_DE.utf8。它还会再创建一个从名称中去掉 + .utf8标签的排序规则。因此,你也可以使用 + de_DE这个名称来使用该排序规则,这样写起来更方便, + 且名称与编码的耦合更小。不过请注意,初始排序规则名称集合仍然取决于平 + 台。 + + + + 如果需要一个LC_COLLATELC_CTYPE取值 + 不同的排序规则,可以使用命令创建新 + 的排序规则。该命令也可以用来从一个已有排序规则创建新的排序规则,这有助 + 于在应用中使用与操作系统无关的排序规则名称。 + + + + 在任何特定数据库中,只有使用该数据库编码的排序规则才有意义。 + pg_collation中的其他条目会被忽略。因此,像 + de_DE这样去掉编码后缀的排序规则名称,在某个给定数 + 据库内可以视为唯一,即使它在全局范围内并不唯一。推荐使用这种去掉后缀 + 的排序规则名称,因为如果你决定改用另一种数据库编码,需要改动的地方会 + 更少。不过要注意, + defaultCPOSIX + 排序规则不受数据库编码影响,始终都可以使用。 + + + + PostgreSQL即使面对具有相同属性的不同排序规 + 则对象,也会把它们视为不兼容。例如: + +SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1; + + 即使CPOSIX排序规则的行为完全 + 相同,这仍然会报错。因此,不建议混用去掉后缀和保留后缀的排序规则名 + 称。 + + + + + + + 字符集支持 + + 字符集 + + + PostgreSQL中的字符集支持允许你以多种字符集 + (也称为编码)存储文本,包括 ISO 8859 系列之类的单字节字符集,以及 + EUC(扩展 Unix 编码)、UTF-8 和 Mule 内部编 + 码等多字节字符集。所有受支持的字符集都可以被客户端透明地使用,但其中少 + 数不支持在服务器内部使用(即不能作为服务器端编码)。默认字符集是在使用 + initdb初始化PostgreSQL + 数据库集簇时选定的。创建数据库时可以覆盖该设置,因此你可以拥有多个数据 + 库,并让每个数据库使用不同的字符集。 + + + 不过,一项重要限制是,每个数据库的字符集必须与数据库的 LC_CTYPE(字符分类)和 LC_COLLATE(字符串排序顺序)区域设置兼容。对于 CPOSIX 区域设置,允许使用任何字符集;对于其他区域设置,只有一种字符集能够正确工作。(但在 Windows 上,UTF-8 编码可以与任何区域设置一起使用。) + + + 支持的字符集 + + + 显示了PostgreSQL中可用的字符集。 + + + + <productname>PostgreSQL</productname> 字符集 + + + + 名称 + 描述 + 语言 + 是否服务器端? + + 字节数/字符 + 别名 + + + + + BIG5 + Big Five + 繁体中文 + + 1-2 + WIN950, Windows950 + + + EUC_CN + 扩展 Unix 编码-CN + 简体中文 + + 1-3 + + + + EUC_JP + 扩展 Unix 编码-JP + 日语 + + 1-3 + + + + EUC_JIS_2004 + 扩展 Unix 编码-JP,JIS X 0213 + 日语 + + 1-3 + + + + EUC_KR + 扩展 Unix 编码-KR + 韩语 + + 1-3 + + + + EUC_TW + 扩展 Unix 编码-TW + 繁体中文、台湾语 + + 1-3 + + + + GB18030 + 国家标准 + 中文 + + 1-4 + + + + GBK + 扩展国家标准 + 简体中文 + + 1-2 + WIN936, Windows936 + + + ISO_8859_5 + ISO 8859-5, ECMA 113 + 拉丁语/西里尔语 + + 1 + + + + ISO_8859_6 + ISO 8859-6, ECMA 114 + 拉丁语/阿拉伯语 + + 1 + + + + ISO_8859_7 + ISO 8859-7, ECMA 118 + 拉丁语/希腊语 + + 1 + + + + ISO_8859_8 + ISO 8859-8, ECMA 121 + 拉丁语/希伯来语 + + 1 + + + + JOHAB + JOHAB + 韩语 + + 1-3 + + + + KOI8R + KOI8-R + 西里尔语(俄语) + + 1 + KOI8 + + + KOI8U + KOI8-U + 西里尔语(乌克兰语) + + 1 + + + + LATIN1 + ISO 8859-1, ECMA 94 + 西欧 + + 1 + ISO88591 + + + LATIN2 + ISO 8859-2, ECMA 94 + 中欧 + + 1 + ISO88592 + + + LATIN3 + ISO 8859-3, ECMA 94 + 南欧 + + 1 + ISO88593 + + + LATIN4 + ISO 8859-4, ECMA 94 + 北欧 + + 1 + ISO88594 + + + LATIN5 + ISO 8859-9, ECMA 128 + 土耳其语 + + 1 + ISO88599 + + + LATIN6 + ISO 8859-10, ECMA 144 + 北欧 + + 1 + ISO885910 + + + LATIN7 + ISO 8859-13 + 波罗的海 + + 1 + ISO885913 + + + LATIN8 + ISO 8859-14 + 凯尔特语 + + 1 + ISO885914 + + + LATIN9 + ISO 8859-15 + 带欧元符号和重音字符的 LATIN1 + + 1 + ISO885915 + + + LATIN10 + ISO 8859-16, ASRO SR 14111 + 罗马尼亚语 + + 1 + ISO885916 + + + MULE_INTERNAL + Mule 内部编码 + 多语种 Emacs + + 1-4 + + + + SJIS + Shift JIS + 日语 + + 1-2 + Mskanji, ShiftJIS, WIN932, Windows932 + + + SHIFT_JIS_2004 + Shift JIS, JIS X 0213 + 日语 + + 1-2 + + + + SQL_ASCII + 未指定(见正文) + 任意 + + 1 + + + + UHC + 统一韩语编码 + 韩语 + + 1-2 + WIN949, Windows949 + + + UTF8 + Unicode,8 位 + 所有 + + 1-4 + Unicode + + + WIN866 + Windows CP866 + 西里尔语 + + 1 + ALT + + + WIN874 + Windows CP874 + 泰语 + + 1 + + + + WIN1250 + Windows CP1250 + 中欧 + + 1 + + + + WIN1251 + Windows CP1251 + 西里尔语 + + 1 + WIN + + + WIN1252 + Windows CP1252 + 西欧 + + 1 + + + + WIN1253 + Windows CP1253 + 希腊语 + + 1 + + + + WIN1254 + Windows CP1254 + 土耳其语 + + 1 + + + + WIN1255 + Windows CP1255 + 希伯来语 + + 1 + + + + WIN1256 + Windows CP1256 + 阿拉伯语 + + 1 + + + + WIN1257 + Windows CP1257 + 波罗的海 + + 1 + + + + WIN1258 + Windows CP1258 + 越南语 + + 1 + ABC, TCVN, TCVN5712, VSCII + + + +
+ + + 并非所有客户端API都支持上表中的全部字符集。例如, + PostgreSQL JDBC 驱动就不支持 + MULE_INTERNALLATIN6、 + LATIN8LATIN10。 + + + SQL_ASCII 设置的行为与其他设置有很大不同。当服务器字符集为 SQL_ASCII 时,服务器按 ASCII 标准解释字节值 0-127,而将字节值 128-255 视为不作解释的字符。设置为 SQL_ASCII 时,不会进行任何编码转换。因此,这一设置与其说是声明使用某种特定编码,不如说是声明不关心编码。在大多数情况下,如果需要处理任何非 ASCII 数据,使用 SQL_ASCII 都是不明智的,因为 PostgreSQL 将无法帮助转换或验证非 ASCII 字符。 +
+ + + 设置字符集 + + + initdbPostgreSQL集 + 簇定义默认字符集(编码)。例如: + + +initdb -E EUC_JP + + + 这会把默认字符集设为EUC_JP(日语的扩展 Unix 编 + 码)。如果你喜欢更长的选项形式,也可以用代 + 替。如果既没有给出也没有给出 + initdb会根据指定的 + 或默认的区域设置,尝试确定应使用的合适编码。 + + + + 你可以在创建数据库时指定非默认编码,只要该编码与所选区域设置兼容: + + +createdb -E EUC_KR -T template0 --lc-collate=ko_KR.euckr --lc-ctype=ko_KR.euckr korean + + + 这将创建一个名为korean的数据库,它使用 + EUC_KR字符集和ko_KR区域设置。 + 另一种实现方式是使用以下 SQL 命令: + + +CREATE DATABASE korean WITH ENCODING 'EUC_KR' LC_COLLATE='ko_KR.euckr' LC_CTYPE='ko_KR.euckr' TEMPLATE=template0; + + + 请注意,上述命令指定复制template0数据库。如果复制 + 任何其他数据库,就不能更改源数据库的编码和区域设置,因为这可能导致数 + 据损坏。详见。 + + + + 数据库编码存储在系统目录pg_database中。可以通过 + psql选项或\l + 命令查看它。 + + +$ psql -l + List of databases + Name | Owner | Encoding | Collation | Ctype | Access Privileges +-----------+----------+-----------+-------------+-------------+------------------------------------- + clocaledb | hlinnaka | SQL_ASCII | C | C | + englishdb | hlinnaka | UTF8 | en_GB.UTF8 | en_GB.UTF8 | + japanese | hlinnaka | UTF8 | ja_JP.UTF8 | ja_JP.UTF8 | + korean | hlinnaka | EUC_KR | ko_KR.euckr | ko_KR.euckr | + postgres | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | + template0 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} + template1 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} +(7 rows) + + + + + + 在大多数现代操作系统上,PostgreSQL能够 + 判断LC_CTYPE设置所隐含的字符集,并强制只允许使用与之 + 匹配的数据库编码。在较老的系统上,你需要自行确保所用编码正是所选区域 + 设置期望的编码。这里的错误很可能会导致区域设置相关操作(例如排序)出 + 现奇怪的行为。 + + + + 即使LC_CTYPE不是C或 + POSIXPostgreSQL + 仍允许超级用户创建使用SQL_ASCII编码的数据库。正如 + 前文所述,SQL_ASCII并不强制数据库中存储的数据具 + 有任何特定编码,因此这种选择存在区域设置相关误行为的风险。这种设置组 + 合已经被弃用,未来某一天可能会被完全禁止。 + + + + + + 服务器和客户端之间的自动字符集转换 + + PostgreSQL 支持在服务器和客户端之间,对某些字符集组合进行自动字符集转换。转换信息保存在系统目录 pg_conversion 中。PostgreSQL 提供了一些预定义转换,如 所示。可以使用 SQL 命令 CREATE CONVERSION 创建新的转换。 + + + 客户端/服务器字符集转换 + + + + 服务器字符集 + 可用的客户端字符集 + + + + + BIG5 + 不支持作为服务器编码 + + + + EUC_CN + EUC_CN, + MULE_INTERNAL, + UTF8 + + + + EUC_JP + EUC_JP, + MULE_INTERNAL, + SJIS, + UTF8 + + + + EUC_JIS_2004 + EUC_JIS_2004, + SHIFT_JIS_2004, + UTF8 + + + + EUC_KR + EUC_KR, + MULE_INTERNAL, + UTF8 + + + + EUC_TW + EUC_TW, + BIG5, + MULE_INTERNAL, + UTF8 + + + + GB18030 + 不支持作为服务器编码 + + + + GBK + 不支持作为服务器编码 + + + + ISO_8859_5 + ISO_8859_5, + KOI8R, + MULE_INTERNAL, + UTF8, + WIN866, + WIN1251 + + + + ISO_8859_6 + ISO_8859_6, + UTF8 + + + + ISO_8859_7 + ISO_8859_7, + UTF8 + + + + ISO_8859_8 + ISO_8859_8, + UTF8 + + + + JOHAB + 不支持作为服务器编码 + + + + KOI8R + KOI8R, + ISO_8859_5, + MULE_INTERNAL, + UTF8, + WIN866, + WIN1251 + + + + KOI8U + KOI8U, + UTF8 + + + + LATIN1 + LATIN1, + MULE_INTERNAL, + UTF8 + + + + LATIN2 + LATIN2, + MULE_INTERNAL, + UTF8, + WIN1250 + + + + LATIN3 + LATIN3, + MULE_INTERNAL, + UTF8 + + + + LATIN4 + LATIN4, + MULE_INTERNAL, + UTF8 + + + + LATIN5 + LATIN5, + UTF8 + + + + LATIN6 + LATIN6, + UTF8 + + + + LATIN7 + LATIN7, + UTF8 + + + + LATIN8 + LATIN8, + UTF8 + + + + LATIN9 + LATIN9, + UTF8 + + + + LATIN10 + LATIN10, + UTF8 + + + + MULE_INTERNAL + MULE_INTERNAL, + BIG5, + EUC_CN, + EUC_JP, + EUC_KR, + EUC_TW, + ISO_8859_5, + KOI8R, + LATIN1LATIN4, + SJIS, + WIN866, + WIN1250, + WIN1251 + + + + SJIS + 不支持作为服务器编码 + + + + SHIFT_JIS_2004 + 不支持作为服务器编码 + + + + SQL_ASCII + 任何(不会执行转换) + + + + UHC + 不支持作为服务器编码 + + + + UTF8 + 所有受支持的编码 + + + + WIN866 + WIN866, + ISO_8859_5, + KOI8R, + MULE_INTERNAL, + UTF8, + WIN1251 + + + + WIN874 + WIN874, + UTF8 + + + + WIN1250 + WIN1250, + LATIN2, + MULE_INTERNAL, + UTF8 + + + + WIN1251 + WIN1251, + ISO_8859_5, + KOI8R, + MULE_INTERNAL, + UTF8, + WIN866 + + + + WIN1252 + WIN1252, + UTF8 + + + + WIN1253 + WIN1253, + UTF8 + + + + WIN1254 + WIN1254, + UTF8 + + + + WIN1255 + WIN1255, + UTF8 + + + + WIN1256 + WIN1256, + UTF8 + + + + WIN1257 + WIN1257, + UTF8 + + + + WIN1258 + WIN1258, + UTF8 + + + + +
+ + + 若要启用自动字符集转换,必须告诉PostgreSQL + 你希望客户端使用哪种字符集(编码)。有几种方法可以做到这一点: + + + + + 在psql中使用\encoding + 命令。\encoding允许动态更改客户端编码。例如,要把 + 编码改成SJIS,可以输入: + + +\encoding SJIS + + + + + + + libpq) + 提供了控制客户端编码的函数。 + + + + + + 使用SET client_encoding TO。 + + 可以使用以下 SQL 命令设置客户端编码: + + +SET CLIENT_ENCODING TO 'value'; + + + 也可以为此使用标准 SQL 语法SET NAMES: + + +SET NAMES 'value'; + + + 要查询当前客户端编码: + + +SHOW client_encoding; + + + 要返回到缺省编码: + + +RESET client_encoding; + + + + + + + 使用PGCLIENTENCODING。如果在客户端环境中定义了 + PGCLIENTENCODING环境变量,那么连接服务器时会自动选 + 择该客户端编码。(之后仍可以用上面提到的任何其他方法覆盖它。) + + + + + + 使用配置变量。如果设置了 + client_encoding变量,那么连接服务器时会自动选 + 择该客户端编码。(之后仍可以用上面提到的任何其他方法覆盖它。) + + + + + + + + 如果某个特定字符无法完成转换,就会报错。例如,假设服务器使用 + EUC_JP而客户端使用LATIN1,此 + 时若返回了一些在LATIN1中没有表示形式的日语字符, + 就会出现错误。 + + + 如果客户端字符集定义为 SQL_ASCII,则无论服务器字符集是什么,都会禁用编码转换。与服务器一样,除非处理的全部是 ASCII 数据,否则使用 SQL_ASCII 是不明智的。 +
+ + + 进一步阅读 + + 这些都是开始了解各种编码系统的良好资料。 + + CJKV Information Processing: Chinese, Japanese, Korean & Vietnamese Computing + + + + 其中对EUC_JPEUC_CN、 + EUC_KREUC_TW做了详细说明。 + + + + + + + + + + Unicode Consortium 网站。 + + + + + + RFC 3629 + + + + 这里定义了UTF-8(8 位 UCS/Unicode 转换格式)。 + + + + + + + +
+ +
diff --git a/zh/9.6/chkpass.sgml b/zh/9.6/chkpass.sgml new file mode 100644 index 00000000..021ceec0 --- /dev/null +++ b/zh/9.6/chkpass.sgml @@ -0,0 +1,60 @@ + + + + chkpass + + + chkpass + + + 此模块实现了用于存储加密密码的数据类型chkpass。每个密码在输入时都会自动转换为加密形式,并且始终以加密形式存储。比较时,只需与明文密码比较,比较函数会在比较之前将其加密。 + + 代码中预留了在判定密码容易被破解时报告错误的机制。不过,目前这只是一个不做任何事情的占位实现。 + + 如果在输入字符串前加上冒号,它就会被视为已加密的密码,直接存储而不再加密。这样就可以输入以前加密过的密码。 + + 输出时会在前面加上冒号。这样就可以转储并重新载入密码,而不必重新加密。如果需要不带冒号的加密密码,请使用raw()函数。这样就可以将此类型与 Apache 的Auth_PostgreSQL模块等工具一起使用。 + + 加密使用标准 Unix 函数crypt(),因此也受该函数所有常见限制的影响,尤其是只考虑密码的前八个字符。 + + 请注意,chkpass数据类型不能建立索引。 + + 使用示例: + + +test=# create table test (p chkpass); +CREATE TABLE +test=# insert into test values ('hello'); +INSERT 0 1 +test=# select * from test; + p +---------------- + :dVGkpXdOrE3ko +(1 row) + +test=# select raw(p) from test; + raw +--------------- + dVGkpXdOrE3ko +(1 row) + +test=# select p = 'hello' from test; + ?column? +---------- + t +(1 row) + +test=# select p = 'goodbye' from test; + ?column? +---------- + f +(1 row) + + + + 作者 + + D'Arcy J.M. Cain(darcy@druid.net + + + diff --git a/zh/9.6/citext.sgml b/zh/9.6/citext.sgml new file mode 100644 index 00000000..e830a59d --- /dev/null +++ b/zh/9.6/citext.sgml @@ -0,0 +1,224 @@ + + + + citext — 大小写不敏感的字符串类型 + + + citext + + + + citext模块提供一种大小写不敏感的字符串类型 + citext。本质上,它在比较值时会在内部调用 + lower。除此之外,它的行为几乎与 + text完全相同。 + + + + 原理 + + + 在PostgreSQL中进行大小写不敏感匹配的标准方法, + 一直是在比较值时使用lower函数,例如: + + +SELECT * FROM tab WHERE lower(col) = LOWER(?); + + + + + 这种做法效果尚可,但有几个缺点: + + + + + + 它会让 SQL 语句变得冗长,而且你必须时刻记得同时对列和查询值调用 + lower。 + + + + + 除非你创建一个使用lower的函数索引,否则它不会使用索引。 + + + + + 如果把列声明为UNIQUEPRIMARY + KEY,隐式生成的索引仍然是大小写敏感的。因此,它既无法用于大小写不敏感搜索,也不能以大小写不敏感的方式强制唯一性。 + + + + + + citext数据类型允许你在 SQL 查询中省去对 + lower的调用,并允许主键不区分大小写。 + 与text一样,citext也与区域设置相关,这意味着大写字符和小写字符如何匹配取决于数据库 + LC_CTYPE设置的规则。同样,这种行为与在查询中使用 + lower完全一致。但由于这是由数据类型透明地完成的, + 因此你不必在查询中额外记住任何特殊处理。 + + + + + + 如何使用 + + 下面是一个简单的使用示例: +CREATE TABLE users ( + nick CITEXT PRIMARY KEY, + pass TEXT NOT NULL +); + +INSERT INTO users VALUES ( 'larry', md5(random()::text) ); +INSERT INTO users VALUES ( 'Tom', md5(random()::text) ); +INSERT INTO users VALUES ( 'Damian', md5(random()::text) ); +INSERT INTO users VALUES ( 'NEAL', md5(random()::text) ); +INSERT INTO users VALUES ( 'Bjørn', md5(random()::text) ); + +SELECT * FROM users WHERE nick = 'Larry'; +这个SELECT语句仍会返回一个元组,尽管nick列中存的是larry,而查询条件写的是Larry。 + + + + + 字符串比较行为 + + + citext在比较时会先把每个字符串转换为小写 + (就像调用了lower一样),然后再按常规方式比较结果。 + 因此,举例来说,如果两个字符串经过lower后得到的结果相同, + 它们就会被视为相等。 + + + + 为了尽可能贴近大小写不敏感排序规则的行为,一些字符串处理操作符和函数都提供了 + citext专用版本。例如,当应用于citext时, + 正则表达式操作符~~*表现相同: + 它们都会以大小写不敏感的方式匹配。!~和 + !~*也是如此,LIKE操作符 + ~~~~*、以及 + !~~!~~*也一样。如果你希望进行大小写敏感匹配,可以把这些操作符的参数转换为text。 + + + + 同样地,如果这些函数的参数是citext,它们也会以大小写不敏感方式进行匹配: + + + + + + regexp_matches() + + + + + regexp_replace() + + + + + regexp_split_to_array() + + + + + regexp_split_to_table() + + + + + replace() + + + + + split_part() + + + + + strpos() + + + + + translate() + + + + + + 对于正则表达式函数,如果你想按大小写敏感方式匹配,可以指定 + c标志来强制大小写敏感匹配。否则,若要获得大小写敏感行为, + 就必须在调用这些函数之前先把值转换为text。 + + + + + + 限制 + + + + + citext的大小写折叠行为依赖于数据库的 + LC_CTYPE设置,因此它如何比较值是在创建数据库时确定的。 + 按照 Unicode 标准中的定义,它并不是真正意义上的大小写不敏感。 + 实际上,这意味着只要你对当前排序规则满意,通常也会对 + citext的比较结果满意。但是,如果数据库中存有多种语言的数据, + 而排序规则只适用于其中某一种语言,那么其他语言的用户可能会发现查询结果并不符合预期。 + + + + + + 从PostgreSQL 9.1 起,你可以为 + citext列或数据值附加COLLATE说明。 + 当前,citext操作符在比较已完成大小写折叠的字符串时,会遵从非默认的 + COLLATE说明;但最初转换为小写这一步,始终仍按数据库的 + LC_CTYPE设置执行(也就是说,等同于给出了 + COLLATE "default")。未来的发行版中,这一点可能会改变,从而使这两步都遵循输入的COLLATE说明。 + + + + + citext不如text高效,因为操作符函数和 B-树比较函数必须复制数据,并将数据转换为小写后才能进行比较。不过,在需要大小写不敏感匹配时,它仍然比使用lower略高效一些。 + + + + + 如果你在某些场景下需要大小写敏感比较,而在另一些场景下又需要大小写不敏感比较, + 那么citext并不会帮上太多忙。标准做法是使用 + text类型,并在需要大小写不敏感比较时手工调用 + lower;如果这类比较只是不常见地出现,这种做法是完全可行的。 + 如果你大多数时候都需要大小写不敏感行为,而只是在少数场景下需要大小写敏感比较, + 那么可以考虑把数据存储为citext,并在需要大小写敏感比较时显式地把列转换为 + text。无论哪种情况,如果你希望这两类搜索都足够快,就都需要建立两个索引。 + + + + + + 包含citext操作符的模式必须位于当前 + search_path中(通常是public); + 如果不在,调用的将是普通的、大小写敏感的text操作符。 + + + + + + + 作者 + + + David E. Wheeler david@kineticode.com + + + + 灵感来自 Donald Fraser 最初编写的citext模块。 + + + + + diff --git a/zh/9.6/client-auth.sgml b/zh/9.6/client-auth.sgml new file mode 100644 index 00000000..510e6af8 --- /dev/null +++ b/zh/9.6/client-auth.sgml @@ -0,0 +1,1161 @@ + + + + 客户端认证 + + + 客户端认证 + + + + 当客户端应用连接到数据库服务器时,它会指定要以哪个 PostgreSQL 数据库用户名连接,这很像以某个特定用户身份登录 Unix 计算机一样。在 SQL 环境中,当前活动的数据库用户名决定了对数据库对象的访问权限 — 详见 。因此,必须限制哪些数据库用户能够连接。 + + + + + + 如 中所述,PostgreSQL 实际上是以 角色 为单位进行权限管理的。在本章中,我们统一使用 数据库用户 来表示 拥有 LOGIN 权限的角色。 + + + + + 认证是数据库服务器确认客户端身份的过程,并据此决定是否允许该客户端应用(或者运行该客户端应用的用户)以请求的数据库用户名进行连接。 + + + + PostgreSQL 提供了多种不同的客户端认证方法。用于认证特定客户端连接的方法可以根据(客户端)主机地址、数据库和用户来选择。 + + + + PostgreSQL 数据库用户名在逻辑上独立于服务器所在操作系统中的用户名。如果某台服务器的所有用户在该机器上也都有账号,那么为他们分配与操作系统用户名一致的数据库用户名是有意义的。不过,接受远程连接的服务器可能有许多数据库用户并没有本地操作系统账号,在这种情况下,数据库用户名与操作系统用户名之间就不必存在任何对应关系。 + + + + <filename>pg_hba.conf</filename> 文件 + + + pg_hba.conf + + + + 客户端认证由一个配置文件控制,该文件按惯例命名为 + pg_hba.conf,并存放在数据库集簇的数据目录中。 + (HBA 代表 host-based authentication,即基于主机的认证。) + 当数据目录由 initdb 初始化时,会安装一个默认的 + pg_hba.conf 文件。不过,也可以把认证配置文件放在别处; + 请参见配置参数 。 + + + pg_hba.conf 文件的基本格式是一组记录,每行一条。空行和 # 注释字符之后的所有文本都会被忽略。记录不能跨行续写。每条记录由若干字段组成,字段之间用空格和/或制表符分隔。如果字段值用双引号括起来,就可以包含空白。在数据库、用户或地址字段中,将关键字(例如 allreplication)用引号括起来,会使其失去特殊含义,只匹配同名的数据库、用户或主机。 + + + 每条认证记录都指定一种连接类型、一个客户端 IP 地址范围(如果该连接类型需要)、一个数据库名、一个用户名,以及对匹配这些参数的连接要使用的认证方法。第一条同时匹配连接类型、客户端地址、请求数据库和用户名的记录会被用来执行认证。这里不存在 继续向后匹配后备 机制:如果选中某条记录而认证失败,就不会再考虑后续记录。如果没有任何记录匹配,则拒绝访问。 + + + 记录可以采用以下七种格式之一 +local database user auth-method auth-options +host database user address auth-method auth-options +hostssl database user address auth-method auth-options +hostnossl database user address auth-method auth-options +host database user IP-address IP-mask auth-method auth-options +hostssl database user IP-address IP-mask auth-method auth-options +hostnossl database user IP-address IP-mask auth-method auth-options +各字段的含义如下: + + local + + + + 该记录匹配使用 Unix 域套接字的连接尝试。没有这种类型的记录时,Unix 域套接字连接将被禁止。 + + + + + + host + + 此记录匹配通过 TCP/IP 发起的连接尝试。host 记录既匹配 SSL 连接,也匹配非 SSL 连接。 + + + + 除非服务器以适当的 + 配置参数值启动, + 否则远程 TCP/IP 连接将不可用,因为默认行为是只在本地回环地址 + localhost 上监听 TCP/IP 连接。 + + + + + + + hostssl + + + + 该记录匹配使用 TCP/IP 发起的连接尝试,但仅当连接使用 + SSL 加密时才匹配。 + + + + 要使用此选项,服务器必须在构建时启用 SSL 支持。 + 此外,还必须在服务器启动时通过设置 配置参数来启用 SSL + (有关更多信息,请参见)。 + + + + + + hostnossl + + + + 该记录类型与 hostssl 的行为相反;它只匹配通过 + TCP/IP 发起且不使用 SSL 的连接尝试。 + + + + + + database + + 指定此记录匹配的数据库名称。all 表示匹配所有数据库。sameuser 表示请求的数据库与请求的用户同名时才匹配。samerole 要求请求的用户是与所请求数据库同名的角色的成员。(samegroupsamerole 已过时但仍被接受的写法。)对于 samerole,只有显式地直接或间接属于该角色,超级用户才被视为其成员,仅凭超级用户身份并不算。replication 表示请求复制连接时匹配此记录(注意,复制连接不指定任何特定数据库)。其他值则表示某个特定的 PostgreSQL 数据库的名称。可以用逗号分隔多个数据库名称。也可以在文件名前加 @,指定一个单独存放数据库名称的文件。 + + + + + user + + 指定此记录匹配的数据库用户名。all 表示匹配所有用户。其他值可以是某个特定数据库用户的名称,也可以是前面带 + 的组名。(请记住,在 PostgreSQL 中,用户和组并无实质区别;+ 实际表示匹配直接或间接属于此角色的任何角色,而不带 + 的名称仅匹配该角色本身。)为此,只有显式地直接或间接属于该角色,超级用户才被视为其成员,仅凭超级用户身份并不算。可以用逗号分隔多个用户名。也可以在文件名前加 @,指定一个单独存放用户名的文件。 + + + + + address + + + 指定此记录匹配的客户端机器地址。此字段可以包含主机名、IP地址范围或下面提到的特殊关键字之一。 + + + + IP地址范围使用标准的数字表示法来指定起始地址,然后是斜杠(/)和一个CIDR掩码长度。 + 掩码长度表示客户端IP地址必须匹配的高位比特数。给定IP地址中右侧的比特应为零。 + IP地址、/和CIDR掩码长度之间不得有任何空白。 + + + + 以这种方式指定的IPv4地址范围的典型示例包括172.20.143.89/32用于单个主机, + 或172.20.143.0/24用于小型网络,或10.6.0.0/16用于较大的网络。 + IPv6地址范围可能看起来像::1/128用于单个主机(在这种情况下是IPv6环回地址)或 + fe80::7a31:c1ff:0000:0000/96用于小型网络。 + 0.0.0.0/0代表所有IPv4地址,::0/0代表所有IPv6地址。 + 要指定单个主机,请对IPv4使用32的掩码长度,对IPv6使用128。在网络地址中,不要省略尾部的零。 + + + + 以IPv4格式给出的条目将仅匹配IPv4连接,以IPv6格式给出的条目将仅匹配IPv6连接, + 即使所代表的地址在IPv4-in-IPv6范围内。请注意,如果系统的C库不支持IPv6地址, + 以IPv6格式给出的条目将被拒绝。 + + + + 你也可以写all来匹配任何IP地址, + samehost来匹配服务器自己的任何IP地址, + 或samenet来匹配服务器直接连接到的任何子网中的任何地址。 + + + + 如果指定了主机名(任何不是IP地址范围或特殊关键字的内容都被视为主机名), + 则将该名称与客户端IP地址的反向名称解析结果进行比较(例如,如果使用DNS,则进行反向DNS查找)。 + 主机名比较不区分大小写。如果匹配成功,则对主机名执行正向名称解析(例如,进行正向DNS查找), + 以检查其解析为的任何地址是否等于客户端IP地址。如果两个方向都匹配,则将条目视为匹配。 + (在pg_hba.conf中使用的主机名应该是客户端IP地址的地址到名称解析返回的名称, + 否则该行将不会匹配。一些主机名数据库允许将IP地址与多个主机名关联, + 但操作系统在要求解析IP地址时只会返回一个主机名。) + + + + 以点(.)开头的主机名规范匹配实际主机名的后缀。 + 因此,.example.com将匹配foo.example.com + (但不匹配单独的example.com)。 + + + + 当在pg_hba.conf中指定主机名时,应确保名称解析相对快速。 + 设置一个本地名称解析缓存可能会有帮助,如nscd。 + 此外,还可能希望启用配置参数log_hostname,以便在日志中看到客户端的主机名而不是 IP 地址。 + + + 此字段仅适用于 hosthostsslhostnossl 记录。 + + + + + 用户有时会想知道为什么主机名以这种看似复杂的方式处理,包括两次名称解析,其中包括对客户端IP地址的反向查找。 + 如果客户端的反向DNS条目未设置或返回了不符合预期的主机名,则使用该功能会变得复杂。 + 这主要是为了效率:这样,连接尝试最多需要两次解析器查找,一次反向查找和一次正向查找。 + 如果某个地址存在解析器问题,那就只会成为该客户端的问题。 + 一个假设的替代实现只进行正向查找的情况下,在每次连接尝试期间都必须解析pg_hba.conf中提到的每个主机名。 + 如果列出了许多名称,这可能会非常慢。 + 如果其中一个主机名存在解析器问题,那么这将成为所有人的问题。 + + + + 此外,实现后缀匹配功能需要进行反向查找,因为需要知道实际客户端主机名 + 以便将其与模式进行匹配。 + + + + 请注意,这种行为与其他流行的基于主机名的访问控制实现一致,例如 + Apache HTTP 服务器和 TCP Wrappers。 + + + + + + + IP-address + IP-mask + + + 这两个字段可以用作IP-address/mask-length + 表示法的替代方案。而不是指定掩码长度,实际掩码在一个单独的列中指定。 + 例如,255.0.0.0表示IPv4的CIDR掩码长度为8, + 而255.255.255.255表示CIDR掩码长度为32。 + + + 这些字段仅适用于 hosthostsslhostnossl 记录。 + + + + + auth-method + + + 指定连接匹配此记录时要使用的认证方法。可选值在此处做了概述;详细说明见 。 + + + + trust + + + + 无条件允许连接。这种方法允许任何能够连接到PostgreSQL数据库服务器的人以任意他们希望的PostgreSQL用户身份登录,无需密码或任何其他认证。详见。 + + + + + + reject + + + + 无条件拒绝连接。这对于过滤掉某些主机很有用,例如一个reject行可以阻止特定主机连接, + 而后面的行允许特定网络中的其余主机连接。 + + + + + + md5 + + + + 要求客户端提供一个经过双重 MD5 哈希的密码以完成认证。 + 详见。 + + + + + + password + + + + 要求客户端提供未加密的密码以完成认证。 + 由于密码会以明文形式通过网络发送,因此不应在不受信任的网络上使用。 + 详见。 + + + + + + gss + + 使用 GSSAPI 认证用户。这仅适用于 TCP/IP 连接。详情参见 + + + + + sspi + + + + 使用 SSPI 对用户进行认证。这仅适用于 Windows。详见。 + + + + + + ident + + + + 通过联系客户端上的 ident 服务器获取客户端的操作系统用户名, + 并检查它是否与请求的数据库用户名匹配。 + Ident 认证只能用于 TCP/IP 连接。 + 当为本地连接指定时,将改为使用 peer 认证。 + 详见。 + + + + + + peer + + + + 从操作系统获取客户端的操作系统用户名,并检查是否与请求的数据库用户名匹配。 + 这仅适用于本地连接。 + 有关详细信息,请参见。 + + + + + + ldap + + + + 使用LDAP服务器进行认证。详见。 + + + + + + radius + + + + 使用 RADIUS 服务器进行认证。详见。 + + + + + + cert + + + + 使用 SSL 客户端证书进行认证。详见。 + + + + + + pam + + + + 使用操作系统提供的可插拔认证模块(PAM)服务进行认证。详见。 + + + + + + bsd + + + + 使用操作系统提供的 BSD 认证服务进行认证。详见。 + + + + + + + + + + + auth-options + + auth-method 字段之后,可以有一个或多个形如 name=value 的字段,用于指定认证方法的选项。下文会详细说明各认证方法有哪些可用选项。 + + 除下文列出的各方法专用选项外,还有一个与方法无关的认证选项 clientcert,可以在任何 hostssl 记录中指定。将其设为 1 时,除了满足认证方法的其他要求外,客户端还必须提供有效(受信任)的 SSL 证书。 + + + + + + + 由 @ 构造引用的文件会被读取为名称列表,其中的名称可以用空白或逗号分隔。注释仍然用 # 引入,与 pg_hba.conf 中相同,并且允许嵌套的 @ 构造。除非 @ 后面的文件名是绝对路径,否则它会被视为相对于引用它的文件所在目录。 + + + 由于每次连接尝试都会按顺序检查 pg_hba.conf 记录,因此记录的顺序很重要。通常,靠前的记录使用较严格的连接匹配条件和较弱的认证方法,靠后的记录使用较宽松的匹配条件和较强的认证方法。例如,可能希望对本地 TCP/IP 连接使用 trust 认证,而要求远程 TCP/IP 连接提供密码。此时,为来自 127.0.0.1 的连接指定 trust 认证的记录,应放在为更大范围的允许客户端 IP 地址指定密码认证的记录之前。 + + + 在启动时以及主服务器进程收到 SIGHUPSIGHUP 信号时,pg_hba.conf 文件会被读取。 + 如果你在运行中的系统上编辑了该文件,就需要通知 postmaster(使用 pg_ctl reload、调用 SQL 函数 pg_reload_conf(),或者使用 kill -HUP)重新读取该文件。 + + + + + + 要连接到一个特定数据库,一个用户必须不仅要通过pg_hba.conf检查,还必须要有该数据库上的CONNECT权限。如果你希望限制哪些用户能够连接到哪些数据库,授予/撤销CONNECT权限通常比在pg_hba.conf项中设置规则简单。 + + + + 展示了一些 pg_hba.conf 条目示例。有关不同认证方法的详细信息,参见下一节。 + + + 示例 <filename>pg_hba.conf</filename> 项 + +# 允许本地系统上的任何用户 +# 通过 Unix 域套接字(本地连接的默认方式)以任意 +# 数据库用户名连接到任意数据库。 +# +# TYPE DATABASE USER ADDRESS METHOD +local all all trust + +# 相同的规则,但是使用本地环回 TCP/IP 连接。 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all 127.0.0.1/32 trust + +# 和前一行相同,但是使用了一个独立的掩码列 +# +# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD +host all all 127.0.0.1 255.255.255.255 trust + +# IPv6 上相同的规则 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all ::1/128 trust + +# 使用主机名的相同规则(通常同时覆盖 IPv4 和 IPv6)。 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all localhost trust + +# 允许来自任意具有 IP 地址192.168.93.x 的主机上任意 +# 用户以 ident 为该连接所报告的相同用户名连接到 +# 数据库 "postgres"(通常是操作系统用户名)。 +# +# TYPE DATABASE USER ADDRESS METHOD +host postgres all 192.168.93.0/24 ident + +# 如果用户的密码被正确提供,允许来自主机 192.168.12.10 +# 的任意用户连接到数据库 "postgres"。 +# +# TYPE DATABASE USER ADDRESS METHOD +host postgres all 192.168.12.10/32 md5 + +# 如果用户的密码被正确提供,允许 example.com 域中主机上 +# 的任意用户连接到任意数据库。 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all .example.com md5 + +# 如果没有前面的 "host" 行,这两行将拒绝所有来自 192.168.54.1 的 +# 连接(因为该条目会先被匹配),但允许来自互联网其他任何位置的 +# GSSAPI 连接。零掩码表示不考虑主机 IP 地址中的任何位, +# 因而会匹配任意主机。 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all 192.168.54.1/32 reject +host all all 0.0.0.0/0 gss + +# 允许来自 192.168.x.x 主机的用户连接到任意数据库,如果它们能够 +# 通过 ident 检查。例如,假设 ident说用户是 "bryanh" 并且他要求以 +# PostgreSQL 用户 "guest1" 连接,如果在 pg_ident.conf 有一个映射 +# "omicron" 的条目表明 "bryanh" 被允许以 "guest1" 连接,则该连接将被允许。 +# +# TYPE DATABASE USER ADDRESS METHOD +host all all 192.168.0.0/16 ident map=omicron + +# 如果这些是本地连接的唯一三行,它们将允许本地用户只连接到 +# 自己的数据库(与其数据库用户名同名的数据库),但管理员和 +# 角色 "support" 的成员除外,他们可以连接到所有数据库。 +# 文件 $PGDATA/admins 包含管理员名称列表。 +# 所有情况下都要求提供密码。 +# +# TYPE DATABASE USER ADDRESS METHOD +local sameuser all md5 +local all @admins md5 +local all +support md5 + +# 上面的最后两行可以被整合为一行: +local all @admins,+support md5 + +# 数据库列也可以用列表和文件名: +local db1,db2,@demodbs all md5 + + + + + + 用户名映射 + + + 用户名映射 + + + + 当使用 Ident 或 GSSAPI 之类的外部认证系统时,发起连接的操作系统用户名可能不同于要使用的数据库用户(角色)。在这种情况下,可以通过用户名映射把操作系统用户名映射为数据库用户。要使用用户名映射,需要在 pg_hba.conf 的选项字段中指定 map=map-name。此选项适用于所有会接收外部用户名的认证方法。由于不同连接可能需要不同映射,在 pg_hba.conf 中通过 map-name 参数指定要使用的映射,以表明每个连接应使用哪个映射。 + + + 用户名映射在 ident 映射文件中定义。该文件默认名为 pg_ident.confpg_ident.conf,存储在集簇的数据目录中。(也可以将映射文件放在其他位置;参见 配置参数。)ident 映射文件中各行的基本格式如下: +map-name system-username database-username +注释和空白的处理方式与 pg_hba.conf 相同。map-name 是任意名称,用于在 pg_hba.conf 中引用此映射。另两个字段指定操作系统用户名及与之匹配的数据库用户名。同一个 map-name 可以重复使用,以便在一项映射中指定多个用户对应关系。 + 一个操作系统用户可以对应多少个数据库用户,没有限制,反之亦然。因此,映射中的条目应理解为此操作系统用户可以作为此数据库用户连接,而不是说二者等同。只要存在一个映射条目,将从外部认证系统取得的用户名与用户请求连接时使用的数据库用户名配对,就会允许该连接。 + 如果 system-username 字段以斜杠(/)开头,该字段的其余部分就会被视为正则表达式。(参见 ,其中介绍了 PostgreSQL 的正则表达式语法。)正则表达式可以包含一个捕获,即用圆括号括起来的子表达式,随后可以在 database-username 字段中使用 \1(反斜杠加数字一)来引用。这样就可以在一行中映射多个用户名,对简单的语法替换尤其有用。例如,以下条目 +mymap /^(.*)@mydomain\.com$ \1 +mymap /^(.*)@otherdomain\.com$ guest +会去掉系统用户名中的域名部分(如果用户名以 @mydomain.com 结尾),并允许系统用户名以 @otherdomain.com 结尾的任何用户登录为 guest。 + + + + + + 记住在默认情况下,一个正则表达式可以只匹配字符串的一部分。如上例所示,使用^$来强制匹配整个系统用户名通常是明智的。 + + + + + 在启动时以及主服务器进程收到 SIGHUPSIGHUP 信号时,pg_ident.conf 文件会被读取。 + 如果你在运行中的系统上编辑了该文件,就需要通知 postmaster(使用 pg_ctl reload、调用 SQL 函数 pg_reload_conf(),或者使用 kill -HUP)重新读取该文件。 + + + + 展示了一个可与 中的 pg_hba.conf 文件配合使用的 pg_ident.conf 文件。在这个示例中,任何登录到 192.168 网络中某台机器上的用户,如果其操作系统用户名不是 bryanhannrobert,都不会被授予访问权限。Unix 用户 robert 只有在尝试以 PostgreSQL 用户 bob 身份连接时才被允许访问,而不能以 robert 或其他身份连接。ann 只能以 ann 身份连接。用户 bryanh 则可以以 bryanhguest1 身份连接。 + + + + + 示例 <filename>pg_ident.conf</filename> 文件 + + +# MAPNAME SYSTEM-USERNAME PG-USERNAME + +omicron bryanh bryanh +omicron ann ann +# bob 在这些机器上有用户名 robert +omicron robert bob +# bryanh 也可以作为 guest1 连接 +omicron bryanh guest1 + + + + + + 认证方法 + 下面几节会更详细地介绍这些认证方法。 + + + 信任认证 + + + 当trust认证被指定时,PostgreSQL假设任何可以连接到服务器的人都被授权使用他们指定的任何数据库用户名(即使是超级用户)访问数据库。当然,在databaseuser列中设置的限制仍然适用。只有当在操作系统层对进入服务器的连接有足够保护时,才应该使用这种方法。 + + + + trust认证对于单用户工作站的本地连接是非常合适和方便的。通常它本身适用于一台多用户机器。不过,只要你利用文件系统权限限制了对服务器的 Unix 域套接字文件的访问,即使在多用户机器上,你也可能可以使用trust。 要做这些限制,你可以设置中描述的unix_socket_permissions配置参数(可能还有unix_socket_group)。 或者你可以设置unix_socket_directories配置参数来把 Unix 域套接字文件放在一个经过恰当限制的目录中。 + + + + 设置文件系统权限只能有助于 Unix 套接字连接。本地 TCP/IP 连接不会被文件系统权限限制。因此,如果你想利用文件系统权限来控制本地安全,那么从pg_hba.conf中移除host ... 127.0.0.1 ...行,或者把它改为一个非trust认证方法。 + + + 只有当你信任由 pg_hba.conf 中指定 trust 的行所允许连接的每台机器上的每个用户时,trust 认证才适合用于 TCP/IP 连接。对来自 localhost(127.0.0.1)以外的任何 TCP/IP 连接使用 trust,通常都不合理。 + + + + + 密码认证 + + + MD5 + + + 密码 + 认证 + + + + 基于密码的认证方法有 md5password。除了密码通过连接发送的方式——前者以 MD5 哈希发送、后者以明文发送——之外,这两种方法的操作是相似的。 + + + + 如果你担心密码嗅探攻击,那么应优先使用 md5。 + 如果可能,应始终避免使用明文 password。 + 不过,md5 不能与功能一起使用。如果连接被 SSL 加密保护着,那么可以安全地使用 password(不过如果依靠 SSL,SSL 证书认证可能是更好的选择)。 + + + PostgreSQL 数据库密码独立于操作系统用户密码。每个数据库用户的密码存储在 pg_authid 系统目录中。可以使用 SQL 命令 管理密码,例如 CREATE USER foo WITH PASSWORD 'secret',如果未为某个用户设置密码,存储的密码就是空值,该用户的密码认证始终会失败。 + + + + + GSSAPI 认证 + + + GSSAPI + + + GSSAPI 是 RFC 2743 定义的安全认证行业标准协议。PostgreSQL 按照 RFC 1964 支持使用 Kerberos 认证的 GSSAPIGSSAPI 为支持它的系统提供自动认证(单点登录)。认证过程本身是安全的,但除非使用 SSL,否则数据库连接上传输的数据不会加密。 + + + 当编译PostgreSQL时,GSSAPI 支持必须被启用,详见。 + + + GSSAPI 使用 Kerberos 时,采用格式为 servicename/hostname@realm 的标准主体。PostgreSQL 服务器会接受其所用 keytab 中包含的任何主体,但客户端建立连接时,必须注意通过 krbsrvname 连接参数指定正确的主体信息。(另见 。)构建时可以使用 ./configure --with-krb-srvnam=whatever,将安装默认值从 postgres 改为其他值。在大多数环境中,无需更改此参数。某些 Kerberos 实现可能要求不同的服务名,例如 Microsoft Active Directory 要求服务名使用大写(POSTGRES)。 + hostname 是服务器机器的完全限定主机名。服务主体的 realm 是服务器机器的首选 realm。 + + 可以通过 pg_ident.conf 将客户端主体映射到不同的 PostgreSQL 数据库用户名。例如,可以将 pgusername@realm 映射为 pgusername。也可以不使用任何映射,直接将完整的 username@realm 主体用作 PostgreSQL 中的角色名。 + + PostgreSQL 还支持一个从主体中去掉 realm 的参数。提供这种方法是为了向后兼容,强烈不建议使用,因为这样就无法区分来自不同 realm 但用户名相同的用户。要启用此行为,将 include_realm 设为 0。对于简单的单 realm 安装环境,如果同时设置 krb_realm 参数(它会检查主体的 realm 是否与 krb_realm 参数值完全一致),这种做法仍是安全的;但与在 pg_ident.conf 中指定显式映射相比,它的能力较弱。 + + 确保 PostgreSQL 服务器账户能够读取服务器的 keytab 文件(最好只能读取,不能写入)。(另见 。)密钥文件的位置由 配置参数指定。默认位置是 /usr/local/pgsql/etc/krb5.keytab(或者构建时用 sysconfdir 指定的目录)。出于安全考虑,建议为 PostgreSQL 服务器使用专用 keytab,而不是放宽系统 keytab 文件的权限。 + keytab 文件由 Kerberos 软件生成;详情参见 Kerberos 文档。以下示例适用于兼容 MIT 的 Kerberos 5 实现: +kadmin% ank -randkey postgres/server.my.domain.org +kadmin% ktadd -k krb5.keytab postgres/server.my.domain.org + + + + 连接数据库时,请确保持有与请求的数据库用户名相匹配的主体票据。例如,数据库用户名为 fred 时,主体 fred@EXAMPLE.COM 可以连接。如果还要允许主体 fred/users.example.com@EXAMPLE.COM,请按 所述使用用户名映射。 + + 以下配置选项适用于 GSSAPI: + + + include_realm + + + 如果设为 0,则在通过用户名映射()之前,会先从已认证用户的主体名中去掉 realm 名称。 + 不建议这样做;它主要是为了向后兼容而保留的,因为在多 realm 环境中这并不安全,除非同时使用了 krb_realm。 + 建议将 include_realm 保持为默认值(1),并在 pg_ident.conf 中提供显式映射,把主体名转换成 PostgreSQL 用户名。 + + + + + + map + + 允许在系统用户名与数据库用户名之间建立映射。详见 。对于 username@EXAMPLE.COM(或较少见的 username/hostbased@EXAMPLE.COM)这样的 GSSAPI/Kerberos 主体,映射所用的用户名是 username@EXAMPLE.COM(或相应的 username/hostbased@EXAMPLE.COM),除非将 include_realm 设为 0,此时映射所见的系统用户名为 username(或 username/hostbased)。 + + + + + krb_realm + + + 设置用于匹配用户主体名的 realm。如果设置了该参数,则只接受来自该 realm 的用户;如果未设置,则允许来自任意 realm 的用户连接,但仍受已执行的用户名映射约束。 + + + + + + + + + SSPI 认证 + + + SSPI + + + SSPI 是一种提供安全认证和单点登录的 Windows 技术。PostgreSQL 会以 negotiate 模式使用 SSPI,尽可能使用 Kerberos,否则自动回退到 NTLM。只有服务器和客户端都运行 Windows,或者在非 Windows 平台上可用 GSSAPI 时,SSPI 认证才能工作。 + + + 当使用Kerberos认证时,SSPIGSSAPI的工作方式相同,详见。 + + + + SSPI 支持下列配置选项: + + + + include_realm + + + 如果设为 0,则在通过用户名映射()之前,会先从已认证用户的主体名中去掉 realm 名称。 + 不建议这样做;它主要是为了向后兼容而保留的,因为在多 realm 环境中这并不安全,除非同时使用了 krb_realm。 + 建议将 include_realm 保持为默认值(1),并在 pg_ident.conf 中提供显式映射,把主体名转换成 PostgreSQL 用户名。 + + + + + + compat_realm + + + 如果设为 1,则会在 include_realm 选项中使用域的 SAM 兼容名称(也称为 NetBIOS 名称)。这是默认值。如果设为 0,则会使用 Kerberos 用户主体名中的真实 realm 名称。 + + + 不要禁用这个选项,除非你的服务器运行在一个域账号(这包括一个域成员系统上的虚拟服务账号)下并且所有通过 SSPI 认证的客户端也在使用域账号,否则认证将会失败。 + + + + + + upn_username + + + 如果此选项与 compat_realm 一起启用,则认证时会使用 Kerberos UPN 中的用户名。如果禁用它(默认值),则使用 SAM 兼容用户名。默认情况下,对新建用户账号而言,这两个名称是相同的。 + + + 注意,如果没有显式指定用户名,libpq 会使用 SAM 兼容名称。如果你使用的是 libpq 或基于它的驱动,应当保持该选项为禁用状态,或者在连接字符串中显式指定用户名。 + + + + + + map + + + 允许在系统用户名和数据库用户名之间进行映射。详见 。 + 对于 SSPI/Kerberos 主体,例如 username@EXAMPLE.COM(或者较少见的 username/hostbased@EXAMPLE.COM),用于映射的用户名分别是 username@EXAMPLE.COM(或 username/hostbased@EXAMPLE.COM),除非已经将 include_realm 设为 0;在那种情况下,映射时视为系统用户名的是 username(或 username/hostbased)。 + + + + + + krb_realm + + + 设置用于匹配用户主体名的 realm。如果设置了该参数,则只接受来自该 realm 的用户;如果未设置,则允许来自任意 realm 的用户连接,但仍受已执行的用户名映射约束。 + + + + + + + + + Ident 认证 + + + ident + + + + ident 认证方法通过从一个 ident 服务器获得客户端的操作系统用户名并且用它作为被允许的数据库用户名(和可选的用户名映射)来工作。它只在 TCP/IP 连接上支持。 + + + + + + 当为一个本地(非 TCP/IP)连接指定 ident 时,将实际使用 peer 认证(见)。 + + + + 以下配置选项适用于 ident: + + + map + + + 允许在系统用户名和数据库用户名之间进行映射。详见 。 + + + + + + + 标识协议在 RFC 1413 中定义。几乎所有类 Unix 操作系统都自带 ident 服务器,默认监听 TCP 端口 113。ident 服务器的基本功能是回答这样的问题:从你的端口 X 连到我的端口 Y 的连接,是哪个用户发起的?由于建立物理连接时,PostgreSQL 已知 XY,因此可以询问连接客户端所在主机上的 ident 服务器,理论上能够确定任意给定连接的操作系统用户。 + + + 这个过程的缺点在于它依赖客户端本身的可信性:如果客户端机器不可信或者已被攻破,攻击者几乎可以在 113 端口上运行任何程序,并返回任意他们选择的用户名。因此,这种认证方法只适用于封闭网络,在这类网络中每台客户端机器都受到严格控制,而且数据库管理员与系统管理员之间保持密切协作。换句话说,你必须信任运行 ident 服务器的那台机器。请注意下面的警告: +
+ RFC 1413 + + 标识协议的本意不是作为一种授权或访问控制协议。 + +
+
+ + + 有些 ident 服务器提供了一个非标准选项,会让返回的用户名被加密,而解密所需密钥只有发起连接机器的管理员才知道。将 ident 服务器与 PostgreSQL 配合使用时,绝不能启用这个选项,因为 PostgreSQL 无法解密返回的字符串,也就无法确定实际用户名。 + +
+ + + Peer 认证 + + + peer + + + + Peer 认证通过从内核获取客户端的操作系统用户名,并把它用作被允许的数据库用户名(可结合可选的用户名映射)来工作。这种方法只支持本地连接。 + + + 以下配置选项适用于 peer: + + + map + + + 允许在系统用户名和数据库用户名之间进行映射。详见 。 + + + + + + + + Peer 认证只在提供 getpeereid() 函数、SO_PEERCRED 套接字参数或类似机制的操作系统上可用。目前这包括 Linux、大多数 BSD 变种(包括 OS X)以及 Solaris。 + + + + + + LDAP 认证 + + + LDAP + + + + 这种认证方法的工作方式与 password 类似,只不过它使用 LDAP 作为密码验证方法。LDAP 只用于验证用户名/密码对。因此,在使用 LDAP 进行认证之前,用户必须已经存在于数据库中。 + + + + LDAP 认证可以在两种模式下工作。第一种模式称为简单绑定模式,服务器会绑定到按 prefix username suffix 形式构造出的可分辨名称。通常,prefix 参数用于指定 cn=,或在 Active Directory 环境中指定 DOMAIN\suffix 则用于指定非 Active Directory 环境中 DN 的剩余部分。 + + + + 第二种模式称为搜索+绑定模式,服务器首先使用由 ldapbinddnldapbindpasswd 指定的固定用户名和密码绑定到 LDAP 目录,并搜索试图登录数据库的用户。如果没有配置用户名和密码,则会尝试对目录进行匿名绑定。搜索会在 ldapbasedn 指定的子树上进行,并尝试对 ldapsearchattribute 指定的属性做精确匹配。一旦在搜索中找到了该用户,服务器会断开连接,再作为该用户重新绑定到目录,并使用客户端指定的密码来验证登录是否正确。这种模式与 Apache mod_authnz_ldappam_ldap 等软件中的 LDAP 认证方案相同。这种方法使目录中用户对象的位置更具灵活性,但会与 LDAP 服务器建立两个独立的连接。 + + + 以下配置选项适用于两种模式: + + ldapserver + + + 要连接的LDAP服务器的名称或IP地址。可以指定多个服务器,用空格分隔。 + + + + + ldapport + + + 要连接的LDAP服务器的端口号。如果未指定端口,则将使用LDAP库的默认端口设置。 + + + + + ldaptls + + 设为 1 时,PostgreSQL 与 LDAP 服务器之间的连接会使用 TLS 加密。注意,这只加密与 LDAP 服务器之间的流量 — 除非使用 SSL,否则与客户端之间的连接仍不加密。 + + + 以下选项仅适用于简单绑定模式: + + ldapprefix + + + 在进行简单绑定认证时,附加到用户名前面以形成绑定 DN 的字符串。 + + + + + ldapsuffix + + + 在进行简单绑定认证时,附加到用户名后面以形成绑定 DN 的字符串。 + + + + 以下选项仅适用于搜索加绑定模式: + + ldapbasedn + + + 在进行搜索+绑定认证时,用作用户搜索起点的根 DN。 + + + + + ldapbinddn + + + 在进行搜索+绑定认证时,用于绑定到目录并执行搜索的用户 DN。 + + + + + ldapbindpasswd + + + 在进行搜索+绑定认证时,用于绑定到目录并执行搜索的用户密码。 + + + + + ldapsearchattribute + + + 在进行搜索+绑定认证时,用于与用户名匹配的属性。如果未指定属性,则会使用 uid 属性。 + + + + + ldapurl + + 符合 RFC 4516 的 LDAP URL。这是另一种指定部分 LDAP 选项的方式,写法更紧凑、更标准。其格式为 +ldap://host[:port]/basedn[?[attribute][?[scope]]] + + scope 必须是以下值之一:baseonesub,通常使用最后一个。只会使用一个属性,而且不支持标准 LDAP URL 的其他某些组件,例如过滤器和扩展。 + + + 对于非匿名绑定,必须将ldapbinddnldapbindpasswd指定为单独的选项。 + + + 要使用加密的 LDAP 连接,除了 ldapurl,还必须使用 ldaptls 选项。不支持 ldaps URL 方案(直接 SSL 连接)。 + + 目前只有 OpenLDAP 支持 LDAP URL,Windows 不支持。 + + + + + + + 将简单绑定模式的配置选项与搜索+绑定模式的配置选项混用是错误的。 + + + + 下面是一个简单绑定 LDAP 配置示例: + +host ... ldap ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net" + + 当请求以数据库用户 someuser 连接数据库服务器时,PostgreSQL 将尝试使用 DN cn=someuser, dc=example, dc=net 和客户端提供的密码绑定到 LDAP 服务器。如果该连接成功,数据库访问就会被授予。 + + + 下面是搜索加绑定配置的示例: +host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapsearchattribute=uid +当请求以数据库用户 someuser 的身份连接数据库服务器时,PostgreSQL 会尝试匿名绑定到 LDAP 服务器(因为没有指定 ldapbinddn),并在指定的基础 DN 下搜索 (uid=someuser)。如果找到了条目,就会尝试使用找到的信息和客户端提供的密码进行绑定。如果第二次连接成功,就会授予数据库访问权限。 + + + 下面是以 URL 形式写出的同一个搜索+绑定配置: + +host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub" + + 某些支持 LDAP 认证的其他软件也使用相同的 URL 格式,因此共享这类配置会更容易。 + + + + + + 如示例中所示,由于 LDAP 通常使用逗号和空格来分割一个 DN 的不同部分,在配置 LDAP 选项时通常有必要使用双引号包围的参数值。 + + + + + + + RADIUS 认证 + + + RADIUS + + + + 这种认证方法的工作方式与 password 类似,只不过它使用 RADIUS 作为密码验证方式。RADIUS 只用于验证用户名/密码对。因此,在使用 RADIUS 进行认证之前,用户必须已经存在于数据库中。 + + + + 使用 RADIUS 认证时,会向配置好的 RADIUS 服务器发送一条 Access Request 消息。 + 该请求的类型为 Authenticate Only,并包含 user namepassword(加密的)以及 NAS Identifier 参数。 + 该请求会使用与服务器共享的密钥进行加密。 + 这台服务器会返回 Access AcceptAccess Reject 作为响应。PostgreSQL 不支持 RADIUS 记账。 + + + + RADIUS 支持下列配置选项: + + + radiusserver + + + 要连接的 RADIUS 服务器的名称或 IP 地址。此参数为必需项。 + + + + + + radiussecret + + + 与 RADIUS 服务器进行安全通信时使用的共享密钥。它在 PostgreSQL 服务器和 RADIUS 服务器上必须完全相同。建议它至少是一个 16 个字符长的字符串。此参数为必需项。 + + + 只有当 PostgreSQL 在构建时启用了 OpenSSL 支持,所使用的加密向量才具有足够的密码学强度。在其他情况下,到 RADIUS 服务器的传输只能被视为经过混淆,而非受到安全保护;如有必要,应额外采取外部安全措施。 + + + + + + + + radiusport + + + 要连接的 RADIUS 服务器端口号。如果未指定端口,则会使用默认端口 1812。 + + + + + + radiusidentifier + + + 在 RADIUS 请求中用作 NAS Identifier 的字符串。这个参数可用作第二个参数,例如标识用户正尝试以哪个数据库用户进行认证,从而便于在 RADIUS 服务器上进行策略匹配。如果未指定标识符,则默认使用 postgresql。 + + + + + + + + + + + 证书认证 + + + 证书 + + + 这种认证方法使用 SSL 客户端证书进行认证,因此仅适用于 SSL 连接。使用此方法时,服务器要求客户端提供有效、受信任的证书,不会向客户端发送密码提示。服务器会将证书的 cn(通用名称)属性与请求的数据库用户名比较,匹配时才允许登录。可以使用用户名映射,允许 cn 与数据库用户名不同。 + + + SSL 证书认证支持下列配置选项: + + + map + + + 允许在系统用户名和数据库用户名之间进行映射。详见 。 + + + + + + + 在指定证书认证的 pg_hba.conf 记录中,认证选项 clientcert 被视为 1,且不能关闭,因为此方法必须使用客户端证书。cert 方法在基本的 clientcert 证书有效性检查之外,还会检查 cn 属性是否与数据库用户名匹配。 + + + + PAM 认证 + + + PAM + + + 这种认证方法与 password 类似,只是使用 PAM(可插拔认证模块)作为认证机制。默认的 PAM 服务名为 postgresql。PAM 仅用于验证用户名与密码的组合,也可选择验证连接的远程主机名或 IP 地址。因此,必须先在数据库中创建该用户,才能使用 PAM 进行认证。有关 PAM 的更多信息,参见 Linux-PAM 页面 + + + PAM 支持下列配置选项: + + + pamservice + + + PAM 服务名称。 + + + + + pam_use_hostname + + + 决定是通过 PAM_RHOST 项向 PAM 模块提供远程 IP 地址还是主机名。默认情况下使用 IP 地址。将此选项设为 1 可改为使用解析出的主机名。主机名解析可能导致登录延迟。(大多数 PAM 配置并不会使用这项信息,因此只有在 PAM 配置被专门设计为利用该信息时,才需要考虑这个设置。) + + + + + + + + + + 如果 PAM 被设置为读取 /etc/shadow,认证将会失败,因为 PostgreSQL 服务器是由非 root 用户启动的。不过,当 PAM 被配置为使用 LDAP 或其他认证方法时,这就不是问题。 + + + + + + BSD 认证 + + + BSD 认证 + + + + 这种认证方法操作起来类似于password,不过它使用 BSD 认证来验证密码。BSD 认证只被用来验证用户名/密码对。因此,在 BSD 认证可以被用于认证之前,用户的角色必须已经存在于数据库中。BSD 认证框架当前只在 OpenBSD 上可用。 + + + + PostgreSQL中的 BSD 认证使用auth-postgresql登录类型,如果login.conf中定义了postgresql登录分类,就会用它来认证。默认情况下这种登录分类不存在,PostgreSQL将使用默认的登录分类。 + + + + + + 要使用 BSD 认证,PostgreSQL 用户账号(也就是运行服务器的操作系统用户)必须首先被加入到auth组中。在 OpenBSD 系统上默认存在auth组。 + + + +
+ + + + 认证问题 + + + 认证失败及相关问题通常会表现为类似下面这样的错误消息: + + + + +FATAL: no pg_hba.conf entry for host "123.123.123.123", user "andym", database "testdb" + + 这种情况通常意味着你已经成功联系到了服务器,但服务器不愿意接受你的连接。正如消息所示,服务器拒绝了该连接请求,因为它没有在自己的 pg_hba.conf 配置文件中找到匹配项。 + + + + +FATAL: password authentication failed for user "andym" + + 这样的消息表示你已经联系到了服务器,而且服务器也愿意继续处理连接,但前提是你必须先通过 pg_hba.conf 文件中指定的认证方法。请检查你提供的密码;如果错误消息提到了 Kerberos 或 ident 等认证类型,也请检查对应的软件配置。 + + + + +FATAL: user "andym" does not exist + + 指定的数据库用户不存在。 + + + + +FATAL: database "testdb" does not exist + + 你试图连接的数据库不存在。注意,如果你没有指定数据库名,默认会使用数据库用户名作为数据库名,但这不一定是所需的数据库名。 + + + + + + 服务器日志中可能包含比返回给客户端的更多认证失败信息。如果你不清楚失败原因,请检查服务器日志。 + + + + +
diff --git a/zh/9.6/config.sgml b/zh/9.6/config.sgml new file mode 100644 index 00000000..a9be4991 --- /dev/null +++ b/zh/9.6/config.sgml @@ -0,0 +1,6431 @@ + + + + 服务器配置 + + + 配置 + 服务器端 + + + + 有许多配置参数会影响数据库系统的行为。本章第一节将介绍如何与配置参数交互。 + 后续各节将详细讨论每个参数。 + + + + 设置参数 + + + + 参数名称和值 + + + 所有参数名都是大小写不敏感的。每个参数都可以接受五种类型之一的值: 布尔、字符串、整数、 + 浮点数或枚举。该类型决定了设置该参数的语法: + + + + + + + 布尔: + 值可以被写成 + on, + off, + true, + false, + yes, + no, + 1, + 0 + (都是大小写不敏感的)或者这些值的任何无歧义前缀。 + + + + + + + 字符串: + 通常值被包括在单引号内,值内部的任何单引号都需要被双写。不过,如果值是一个简单数字或者 + 标识符,引号通常可以被省略。 + + + + + + + + 数字(整数和浮点数): + 只有浮点数参数才允许使用小数点。不要使用千位分隔符。不要求使用引号。 + + + + + + + 带单位的数字: + 一些数字参数具有隐含单位,因为它们描述的是内存或时间量。单位可能是千字节、块 + (通常为 8 千字节)、毫秒、秒或分钟。这类设置若给出不带单位的数字值,就会使用该设置的默认单位, + 可以通过 pg_settings.unit 了解该默认单位。为了方便, + 也可以显式指定单位,例如把时间值写成 '120 ms',系统会将其转换为该参数的实际单位。 + 注意,要使用这一特性,值必须写成字符串(带引号)。单位名称区分大小写,并且数字值与单位之间可以有空白。 + + + + + 可用的内存单位是 kB(千字节)、 + MB(兆字节)、GB(吉字节)和 + TB(太字节)。内存单位的乘数是 1024,而不是 1000。 + + + + + + 可用的时间单位是 + ms(毫秒)、 + s(秒)、min(分钟)、 + h(小时)和d(天)。 + + + + + + + + + + + 枚举: + 枚举类型的参数以与字符串参数相同的方式指定,但被限制到一组有限的值。 这样一个参数可用的值可以在pg_settings.enumvals + 中找到。枚举参数值是大小写无关的。 + + + + + + + + 通过配置文件影响参数 + + + 设置这些参数最基本的方法是编辑文件 + postgresql.confpostgresql.conf, + 它通常位于数据目录中。在数据库集簇目录初始化时,会安装该文件的一个默认副本。其内容示例如下: + +# This is a comment +log_connections = yes +log_destination = 'syslog' +search_path = '"$user", public' +shared_buffers = 128MB + + 每行指定一个参数。名称和值之间的等号是可选的。空白不重要(引号括起的参数值内部除外),空行会被忽略。 + 井号(#)表示该行余下部分是注释。不是简单标识符或数字的参数值必须用单引号括起。 + 要在参数值中嵌入单引号,可以写两个单引号(推荐)或使用反斜线转义单引号。 + 如果文件包含相同参数的多个条目,则忽略除最后一个之外的所有条目。 + + + + 以这种方式设定的参数为集簇提供了默认值。除非这些设置被覆盖,活动会话看到的就是这些设置。 + 下面的小节描述了管理员或用户覆盖这些默认值的方法。 + + + + + SIGHUP + + 主服务器进程每次收到SIGHUP信号(最简单的方法是从命令行运行pg_ctl reload或调用 SQL 函数pg_reload_conf()来发送这个信号)后都会重新读取这个配置 + 文件。主服务器进程还会把这个信号传播给所有正在运行的服务器进程,这样现有的会话也能采用新 + 值(要等待它们完成当前正在执行的客户端命令之后才会发生)。另外,你可以直接向一个单一服务 + 器进程发送该信号。有些参数只能在服务器启动时设置,在配置文件中对这些条目的修改将被忽略, + 直到下次服务器重启。配置文件中的非法参数设置也会在SIGHUP处理过程中被 + 忽略(但是会记录日志)。 + + + + 除了 postgresql.conf 之外,PostgreSQL + 数据目录还包含文件 + postgresql.auto.confpostgresql.auto.conf, + 它与 postgresql.conf 采用相同的格式,但设计为自动编辑而非手工编辑。 + 这个文件保存了通过命令提供的设置。 + 每当读取 postgresql.conf 时,也会读取该文件,并以同样的方式使其中设置生效。 + postgresql.auto.conf 中的设置会覆盖 postgresql.conf 中的设置。 + + + + 外部工具也可以修改 postgresql.auto.conf。 + 不建议在服务器运行时这样做, + 因为并发的 ALTER SYSTEM 命令可能会覆盖这些更改。 + 这类工具可能只是简单地在文件末尾追加新设置,也可能选择删除重复设置和/或注释 + (正如 ALTER SYSTEM 那样)。 + + + + 系统视图pg_file_settings + 可以有助于对配置文件中的更改进行提前测试,或者在SIGHUP + 信号没有达到预期效果时用来诊断问题。 + + + + + + 通过SQL影响参数 + + + PostgreSQL提供了三个SQL命令来建立配置默认值。 + 已经提到过的命令提供了一种改变全局默认值的从SQL可 + 访问的方法;它在功效上等效于编辑postgresql.conf。此外,还有两个命令 + 可以针对每个数据库或者每个角色设置默认值: + + + + + + + 命令允许针对各个数据库覆盖全局设置。 + + + + + + + 命令允许用针对特定用户设置的值来覆盖全局设置和数据库设置。 + + + + + + 只有当开始一个新的数据库会话时,用ALTER DATABASE和 + ALTER ROLE设置的值才会被应用。它们会覆盖从配置文件或服务器命令行 + 获得的值,并且作为该会话后续的默认值。注意某些设置在服务器启动后不能被更改,并且因此 + 不能被这些命令(或者下文列举的命令)设置。 + + + + 一旦一个客户端连接到数据库,PostgreSQL会提供两个额外的SQL命令( + 以及等效的函数)用以影响会话本地的配置设置: + + + + + + + 命令允许查看所有参数的当前值。对应的函数为 current_setting(setting_name text)。 + + + + + + + 命令允许修改可在会话本地设置的参数的当前值;它对其他会话没有影响。对应的函数为 set_config(setting_name, new_value, is_local)。 + + + + + + 此外,系统视图pg_settings可以被用来查看和改变 + 会话本地的值: + + + + + + + 查询这个视图与使用SHOW ALL相似,但是可以提供更多细节。它也更加灵活, + 因为可以为它指定过滤条件或者把它与其他关系进行连接。 + + + + + + + 在这个视图上使用并且指定更新setting + 列,其效果等同于发出SET命令。例如,下面的命令 + +SET configuration_parameter TO DEFAULT; + + 等效于: + +UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter'; + + + + + + + + + + 通过 Shell 影响参数 + + + 除了设置全局默认值或在数据库、角色级别覆盖默认值之外,你还可以通过 shell 工具把设置 + 传递给PostgreSQL。服务器和libpq + 客户端库都能通过 shell 接受参数值。 + + + + + + + 在服务器启动期间,可以通过命令行参数把参数设置传递给 + postgres命令。例如: + +postgres -c log_connections=yes -c log_destination='syslog' + + 这种方式提供的设置会覆盖通过postgresql.conf或者 + ALTER SYSTEM提供的设置,因此除了重启服务器之外无法从全局上改变它们。 + + + + + + + 当通过libpq启动一个客户端会话时,可以使用PGOPTIONS + 环境变量指定参数设置。这种方式建立的设置构成了会话生存期间的默认值,但是不会影响 + 其他的会话。由于历史原因,PGOPTIONS的格式和启动 + postgres命令时用到的相似,特别是标志必须被指定。 + 例如: + +env PGOPTIONS="-c geqo=off -c statement_timeout=5min" psql + + + + + 通过 shell 或者其他方式,其他客户端和库可能提供它们自己的机制,以便允许用户在不直接 + 使用SQL命令的前提下修改会话设置。 + + + + + + + + + 管理配置文件内容 + + + PostgreSQL提供了一些特性用于把复杂的 + postgresql.conf文件分解成子文件。在管理多个具有相关但不完全相同 + 配置的服务器时,这些特性特别有用。 + + + + + include + 配置文件中的 + + 除了单个参数设置之外,postgresql.conf 文件还可以包含 + include 指令,用来指定另一个要读取和处理的文件,就像把该文件插入到配置文件的这个位置一样。 + 这一特性允许把一个配置文件拆分成多个物理上独立的部分。include 指令的形式如下: + +include 'filename' + + 如果文件名不是绝对路径,则会被解释为相对于引用它的配置文件所在目录的路径。include 可以嵌套。 + + + + + include_if_exists + 配置文件中的 + + 还有一个 include_if_exists 指令,其行为与 include 相同, + 但在被引用文件不存在或无法读取时有所不同。普通的 include 会将其视为错误, + 而 include_if_exists 只会记录一条消息并继续处理引用它的配置文件。 + + + + + include_dir + 配置文件中的 + + postgresql.conf 文件也可以包含 include_dir 指令, + 用来指定一个应被包含的配置文件目录。其用法如下: + +include_dir 'directory' + + 非绝对目录名会被解释为相对于引用它的配置文件所在目录的路径。在指定目录中, + 只有名称以 .conf 结尾的非目录文件才会被包含。以 . + 开头的文件名也会被忽略,以避免在某些平台上误处理隐藏文件。包含目录中的多个文件会按文件名顺序处理 + (依据 C 区域规则排序,即数字在字母之前,大写字母在小写字母之前)。 + + + + 包含文件或目录可以用来在逻辑上分隔数据库配置的各个部分,而不是用一个很大的postgresql.conf文件。 + 考虑一个有两台数据库服务器的公司,每一个都有不同的内存量。 + 两者很可能会共享部分配置,例如日志设置。但是两者关于内存的参数将会不同。 + 并且还可能会有服务器相关的自定义。 + 一种管理这类情况的方法是将你的站点的自定义配置修改分成三个文件。 + 你可以把下面的内容加入到你的postgresql.conf文件末尾来包含它们: + +include 'shared.conf' +include 'memory.conf' +include 'server.conf' + + 所有的系统将会有相同的shared.conf。 + 每个有特定内存量的服务器可以共享相同的memory.conf。 + 你可能对所有 8GB 内存的服务器有一个,而对那些 16GB 内存的服务器有另一个。 + 并且最后server.conf可以装有真正服务器相关的配置信息。 + + + + 另一种做法是创建一个配置文件目录,并把这些信息放到其中的文件里。 + 例如,一个conf.d目录可以在postgresql.conf的末尾被引用: + +include_dir 'conf.d' + + 然后你可以这样命名conf.d目录中的文件: + +00shared.conf +01memory.conf +02server.conf + + 这种命名习惯建立了这些文件将被载入的清晰顺序。这是很重要的,因为在服务器读取配置 + 文件时,对于一个特定的参数只有最后碰到的一个设置才会被使用。在这个示例中, + conf.d/02server.conf设置的东西将会覆盖在 + conf.d/01memory.conf中相同参数的值。 + + + + 你还可以使用这种配置目录方法,在命名文件时更有描述性: + +00shared.conf +01memory-8GB.conf +02server-foo.conf + + 这种形式的安排为每个配置文件变体给定了一个唯一的名称。当多个服务器把它们的配置全部存储在一个位置(例如在一个版本控制仓库中)时,这可以帮助消除歧义(在版本控制下存储数据库配置文件是另一个值得考虑的好方法)。 + + + + + + + 文件位置 + + + 除了已经提到过的postgresql.conf文件之外,PostgreSQL还使用另外两个手工编辑的配置文件,它们控制客户端认证(其使用在中讨论)。默认情况下,所有三个配置文件都存放在数据库集簇的数据目录中。 本节描述的参数允许配置文件放在别的地方(这么做可以简化管理,特别是如果配置文件被独立放置,可以很容易保证它得到恰当的备份)。 + + + + + + data_directory (string) + + data_directory配置参数 + + + + + + 指定用于数据存储的目录。这个参数只能在服务器启动时设置。 + + + + + + + config_file (string) + + config_file配置参数 + + + + + + 指定主服务器配置文件(通常叫postgresql.conf)。这个参数只能在postgres命令行上设置。 + + + + + + + hba_file (string) + + hba_file配置参数 + + + + + + 指定基于主机认证配置文件(通常叫pg_hba.conf)。这个参数只能在服务器启动的时候设置。 + + + + + + + ident_file (string) + + ident_file配置参数 + + + + + + 指定用于用户名称映射的配置文件(通常叫pg_ident.conf)。这个参数只能在服务器启动的时候设置。另见。 + + + + + + + external_pid_file (string) + + external_pid_file配置参数 + + + + + + 指定服务器应创建的额外进程 ID(PID)文件的名称,供服务器管理程序使用。这个参数只能在服务器启动的时候设置。 + + + + + + + 在默认安装中不会显式设置以上参数。相反,命令行参数或者环境变量PGDATA指定数据目录,并且上述配置文件都能在数据目录中找到。 + + + + 如果你想把配置文件放在别的地方而不是数据目录中,那么postgres 命令行选项或者环境变量PGDATA必须指向包含配置文件的目录,并且postgresql.conf中(或者命令行上)的data_directory参数必须设置为数据目录的实际位置。请注意,data_directory将覆盖PGDATA指定的数据目录位置,但是不覆盖配置文件的位置。 + + + + 如果你愿意,可以使用选项config_filehba_file和/或ident_file单独指定配置文件名称和位置。config_file只能在postgres命令行上指定,但是其他参数可以在主配置文件中设置。如果所有三个参数外加data_directory被显式地设置,则不必指定PGDATA。 + + + + 在设置任何这些参数时,相对路径将被解释为相对于启动 postgres 时所在目录的路径。 + + + + + 连接和认证 + + + 连接设置 + + + + + listen_addresses (string) + + listen_addresses配置参数 + + + + + 指定服务器用于监听客户端应用连接的 TCP/IP 地址。此值采用以逗号分隔的主机名和/或数字 IP 地址列表的形式。 + 特殊项*对应所有可用的 IP 接口。项0.0.0.0允许监听所有 IPv4 地址, + 而::允许监听所有 IPv6 地址。如果列表为空,服务器不会监听任何 IP 接口,此时只能通过 Unix 域套接字连接。 + 默认值为localhost,只允许建立本地 TCP/IP 回环连接。 + 在客户端认证()允许对谁可以访问服务器进行细粒度控制的同时,listen_addresses + 控制哪些接口接受连接尝试,这可以帮助防止在不安全的网络接口上重复恶意连接请求。此参数只能在服务器启动时设置。 + + + + + + + port (integer) + + port配置参数 + + + + + 服务器监听的 TCP 端口,默认是 5432。请注意,服务器监听的所有 IP 地址都使用同一个端口号。 + 此参数只能在服务器启动时设置。 + + + + + + max_connections (integer) + + max_connections配置参数 + + + + + 决定数据库服务器允许的最大并发连接数。默认值通常是 100 个连接,但如果内核设置不支持 + (在 initdb 期间确定),则可能更少。这个参数只能在服务器启动时设置。 + + + + 当运行一个备库时,你必须设置这个参数等于或大于主库上的参数。 + 否则,备库上将不允许查询。 + + + + + + superuser_reserved_connections + (integer) + + superuser_reserved_connections配置参数 + + + + + 决定为 PostgreSQL 超级用户连接保留多少个连接。 + 同时活跃的连接数最多始终只能达到 。 + 当活跃并发连接数至少达到 max_connections 减去 + superuser_reserved_connections 时,新连接将只接受超级用户, + 并且不再接受新的复制连接。 + + + + 默认值是 3 个连接。该值必须小于 max_connections。 + 这个参数只能在服务器启动时设置。 + + + + + + unix_socket_directories (string) + + unix_socket_directories配置参数 + + + + + 指定服务器用于监听来自客户端应用的连接的 Unix 域套接字目录。通过列出用逗号分隔的多个目录可以建立多个套接字。 + 项之间的空白被忽略,如果你需要在名字中包括空白或逗号,在目录名周围放上双引号。 + 一个空值指定在任何 Unix 域套接字上都不监听,在这种情况中只能使用 TCP/IP 套接字来连接到服务器。 + 默认值通常是 /tmp,但可以在构建时更改。 + 这个参数只能在服务器启动时设置。 + + + + 除了套接字文件本身(名为.s.PGSQL.nnnn,其中nnnn是服务器的端口号),一个名为.s.PGSQL.nnnn.lock的普通文件会在每一个unix_socket_directories目录中被创建。 + 任何一个都不应该被手工移除。 + + + + Windows 没有 Unix 域套接字,因此此参数在 Windows 上没有意义。 + + + + + + unix_socket_group (string) + + unix_socket_group配置参数 + + + + + 设置 Unix 域套接字的所属组(套接字的所属用户总是启动服务器的用户)。可以与选项unix_socket_permissions一起用于对 Unix域连接进行访问控制。默认是一个空字符串,表示服务器用户的默认组。这个参数只能在服务器启动时设置。 + + + + Windows 没有 Unix 域套接字,因此此参数在 Windows 上没有意义。 + + + + + + unix_socket_permissions (integer) + + unix_socket_permissions配置参数 + + + + + 设置 Unix 域套接字的访问权限。Unix 域套接字使用通常的 Unix 文件系统权限集。参数值应是以 chmodumask 系统调用所接受格式指定的数字权限模式。(要使用惯用的八进制格式,数字必须以 0(零)开头。) + + + 默认权限是 0777,表示任何人都可以连接。合理的其他取值包括 0770(仅属主和所属组,另见 unix_socket_group)和 0700(仅属主)。(注意,对 Unix 域套接字而言,只有写权限起作用,因此设置或撤销读权限和执行权限没有意义。) + + + 此访问控制机制独立于 中描述的机制。 + + + 此参数只能在服务器启动时设置。 + + + 此参数对完全忽略套接字权限的系统无效,尤其是 Solaris(截至 Solaris 10)。在这些系统上,可以将 unix_socket_directories 指向一个仅向目标用户授予搜索权限的目录,以达到类似效果。Windows 没有 Unix 域套接字,因此此参数在 Windows 上也没有意义。 + + + + + + + bonjour (boolean) + + bonjour配置参数 + + + + + 启用通过 Bonjour 通告服务器存在的功能。默认值为关闭。此参数只能在服务器启动时设置。 + + + + + + + bonjour_name (string) + + bonjour_name配置参数 + + + + + 指定 Bonjour 服务名。空字符串 ''(默认值)表示使用计算机名。 + 如果编译时未启用 Bonjour 支持,则此参数会被忽略。此参数只能在服务器启动时设置。 + + + + + + tcp_keepalives_idle (integer) + + tcp_keepalives_idle配置参数 + + + + + 指定在没有活动多少秒之后,TCP 应向客户端发送 keepalive 消息。值 0 表示使用系统默认值。 + 此参数仅在支持TCP_KEEPIDLE或等效套接字选项的系统以及 Windows 上可用;在其他系统上,它必须为零。 + 在通过 Unix 域套接字连接的会话中,此参数会被忽略,并始终读作零。 + + + + 在 Windows 上,值 0 会将此参数设置为 2 小时,因为 Windows 不提供读取系统默认值的方法。 + + + + + + + tcp_keepalives_interval (integer) + + tcp_keepalives_interval配置参数 + + + + + 指定未被客户端确认收到的 TCP keepalive 消息在多少秒后应被重传。值 0 表示使用系统默认值。 + 此参数仅在支持TCP_KEEPINTVL或等效套接字选项的系统以及 Windows 上可用;在其他系统上,它必须为零。 + 在通过 Unix 域套接字连接的会话中,此参数会被忽略,并始终读作零。 + + + + 在 Windows 上,值 0 会将此参数设置为 1 秒,因为 Windows 不提供读取系统默认值的方法。 + + + + + + + tcp_keepalives_count (integer) + + tcp_keepalives_count配置参数 + + + + + 指定在服务器与客户端之间的连接被视为中断之前,可以丢失多少个 TCP keepalive 消息。 + 值 0 表示使用系统默认值。 + 这个参数只有在支持 TCP_KEEPCNT 或等效套接字选项的系统上才可用 + ;在其他系统上,它必须为零。在通过 Unix 域套接字连接的会话中, + 这个参数会被忽略,并始终读作零。 + + + + Windows 不支持此参数,它必须为零。 + + + + + + + + + 安全和认证 + + + + authentication_timeout (integer) + 超时客户端认证 + 客户端认证期间超时 + + authentication_timeout配置参数 + + + + + + 允许完成客户端认证的最长时间,以秒为单位。如果一个客户端没有在这段时间里完成认证协议,服务器将关闭连接。 + 这样就避免了出问题的客户端无限制地占有一个连接。 + 默认值是 1分钟(1m)。这个参数只能在服务器命令行上或者在postgresql.conf文件中设置。 + + + + + + ssl (boolean) + + ssl配置参数 + + + + + 启用SSL连接。使用前请阅读。默认值是off。这个参数只能在服务器启动时设置。SSL通信只能在 TCP/IP 连接上使用。 + + + + + + ssl_ca_file (string) + + ssl_ca_file配置参数 + + + + + 指定包含 SSL 服务器证书颁发机构(CA)的文件名。默认值为空,表示没有载入 CA 文件,并且客户端证书验证没有被执行。(在以前的 PostgreSQL 版本中,此文件名被硬编码为root.crt。)相对路径是相对于数据目录的。这个参数只能在服务器启动时设置。 + + + + + + + ssl_cert_file (string) + + ssl_cert_file配置参数 + + + + + + 指定包含 SSL 服务器证书的文件名。默认值是server.crt。相对路径是相对于数据目录的。这个参数只能在服务器启动时设置。 + + + + + + ssl_crl_file (string) + + ssl_crl_file配置参数 + + + + + 指定包含 SSL 服务器证书吊销列表(CRL)的文件名。默认为空,表示不加载 CRL 文件。(在以前的 PostgreSQL 版本中,此文件名被硬编码为root.crl。)相对路径是相对于数据目录的。这个参数只能在服务器启动时设置。 + + + + + + + ssl_key_file (string) + + ssl_key_file配置参数 + + + + + + 指定包含 SSL 服务器私钥的文件名。默认值是server.key。相对路径是相对于数据目录的。这个参数只能在服务器启动时设置。 + + + + + + ssl_ciphers (string) + + ssl_ciphers配置参数 + + + + 指定允许 SSL 连接使用的SSL密码套件列表。该设置的语法及支持的值列表,可参见OpenSSL包中的ciphers手册页。此设置只影响使用 TLS 1.2 及更低版本的连接。目前没有控制 TLS 1.3 连接所用密码套件的设置。默认值是HIGH:MEDIUM:+3DES:!aNULL。除非你有特定的安全需求,否则通常是合理的。这个参数只能在服务器启动时设置。 + + 默认值的解释: + + HIGH + + + 使用HIGH组中密码(例如 AES、Camellia、3DES)的密码套件。 + + + + + + MEDIUM + + + 使用MEDIUM组中密码(例如 RC4、SEED)的密码套件。 + + + + + + +3DES + + + OpenSSL对HIGH的默认排序有问题, + 因为它将 3DES 排在 AES128 之前。这是不正确的,因为 3DES 的安全性低于 AES128, + 并且速度也慢得多。+3DES会把它重新排序到其他所有 + HIGHMEDIUM密码之后。 + + + + + + !aNULL + + + 禁用不进行认证的匿名密码套件。这类密码套件容易遭受中间人攻击,因此不应使用。 + + + + + + + + 可用的密码套件细节可能会随着OpenSSL 版本变化。 + 可使用命令 openssl ciphers -v 'HIGH:MEDIUM:+3DES:!aNULL'来查看当前安装的OpenSSL版本的实际细节。 + 注意这个列表是根据服务器密钥类型在运行时过滤过的。 + + + + + + ssl_prefer_server_ciphers (bool) + + ssl_prefer_server_ciphers配置参数 + + + + + 指定是否使用服务器的 SSL 密码套件优先顺序,而非客户端的优先顺序。默认值为 true。此参数只能在服务器启动时设置。 + + + 旧版 PostgreSQL没有此设置,始终采用客户端的优先顺序。此设置主要用于与这些旧版本保持向后兼容。采用服务器的优先顺序通常更好,因为服务器更可能得到适当配置。 + + + + + + + + ssl_ecdh_curve (string) + + ssl_ecdh_curve 配置参数 + + + + + 指定在 ECDH 密钥交换中使用的曲线名称。所有连接的客户端都必须支持该曲线。它不必与服务器椭圆曲线密钥使用的曲线相同。默认值为 prime256v1。此参数只能在服务器启动时设置。 + + + OpenSSL 中最常见曲线的名称:prime256v1(NIST P-256)、secp384r1(NIST P-384)、secp521r1(NIST P-521)。 + + + + 可以用 openssl ecparam -list_curves 命令显示可用曲线的完整列表,但其中并非所有曲线都能用于 TLS。 + + + + + + password_encryption (boolean) + + password_encryption配置参数 + + + + + 在 中指定密码,且既没有写 ENCRYPTED 也没有写 UNENCRYPTED 时,此参数决定密码是否被加密。默认值为 on(加密密码)。 + + + + + + + krb_server_keyfile (string) + + krb_server_keyfile配置参数 + + + + + 设置服务器的Kerberos密钥文件的位置。详情请参考。 + 这个参数只能在postgresql.conf文件中或者服务器命令行上设置。 + + + + + + + krb_caseins_users (boolean) + + krb_caseins_users配置参数 + + + + + + 设置是否应该以大小写不敏感的方式对待GSSAPI用户名。默认值是off(大小写敏感)。这个参数只能在postgresql.conf文件中或者服务器命令行上设置。 + + + + + + + + db_user_namespace (boolean) + + db_user_namespace 配置参数 + + + + + 此参数启用各数据库独立的用户名。默认关闭。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + 如果启用此参数,你应以username@dbname的形式创建用户。 + 连接的客户端传入username时,会在用户名后附加@和数据库名, + 然后由服务器查找此数据库专属的用户名。请注意,在 SQL 环境中创建名称包含@的用户时,需要用引号括起用户名。 + + + + 启用此参数后,仍然可以创建普通的全局用户。只需在客户端指定用户名时附加@,例如joe@。 + 服务器查找用户名之前会去掉@。 + + + + db_user_namespace会使客户端和服务器的用户名表示形式不同。 + 认证检查始终使用服务器端的用户名,因此认证方法必须针对服务器端的用户名配置,而不是客户端的用户名。 + 由于md5在客户端和服务器上都使用用户名作为盐值,md5不能与db_user_namespace一起使用。 + + + + + 此特性旨在作为找到完整解决方案之前的临时措施。届时将移除此选项。 + + + + + + + + + + 资源消耗 + + + 内存 + + + + shared_buffers (integer) + + shared_buffers配置参数 + + + + + 设置数据库服务器用于共享内存缓冲区的内存量。默认值通常为 128 兆字节(128MB),但如果内核设置不支持,则可能更小(在 initdb 期间确定)。此设置必须至少为 128 千字节。(BLCKSZ 的非默认值会改变该最小值。)不过,要获得良好性能,通常需要远高于该最小值的设置。此参数只能在服务器启动时设置。 + + + 如果专用数据库服务器具有 1GB 或更多内存,shared_buffers 的合理初始值是系统内存的 25%。对于某些工作负载,将 shared_buffers 设得很大也有效,但由于 PostgreSQL 同时依赖操作系统缓存,将超过 40% 的内存分配给 shared_buffers 不太可能比更小的值效果更好。将 shared_buffers 设得更大时,通常还需要相应增加 max_wal_size,以便将大量新数据或已修改数据的写入过程分散到更长的时间内。 + + + 对于内存少于 1GB 的系统,适合使用更小的内存比例,以便为操作系统留出足够空间。另外,在 Windows 上,很大的 shared_buffers 设置并不那么有效。让该设置保持相对较低、更多地使用操作系统缓存,可能得到更好的结果。Windows 系统上 shared_buffers 的有效范围通常是从 64MB 到 512MB。 + + + + + + huge_pages (enum) + + huge_pages配置参数 + + + + + 启用或禁用巨型内存页。有效值为 try(默认值)、onoff。 + + + 目前只有 Linux 支持此特性。在其他系统上,设置为 try 时会忽略此设置。 + + + 使用巨型页可缩小页表,减少内存管理所需的 CPU 时间,从而提高性能。更多信息参见 。 + + + 将 huge_pages 设为 try 时,服务器会尝试使用巨型页,失败后则回退到普通内存分配。设为 on 时,使用巨型页失败会导致服务器无法启动。设为 off 时,不使用巨型页。 + + + + + + temp_buffers (integer) + + temp_buffers配置参数 + + + + + 设置每个数据库会话使用的临时缓冲区的最大数量。这些是会话本地的缓冲区,仅用于访问临时表。默认值为 8 兆字节(8MB)。可以在单个会话内更改此设置,但必须在该会话首次使用临时表之前更改;此后尝试更改该值,对该会话不会产生影响。 + + + 会话会按需分配临时缓冲区,上限为 temp_buffers。对于实际不需要很多临时缓冲区的会话,将此参数设得较大时,开销仅为 temp_buffers 每增加一就多分配一个缓冲区描述符,约为 64 字节。不过,如果实际使用了某个缓冲区,还会为它额外消耗 8192 字节(一般而言为 BLCKSZ 字节)。 + + + + + + max_prepared_transactions (integer) + + max_prepared_transactions配置参数 + + + + + 设置可同时处于预备状态的事务的最大数量(见 )。将此参数设为零(默认值)会禁用预备事务功能。此参数只能在服务器启动时设置。 + + + 如果不打算使用预备事务,应将此参数设为零,以防意外创建预备事务。如果使用预备事务,通常应将 max_prepared_transactions 设为不小于 的值,以便每个会话都能有一个待处理的预备事务。 + + + 运行备库时,必须将此参数设为与主库相同或更大的值。否则,备库上将不允许执行查询。 + + + + + + work_mem (integer) + + work_mem配置参数 + + + + + 指定内部排序操作和哈希表在写入临时磁盘文件之前可使用的内存量。默认值为 4 兆字节(4MB)。请注意,复杂查询可能并行执行多个排序或哈希操作,每个操作在开始向临时文件写入数据之前,都可以使用此值指定的内存量。此外,多个正在运行的会话也可能并发执行此类操作。因此,使用的总内存量可能是 work_mem 值的数倍;选择此值时必须考虑这一点。排序操作用于 ORDER BYDISTINCT 和归并连接。哈希表用于哈希连接、基于哈希的聚合、以及基于哈希的 IN 子查询处理。 + + + + + + maintenance_work_mem (integer) + + maintenance_work_mem配置参数 + + + + + 指定维护操作(如 VACUUMCREATE INDEXALTER TABLE ADD FOREIGN KEY)可使用的最大内存量。默认值为 64 兆字节(64MB)。由于一个数据库会话一次只能执行一个此类操作,而一个数据库系统通常也不会并发运行很多此类操作,因此可以安全地将该值设得远大于 work_mem。更大的设置可能改善清理和恢复数据库转储的性能。 + + + 注意,自动清理运行时,最多可能分配此内存量的 倍,因此不要将默认值设得过高。单独设置 可能有助于控制这一点。 + + + 注意,在收集死元组标识符时,VACUUM 最多只能使用 1GB 内存。 + + + + + + replacement_sort_tuples (integer) + + replacement_sort_tuples 配置参数 + + + + + 当待排序的元组数小于此数值时,排序会使用置换选择而非快速排序来产生第一个输出有序段。 + 在内存受限的环境中,如果较大排序操作的输入元组具有很强的物理顺序与逻辑顺序相关性,这可能很有用。 + 请注意,这不包括具有反向相关性的输入元组。 + 置换选择算法可能产生一个无需归并的长有序段,而默认策略会产生许多必须归并才能得到最终排序输出的有序段。 + 这可能让排序操作更快完成。 + + + 默认值为 150,000 个元组。请注意,更高的值通常也不会让效果好很多,甚至可能适得其反, + 因为优先队列对可用 CPU 缓存的大小很敏感,而默认策略使用缓存无关算法对有序段进行排序。 + 这一特性让默认排序策略能够自动、透明地有效利用可用 CPU 缓存。 + + + 将maintenance_work_mem设置为默认值,通常会使工具命令的外部排序(例如CREATE INDEX构建 B-树索引时使用的排序) + 完全不会使用置换选择排序,除非输入元组相当宽。 + + + + + + autovacuum_work_mem (integer) + + autovacuum_work_mem配置参数 + + + + + 指定每个自动清理工作进程可使用的最大内存量。默认值为 -1,表示改用 的值。该设置不影响其他上下文中运行的 VACUUM 的行为。此参数只能在 postgresql.conf 文件中或服务器命令行上设置。 + + + 在收集死元组标识符时,自动清理最多只能使用 1GB 内存,因此将 autovacuum_work_mem 设得更高,不会影响自动清理扫描表时能收集的死元组数量。 + + + + + + max_stack_depth (integer) + + max_stack_depth配置参数 + + + + + + 指定服务器执行栈的最大安全深度。此参数的理想设置是由内核强制执行的实际栈大小限制 + (如由ulimit -s或本地等效设置),减去大约一兆字节的安全余量。 + 需要安全余量是因为服务器中并非每个例程都检查栈深度,而只在可能递归的关键例程(如表达式求值)中检查。 + 默认设置为两兆字节(2MB), + 这是保守且不太可能引起崩溃的小值。但是,这可能太小,无法执行复杂函数。 + 只有超级用户才能更改此设置。 + + + + 把max_stack_depth参数设置得高于实际的内核限制将意味着一个失控的递归函数可能会导致一个独立的后端进程崩溃。 在PostgreSQL能够检测内核限制的平台上, 服务器将不允许把这个参数设置为一个不安全的值。不过,并非所有平台都能提供该信息,所以我们还是建议你在选择值时要小心。 + + + + + + dynamic_shared_memory_type (enum) + + dynamic_shared_memory_type配置参数 + + + + + 指定服务器应使用的动态共享内存实现。可选值为 posix(使用 shm_open 分配的 POSIX 共享内存)、sysv(通过 shmget 分配的 System V 共享内存)、windows(Windows 共享内存)、mmap(使用存放在数据目录中的内存映射文件模拟共享内存),以及 none(禁用此功能)。并非所有平台都支持所有值;第一个受支持的选项是该平台的默认值。mmap 不是任何平台的默认选项,通常不建议使用,因为操作系统可能会反复将修改过的页面写回磁盘,增加系统 I/O 负载;不过,在调试、将 pg_dynshmem 目录存放在 RAM 磁盘上,或其他共享内存设施不可用时,它可能有用。 + + + + + + + + + 磁盘 + + + + temp_file_limit (integer) + + temp_file_limit配置参数 + + + + + 指定一个进程可用于临时文件的最大磁盘空间,例如排序和哈希临时文件,或保留游标的存储文件。尝试超过此限制的事务将被取消。该值以千字节为单位。-1(默认值)表示没有限制。只有超级用户才能更改此设置。 + + + 此设置限制单个 PostgreSQL 进程在任意时刻使用的所有临时文件的总空间。需要注意,显式临时表所用的磁盘空间计入该上限;计入的是查询执行过程中内部使用的临时文件。 + + + + + + + + + 内核资源使用 + + + + max_files_per_process (integer) + + max_files_per_process配置参数 + + + + + 设置每个服务器子进程允许同时打开的最大文件数量。默认值为一千个文件。如果内核强制实施了安全的每进程上限,就不必担心此设置。但在某些平台上(尤其是大多数 BSD 系统),内核允许单个进程打开的文件数量很大,如果很多进程都尝试打开这么多文件,就会远超系统实际能够支持的总量。如果遇到 Too many open files(打开的文件过多)错误,可尝试减小此设置。此参数只能在服务器启动时设置。 + + + + + + + + + 基于代价的清理延迟 + + + 执行 命令期间,系统维护一个内部计数器,记录已执行的各种 I/O 操作的估算代价。 + 当累计代价达到上限(由 vacuum_cost_limit 指定)时,执行该操作的进程会休眠一小段时间,时长由 vacuum_cost_delay 指定。 + 随后重置计数器并继续执行。 + + + + 此功能让管理员能够降低这些命令对并发数据库活动的 I/O 影响。在许多情况下,VACUUMANALYZE 等维护命令是否快速完成并不重要, + 但避免它们显著干扰系统执行其他数据库操作的能力通常很重要。基于代价的清理延迟为管理员提供了实现这一点的方法。 + + + + 对于手动执行的 VACUUM 命令,此功能默认禁用。要启用它,将 vacuum_cost_delay 变量设为非零值。 + + + + + vacuum_cost_delay (integer) + + vacuum_cost_delay 配置参数 + + + + + 超过代价上限后,进程将休眠的时长,单位为毫秒。默认值为零,表示禁用基于代价的清理延迟功能。正值会启用基于代价的清理。注意,在许多系统上,休眠延迟的有效分辨率为 10 毫秒;将 vacuum_cost_delay 设为不是 10 的倍数的值,可能与将它设为下一个更大的 10 的倍数效果相同。 + + + 使用基于代价的清理时,vacuum_cost_delay 的合适值通常较小,例如 10 或 20 毫秒。调整清理的资源消耗时,最好更改其他清理代价参数。 + + + + + + + vacuum_cost_page_hit (integer) + + vacuum_cost_page_hit配置参数 + + + + + 清理在共享缓冲区缓存中找到的缓冲区时所计入的估算代价。它表示锁定缓冲池、查找共享哈希表和扫描页内容的代价。默认值为 1。 + + + + + + vacuum_cost_page_miss (integer) + + vacuum_cost_page_miss配置参数 + + + + + 清理必须从磁盘读取的缓冲区时所计入的估算代价。它表示锁定缓冲池、查找共享哈希表、从磁盘读取所需数据块并扫描其内容所需的工作量。默认值为 10。 + + + + + + + vacuum_cost_page_dirty (integer) + + vacuum_cost_page_dirty配置参数 + + + + + 清理操作修改原本干净的数据块时所计入的估算代价。它表示再次将脏块刷盘所需的额外 I/O。默认值为 20。 + + + + + + + vacuum_cost_limit (integer) + + vacuum_cost_limit配置参数 + + + + + 会使清理进程休眠的累计代价。默认值为 200。 + + + + + + + + 某些操作持有关键的锁,因此应尽快完成。这些操作期间不会发生基于代价的清理延迟,所以累计代价可能远超指定上限。 + 为避免此时出现无益的长时间延迟,实际延迟按 vacuum_cost_delay * accumulated_balance / vacuum_cost_limit 计算, + 但最大不超过 vacuum_cost_delay * 4。 + + + + + + 后台写入器 + + + 有一个独立的服务器进程,称为后台写入器,负责写出(新的或修改过的)共享缓冲区。 + 当干净的共享缓冲区数量似乎不足时,后台写入器会将一些脏缓冲区写入文件系统,并将其标记为干净。 + 这可以降低处理用户查询的服务器进程找不到干净缓冲区、因而不得不自行写出脏缓冲区的可能性。 + 不过,后台写入器确实会使总体 I/O 负载有所增加:反复变脏的页面原本可能在每个检查点间隔中只写出一次, + 而后台写入器可能在同一间隔内随着它变脏而多次写出。本节参数可用于根据实际需求调整此行为。 + + + + + bgwriter_delay (integer) + + bgwriter_delay配置参数 + + + + + 指定后台写入器各轮活动之间的延迟。每一轮中,写入器会对一定数量的脏缓冲区发出写操作(由下面的参数控制),然后休眠 bgwriter_delay 毫秒,再重复此过程。不过,当缓冲池中没有脏缓冲区时,它会进入更长的休眠,而不受 bgwriter_delay 限制。默认值为 200 毫秒(200ms)。注意,在许多系统上,休眠延迟的有效分辨率为 10 毫秒;将 bgwriter_delay 设为不是 10 的倍数的值,可能与将它设为下一个更大的 10 的倍数效果相同。此参数只能在 postgresql.conf 文件中或服务器命令行上设置。 + + + + + + + bgwriter_lru_maxpages (integer) + + bgwriter_lru_maxpages配置参数 + + + + + 后台写入器每轮写出的缓冲区数量不会超过此值。设为零会禁用后台写入。(由另一个独立的专用辅助进程管理的检查点不受影响。)默认值为 100 个缓冲区。此参数只能在 postgresql.conf 文件中或服务器命令行上设置。 + + + + + + + bgwriter_lru_multiplier (floating point) + + bgwriter_lru_multiplier配置参数 + + + + + 每轮写出的脏缓冲区数量取决于最近几轮服务器进程所需的新缓冲区数量。将近期平均需求乘以 bgwriter_lru_multiplier,即可估算下一轮所需的缓冲区数量。写入器会写出脏缓冲区,直到可用的干净且可重用缓冲区达到这一数量。(不过,每轮写出的缓冲区数量不会超过 bgwriter_lru_maxpages。)因此,设为 1.0 表示采用恰好及时策略,写出的缓冲区数量恰好等于预测需求量。更大的值可为需求突增留出余量,而更小的值则有意将部分写操作留给服务器进程执行。默认值为 2.0。此参数只能在 postgresql.conf 文件中或服务器命令行上设置。 + + + + + + bgwriter_flush_after (integer) + + bgwriter_flush_after 配置参数 + + + + + 每当后台写入器写出的数据超过bgwriter_flush_after 字节时,就尝试强制操作系统将这些写操作发往底层存储。这样可以限制内核页缓存中的脏数据量,降低在检查点结束时调用 fsync,或操作系统在后台以较大批次写回数据时发生停顿的可能性。通常,这能大幅降低事务延迟,但在某些情况下性能可能下降,尤其是工作负载大于 而小于操作系统页缓存时。此设置在某些平台上可能没有效果。有效范围为 0(禁用强制写回)至 2MB。Linux 上的默认值为 512kB,其他平台为 0。(如果 BLCKSZ 不是 8kB,默认值和最大值将按比例变化。)此参数只能在 postgresql.conf 文件中或服务器命令行上设置。 + + + + + + + 较小的 bgwriter_lru_maxpagesbgwriter_lru_multiplier 可以降低后台写入器造成的额外 I/O 负载, + 但也会增加服务器进程必须自行发出写操作的可能性,从而延迟交互式查询。 + + + + + 异步行为 + + + + effective_io_concurrency (integer) + + effective_io_concurrency配置参数 + + + + + 设置 PostgreSQL 预期可以同时执行的并发磁盘 I/O 操作数量。提高此值会增加单个 PostgreSQL 会话尝试并行发起的 I/O 操作数量。允许的范围为 1 至 1000,或设为零以禁用异步 I/O 请求。目前,此设置仅影响位图堆扫描。 + + + 对于磁盘,可以将为数据库提供存储的 RAID 0 条带或 RAID 1 镜像中的独立磁盘数量作为合理初始值。(对于 RAID 5,不应计入校验盘。)不过,如果数据库经常忙于执行并发会话发出的多个查询,较小的值可能就足以使磁盘阵列保持繁忙。超过使磁盘保持繁忙所需的值只会增加 CPU 开销。SSD 和其他基于内存的存储通常可以处理大量并发请求,因此最佳值可能达到数百。 + + + 异步 I/O 依赖于有效的 posix_fadvise 函数,而某些操作系统缺少此函数。如果该函数不存在,将此参数设为任何非零值都会报错。在某些操作系统(如 Solaris)上,该函数虽然存在,却实际上不做任何事情。 + + + 支持此功能的系统上默认值为 1,其他系统为 0。对于位于特定表空间中的表,可以通过设置同名的表空间参数覆盖此值(见 )。 + + + + + + max_worker_processes (integer) + + max_worker_processes配置参数 + + + + + 设置系统能够支持的后台进程的最大数量。此参数只能在服务器启动时设置。默认值为 8。 + + + 运行备库时,必须将此参数设为与主库相同或更大的值。否则,备库上将不允许执行查询。 + + + + + + + max_parallel_workers_per_gather (integer) + + max_parallel_workers_per_gather 配置参数 + + + + + 设置单个 Gather 节点能够启动的工作进程的最大数量。并行工作进程取自 建立的进程池。注意,运行时实际可用的工作进程可能不足请求的数量。此时,计划会使用少于预期的工作进程运行,效率可能较低。设为 0(即默认值)会禁用并行查询执行。 + + + 注意,并行查询消耗的资源可能远多于非并行查询,因为每个工作进程都是完全独立的进程,对系统的影响大致相当于额外增加一个用户会话。选择此设置的值,以及配置其他控制资源使用的设置(如 )时,都应考虑这一点。work_mem 等资源限制分别应用于每个工作进程,因此所有进程的总资源用量可能远高于单个进程通常的用量。例如,使用 4 个工作进程的并行查询,其 CPU 时间、内存、I/O 带宽等用量可能达到完全不使用工作进程的查询的 5 倍。 + + + 并行查询的更多信息参见 。 + + + + + + + backend_flush_after (integer) + + backend_flush_after 配置参数 + + + + + 每当单个后端写出的数据超过backend_flush_after 字节时,就尝试强制操作系统将这些写操作发往底层存储。这样可以限制内核页缓存中的脏数据量,降低在检查点结束时调用 fsync,或操作系统在后台以较大批次写回数据时发生停顿的可能性。通常,这能大幅降低事务延迟,但在某些情况下性能可能下降,尤其是工作负载大于 而小于操作系统页缓存时。此设置在某些平台上可能没有效果。有效范围为 0(禁用强制写回)至 2MB。默认值为 0,即不强制写回。(如果 BLCKSZ 不是 8kB,最大值将按比例变化。) + + + + + + + old_snapshot_threshold (integer) + + old_snapshot_threshold 配置参数 + + + + + 设置快照在使用时不会发生 snapshot too old 错误的最短可用时间。此参数只能在服务器启动时设置。 + + + 超过该阈值后,旧数据可以被清理掉。这有助于在快照长期保持使用时防止膨胀。为了避免因清理本应对该快照可见的数据而得到错误结果,当快照年龄超过该阈值,且该快照被用于读取自其建立以来已被修改过的页面时,就会报错。 + + + 值 -1 会禁用此功能,也是默认值。对生产环境而言,有用的取值大概从几个小时到几天不等。此设置会被强制调整为分钟粒度;较小的值(例如 01min)之所以被允许,只是因为它们有时可用于测试。虽然允许设置到 60d 这么高,但在许多工作负载中,严重膨胀或事务 ID 回卷可能会在更短时间内发生。 + + + 启用此功能后,关系末尾释放出来的空间不能返还给操作系统,因为那样可能会移除检测 snapshot too old 条件所需的信息。分配给某个关系的全部空间仍归属于该关系,只能在该关系内重用,除非显式释放(例如使用 VACUUM FULL)。 + + + 此设置不保证在任何特定情况下都一定会报错。实际上,如果仍能从某个对象(例如已物化结果集的游标)生成正确结果,那么即使被引用表中的底层行已被清理掉,也不会报错。有些表不能安全地提前清理,因此不受此设置影响。例如系统目录,以及任何带有哈希索引的表。对于这类表,此设置既不会减少膨胀,也不会在扫描时引入 snapshot too old 错误的可能性。 + + + + + + + + + 预写式日志 + + + 参阅获取调节这些设置的额外信息。 + + + + 设置 + + + + wal_level (enum) + + wal_level配置参数 + + + + + wal_level决定多少信息写入到 WAL 中。默认值是minimal,它只写入从崩溃或立即关机中进行恢复所需的信息。replica会增加 WAL 归档所需的日志,以及在备库上运行只读查询所需的信息。最后,logical会增加支持逻辑解码所需的信息。每个层次包括所有更低层次记录的信息。这个参数只能在服务器启动时设置。 + + + 在minimal级别,可以安全地跳过一些批量操作的 WAL 日志记录,使这些操作快得多(参见)。 + 可以应用此优化的操作包括: + + CREATE TABLE AS + CREATE INDEX + CLUSTER + 向同一事务中创建或截断的表执行COPY + + 但 minimal 级别的 WAL 不包含足够的信息来从基础备份和 WAL 日志重建数据,因此必须使用replica或更高级别来启用 WAL 归档 + ()和流复制。 + + + 在logical级别上,记录与replica相同的信息,以及从WAL中提取逻辑变更集所需的信息。 + 使用logical级别会增加 WAL 的数量,特别是如果许多表被配置为REPLICA IDENTITY FULL, + 并且执行了许多UPDATEDELETE语句。 + + + 在 9.6 之前的版本中,这个参数也允许值archivehot_standby。现在仍然接受这些值,但是它们会被映射到replica。 + + + + + + + fsync (boolean) + + fsync配置参数 + + + + + + 如果打开这个参数,PostgreSQL服务器将尝试确保更新被物理地写入到磁盘,做法是发出fsync()系统调用或者使用多种等价的方法(见)。这保证了数据库集簇在一次操作系统或者硬件崩溃后能恢复到一个一致的状态。 + + + + 虽然关闭fsync常常可以得到性能上的收益,但当发生断电或系统崩溃时可能造成不可恢复的数据损坏。因此,只有在能很容易地从外部数据中重建整个数据库时才建议关闭fsync。 + + + + 可以安全关闭fsync的情形包括:从备份文件初始装载一个新数据库集簇;用数据库集簇处理一批数据,处理后就丢弃并重建该数据库;或者使用经常重建且不用于故障切换的只读数据库克隆。仅有高质量硬件不足以成为关闭fsync的理由。 + + + + 为确保将fsync从关闭改为打开后能够可靠恢复,必须将内核中所有已修改的缓冲区强制写入持久存储。可以在集簇已关闭或 fsync 已开启时,通过运行initdb --sync-only、运行sync、卸载文件系统或重启服务器来完成。 + + + + 在很多情况下,为非关键事务关闭,可以获得关闭fsync所带来的大部分潜在性能收益,同时避免伴随的数据损坏风险。 + + + + fsync只能在postgresql.conf文件中或在服务器命令行上设置。如果你关闭这个参数,请也考虑关闭。 + + + + + + synchronous_commit (enum) + + synchronous_commit配置参数 + + + + + 指定数据库服务器向客户端返回成功指示之前,必须完成多少 WAL 处理。有效值为remote_applyon(默认值)、remote_writelocaloff。 + + + + 如果synchronous_standby_names为空,只有onoff两种设置有意义;remote_applyremote_writelocal提供的本地同步级别都与on相同。所有非off模式在本地都会等待 WAL 刷写到磁盘。在off模式下则无需等待,因此,向客户端报告成功后,可能还要经过一段时间,才能保证事务不会因服务器崩溃而丢失。(最大延迟为的三倍。)与不同,将此参数设为off不会带来数据库不一致的风险:操作系统或数据库崩溃可能会使一些最近报告已提交的事务丢失,但数据库状态会与这些事务已正常中止时完全相同。因此,当性能比完全确保事务持久性更重要时,关闭synchronous_commit可以是一种有用的替代方案。更多讨论见。 + + + + 如果非空,synchronous_commit还控制事务提交是否等待备库处理其 WAL 记录。 + + + + 设为remote_apply时,提交会等待当前同步备库回复,确认已收到并应用该事务的提交记录,使其对备库上的查询可见,并且已将其写入备库的持久存储。由于需要等待 WAL 重放,这会比之前的设置产生大得多的提交延迟。设为on时,提交会等待当前同步备库回复,确认已收到事务的提交记录,并已将其刷写到持久存储。这能保证事务不会丢失,除非主库和所有同步备库的数据库存储都损坏。设为remote_write时,提交会等待当前同步备库回复,确认已收到事务的提交记录,并已将其写入各自的文件系统。此设置能保证备库上的PostgreSQL实例崩溃时数据不丢失,但不能保证备库发生操作系统级别崩溃时数据不丢失,因为数据未必已写入备库的持久存储。设为local时,提交会等待本地刷盘,但不等待复制。使用同步复制时通常不希望采用这种设置,提供它是为了使选项完整。 + + + + 此参数可以随时更改;每个事务的行为由提交时生效的设置决定。因此,让一些事务同步提交、另一些事务异步提交是可行且有用的。例如,当默认设置要求同步提交时,可以在一个包含多条语句的事务中执行SET LOCAL synchronous_commit TO OFF,使该事务异步提交。 + + + + 汇总了synchronous_commit各种设置具备的能力。 + + + + synchronous_commit 模式 + + + + + + + + + synchronous_commit 设置 + 本地提交持久性 + PG 崩溃后备库提交持久性 + OS 崩溃后备库提交持久性 + 备库查询一致性 + + + + + + + remote_apply + + + + + + + + on + + + + + + + + remote_write + + + + + + + + local + + + + + + + + off + + + + + + + + +
+ +
+
+ + + + wal_sync_method (enum) + + wal_sync_method配置参数 + + + + + + 用于将 WAL 更新强制写入磁盘的方法。如果fsync关闭,此设置就没有作用,因为 WAL 文件更新根本不会被强制写入磁盘。可选值为: + + + + + + open_datasync(用open()选项O_DSYNC写 WAL 文件) + + + + + + fdatasync(在每次提交时调用fdatasync()) + + + + + + fsync(在每次提交时调用fsync()) + + + + + + fsync_writethrough(在每次提交时调用fsync(),强制穿透任何磁盘写缓存) + + + + + + open_sync(用open()选项O_SYNC写 WAL 文件) + + + + + + open_* 选项还会使用O_DIRECT(如果可用)。 + 不是在所有平台上都能使用所有这些选择。 + 默认值是列表中第一个被平台支持的那个, 不过fdatasync是 Linux 和 FreeBSD 中的默认值。 + 默认值不一定最合适;可能需要更改此设置或系统配置的其他方面,以确保崩溃时的数据安全或达到最佳性能。 + 这些方面在中讨论。 + 这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + full_page_writes (boolean) + + full_page_writes配置参数 + + + + + 启用此参数时,PostgreSQL服务器会在检查点之后首次修改每个磁盘页面时,将该页面的全部内容写入 WAL。这样做是因为,操作系统崩溃时正在进行的页面写入可能只完成了一部分,导致磁盘页面混有新旧数据。通常存储在 WAL 中的行级变更数据不足以在崩溃恢复时完整还原这样的页面。保存整页镜像能保证正确恢复页面,但会增加必须写入 WAL 的数据量。(由于 WAL 重放总是从检查点开始,只需在检查点之后首次修改每个页面时这样做。因此,减少整页写入开销的一种方法是增大检查点间隔参数。) + + + + 关闭此参数可以加快正常操作,但系统故障后可能出现不可恢复的数据损坏或静默数据损坏。风险与关闭fsync类似,虽然较小,但也只有在该参数建议的相同情形下才应关闭此参数。 + + + + 关闭这个选项并不影响用于时间点恢复(PITR)的 WAL 归档使用(见)。 + + + + 这个参数只能在postgresql.conf文件中或在服务器命令行上设置。默认值是on。 + + + + + + + wal_log_hints (boolean) + + wal_log_hints配置参数 + + + + + + 当这个参数为on时,PostgreSQL服务器在检查点之后首次修改页面时把该磁盘页面的整个内容都写入 WAL,即使对所谓的提示位做非关键修改也会这样做。 + + + + 如果启用了数据校验和,提示位更新总是会被 WAL 记录并且这个设置会被忽略。你可以使用这个 + 设置测试如果你的数据库启用了数据校验和,会有多少额外的 WAL 记录发生。 + + + + 这个参数只能在服务器启动时设置。默认值是off。 + + + + + + wal_compression (boolean) + + wal_compression配置参数 + + + + + 当这个参数为on时,PostgreSQL服务器在打开时或在基础备份期间,压缩写入WAL的整页镜像。在 WAL 重放期间将对压缩的整页镜像进行解压。默认值是off。只有超级用户能更改这个设置。 + + + + 开启此参数可以减少 WAL 数据量,而且不会增加不可恢复的数据损坏风险;代价是在记录 WAL 时压缩、重放 WAL 时解压会额外消耗一些 CPU。 + + + + + + wal_buffers (integer) + + wal_buffers配置参数 + + + + + 用于还未写入磁盘的 WAL 数据的共享内存量。默认值 -1 选择等于的 1/32 的尺寸(大约3%),但是不小于64kB也不大于 WAL 段的尺寸(通常为16MB)。如果自动的选择太大或太小可以手工设置该值,但是任何小于32kB的正值都将被当作32kB。 + 这个参数只能在服务器启动时设置。 + + + + 在每次事务提交时,WAL 缓冲区的内容被写出到磁盘,因此极大的值不太可能带来显著收益。不过,把这个值设置为至少几个兆字节可以在一个繁忙的服务器(其中很多客户端会在同一时间提交)上提高写性能。由默认设置 -1 选择的自动调节将在大部分情况下得到合理的结果。 + + + + + + + wal_writer_delay (integer) + + wal_writer_delay配置参数 + + + + + 指定 WAL 写入器刷写 WAL 的频繁程度。刷写 WAL 之后,它会休眠wal_writer_delay毫秒,除非被异步提交的事务唤醒。 + 如果上一次刷写距今少于wal_writer_delay毫秒,并且此后产生的 WAL 少于wal_writer_flush_after字节, + 则只将 WAL 写入操作系统,而不刷写到磁盘。 + 默认值为 200 毫秒(200ms)。请注意,许多系统的休眠延迟有效分辨率为 10 毫秒; + 将wal_writer_delay设置为非 10 的倍数的值,可能与设置为下一个更大的 10 的倍数具有相同效果。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + wal_writer_flush_after (integer) + + wal_writer_flush_after 配置参数 + + + + + 指定 WAL 写入器刷写 WAL 的频繁程度。如果上一次刷写距今少于wal_writer_delay毫秒, + 并且此后产生的 WAL 少于wal_writer_flush_after字节,则只将 WAL 写入操作系统,而不刷写到磁盘。 + 如果wal_writer_flush_after设置为0,则立即刷写 WAL 数据。 + 默认值为1MB。此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + commit_delay (integer) + + commit_delay配置参数 + + + + + 设置commit_delay会在发起 WAL 刷盘之前添加以微秒为单位的时间延迟。 + 如果系统负载足够高,使得在给定时间间隔内有更多事务准备提交, + 这可以通过允许更多事务通过一次 WAL 刷盘来提高组提交吞吐量。 + 不过,每次 WAL 刷盘的延迟也会因此增加,最多增加commit_delay微秒。 + 如果没有其他事务准备提交,等待就没有意义,因此仅当即将发起刷盘时至少还有 + commit_siblings个其他活动事务,才会等待。 + 此外,如果禁用了fsync,也不会等待。 + 默认commit_delay为零(无延迟)。 + 只有超级用户能更改这个设置。 + + + 在PostgreSQL的 9.3 发布之前,commit_delay的行为不同并且效果更差:它只影响提交,而不是所有 WAL 刷写,并且即使 WAL 刷盘更早完成,也会等待整个配置的延迟时间。从PostgreSQL 9.3 开始,第一个准备好刷写的进程会等待配置的间隔,而后续的进程只等到领先者完成刷写操作。 + + + + + + + commit_siblings (integer) + + commit_siblings配置参数 + + + + + + 执行commit_delay延迟前要求的并发活动事务的最小数目。大一些的值会导致在延迟间隔期间更可能有至少另外一个事务准备好提交。默认值是五个事务。 + + + + +
+
+ + 检查点 + + + + checkpoint_timeout (integer) + + checkpoint_timeout配置参数 + + + + + 自动 WAL 检查点之间的最长时间,以秒为单位。 + 合理的范围在 30 秒到 1 天之间。默认是 5 分钟(5min)。增加这个参数的值可能会增加崩溃恢复所需的时间。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + checkpoint_completion_target (floating point) + + checkpoint_completion_target配置参数 + + + + + 指定检查点完成的目标,作为检查点之间总时间的一部分。默认是 0.5。 + 这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + checkpoint_flush_after (integer) + + checkpoint_flush_after 配置参数 + + + + + 当执行检查点时写入的数据量超过checkpoint_flush_after字节时,就尝试强制 OS 把这些写发送到底层存储。 + 这样做将会限制内核页面高速缓存中的脏数据数量,降低在检查点末尾发出 fsync 或者 OS 在后台大批量写回数据时被卡住的可能性。 + 这通常能显著降低事务延迟,但是也有一些情况(特别是负载超过但小于 OS 页面高速缓存)的性能会降低。 + 这种设置可能会在某些平台上没有效果。 + 合法的范围在0(禁用强制写回)和2MB之间。Linux 上的默认值是256kB,其他平台上是0(如果BLCKSZ不是8kB,则默认值和最大值会按比例缩放到它)。这个参数只能在postgresql.conf文件中或者服务器命令行上设置。 + + + + + + checkpoint_warning (integer) + + checkpoint_warning配置参数 + + + + + 如果由于填充检查点段文件导致的检查点之间的间隔低于这个参数表示的秒数,那么就向服务器日志写一个消息(它建议增加max_wal_size的值)。 + 默认值是 30 秒(30s)。零则关闭警告。如果checkpoint_timeout低于checkpoint_warning,则不会有警告产生。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + max_wal_size (integer) + + max_wal_size 配置参数 + + + + + 在自动检查点期间允许WAL增长的最大大小。这是一个软限制;在特殊情况下,如重负载、失败的archive_command,或高wal_keep_segments设置下,WAL大小可能会超过max_wal_size。 + 默认值为1 GB。 + 增加此参数可能会增加崩溃恢复所需的时间。 + 此参数只能在postgresql.conf文件或服务器命令行中设置。 + + + + + + min_wal_size (integer) + + min_wal_size 配置参数 + + + + + 只要 WAL 磁盘用量保持在这个设置之下,在检查点时旧的 WAL 文件总是 + 被回收以便未来使用,而不是直接被删除。这可以被用来确保有足够的 + WAL 空间被保留来应付 WAL 使用的高峰,例如运行大型的批处理任务。 + 默认是 80 MB。这个参数只能在postgresql.conf + 或者服务器命令行中设置。 + + + + + + + + 归档 + + + + + archive_mode (enum) + + archive_mode配置参数 + + + + + + 当启用archive_mode时,完成的WAL段会通过设置 + 发送到归档存储。除了用于禁用归档的off外,还有两种模式:on和 + always。在正常操作期间,这两种模式之间没有区别,但当设置为always时, + WAL归档程序在归档恢复或备库模式下也会被启用。在always模式下,从归档中恢复的所有文件 + 或通过流复制传输的文件将被再次归档。详细信息请参见 + 。 + + + + archive_modearchive_command是 + 单独的变量,这样archive_command可以在不离开 + 归档模式的情况下进行更改。 + 此参数只能在服务器启动时设置。 + 当wal_level设置为minimal时, + 无法启用archive_mode。 + + + + + + + archive_command (string) + + archive_command配置参数 + + + + + + 本地 shell 命令被执行来归档一个完成的 WAL 文件段。字符串中的任何%p被替换成要被归档的文件的路径名, 而%f只被文件名替换(路径名是相对于服务器的工作目录, 即集簇的数据目录)。如果要在命令里嵌入一个真正的%字符,可以使用%%。有一点很重要,该命令只在成功时返回一个零作为退出状态。更多信息请见。 + + + + 这个参数只能在postgresql.conf文件或服务器命令行中设置。 + 除非在服务器启动时启用了archive_mode,否则将被忽略。 + 如果archive_command是空字符串(默认值),而archive_mode已启用, + WAL归档将暂时被禁用,但服务器将继续积累WAL段文件,期望很快会提供命令。 + 将archive_command设置为一个什么都不做但返回true的命令,例如/bin/true(Windows上为REM), + 实际上禁用了归档,但也破坏了用于归档恢复所需的WAL文件链,因此只应在不寻常的情况下使用。 + + + + + + archive_timeout (integer) + + archive_timeout配置参数 + + + + + 只针对已完成的 WAL 段调用。 + 因此,如果您的服务器生成的WAL流量较少(或者在这样做时有间歇期),在事务完成和安全记录到归档存储之间可能会有很长的延迟。 + 为了限制未归档数据的年龄,您可以将archive_timeout设置为强制服务器定期切换到新的WAL段文件。 + 当此参数大于零时,只要自上次段文件切换以来经过了指定的秒数,并且存在任何数据库活动(包括单个检查点),服务器将切换到新的段文件。(增大 checkpoint_timeout 可以减少空闲系统上不必要的检查点。) + 请注意,由于强制切换而提前关闭的归档文件仍然与完全填满的文件长度相同。因此,使用非常短的archive_timeout是不明智的,它会使您的归档存储膨胀。 + 通常,将archive_timeout设置为一分钟左右是合理的。如果您希望数据比这更快地从主库复制出来,您应该考虑使用流复制而不是归档。 + 此参数只能在postgresql.conf文件或服务器命令行中设置。 + + + + + + + +
+ + + 复制 + + + 这些设置控制内置流复制特性(见)的行为。服务器将可以是主库或备库。主库能发送数据,而备库总是被复制数据的接收者。当使用级联复制(见)时,备库也可以是发送者,同时也是接收者。这些参数主要用于发送服务器和备库,尽管某些只在主库上有意义。如果有必要,设置可以在集簇中变化而不出问题。 + + + + 发送服务器 + + + 这些参数可以在任何发送复制数据给一个或多个备库的服务器上设置。主库总是一个发送服务器,因此这些参数总是要在主库上设置。这些参数的角色和含义不会在一个备库变成主库后改变。 + + + + + max_wal_senders (integer) + + max_wal_senders配置参数 + + + + + 指定来自备库或流式基础备份客户端的并发连接的最大数量(即同时运行 WAL 发送进程的最大数)。 + 默认值为 0,即禁用复制。WAL 发送进程计入总连接数,因此此参数的值不能高于。 + 流式客户端突然断开连接可能留下一个孤立连接槽,直到达到超时,因此此参数应设置得略高于预期的最大客户端数,使断开连接的客户端能够立即重新连接。 + 此参数只能在服务器启动时设置。此外,wal_level必须设置为replica或更高级别,才允许来自备库的连接。 + + + + + + max_replication_slots (integer) + + max_replication_slots配置参数 + + + + + 指定服务器可以支持的复制槽(见) + 最大数量。默认值为 0。这个参数只能在服务器启动时设置。将它设置为一个比当前已有复制槽要少的值会阻碍服务器启动。此外,要允许使用复制槽, + wal_level必须被设置为replica或 + 更高。 + + + + + + wal_keep_segments (integer) + + wal_keep_segments 配置参数 + + + + + 指定在pg_xlog目录中保留的过去日志文件段的最小数量,以便备库可能需要获取它们来进行流复制。 + 每个段通常为 16 兆字节。如果连接到发送服务器的备库落后超过wal_keep_segments个段, + 发送服务器可能会移除备库仍需要的 WAL 段,在这种情况下,复制连接将被终止。下游连接最终也会因此失败。 + (但是,如果使用了 WAL 归档,备库可以通过从归档中获取该段来恢复。) + + + + 此设置只指定在pg_xlog中保留的最小段数;系统可能需要为 WAL 归档或从检查点恢复而保留更多段。 + 如果wal_keep_segments为零(默认值),系统不会为备库额外保留任何段,因此备库可用的旧 WAL 段数 + 取决于前一个检查点的位置和 WAL 归档的状态。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + wal_sender_timeout (integer) + + wal_sender_timeout配置参数 + + + + + 终止处于非活动状态超过指定毫秒数的复制连接。这有助于发送服务器检测备库崩溃或网络中断。 + 值零禁用超时机制。此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + 默认值为 60 秒。 + + + + + + track_commit_timestamp (bool) + + track_commit_timestamp 配置参数 + + + + + 记录事务的提交时间。该参数只能在postgresql.conf文件中或在服务器命令行上设置。默认值是off。 + + + + + + + + + 主库 + + + 这些参数可以在向一个或多个备库发送复制数据的主库上设置。 + 除这些参数外,还必须在主库上适当设置 , + 也可以选择启用 WAL 归档(见 )。 + 这些参数在备库上的取值不影响备库运行,不过也可以预先设置,以备将来提升为主库。 + + + + + + synchronous_standby_names (string) + + synchronous_standby_names配置参数 + + + + + 如所述,这个参数指定一个支持同步复制的备库的列表。 + 将有一个或多个活动的同步备库,在这些备库确认收到它们的数据之后,等待提交的事务将被允许继续下去。 + 同步备库是那些名字在这个列表中出现得较早,并且当前已连接并且正在实时流式传输数据(如pg_stat_replication视图中streaming的状态所示)的服务器。 + 在这个列表中出现得较晚的其他备库表示潜在的同步备库。如果当前的任何同步备库因为某种原因断开连接,它将立刻被下一个最高优先级的备库替代。 + 指定多于一台备库名称可以得到非常高的可用性。 + + + 这个参数使用下面的语法之一来指定一个备库列表: + +num_sync ( standby_name [, ...] ) +standby_name [, ...] + + 其中num_sync是事务需要等待其回复的同步备库的数量,standby_name是一个备库的名称。例如,设置3 (s1, s2, s3, s4)会让事务提交等待,直到它们的 WAL 记录被从备库s1s2s3以及s4中选出的三台较高优先级备库收到为止。 + + + PostgreSQL版本 9.6 之前使用过第二种语法,目前也仍然支持。它和num_sync等于 1 的第一种语法相同。例如,1 (s1, s2)s1, s2具有相同的含义:s1或者s2会被选中作为同步备库。 + + + 用于这一目的的备库名称是其 WAL 接收器的primary_conninfo中设置的application_name。没有机制强制唯一性。在出现重复的情况下,匹配的备库之一将被认为是较高优先级,不过无法弄清到底是哪一个。 + 特殊项*匹配任意application_name,包括默认的应用名walreceiver。 + + + + + 每一个standby_name都应该具有合法 SQL 标识符的形式,除非它是*。如果必要你可以使用双引号。但是注意在比较standby_name和备库应用程序名称时是大小写不敏感的(不管有没有双引号)。 + + + + 如果这里没有指定同步备库名称,那么不启用同步复制并且事务提交将不会等待复制。这是默认的配置。即便当同步复制被启用时,个体事务也可以被配置为不等待复制,做法是将参数设置为localoff。 + + + 这个参数只能在postgresql.conf文件中或通过服务器命令行进行设置。 + + + + + + + + vacuum_defer_cleanup_age (integer) + + vacuum_defer_cleanup_age 配置参数 + + + + + 指定VACUUMHOT更新延迟清理死行版本的事务数。默认值为零个事务, + 意味着可以尽快移除死行版本,也就是在它们不再对任何打开的事务可见时立即移除。 + 如所述,在为热备服务器提供支持的主库上,你可能希望将此参数设置为非零值。 + 这让备库上的查询有更多时间完成,而不会因过早清理行而发生冲突。 + 但是,由于此值以主库上发生的写事务数计量,很难预测会给备库查询带来多少额外的宽限时间。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + 你也应考虑在备库上设置hot_standby_feedback,作为使用此参数的替代方案。 + + + 这不会阻止清理已经达到old_snapshot_threshold指定年龄的死行。 + + + + + + + + 备库 + + + 这些设置控制备库接收复制数据时的行为。 + 它们在主库的值是无关的。 + + + + + + + hot_standby (boolean) + + hot_standby配置参数 + + + + + + 指定在恢复期间,你是否能够连接并运行查询,如中所述。默认值是off。这个参数只能在服务器启动时设置。它只在归档恢复期间或备库模式下才有效。 + + + + + + max_standby_archive_delay (integer) + + max_standby_archive_delay配置参数 + + + + + 当热备处于活动状态时,此参数确定备库在取消与即将应用的WAL条目冲突的备库查询之前应等待多长时间,如 + 中所述。 + max_standby_archive_delay在从WAL归档中读取WAL数据时适用(因此不是当前的)。 + 如果未指定单位,则将其视为毫秒。 + 默认值为30秒。 + 值为-1允许备库永远等待冲突查询完成。 + 此参数只能在postgresql.conf文件或服务器命令行中设置。 + + 注意,max_standby_archive_delay并不等同于查询在被取消前可以运行的最长时间;它表示应用任意一个 WAL 段的数据所允许的最长总时间。因此,如果某个查询先前在处理该 WAL 段时已造成显著延迟,后续冲突查询的宽限时间就会短得多。 + + + + + max_standby_streaming_delay (integer) + + max_standby_streaming_delay配置参数 + + + + + 当热备处于活动状态时,此参数确定备库在取消与即将应用的WAL条目冲突的备库查询之前应等待多长时间,如中所述。 + max_standby_streaming_delay在通过流复制接收WAL数据时应用。 + 如果未指定单位,则将其视为毫秒。 + 默认值为30秒。 + 值为-1允许备库永远等待冲突查询完成。 + 此参数只能在postgresql.conf文件或服务器命令行中设置。 + + 注意,max_standby_streaming_delay并不等同于查询在被取消前可以运行的最长时间;它表示从主库接收到 WAL 数据后,允许用于应用这些数据的最长总时间。因此,如果某个查询已造成显著延迟,后续冲突查询的宽限时间就会短得多,直到备库再次赶上进度。 + + + + + wal_receiver_status_interval (integer) + + wal_receiver_status_interval配置参数 + + + + + 指定在备库上的 WAL 接收进程向主库或上游备库发送有关复制进度的信息的最小频度,它可以使用pg_stat_replication视图看到。 + 备库将报告最后写入的事务日志位置、最后刷盘的位置以及最后应用的位置。 + 这个参数的值是报告之间的最大间隔,以秒为单位。 + 每次写入或刷盘位置改变时会发送状态更新,或者至少按这个参数指定的频度发送。 + 因此,应用位置可能比真实位置略微滞后。 + 默认值是 10 秒。 + 将此参数设置为零会完全禁用状态更新。 + 这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + hot_standby_feedback (boolean) + + hot_standby_feedback配置参数 + + + + + 指定一个热备机是否将会向主库或上游备库发送有关于备库上当前正被执行的查询的反馈。这个参数可以被用来消除由清理记录引起的查询取消,但是可能导致在主库上用于某些负载的数据库膨胀。反馈消息的发送频度不会高于每个wal_receiver_status_interval周期发送一次。默认值是off。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + 如果使用级联复制,反馈将被向上游传递直到它最后到达主库。备库在接收到反馈之后除了传递给上游不会做任何其他操作。 + + + 此设置不会覆盖主库上old_snapshot_threshold的行为。 + 如果备库上的快照超过了主库的快照年龄阈值,它可能失效,从而导致备库上的事务被取消。 + 这是因为old_snapshot_threshold旨在为死行版本导致表膨胀的持续时间设定一个绝对限制, + 而备库的配置不应允许违反这个限制。 + + + + + + wal_receiver_timeout (integer) + + wal_receiver_timeout配置参数 + + + + + 终止处于非活动状态超过指定毫秒数的复制连接。这有助于接收数据的备库检测主库节点崩溃或网络中断。 + 值零禁用超时机制。此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + 默认值为 60 秒。 + + + + + + wal_retrieve_retry_interval (integer) + + wal_retrieve_retry_interval 配置参数 + + + + + 指定当从任何来源(流复制、本地pg_xlog或者 WAL 归档)都得不到 WAL 数据时,备库应该等待多久才去重新尝试获取 WAL 数据。 + 如果指定值时没有单位,则以毫秒为单位。默认值是 5 秒。 + 这个参数只能在postgresql.conf文件或者服务器命令行中设置。 + + + 当恢复中的节点需要控制等待新 WAL 数据可用的时间时,此参数很有用。例如,在归档恢复中,减小此参数的值可以让恢复更快地检测到新的 WAL 日志文件。 + 在 WAL 活动较少的系统上,增大此值会减少访问 WAL 归档所需的请求数;这在例如按基础设施访问次数计量的云环境中很有用。 + + + + + + + + + + + + 查询规划 + + + 规划器方法配置 + + + 这些配置参数提供了一种较为粗糙的方法,用于影响查询优化器所选择的查询计划。 + 如果优化器为某个特定查询选择的默认计划并不理想,一种临时解决方案是使用这些配置参数, + 强制优化器选择另一种计划。 + 改善优化器所选计划质量的更好办法包括调整规划器代价常数 + (见)、手工运行 + 、增加 + 配置参数的值,以及使用 + ALTER TABLE SET STATISTICS 增加为特定列收集的统计信息量。 + + + + + + + enable_bitmapscan (boolean) + + 位图扫描 + + + enable_bitmapscan 配置参数 + + + + + 启用或禁用查询规划器对位图扫描计划类型的使用。默认值为 on。 + + + + + + + + + + + enable_hashagg (boolean) + + enable_hashagg 配置参数 + + + + + 启用或禁用查询规划器对哈希聚合计划类型的使用。默认值为 on。 + + + + + + + + enable_hashjoin (boolean) + + enable_hashjoin 配置参数 + + + + + 启用或禁用查询规划器对哈希连接计划类型的使用。默认值为 on。 + + + + + + + + + + enable_indexscan (boolean) + + 索引扫描 + + + enable_indexscan 配置参数 + + + + + 启用或禁用查询规划器对索引扫描计划类型的使用。默认值为 on。 + + + + + + enable_indexonlyscan (boolean) + + enable_indexonlyscan 配置参数 + + + + + 启用或禁用查询规划器对仅索引扫描计划类型的使用(参见 )。默认值为 on。 + + + + + + + + enable_material (boolean) + + enable_material 配置参数 + + + + + + 允许或者禁止查询规划器使用物化。它不可能完全禁用物化,但是关闭这个变量将阻止规划器插入物化节点,除非为了保证正确性。默认值是on。 + + + + + + + + enable_mergejoin (boolean) + + enable_mergejoin 配置参数 + + + + + 启用或禁用查询规划器对归并连接计划类型的使用。默认值为 on。 + + + + + + + + enable_nestloop (boolean) + + enable_nestloop 配置参数 + + + + + + 允许或禁止查询规划器使用嵌套循环连接计划。它不可能完全禁止嵌套循环连接,但是关闭这个变量将使得规划器尽可能优先使用其他方法。默认值是on。 + + + + + + + enable_seqscan (boolean) + + 顺序扫描 + + + enable_seqscan配置参数 + + + + + + 允许或禁止查询规划器使用顺序扫描计划类型。它不可能完全禁止顺序扫描,但是关闭这个变量将使得规划器尽可能优先使用其他方法。默认值是on。 + + + + + + + enable_sort (boolean) + + enable_sort配置参数 + + + + + + 允许或禁止查询规划器使用显式排序步骤。它不可能完全禁止显式排序,但是关闭这个变量将使得规划器尽可能优先使用其他方法。默认值是on。 + + + + + + + enable_tidscan (boolean) + + enable_tidscan配置参数 + + + + + + 允许或禁止查询规划器使用TID扫描计划类型。默认值是on。 + + + + + + + + 规划器代价常量 + + + 这一节中描述的代价变量可以按照任意尺度衡量。我们只关心它们的相对值,将它们以相同的因子缩放不会影响规划器的选择。默认情况下,这些代价变量是基于顺序页面获取的代价的,即seq_page_cost被设置为1.0并且其他代价变量都参考它来设置。不过你可以使用你喜欢的不同尺度,例如在一个特定机器上以毫秒为单位的实际执行时间。 + + + + + + 没有明确的方法可以确定代价变量的理想值。最好将它们视为某个数据库安装实例所接收的全部查询组合的平均值。因此,仅凭少数几次试验就修改它们是非常冒险的。 + + + + + + + + seq_page_cost (floating point) + + seq_page_cost配置参数 + + + + + + 设置规划器对一系列顺序磁盘页面读取中单次读取的代价估计。默认值是 1.0。对于某个表空间内的表和索引,可以通过设置该表空间的同名参数来覆盖此值(见)。 + + + + + + random_page_cost (floating point) + + random_page_cost配置参数 + + + + + 设置规划器对一次非顺序磁盘页面读取的代价估计。默认值是 4.0。对于某个表空间内的表和索引,可以通过设置该表空间的同名参数来覆盖此值(见)。 + + + + 减少这个值(相对于seq_page_cost)将导致系统更倾向于索引扫描;提高它将让索引扫描看起来相对更昂贵。你可以一起提高或降低两个值来改变磁盘 I/O 代价相对于 CPU 代价的重要性,后者由下列参数描述。 + + + + 对机械磁盘存储的随机访问通常远不止比顺序访问贵四倍。不过,仍使用较低的默认值(4.0), + 因为假定对磁盘的大多数随机访问(例如索引读取)都将在缓存中命中。 + 可以将默认值理解为:假设随机访问比顺序访问慢 40 倍,同时预期 90% 的随机读取能够命中缓存。 + + + + 如果你认为 90% 的缓存命中率不符合你的工作负载,可以增大 random_page_cost,以更好地反映随机存储读取的真实代价。 + 对应地,如果你的数据很可能完全缓存在内存中,例如数据库小于服务器总内存,则降低 random_page_cost 可能更合适。 + 对于随机读取代价相对于顺序读取较低的存储,例如固态硬盘,也可以用较低的 random_page_cost 值来更好地建模,例如1.1。 + + + + + + 尽管系统允许将random_page_cost设置得小于seq_page_cost,但这不符合实际物理情况。不过,如果数据库完全缓存在 RAM 中,将它们设置为相等是合理的,因为此时非顺序访问页面不会产生额外代价。同样,对于大部分数据已缓存的数据库,应相对于 CPU 参数降低这两个值,因为读取一个已在 RAM 中的页面的代价远小于通常的页面读取代价。 + + + + + + + + cpu_tuple_cost (floating point) + + cpu_tuple_cost配置参数 + + + + + + 设置规划器对一次查询中处理每一行的代价估计。默认值是 0.01。 + + + + + + + cpu_index_tuple_cost (floating point) + + cpu_index_tuple_cost配置参数 + + + + + + 设置规划器对一次索引扫描中处理每一个索引项的代价估计。默认值是 0.005。 + + + + + + + cpu_operator_cost (floating point) + + cpu_operator_cost配置参数 + + + + + + 设置规划器对于一次查询中处理每个操作符或函数的代价估计。默认值是 0.0025。 + + + + + + + parallel_setup_cost (floating point) + + parallel_setup_cost 配置参数 + + + + + + 设置规划器对启动并行工作进程的代价估计。默认是 1000。 + + + + + + + parallel_tuple_cost (floating point) + + parallel_tuple_cost 配置参数 + + + + + + 设置规划器对于从一个并行工作进程传递一个元组给另一个进程的代价估计。默认是 0.1。 + + + + + + min_parallel_relation_size (integer) + + min_parallel_relation_size配置参数 + + + + + 设置为并行扫描所考虑的关系的最小尺寸。 + 默认值是8兆字节(8MB)。 + + + + + + effective_cache_size (integer) + + effective_cache_size配置参数 + + + + + 设置规划器对一个单一查询可用的有效磁盘缓存尺寸的假设。 + 这个参数会被考虑在使用一个索引的代价估计中,更高的数值会使得索引扫描更可能被使用,更低的数值会使得顺序扫描更可能被使用。 + 在设置这个参数时,你还应该考虑PostgreSQL的共享缓冲区以及将被用于PostgreSQL数据文件的内核磁盘缓存,尽管有些数据可能在两个地方都存在。 + 另外,还要考虑预计在不同表上的并发查询数目,因为它们必须共享可用的空间。 + 这个参数对PostgreSQL分配的共享内存尺寸没有影响,它也不会预留内核磁盘缓存,它只用于估计的目的。系统也不会假设在查询之间数据会保留在磁盘缓存中。 + 默认值是 4吉字节(4GB)。 + + + + + + + + + + 遗传查询优化器 + + + 遗传查询优化器(GEQO)是一种使用启发式搜索进行查询规划的算法。它可以缩短复杂查询(连接很多关系的查询)的规划时间,代价是生成的计划有时不如常规穷举搜索算法找到的计划。更多信息见。 + + + + + + + geqo (boolean) + + 遗传查询优化 + + + GEQO + 遗传查询优化 + + + geqo配置参数 + + + + + + 允许或禁止遗传查询优化。默认是启用。在生产环境中通常最好不要关闭它。geqo_threshold变量提供了对 GEQO 更细粒度的控制。 + + + + + + + geqo_threshold (integer) + + geqo_threshold配置参数 + + + + + + 只有当涉及的FROM项数量至少有这么多个的时候,才使用遗传查询优化(注意一个FULL OUTER JOIN只被计为一个FROM项)。默认值是 12。对于更简单的查询,通常会使用普通的穷举搜索规划器,但是对于有很多表的查询穷举搜索会花很长时间,通常比执行一个次优的计划带来的惩罚值还要长。因此,在查询尺寸上的一个阈值是管理 GEQO 使用的一种方便的方法。 + + + + + + + geqo_effort (integer) + + geqo_effort配置参数 + + + + + + 控制 GEQO 中规划时间和查询计划质量之间的权衡。此变量必须是 1 到 10 之间的整数。默认值是 5。较大的值会增加查询规划所用的时间,但也会提高选择高效查询计划的可能性。 + + + + geqo_effort实际并不直接做任何事情;它只是被用来计算其他影响 GEQO 行为的变量(如下所述)的默认值。如果你愿意,你可以手工设置其他参数。 + + + + + + + geqo_pool_size (integer) + + geqo_pool_size配置参数 + + + + + + 控制 GEQO 使用的池尺寸,它就是遗传种群中的个体数目。它必须至少为 2,且有用的值通常在 100 到 1000 之间。如果它被设置为零(默认设置)则会基于geqo_effort和查询中表的数量选择一个合适的值。 + + + + + + + geqo_generations (integer) + + geqo_generations配置参数 + + + + + + 控制 GEQO 使用的代数,即算法的迭代次数。它必须至少为 1,通常有用的值与池大小处于相同范围。如果设置为零(默认设置),则根据geqo_pool_size选择合适的值。 + + + + + + + geqo_selection_bias (floating point) + + geqo_selection_bias配置参数 + + + + + + 控制 GEQO 使用的选择偏好。选择偏好是种群中的选择压力。值可以是 1.5 到 2.0 之间,后者是默认值。 + + + + + + + geqo_seed (floating point) + + geqo_seed配置参数 + + + + + + 控制 GEQO 使用的随机数生成器的初始值,随机数生成器用于在连接顺序搜索空间中选择随机路径。该值可以从 0 (默认值)到 1。变化该值会改变被探索的连接路径集合,并且可能使找到的最优路径变得更好或更差。 + + + + + + + + 其他规划器选项 + + + + + + default_statistics_target (integer) + + default_statistics_target配置参数 + + + + + + 为没有通过ALTER TABLE SET STATISTICS设置列相关目标的表列设置默认统计目标。更大的值增加了需要做ANALYZE的时间,但是可能会改善规划器的估计质量。默认值是 100。有关PostgreSQL查询规划器使用的统计信息的更多内容, 请参考。 + + + + + + constraint_exclusion (enum) + + 约束排除 + + + constraint_exclusion配置参数 + + + + + 控制查询规划器对表约束的使用,以优化查询。 + constraint_exclusion的允许值是on(对所有表检查约束)、off(从不检查约束)和partition(只对继承的子表和UNION ALL子查询检查约束)。 + partition是默认设置。它通常与继承和分区表一起使用来提高性能。 + + + + 当此参数允许对某个表使用约束排除时,规划器会比较查询条件与该表的CHECK约束,并且忽略那些条件违反约束的表扫描。例如: + + +CREATE TABLE parent(key integer, ...); +CREATE TABLE child1000(check (key between 1000 and 1999)) INHERITS(parent); +CREATE TABLE child2000(check (key between 2000 and 2999)) INHERITS(parent); +... +SELECT * FROM parent WHERE key = 2400; + + + 在启用约束排除时,这个SELECT将完全不会扫描child1000,从而提高性能。 + + + + 目前,约束排除仅在通常用于实现表分区的情况下默认启用。 + 为所有表启用它会增加额外的规划开销,这在简单查询上相当明显,而且通常不会为简单查询带来好处。 + 如果没有分区表,你可能希望完全关闭它。 + + + + 更多关于使用约束排除实现分区的信息请参阅。 + + + + + + + cursor_tuple_fraction (floating point) + + cursor_tuple_fraction配置参数 + + + + + + 设置规划器对将被检索的一个游标的行的比例的估计。默认值是 0.1。更小的值使得规划器偏向为游标使用快速开始计划,它将很快地检索前几行但是可能需要很长时间来获取所有行。更大的值强调总的估计时间。最大设置为 1.0,游标将和普通查询完全一样地被规划,只考虑总估计时间并且不考虑前几行会被多快地返回。 + + + + + + + from_collapse_limit (integer) + + from_collapse_limit配置参数 + + + + + + 如果生成的FROM列表不超过这么多项,规划器将把子查询融合到上层查询。较小的值可以减少规划时间,但是可能 会生成较差的查询计划。默认值是 8。详见。 + + + + 将这个值设置为或更大,可能触发使用 GEQO 规划器,从而产生非最优计划。见。 + + + + + + + join_collapse_limit (integer) + + join_collapse_limit配置参数 + + + + + + 如果得出的列表中不超过这么多项,那么规划器将把显式JOIN(除了FULL JOIN)结构重写到 FROM项列表中。较小的值可减少规划时间,但是可能会生成差些的查询计划。 + + + + 默认情况下,这个变量被设置成和from_collapse_limit相同, 这样适合大多数使用。把它设置为 1 可避免任何显式JOIN的重排序。因此查询中指定的显式连接顺序就是关系被连接的实际顺序。因为查询规划器并不是总能 选取最优的连接顺序,高级用户可以选择暂时把这个变量设置为 1,然后显式地指定他们想要的连接顺序。更多信息请见。 + + + + 将这个值设置为或更大,可能触发使用 GEQO 规划器,从而产生非最优计划。见。 + + + + + + + + force_parallel_mode (enum) + + force_parallel_mode 配置参数 + + + + + 允许为测试目的使用并行查询,即使预期不会带来性能收益。 + force_parallel_mode允许的值包括 + off(仅在预期能提高性能时使用并行模式)、 + on(对所有被认为可以安全并行的查询强制使用并行查询),以及 + regress(类似于on,但还有下文说明的额外行为变化)。 + + + + 更具体地说,将此值设置为on会在任何看起来可以安全并行的查询计划顶部添加一个Gather节点, + 让查询在并行工作进程中运行。即使没有可用的并行工作进程或无法使用并行工作进程, + 在并行查询上下文中不允许的操作(例如启动子事务)也会被禁止,除非规划器认为这会使查询失败。 + 如果设置此选项后出现失败或意外结果,查询使用的某些函数可能需要被标记为PARALLEL UNSAFE + (也可能是PARALLEL RESTRICTED)。 + + + + 将此值设置为regress具有设置为on的所有效果,另外还有一些旨在方便自动回归测试的效果。 + 通常,来自并行工作进程的消息会包含一行说明这一点的上下文信息,但regress设置会抑制此行,使输出与非并行执行时相同。 + 此外,此设置添加到计划中的Gather节点会在EXPLAIN输出中隐藏, + 使输出与此设置为off时的输出一致。 + + + + + + + + + 错误报告和日志 + + + 服务器日志 + + + + 日志记录到哪里 + + + 日志写到哪里 + + + + + + + log_destination (string) + + log_destination配置参数 + + + + + PostgreSQL支持多种记录服务器消息的方法,包括stderrcsvlogsyslog。在 Windows 上,还支持eventlog。将此参数设为所需日志目的地的逗号分隔列表。默认只将日志记录到stderr。此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + 如果csvlog被包括在log_destination中,日志项会以逗号分隔值CSV)格式被输出,这样可以很方便地把日志载入到程序中。详见。要产生 CSV 格式的日志输出,必须启用。 + + + + + + 在大多数 Unix 系统上,你将需要修改系统的syslog守护进程的配置来使用log_destinationsyslog选项。PostgreSQL可以在syslog设施LOCAL0LOCAL7中记录(见),但是大部分平台上的默认syslog配置会丢弃所有这种消息。你将需要增加这样的内容: + +local0.* /var/log/postgresql + + 到syslog守护进程的配置文件来让它工作。 + + + + 在 Windows 上,当你使用log_destinationeventlog选项时,你应该在操作系统中注册一个事件源及其库,这样 Windows 事件查看器能够清楚地显示事件日志消息。详见。 + + + + + + + + logging_collector (boolean) + + logging_collector配置参数 + + + + + + 这个参数启用日志收集器,它是一个捕捉被发送到stderr的日志消息的后台进程,并且它会将这些消息重定向到日志文件中。这种方法比记录到syslog通常更有用,因为某些类型的消息可能不会在syslog输出中出现(一个常见的示例是动态链接器错误消息;另一个示例是由archive_command等脚本产生的错误消息)。这个参数只能在服务器启动时设置。 + + + + + + 也可以不使用日志收集器而把日志记录到stderr,日志消息将只会去到服务器的stderr被定向到的位置。不过,那种方法只适合于低日志量,因为它没有提供便捷的方法来轮转日志文件。还有,在某些平台上,不使用日志收集器可能会导致日志输出丢失或混杂,因为多个进程并发写入同一个日志文件时会覆盖彼此的输出。 + + + + + + + 日志收集器被设计成从来不会丢失消息。这意味着在极高的负载下,如果服务器进程试图在收集器已经落后时发送更多的日志消息,那么它可能会被阻塞。相反,syslog倾向于在无法写入消息时丢掉消息,这意味着在这样的情况下它可能会无法记录某些消息,但是它不会阻塞系统的其他部分。 + + + + + + + + + log_directory (string) + + log_directory配置参数 + + + + + + 当logging_collector被启用时,这个参数决定日志文件将被在哪个目录下创建。它可以被指定为一个绝对路径,也可以被指定为一个相对于集簇数据目录的相对路径。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + 默认是pg_log。 + + + + + + log_filename (string) + + log_filename配置参数 + + + + + 当logging_collector被启用时,这个参数设置被创建的日志文件的文件名。 + 该值被视为一种strftime模式,因此%转义可以被用来指定根据时间变化的文件名(注意如果有任何依赖时区的%转义,计算将在由指定的时区中完成)。 + 被支持的%转义和开放组织的strftime说明中列举的类似。 + 注意系统的strftime不会被直接使用,因此平台相关(非标准)的扩展无法工作。 + 默认是postgresql-%Y-%m-%d_%H%M%S.log。 + + + 如果你不使用转义来指定一个文件名,你应该计划使用一个日志轮转工具来避免最终填满整个磁盘。在 8.4 发行之前,如果不存在%转义,PostgreSQL将追加新日志文件创建时间的纪元,但是现在已经不再这样做了。 + + + 如果在log_destination中启用了 CSV 格式输出,.csv将会被追加到时间戳日志文件名中来创建 CSV 格式输出(如果log_filename.log结尾,该后缀会被替换)。 + + + 这个参数只能在postgresql.conf文件中或通过服务器命令行进行设置。 + + + + + + + log_file_mode (integer) + + log_file_mode配置参数 + + + + + + 在 Unix 系统上,当logging_collector被启用时,这个参数设置日志文件的权限(在微软 Windows 上这个参数将被忽略)。这个参数值应当是一个数字形式的模式,它可以被chmodumask系统调用接受(要使用通常的八进制格式,该数字必须以一个0(零)开始)。 + + + + 默认的权限是0600,表示只有服务器拥有者才能读取或写入日志文件。其他常用的设置是0640,它允许拥有者的组成员读取文件。不过要注意你需要修改为将文件存储在集簇数据目录之外的某个位置,才能利用这个设置。在任何情况下,让日志文件变成任何人都可读是不明智的,因为日志文件中可能包含敏感数据。 + + + + 这个参数只能在postgresql.conf文件中或通过服务器命令行进行设置。 + + + + + + log_rotation_age (integer) + + log_rotation_age配置参数 + + + + + 当logging_collector被启用时,这个参数决定单个日志文件的最长使用时间。经过指定的分钟数之后,将创建一个新的日志文件。 + 将这个参数设置为零将禁用基于时间的新日志文件创建。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + log_rotation_size (integer) + + log_rotation_size配置参数 + + + + + 当logging_collector被启用时,这个参数决定一个个体日志文件的最大尺寸。 + 当指定千字节数的数据被写入一个日志文件后,将创建一个新的日志文件。 + 设置为零时将禁用基于大小创建新的日志文件。 + 这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + log_truncate_on_rotation (boolean) + + log_truncate_on_rotation配置参数 + + + + + + 当logging_collector被启用时,这个参数将导致PostgreSQL截断(覆盖而不是追加)任何已有的同名日志文件。不过,截断只在一个新文件由于基于时间的轮转被打开时发生,在服务器启动或基于尺寸的轮转时不会发生。如果被关闭,在所有情况下以前存在的文件将被追加。例如,使用这个设置和一个类似postgresql-%H.loglog_filename将导致产生 24 个每小时的日志文件,并且循环地覆盖它们。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + 示例:保留7天的日志,每天一个日志文件,命名为server_log.Monserver_log.Tue,等等,并自动用本周的日志覆盖上周的日志,将log_filename设置为server_log.%a,将log_truncate_on_rotation设置为on,将log_rotation_age设置为1440。 + + + + 示例:要保留 24 小时的日志,每个小时一个日志文件,如果日志文件尺寸超过 1GB,也会提前轮转。可以这样做:将log_filename设置为server_log.%H%M、 + 将log_truncate_on_rotation设置为on、 + 将log_rotation_age设置为60并且 + 将log_rotation_size设置为1000000。 + 在log_filename中包括%M允许发生任何尺寸驱动的轮转来选择一个不同于每个小时的初始文件名的新文件名。 + + + + + + + syslog_facility (enum) + + syslog_facility配置参数 + + + + + + 当启用了向syslog记录时,这个参数决定要使用的syslog设施。你可以在LOCAL0LOCAL1LOCAL2LOCAL3LOCAL4、 + LOCAL5LOCAL6LOCAL7中选择,默认值是LOCAL0。还请参阅系统的syslog守护进程的文档。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + syslog_ident (string) + + syslog_ident配置参数 + + + + + + 当启用了向syslog记录时,这个参数决定用来标识syslog中的PostgreSQL消息的程序名。默认值是postgres。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + syslog_sequence_numbers (boolean) + + syslog_sequence_numbers 配置参数 + + + + + + + 当日志被记录到syslog并且这个设置为 on (默认)时,每一个消息会被加上一个增长的序号作为前缀(例如[2])。这种行为避开了很多 syslog 实现默认采用的--- 上一个消息重复 N 次 ---形式。在现代 syslog 实现中,抑制重复消息是可以配置的(例如rsyslog中的$RepeatedMsgReduction),因此这个参数可能不是必需的。此外,如果你真的想抑制重复消息,你可以把这个参数设置为 off。 + + + + 这个参数只能在postgresql.conf文件或者服务器命令行上设置。 + + + + + + + syslog_split_messages (boolean) + + syslog_split_messages 配置参数 + + + + + + 当启用把日志记录到syslog时,这个参数决定消息如何送达 syslog。当设置为 on(默认)时,消息会被分成行,并且长的行也会被划分以便能够放到 1024 字节中,这是传统 syslog 实现一种典型的尺寸限制。当设置为 off 时,PostgreSQL 服务器日志消息会被原样送达 syslog 服务,而处理可能的大体量消息的任务由 syslog 服务负责。 + + + + 如果 syslog 最终被记录到一个文本文件中,那么两种设置的效果是一样的,但最好设置为 on,因为大部分 syslog 实现要么不能处理大型消息,要么需要做特殊的配置以处理大型消息。但是如果 syslog 最终写入到某种其他媒介,让消息保持逻辑上的完整性可能是必要的,也可能更有用。 + + + + 这个参数只能在postgresql.conf文件中或通过服务器命令行进行设置。 + + + + + + event_source (string) + + event_source配置参数 + + + + + 当启用了向事件日志记录时,这个参数决定用来标识日志中PostgreSQL消息的程序名。默认值是PostgreSQL。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + + 什么时候记录日志 + + + + + + log_min_messages (enum) + + log_min_messages配置参数 + + + + + + 控制将哪些消息级别写入服务器日志。 + 有效值为DEBUG5DEBUG4, + DEBUG3DEBUG2DEBUG1, + INFONOTICEWARNING, + ERRORLOGFATAL和 + PANIC。每个级别包括其后的所有级别。 + 级别越高,发送到日志的消息越少。默认值为WARNING。 + 请注意,在中,LOG的排名不同。 + 只有超级用户能更改这个设置。 + + + + + + + log_min_error_statement (enum) + + log_min_error_statement配置参数 + + + + + + 控制在服务器日志中记录哪些导致错误条件的SQL语句。对于达到指定严重级别或更高级别的消息,其日志条目中会包含当前 SQL 语句。 + 有效值为DEBUG5、 + DEBUG4DEBUG3、 + DEBUG2DEBUG1、 + INFONOTICE、 + WARNINGERROR、 + LOG、 + FATALPANIC。 + 默认值为ERROR,这意味着导致错误、日志消息、致命错误或紧急情况的语句将被记录。 + 要有效地关闭记录失败的语句, + 将此参数设置为PANIC。 + 只有超级用户能更改这个设置。 + + + + + + log_min_duration_statement (integer) + + log_min_duration_statement配置参数 + + + + + 如果一条已完成语句的运行时间至少达到指定毫秒数,就记录其持续时间。 + 将此值设置为零会打印所有语句的持续时间。 + 负一(默认值)禁用语句持续时间记录。例如,如果设置为250ms, + 则会记录所有运行 250ms 或更长时间的 SQL 语句。启用此参数有助于发现应用中未优化的查询。 + 只有超级用户能更改这个设置。 + + + + 对于使用扩展查询协议的客户端,Parse、Bind 和 Execute 步骤的持续时间将被独立记录。 + + + + + + 当把这个选项和一起使用时,已经被log_statement记录的语句文本不会在持续时间日志消息中重复。如果你没有使用syslog,我们推荐你使用记录 PID 或会话 ID,这样你可以使用进程 ID 或会话 ID 把语句消息链接到后来的持续时间消息。 + + + + + + + + + 解释了PostgreSQL所使用的消息严重级别。如果日志输出被发送到syslog或 Windows 的eventlog,严重级别会按照表中所示进行转换。 + + + + 消息严重级别 + + + + + 严重性 + 用法 + syslog + eventlog + + + + + + DEBUG1..DEBUG5 + 为开发者提供逐级更加详细的信息。 + DEBUG + INFORMATION + + + + INFO + 提供用户隐式要求的信息,例如来自VACUUM VERBOSE的输出。 + INFO + INFORMATION + + + + NOTICE + 提供可能对用户有用的信息,例如长标识符截断提示。 + NOTICE + INFORMATION + + + + WARNING + 提供可能出现的问题的警告,例如在一个事务块外COMMIT + NOTICE + WARNING + + + + ERROR + 报告一个导致当前命令中断的错误。 + WARNING + ERROR + + + + LOG + 报告管理员可能感兴趣的信息,例如检查点活动。 + INFO + INFORMATION + + + + FATAL + 报告一个导致当前会话中断的错误。 + ERR + ERROR + + + + PANIC + 报告一个导致所有数据库会话中断的错误。 + CRIT + ERROR + + + +
+ +
+ + 记录哪些内容 + + + + + + + application_name (string) + + application_name配置参数 + + + + + + application_name可以是任意小于NAMEDATALEN个字符(标准编译中是 64 个字符)的字符串。应用通常在连接服务器时设置此值。该名称将被显示在pg_stat_activity视图中并被包括在 CSV 日志项中。也可以通过将其包括在普通日志项中。只有可打印 ASCII 字符能被使用在application_name之中。其他字符将被替换为问号(?)。 + + + + + + debug_print_parse (boolean) + + debug_print_parse配置参数 + + + debug_print_rewritten (boolean) + + debug_print_rewritten配置参数 + + + debug_print_plan (boolean) + + debug_print_plan配置参数 + + + + + + 这些参数将会让多种调试输出被发出。当被设置时,它们为每一个被执行的查询打印结果分析树、查询重写器输出或执行计划。这些消息在LOG消息级别上被发出,因此默认情况下它们将出现在服务器日志中但不会被发送到客户端。你可以通过调整和/或来改变这种情况。这些参数默认是关闭的。 + + + + + + debug_pretty_print (boolean) + + debug_pretty_print配置参数 + + + + + + 当被设置时,debug_pretty_print会缩进由debug_print_parse、 + debug_print_rewritten或 + debug_print_plan产生的输出。这将导致比关闭参数时使用的紧凑模式可读性更强但是更长的输出。它默认是打开的。 + + + + + + + log_checkpoints (boolean) + + log_checkpoints配置参数 + + + + + + 导致检查点和重启点在服务器日志中记录。日志消息中包括一些统计信息, + 包括写入的缓冲区数量和写入它们所花费的时间。此参数只能在 + postgresql.conf文件或服务器命令行中设置。默认值为关闭。 + + + + + + log_connections (boolean) + + log_connections 配置参数 + + + + + 记录每次尝试连接服务器的操作,以及客户端认证的成功完成。 + 只有超级用户可以在会话开始时更改此参数,并且在会话中完全无法更改它。 + 默认值为off。 + + + + + + 某些客户端程序(例如psql)在判断是否需要密码时会尝试连接两次,因此重复的收到连接消息并不一定表示一个错误。 + + + + + + + log_disconnections (boolean) + + log_disconnections配置参数 + + + + + 导致会话终止被记录。日志输出提供类似于log_connections的信息,以及会话的持续时间。 + 只有超级用户可以在会话开始时更改此参数,而且在会话中根本无法更改。 + 默认值为off。 + + + + + + + log_duration (boolean) + + log_duration配置参数 + + + + + 记录每个已完成语句的持续时间。 + 默认值为off。 + 只有超级用户能更改这个设置。 + + + + 对于使用扩展查询协议的客户端,Parse、Bind 和 Execute 步骤的持续时间将被独立记录。 + + + + + 启用这个选项和设置为零之间的区别是,超过log_min_duration_statement强制查询的文本被记录,但这个选项不会。因此,如果log_durationon并且log_min_duration_statement为正值,所有持续时间都将被记录,但是只有超过阈值的语句才会被记录查询文本。这种行为有助于在高负载安装中收集统计信息。 + + + + + + + + log_error_verbosity (enum) + + log_error_verbosity配置参数 + + + + + + 控制在服务器日志中记录的每条消息的详细程度。有效值为TERSE, + DEFAULTVERBOSE,它们依次在显示的消息中增加更多字段。 + TERSE不包括DETAILHINT, + QUERYCONTEXT错误信息的记录。 + VERBOSE输出包括SQLSTATE错误代码 + (另请参见)以及生成错误的源代码文件名、函数名和行号。 + 只有超级用户能更改这个设置。 + + + + + + + log_hostname (boolean) + + log_hostname配置参数 + + + + + + 默认情况下,连接日志消息只显示连接主机的 IP 地址。打开这个参数将导致也记录主机名。注意根据你的主机名解析设置,这可能会导致不可忽视的性能开销。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + log_line_prefix (string) + + log_line_prefix配置参数 + + + + + 这是一个printf风格的字符串,会输出在每个日志行的开头。 + %字符用于引入转义序列,它们会被替换为下表所述的状态信息。 + 无法识别的转义会被忽略。其他字符会直接复制到日志行中。有些转义只被会话进程识别,后台进程(例如主服务器进程)会将其视为空。 + 在 % 之后、选项之前指定数值字面量,可以使状态信息左对齐或右对齐。 + 负值会在状态信息的右侧填充空格,使其达到最小宽度;正值则在左侧填充。填充有助于提高日志文件的可读性。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。默认值是一个空字符串。 + + + + + + + 转义 + 效果 + 只限会话 + + + + + %a + 应用名 + + + + %u + 用户名 + + + + %d + 数据库名 + + + + %r + 远程主机名或 IP 地址,以及远程端口 + + + + %h + 远程主机名或 IP 地址 + + + + %p + 进程 ID + + + + %t + 无毫秒的时间戳 + + + + %m + 带毫秒的时间戳 + + + + %n + 带毫秒精度的 Unix 时间戳 + + + + %i + 命令标签:会话当前命令的类型 + + + + %e + SQLSTATE 错误代码 + + + + %c + 会话 ID:见下文 + + + + %l + 对每个会话或进程的日志行号,从 1 开始 + + + + %s + 进程开始的时间戳 + + + + %v + 虚拟事务 ID (backendID/localXID) + + + + %x + 事务 ID (如果未分配则为 0) + + + + %q + 不产生输出,但是告诉非会话进程在字符串的这一点停止;会话进程忽略 + + + + %% + 字面字符 % + + + + + %c转义会打印一个近乎唯一的会话标识符,由两个以点分隔的 4 字节十六进制数(不含前导零)组成。这两个数分别是进程启动时间和进程 ID,因此%c也可以用作节省空间的方式来打印这些信息。例如,要从pg_stat_activity生成会话标识符,可以使用以下查询: + +SELECT to_hex(trunc(EXTRACT(EPOCH FROM backend_start))::integer) || '.' || + to_hex(pid) +FROM pg_stat_activity; + + + + + + + + 如果你为log_line_prefix设置了非空值,你通常应该让它的最后一个字符为空格,这样用以提供和日志行的剩余部分的视觉区别。也可以使用标点符号。 + + + + + + + Syslog产生自己的时间戳和进程 ID 信息,因此如果你记录到syslog你可能不希望包括那些转义。 + + + + + + + + + log_lock_waits (boolean) + + log_lock_waits配置参数 + + + + + + 控制当会话等待时间超过以获取锁时是否生成日志消息。 + 这对于确定锁等待是否导致性能不佳很有用。默认值为off。 + 只有超级用户能更改这个设置。 + + + + + + + log_statement (enum) + + log_statement配置参数 + + + + + + 控制哪些 SQL 语句被记录。有效值是 + none (off)、ddlmod和 + all(所有语句)。ddl记录所有数据定义语句,例如CREATEALTER和 + DROP语句。mod记录所有ddl语句,外加数据修改语句例如INSERT, + UPDATEDELETETRUNCATE, + 和COPY FROM。 + 如果PREPAREEXECUTE和 + EXPLAIN ANALYZE包含合适类型的命令,它们也会被记录。对于使用扩展查询协议的客户端,当收到一个 Execute 消息时会产生日志并且会包括 Bind 参数的值(任何内嵌的单引号会被双写)。 + + + + 默认值为none。 + 只有超级用户能更改这个设置。 + + + + + + 即使使用log_statement = all设置,包含简单语法错误的语句也不会被记录。这是因为只有在完成基本语法解析并确定了语句类型之后才会发出日志消息。在扩展查询协议的情况下,在 Execute 阶段之前(即在解析分析或规划期间)出错的语句也不会被记录。将log_min_error_statement设置为ERROR(或更低)来记录这种语句。 + + + + + + + + + log_replication_commands (boolean) + + log_replication_commands 配置参数 + + + + + + 每个复制命令都会被记录在服务器日志中。 + 有关复制命令的更多信息,请参见。 + 默认值为off。 + 只有超级用户能更改这个设置。 + + + + + + log_temp_files (integer) + + log_temp_files配置参数 + + + + + 控制临时文件名和大小的日志记录。 + 临时文件可以用于排序、hash 和临时查询结果。 + 每当删除临时文件时都会发出日志记录。 + 值为零时记录所有临时文件信息,而正值仅记录大小大于或等于指定千字节数的文件。 + 默认设置为-1,禁用此类日志记录。 + 只有超级用户能更改这个设置。 + + + + + + + log_timezone (string) + + log_timezone配置参数 + + + + + + 设置在服务器日志中写入的时间戳的时区。和不同,这个值是集簇范围的,因此所有会话将报告一致的时间戳。内置默认值是GMT,但是通常会被在postgresql.conf中覆盖。initdb将安装一个对应于其系统环境的设置。详见。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + + 使用 CSV 格式的日志输出 + + csvlog加入log_destination列表中,可以方便地将日志文件导入数据库表。此选项以逗号分隔值(CSV)格式输出日志行,包含以下列:带毫秒的时间戳、用户名、数据库名、进程 ID、客户端主机:端口号、会话 ID、会话内行号、命令标签、会话开始时间、虚拟事务 ID、常规事务 ID、错误严重性、SQLSTATE 代码、错误消息、错误消息详情、提示、引发错误的内部查询(如果有)、该内部查询中错误位置的字符数、错误上下文、引发错误的用户查询(如果有且由log_min_error_statement启用)、该用户查询中错误位置的字符数、错误在 PostgreSQL 源代码中的位置(如果log_error_verbosity设置为verbose)和应用名称。 + 以下是用于存储 CSV 格式日志输出的示例表定义: + + +CREATE TABLE postgres_log +( + log_time timestamp(3) with time zone, + user_name text, + database_name text, + process_id integer, + connection_from text, + session_id text, + session_line_num bigint, + command_tag text, + session_start_time timestamp with time zone, + virtual_transaction_id text, + transaction_id bigint, + error_severity text, + sql_state_code text, + message text, + detail text, + hint text, + internal_query text, + internal_query_pos integer, + context text, + query text, + query_pos integer, + location text, + application_name text, + PRIMARY KEY (session_id, session_line_num) +); + + + + + 使用COPY FROM命令将一个日志文件导入到这个表中: + + +COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; + + 也可以作为外部表访问该文件,使用提供的 模块。 + + + + 你可以做一些事情来简化导入 CSV 日志文件: + + + + + 设置log_filenamelog_rotation_age,为日志文件提供一致且可预测的命名方案。这样就能预测文件名,并知道单个日志文件何时已完成写入、可以导入。 + + + + + + 将log_rotation_size设置为 0 来禁用基于尺寸的日志轮转,因为它使得日志文件名难以预测。 + + + + + + 将log_truncate_on_rotation设置为on,这样在同一个文件中旧日志数据不会与新数据混杂。 + + + + + + 上述表定义包括一个主键声明。这有助于避免意外地两次导入相同的信息。COPY命令一次提交所有它导入的数据,因此任何错误将导致整个导入失败。如果你导入一个部分完成的日志文件并且稍后当它完全完成后再次导入,主键违背将导致导入失败。请等到日志完成且被关闭之后再导入。这个过程也可以避免意外地导入部分完成的行,这种行也将导致COPY失败。 + + + + + + + + 进程标题 + + + 这些设置控制服务器进程的进程标题如何修改。通常可以通过ps等程序查看进程标题,在 Windows 上则可以使用Process Explorer。详情参见。 + + + + + cluster_name (string) + + cluster_name 配置参数 + + + + + 设置此集簇所有服务器进程的进程标题中显示的集簇名称。这个名称可以是任何长度少于NAMEDATALEN个字符(在标准编译中是 64字符)的任何字符串。只有可打印的 ASCII 字符能被用在cluster_name值中。其他字符将被替换为问号(?)。如果这个参数被设置为空字符串''(也是默认值),将不会显示名称。这个参数只能在服务器启动时设置。 + + + + + + + update_process_title (boolean) + + update_process_title 配置参数 + + + + + + 启用后,每次服务器接收到新的 SQL 命令时都会更新进程标题。 + 在大多数平台上,默认情况下此设置为on,但在Windows上默认为off, + 因为该平台更新进程标题的开销较大。 + 只有超级用户能更改这个设置。 + + + + + +
+ + + 运行时统计数据 + + + 查询和索引统计信息收集器 + + + 这些参数控制服务器范围内的统计信息收集功能。 + 启用统计信息收集后,产生的数据可以通过pg_stat和 + pg_statio系列系统视图进行访问。 + 更多信息请参阅。 + + + + + + track_activities (boolean) + + track_activities配置参数 + + + + + 启用对每个会话当前执行命令的信息收集,包括命令开始执行的时间。 + 此参数默认为开启状态。请注意,即使启用了此参数,该信息也只有超级用户和拥有被报告会话的用户才能看到,因此不应构成安全风险。 + 只有超级用户能更改这个设置。 + + + + + + track_activity_query_size (integer) + + track_activity_query_size配置参数 + + + + + 为每个活动会话指定存储当前执行命令的文本所预留的字节数,它们被用于pg_stat_activity.query字段。 + 默认值是 1024字节。这个参数只能在服务器启动时被设置。 + + + + + + + track_counts (boolean) + + track_counts配置参数 + + + + + + 启用对数据库活动的统计信息收集。 + 此参数默认为开启,因为自动清理守护进程需要这些收集到的信息。 + 只有超级用户能更改这个设置。 + + + + + + track_io_timing (boolean) + + track_io_timing配置参数 + + + + + 启用数据库I/O调用的计时。 默认情况下,此参数处于关闭状态,因为它将重复查询操作系统的当前时间,这可能会在某些平台上造成显著的开销。 你可以使用工具来测量系统上计时的开销。 + I/O计时信息显示在中,也显示在使用BUFFERS选项的输出中, + 并由提供。只有超级用户能更改这个设置。 + + + + + + + track_functions (enum) + + track_functions配置参数 + + + + + + 启用函数调用次数和耗时的跟踪。指定pl以仅跟踪过程语言函数, + all以同时跟踪SQL和C语言函数。默认值为none, + 即禁用函数统计跟踪。只有超级用户能更改这个设置。 + + + + + + 简单到足以被内联到调用查询中的 SQL 语言函数不会被跟踪, 而不管这个设置。 + + + + + + + stats_temp_directory (string) + + stats_temp_directory配置参数 + + + + + 设置存放临时统计信息数据的目录。它既可以是相对于数据目录的路径,也可以是绝对路径。默认值是 + pg_stat_tmp。将其指向基于 RAM 的文件系统可以减少物理 I/O 需求,并可能提升性能。 + 这个参数只能在postgresql.conf文件中或服务器命令行上设置。 + + + + + + + + + 统计监控 + + + + log_statement_stats (boolean) + + log_statement_stats配置参数 + + + log_parser_stats (boolean) + + log_parser_stats配置参数 + + + log_planner_stats (boolean) + + log_planner_stats配置参数 + + + log_executor_stats (boolean) + + log_executor_stats配置参数 + + + + + 对于每个查询,将各自模块的性能统计输出到服务器日志中。这是一个简单的性能分析工具,类似于Unix getrusage()操作系统功能。 + log_statement_stats报告整个语句的统计信息,而其他选项报告每个模块的统计信息。 + log_statement_stats不能与任何单独模块选项一起启用。所有这些选项默认情况下都是禁用的。 + 只有超级用户才能更改这些设置。 + + + + + + + + + + + 自动清理 + + + 自动清理 + 配置参数 + + + 这些设置控制autovacuum特性的行为。更多信息见。注意,其中许多设置可以按表覆盖,参见。 + + + + + + + autovacuum (boolean) + + autovacuum配置参数 + + + + + + 控制服务器是否运行自动清理启动器后台进程。默认为开启, + 不过要自动清理正常工作还需要启用。 + 该参数只能在postgresql.conf文件或服务器命令行中设置, + 不过,通过更改表存储参数可以为表禁用自动清理。 + + + + 注意即使该参数被禁用,系统也会在需要防止事务ID回卷时发起自动清理进程。详情请见。 + + + + + + log_autovacuum_min_duration (integer) + + log_autovacuum_min_duration 配置参数 + + + + + 当自动清理执行的操作运行时间至少达到指定毫秒数时,就会记录该操作。将此设置为零会记录所有自动清理操作。 + 负一(默认值)会禁用记录自动清理操作。 + 例如,如果将其设置为250ms,则所有运行时间为250ms或更长的自动清理和分析都将被记录。 + 此外,当此参数设置为任何非-1值时,如果由于冲突锁而跳过自动清理操作,则会记录消息。 + 启用此参数可帮助跟踪自动清理活动。 + 此参数只能在postgresql.conf文件或服务器命令行中设置;但可以通过更改表存储参数来覆盖对单个表的设置。 + + + + + + autovacuum_max_workers (integer) + + autovacuum_max_workers配置参数 + + + + + 指定任一时刻可能运行的自动清理进程(自动清理启动器除外)的最大数量。默认值为三个。 + 这个参数只能在服务器启动时设置。 + + + + + + autovacuum_naptime (integer) + + autovacuum_naptime配置参数 + + + + + 指定自动清理在任意给定数据库上各次运行之间的最小间隔。在每一轮中后台进程检查数据库并根据需要为数据库中的表发出VACUUMANALYZE命令。 + 延迟以秒为单位。默认值为1分钟(1min)。该参数只能在postgresql.conf文件或在服务器命令行上设置。 + + + + + + autovacuum_vacuum_threshold (integer) + + autovacuum_vacuum_threshold 配置参数 + + + + + + 指定能在一个表上触发VACUUM的被更新或被删除元组的最小数量。默认值为50个元组。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + autovacuum_analyze_threshold (integer) + + autovacuum_analyze_threshold 配置参数 + + + + + + 指定能在一个表上触发ANALYZE的被插入、被更新或被删除元组的最小数量。默认值为50个元组。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + autovacuum_vacuum_scale_factor (floating point) + + autovacuum_vacuum_scale_factor 配置参数 + + + + + + 指定一个表尺寸的分数,在决定是否触发VACUUM时将它加到autovacuum_vacuum_threshold上。默认值为0.2(表尺寸的20%)。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + autovacuum_analyze_scale_factor (floating point) + + autovacuum_analyze_scale_factor 配置参数 + + + + + + 指定一个表尺寸的分数,在决定是否触发ANALYZE时将它加到autovacuum_analyze_threshold上。默认值为0.1(表尺寸的10%)。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + autovacuum_freeze_max_age (integer) + + autovacuum_freeze_max_age 配置参数 + + + + + + 指定在一个VACUUM操作被强制执行来防止表中事务ID回卷之前,一个表的pg_class.relfrozenxid域能保持的最大年龄(事务的)。注意即便自动清理被禁用,系统也将发起自动清理进程来阻止回卷。 + + + + 清理也允许从pg_clog子目录中移除旧文件,这也是为什么默认值被设置为较低的2亿事务。该参数只能在服务器启动时设置,但是对于个别表可以通过修改表存储参数来降低该设置。详见。 + + + + + + autovacuum_multixact_freeze_max_age (integer) + + autovacuum_multixact_freeze_max_age 配置参数 + + + + + + 指定在一个VACUUM操作被强制执行来防止表中多事务ID回卷之前,一个表的pg_class.relminmxid域能保持的最大年龄(多事务的)。注意即便自动清理被禁用,系统也将发起自动清理进程来阻止回卷。 + + + + 清理多事务也允许从pg_multixact/memberspg_multixact/offsets子目录中移除旧文件,这也是为什么默认值被设置为较低的4亿个多事务。该参数只能在服务器启动时设置,但是对于个别表可以通过修改表存储参数来降低该设置。详见。 + + + + + + autovacuum_vacuum_cost_delay (integer) + + autovacuum_vacuum_cost_delay 配置参数 + + + + + 指定用于自动VACUUM操作中的代价延迟值。如果指定-1,则使用值。 + 默认值为20毫秒。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + autovacuum_vacuum_cost_limit (integer) + + autovacuum_vacuum_cost_limit 配置参数 + + + + + + 指定用于自动VACUUM操作中的代价限制值。如果指定-1(默认值),则使用值。注意该值被按比例地分配到运行中的自动清理工作进程上(如果有多个),因此每一个工作进程的限制值之和不会超过这个变量中的值。该参数只能在postgresql.conf文件或在服务器命令中设置。但是对个别表可以通过修改表存储参数来覆盖该设置。 + + + + + + + + + 客户端连接默认值 + + + 语句行为 + + + + + client_min_messages (enum) + + client_min_messages配置参数 + + + + + + 控制被发送给客户端的消息级别。有效值是DEBUG5、 + DEBUG4DEBUG3DEBUG2、 + DEBUG1LOGNOTICE、 + WARNINGERROR。 + 每个级别都包括其后的所有级别。级别越靠后,被发送的消息越少。默认值是NOTICE。 + 注意LOG在这里的排序与中的不同。 + + + + INFO 级别的消息总是被发送到客户端。 + + + + + + search_path (string) + + search_path配置参数 + + 路径用于模式 + + + + 这个变量指定当一个对象(表、数据类型、函数等)被用一个无模式限定的简单名称引用时,搜索该对象时的模式顺序。当在不同模式中有同名对象时,将使用第一个在搜索路径中被找到的对象。一个不属于搜索路径中任何一个模式的对象只能通过用限定名(带点号)指定包含它的模式来引用。 + + + + search_path的值必须是一个逗号分隔的模式名列表。任何不是一个已有模式的名称,或者是一个用户不具有USAGE权限的模式,将被静默忽略。 + + + + 如果列表项之一是特殊名$user,则具有CURRENT_USER返回的名字的模式将取代它(如果有这样一个模式并且该用户有该模式的USAGE权限;如果没有,$user会被忽略)。 + + + + 系统目录模式pg_catalog总是被搜索,不管它是否在搜索路径中被提及。如果它在路径中被提及,那么它将被按照路径指定的顺序搜索。如果pg_catalog不在路径中,则它将在任何路径项之前被搜索。 + + + + + 同样,如果存在,当前会话的临时表模式pg_temp_nnn总是被搜索。 + 可以通过使用别名pg_temppg_temp在路径中显式列出它。 + 如果它没有列在路径中,则首先搜索它(甚至在pg_catalog之前)。 + 然而,临时模式仅用于搜索关系(表、视图、序列等)和数据类型名称。 + 它永远不会用于搜索函数或操作符名称。 + + + + 当对象创建时没有指定一个特定目标模式,它们将被放置在search_path中第一个合法模式中。如果搜索路径为空将报告一个错误。 + + + + 这个参数的默认值是"$user", public。这种设置支持共享使用数据库(用户没有私有模式,所有人共享使用public)、每个用户拥有私有模式,以及二者的组合。 + 还可以通过全局或针对每个用户修改默认搜索路径设置来获得其他效果。 + + + + 更多有关模式处理的信息,请参考。特别地,只有当数据库只有一个用户或者有少数的相互信任的用户时,默认配置是合适的。 + + + + 搜索路径的当前有效值可以通过SQL函数current_schemas检查(见)。它和检查search_path的值不太一样,因为current_schemas显示出现在search_path中的项如何被解析。 + + + + + + row_security (boolean) + + row_security 配置参数 + + + + + + 这个变量控制是否以抛出一个错误来代替应用一条行安全性策略。在设置为on时,策略正常应用。在设置为off时,原本会应用至少一条策略的查询就会失败。默认为on。受限的行可见性可能导致不正确的结果时,可将其改成off。例如,pg_dump默认会做这种更改。这个变量对能绕过每一条行安全性策略的角色(即超级用户和具有BYPASSRLS属性的角色)没有效果。 + + + + 更多关于行安全性策略的信息请见。 + + + + + + default_tablespace (string) + + default_tablespace配置参数 + + 表空间默认 + + + + 这个变量指定当一个CREATE命令没有显式指定一个表空间时,创建对象(表和索引)的默认表空间。 + + + + 该值要么是一个表空间的名字,要么是一个指定使用当前数据库默认表空间的空字符串。如果该值和任何现有表空间的名字都不匹配,PostgreSQL将自动使用当前数据库的默认表空间。如果指定了一个非默认的表空间,用户必须对它有CREATE权限,否则创建尝试将失败。 + + + + 这个变量不被用于临时表,对临时表会使用。 + + + + 创建数据库时也不会使用这个变量。默认情况下,一个新数据库会从它的模板数据库继承其表空间设置。 + + + + 有关表空间的更多的信息,请见。 + + + + + + + temp_tablespaces (string) + + temp_tablespaces配置参数 + + 表空间临时 + + + + + 这个变量指定当一个CREATE命令没有显式指定一个表空间时,创建临时对象(临时表和临时表上的索引)的默认表空间。用于排序大型数据集的临时文件也被创建在这些表空间中。 + + + + 该值是一个表空间名字的列表。当列表中有多于一个名称时,每次一个临时对象被创建时PostgreSQL随机选择列表中的一个成员。例外是在一个事务中,连续创建的临时对象被依次放置在列表中的连续表空间中。如果列表中被选中的元素是一个空字符串,PostgreSQL将自动使用当前数据库的默认表空间。 + + + + 当temp_tablespaces被交互式地设置时,指定一个不存在的表空间是一种错误,指定一个用户不具有CREATE权限的表空间也同样是错误。不过,当使用一个之前设置的值时,不存在的表空间会被忽略,就像用户缺少CREATE权限的表空间一样。特别是,使用一个在postgresql.conf中设置的值时,这条规则起效。 + + + + 默认值是一个空字符串,它使得所有临时对象被创建在当前数据库的默认表空间中。 + + + + 参阅。 + + + + + + check_function_bodies (boolean) + + check_function_bodies配置参数 + + + + + 这个参数通常为打开。 + 当设置为off时,它禁用期间对函数体字符串的验证。 + 禁用验证避免了验证处理的副作用,也避免前向引用等问题导致的误报。 + 在代表其他用户载入函数之前设置这个参数为offpg_dump会自动这样做。 + + + + + + + default_transaction_isolation (enum) + + 事务隔离级别 + 设置默认值 + + + default_transaction_isolation配置参数 + + + + + + 每个 SQL 事务都有一个隔离级别,可以是读未提交读已提交可重复读或者可串行化。这个参数控制每个新事务的默认隔离级别。默认是读已提交。 + + + + 更多信息请参阅。 + + + + + + + default_transaction_read_only (boolean) + + 只读事务 + 设置默认值 + + + default_transaction_read_only配置参数 + + + + + + 一个只读的 SQL 事务不能修改非临时表。这个参数控制每个新事务的默认只读状态。默认是off(读/写)。 + + + + 更多信息请参考。 + + + + + + default_transaction_deferrable (boolean) + + 可延迟事务 + 设置默认值 + + + default_transaction_deferrable配置参数 + + + + + 当运行在serializable隔离级别时,一个可延迟只读 SQL 事务可能在获准继续之前被延迟一段时间。但是,一旦它开始执行就不会产生任何用来保证可串行化性的开销;因此串行化代码将没有任何理由因为并发更新而强制它中止,使得这个选项适合于长时间运行的只读事务。 + + + + 这个参数控制每个新事务的默认可延迟状态。目前它对读写事务或者那些运行在低于serializable隔离级别上的事务无效。默认值是off。 + + + + 更多信息请参考。 + + + + + + + transaction_isolation (enum) + + 事务隔离级别 + + + transaction_isolation配置参数 + + + + + + 此参数反映当前事务的隔离级别。在每个事务开始时,它被设置为 + 的当前值。 + 任何后续更改它的尝试都相当于命令。 + + + + + + + transaction_read_only (boolean) + + 只读事务 + + + transaction_read_only 配置参数 + + + + + + 此参数反映当前事务的只读状态。 + 在每个事务的开始,它被设置为的当前值。 + 任何后续更改它的尝试都等同于命令。 + + + + + + + transaction_deferrable (boolean) + + 可延迟事务 + + + transaction_deferrable 配置参数 + + + + + + 此参数反映当前事务的可延迟性状态。 + 在每个事务的开始,它被设置为的当前值。 + 任何后续更改它的尝试都等同于命令。 + + + + + + + session_replication_role (enum) + + session_replication_role配置参数 + + + + + 控制当前会话中与复制相关的触发器和规则的触发。设置此变量需要超级用户权限,并会丢弃任何先前缓存的查询计划。 + 可用值为origin(默认值)、replicalocal。 + 更多信息参见。 + + + + + + statement_timeout (integer) + + statement_timeout配置参数 + + + + + 中止任何使用了超过指定毫秒数的语句,从客户端命令到达服务器时开始计时。 + 如果log_min_error_statement被设置为ERROR或更低,语句如果超时也会被记录。 + 一个零值(默认)将禁用超时。 + + + + 我们不推荐在postgresql.conf中设置statement_timeout,因为它会影响所有会话。 + + + + + + lock_timeout (integer) + + lock_timeout配置参数 + + + + + 如果任何语句在试图获取表、索引、行或其他数据库对象上的锁时等待超过指定的毫秒数,该语句将被中止。 + 该时间限制独立地应用于每一次锁获取尝试。该限制会应用到显式锁定请求(如LOCK TABLE或不带NOWAITSELECT FOR UPDATE)和隐式获得的锁。 + 一个零值(默认)将禁用超时。 + + + + 与statement_timeout不同,这个超时只在等待锁时发生。注意如果statement_timeout为非零,设置lock_timeout为相同或更大的值没有意义,因为语句超时将总是第一个被触发。 + 如果log_min_error_statement 被设置为ERROR 或更低,超时的语句将被记录。 + + + + 我们不推荐在postgresql.conf中设置lock_timeout,因为它会影响所有会话。 + + + + + + idle_in_transaction_session_timeout (integer) + + idle_in_transaction_session_timeout 配置参数 + + + + + 终止任何具有打开事务、且闲置时间超过指定的毫秒数的会话。这使得该会话持有的锁能够被释放,连接槽能够被重用, + 也使得仅对此事务可见的元组能够被清理。更多详情参见。 + + + 默认值 0 禁用此特性。 + + + + + + vacuum_freeze_table_age (integer) + + vacuum_freeze_table_age配置参数 + + + + + + 如果表的pg_class.relfrozenxid字段达到此设置指定的年龄,VACUUM就会执行一次激进扫描。激进扫描与常规VACUUM不同,它会访问每一个可能包含未冻结 XID 或 MXID 的页面,而不仅仅是那些可能包含死元组的页面。默认值为 1.5 亿个事务。尽管用户可以将该值设置在 0 到 20 亿之间,VACUUM仍会悄悄将其有效值限制为不超过的 95%,以便在针对该表启动防回卷自动清理之前,周期性手工VACUUM仍有机会运行。详见。 + + + + + + vacuum_freeze_min_age (integer) + + vacuum_freeze_min_age配置参数 + + + + + 指定VACUUM在扫描表时用来决定是否冻结行版本的截止年龄(以事务计)。默认值是 5000 万个事务。尽管用户可以将该值设置为 0 到 10 亿之间的任意值,VACUUM会悄悄将有效值限制为不超过的一半,这样强制 autovacuum 之间就不会间隔过短。更多信息请参见。 + + + + + + vacuum_multixact_freeze_table_age (integer) + + vacuum_multixact_freeze_table_age配置参数 + + + + + + 如果表的pg_class.relminmxid字段达到此设置指定的年龄,VACUUM就会执行一次激进扫描。激进扫描与常规VACUUM不同,它会访问每一个可能包含未冻结 XID 或 MXID 的页面,而不仅仅是那些可能包含死元组的页面。默认值为 1.5 亿个多事务。尽管用户可以将该值设置在 0 到 20 亿之间,VACUUM仍会悄悄将其有效值限制为不超过的 95%,以便在针对该表启动防回卷清理之前,周期性手工VACUUM仍有机会运行。详见。 + + + + + + vacuum_multixact_freeze_min_age (integer) + + vacuum_multixact_freeze_min_age配置参数 + + + + + 指定VACUUM在扫描表时用来决定是否将多事务 ID 替换为较新的事务 ID 或多事务 ID 的截止年龄(以多事务计)。默认值是 500 万个多事务。尽管用户可以将该值设置为 0 到 10 亿之间的任意值,VACUUM会悄悄将有效值限制为不超过的一半,这样强制 autovacuum 之间就不会间隔过短。更多信息请参见。 + + + + + + + bytea_output (enum) + + bytea_output配置参数 + + + + + + 设置bytea类型值的输出格式。有效值是hex(默认)和 escape(传统的 PostgreSQL 格式)。详见。不管这个设置的值如何,bytea类型总是接受这两种格式的输入。 + + + + + + + xmlbinary (enum) + + xmlbinary配置参数 + + + + + + 设置二进制值如何被编码为 XML。例如,这适用于通过xmlelement函数或xmlforest函数将bytea值转换到 XML 值。可能的值有base64hex,它们都是用 XML 模式标准定义的。默认值是base64。更多关于 XML 相关函数的信息可参阅。 + + + + 这里的实际选择主要取决于偏好,只受客户端应用中可能存在的限制的约束。两种方法都支持所有可能的值,尽管十六进制编码会比 base64 编码略大。 + + + + + + + xmloption (enum) + + xmloption配置参数 + + + SET XML OPTION + + + XML 选项 + + + + + + 设置在 XML 与字符串值之间进行转换时,隐含采用DOCUMENT还是CONTENT。 + 有关说明参见。有效值是DOCUMENTCONTENT。默认值是CONTENT。 + + + + 根据 SQL 标准,设置这个选项的命令是: + +SET XML OPTION { DOCUMENT | CONTENT }; + + 这种语法在 PostgreSQL 也可用。 + + + + + + gin_pending_list_limit (integer) + + gin_pending_list_limit 配置参数 + + + + + 设置fastupdate被启用时可以使用的 GIN索引的待处理列表的最大尺寸。 + 如果该列表增长到超过这个最大尺寸,会通过批量将其中的项移入索引的主 GIN 数据结构来清理列表。 + 默认值是四兆字节(4MB)。 + 可以通过更改索引的存储参数来为个别 GIN 索引覆盖这个设置。更多信息请见。 + + + + + + + + 区域设置和格式化 + + + + + + DateStyle (string) + + DateStyle配置参数 + + + + + + 设置日期和时间值的显示格式,以及解释有歧义的日期输入值的规则。由于历史原因, 这个变量包含两个独立的部分:输出格式声明(ISOPostgresSQLGerman)、 输入/输出的年/月/日顺序(DMYMDYYMD)。这些可以被独立设置或者一起设置。关键字EuroEuropeanDMY的同义词;关键字USNonEuroNonEuropeanMDY的同义词。详见。内置默认值是ISO, MDY,但是initdb将用对应于选中的lc_time区域设置行为的设置初始化配置文件。 + + + + + + IntervalStyle (enum) + + IntervalStyle配置参数 + + + + + 设置时间间隔值的显示格式。值sql_standard会生成符合SQL标准时间间隔字面量的输出。 + 值postgres(默认值)的输出与PostgreSQL 8.4 之前版本中设为ISO时的输出一致。 + 值postgres_verbose的输出与PostgreSQL 8.4 之前版本中DateStyle设为非ISO输出时的输出一致。 + 值iso_8601会生成符合 ISO 8601 第 4.4.3.2 节定义的时间间隔带标志符格式的输出。 + + + IntervalStyle参数也会影响对有歧义的时间间隔输入的解释。详见。 + + + + + + + TimeZone (string) + + TimeZone配置参数 + + 时区 + + + + + 设置用于显示和解释时间戳的时区。内置默认值是GMT,但是它通常会在postgresql.conf中被覆盖;initdb将安装一个对应于其系统环境的设置。详见。 + + + + + + + timezone_abbreviations (string) + + timezone_abbreviations配置参数 + + 时区名称 + + + + + 设置服务器接受的日期时间输入中使用的时区缩写集合。默认值为'Default', 这个集合在全世界大多数地方都能工作。也还有'Australia''India',以及可能为一种特定安装定义的其他集合。详见。 + + + + + + extra_float_digits (integer) + + 有效数字 + + + 浮点数 + 显示 + + + extra_float_digits配置参数 + + + + + 此参数调整浮点值显示的位数,包括float4float8和几何数据类型。 + 参数值会加到标准位数(按情况使用FLT_DIGDBL_DIG)上。 + 此值最高可以设置为 3,以包含部分有效的数位;这对于转储需要精确还原的浮点数据特别有用。 + 也可以设置为负数,以抑制不需要的数位。另见。 + + + + + + + client_encoding (string) + + client_encoding配置参数 + + 字符集 + + + + + 设置客户端编码(字符集)。默认使用数据库编码。PostgreSQL服务器所支持的字符集在中描述。 + + + + + + + lc_messages (string) + + lc_messages配置参数 + + + + + + 设置消息显示的语言。可接受的值是系统相关的;详见。如果这个变量被设置为空字符串(默认),那么该值将以一种系统相关的方式从服务器的执行环境中继承。 + + + + 在一些系统上,这个区域设置类别并不存在。仍然可以设置这个变量,只是不会有任何效果。同样,所期望语言的翻译消息也可能不存在。在这种情况下,你将仍然继续看到英文消息。 + + + + 只有超级用户能更改这个设置,因为它会同时影响发送到服务器日志和客户端的消息,设置不当可能降低服务器日志的可读性。 + + + + + + + lc_monetary (string) + + lc_monetary配置参数 + + + + + + 设置用于格式化货币量的区域设置,例如用to_char函数族。可接受的值是系统相关的;详见。如果这个变量被设置为空字符串(默认),那么该值将以一种系统相关的方式从服务器的执行环境中继承。 + + + + + + + lc_numeric (string) + + lc_numeric配置参数 + + + + + + 设置用于格式化数字的区域设置,例如用to_char函数族。可接受的值是系统相关的;详见。如果这个变量被设置为空字符串(默认),那么该值将以一种系统相关的方式从服务器的执行环境中继承。 + + + + + + + lc_time (string) + + lc_time配置参数 + + + + + + 设置用于格式化日期和时间的区域设置,例如用to_char函数族。可接受的值是系统相关的;详见。如果这个变量被设置为空字符串(默认),那么该值将以一种系统相关的方式从服务器的执行环境中继承。 + + + + + + + default_text_search_config (string) + + default_text_search_config配置参数 + + + + + + 选择被那些没有显式参数指定配置的文本搜索函数变体使用的文本搜索配置。详见。内置默认值是pg_catalog.simple,但是如果能够标识一个匹配该区域设置的配置,initdb将用对应于选中的lc_ctype区域设置的值初始化配置文件。 + + + + + + + + + + 共享库预载入 + + + 为了载入附加的功能或者达到提高性能的目的,可用多个设置来预先载入共享库到服务器中。 + 例如'$libdir/mylib'设置会使mylib.so(或者某些平台上的mylib.sl)从安装的标准库目录被预装载。这些设置之间的区别在于生效的时间以及改变它们所需的权限。 + + + + 可以用这个方法预装载PostgreSQL的过程语言库,通常是使用'$libdir/plXXX'语法,其中的XXXpgsqlperltclpython。 + + + + 对于上述每个参数,如果要加载多个库,库名之间用逗号分隔。除非用双引号括起,所有库名都会被转换为小写。 + + + + 只有特别为与PostgreSQL一起使用设计的共享库才能以这种方式载入。每一个PostgreSQL支持 + 的库都有一个魔法块,它会被检查以保证兼容性。由于这个原因,非 PostgreSQL 库无法 + 以这种方式被载入。你可能可以使用操作系统的工具(如LD_PRELOAD)载入它。 + + + + 一般来说,请参考特定模块的文档来用推荐的方法载入它。 + + + + + local_preload_libraries (string) + + local_preload_libraries配置参数 + + + $libdir/plugins + + + + + 这个变量指定一个或者多个要在连接开始时预载入的共享库。 + 这个参数值只在连接开始时生效。后续的更改不会有任何效果。 + 如果一个指定的库没有找到,连接尝试将会失败。 + + + + 任何用户都能设置这个选项。正因为如此,能被这样载入的库被严格限制为出现于安装的标准库 + 目录中plugins子目录下的共享库(保证只有安全的库被安装到 + 这里是数据库管理员的责任)。local_preload_libraries中的项可以显式 + 指定这个目录,例如$libdir/plugins/mylib,或者只是指定库的 + 名称 — mylib 和 + $libdir/plugins/mylib的效果是相同的。 + + + + 这个特性的目的是允许非特权用户在特定的会话中载入用于调试或性能测量的库, + 而无需一个显式的LOAD命令。为了这个目的,通常通过使用客户端的PGOPTIONS环境变量或者 + ALTER ROLE SET来设置这个参数。 + + + + 不过,除非一个模块被特别设计成由非超级用户以这种方式使用,通常不推荐使用这个设置。应该看看 + 。 + + + + + + + session_preload_libraries (string) + + session_preload_libraries配置参数 + + + + + 这个变量指定在连接开始时要预加载的一个或多个共享库。只有超级用户能更改这个设置。 + 参数值仅在连接开始时生效。后续更改不会生效。如果指定的库未找到,连接尝试将失败。 + + + + 这个特性的意图是允许在特定会话中载入调试用的或者测量性能的库,而不需要显式的给出一个 + LOAD命令。例如,通过用ALTER ROLE SET设置这个参数可以 + 为一个给定用户名下的所有会话启用。还有,无需重启 + 服务器就能更改这个参数(但是只有新会话启动时才会生效),这样可以以这种方式更容易地增 + 加新模块,即便它们会应用到所有会话。 + + + + 和不同,相对于在库被第一次使用 + 时载入它,在会话开始时载入库并没有明显的性能优势。不过,当使用连接池时这样做还是有一些 + 优势。 + + + + + + shared_preload_libraries (string) + + shared_preload_libraries配置参数 + + + + + 这个变量指定一个或者多个要在服务器启动时预载入的共享库。这个参数只能在服务器启动时设置。如果指定的库没有找到,服务器将无法启动。 + + + + 有些库需要执行只能在postmaster启动时发生的特定操作,例如分配共享内存、保留轻量级锁 + 或者启动后台工作者。这些库必须通过这个参数在服务器启动时载入。每个库的详情请见文档。 + + + + 其他库也能被预载入。通过预载入一个共享库,当该库被第一次使用时就可以避免库的启动时间。 + 不过,启动每个新服务器进程的时间可能会略有增加,即使该进程从不使用该库。因此,推荐只 + 把这个参数用于那些要在大多数会话中使用的库上。还有,改变这个参数要求重启服务器,因此 + 对于短期的调试任务来说这不是好的选择,应该转用 + 。 + + + + + + 在 Windows 主机上,在服务器启动时预载入一个库并不会减少启动每个新服务器进程所需的 + 时间;每一个服务器进程将会重新载入所有预载入的库。不过,对于那些要在postmaster启动时 + 执行操作的库来说,Windows 主机上的 + shared_preload_libraries仍然有用。 + + + + + + + + + 其他默认值 + + + + + dynamic_library_path (string) + + dynamic_library_path配置参数 + + 动态装载 + + + + 如果需要打开一个可以动态装载的模块并且在CREATE FUNCTIONLOAD命令中指定的文件名没有目录部分(即名字中不包含斜线),那么系统将搜索这个路径以查找所需的文件。 + + + 参数dynamic_library_path的值必须是由冒号(Windows上为分号)分隔的绝对目录路径列表。如果某个列表元素以特殊字符串$libdir开头,则会使用编译时确定的PostgreSQL软件包的库目录来替换$libdir;该目录是标准PostgreSQL发行版所提供模块的安装位置。(使用pg_config --pkglibdir可以找出此目录的名称。)例如: +dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir' +或者,在 Windows 环境中: +dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' + + + + + 这个参数的默认值是'$libdir'。如果该值被设置为一个空字符串,则关闭自动路径搜索。 + + + + 这个参数可以由超级用户在运行时更改, + 但以这种方式进行的设置只会持续到客户端连接结束,因此这种方法应该保留用于开发目的。 + 推荐设置此参数的方法是在postgresql.conf配置文件中。 + + + + + + + gin_fuzzy_search_limit (integer) + + gin_fuzzy_search_limit配置参数 + + + + + + GIN 索引扫描返回的集合尺寸的软上限。详见。 + + + + + + + + + + 锁管理 + + + + + deadlock_timeout (integer) + + 死锁 + 期间超时 + + + 超时 + 死锁 + + + deadlock_timeout配置参数 + + + + + 指定在检查是否发生死锁之前等待锁的毫秒数。检查死锁相对昂贵,因此服务器不会每次等待锁时都运行它。 + 我们乐观地假设在生产应用程序中死锁并不常见,所以在检查死锁之前只是等待一段时间。 + 增加此值会减少在不必要的死锁检查中浪费的时间,但会减慢实际死锁错误的报告速度。 + 默认值为一秒(1s),这可能是你在实践中想要的最小值。 + 在负载较重的服务器上,你可能希望提高它。 + 理想情况下,设置应超过你的典型事务时间,以提高在等待者决定检查死锁之前释放锁的几率。 + 只有超级用户能更改这个设置。 + + + + 当被设置时,这个参数也决定发出关于锁等待的日志之前等待的时间量。如果你想调查锁延迟,你可能希望设置一个比正常的deadlock_timeout小的值。 + + + + + + max_locks_per_transaction (integer) + + max_locks_per_transaction配置参数 + + + + + 共享锁表跟踪在max_locks_per_transaction * ( + ) 个对象(如表)上的锁。因此,在任何一个时刻,只有不超过这么多个可区分对象能够被锁住。这个参数控制为每个事务分配的对象锁的平均数量。只要所有事务的锁都能放入锁表中,单个事务就可以锁住更多对象。这是能被锁住的行数,那个值是没有限制的。默认值 64 已经被历史证明是足够的,但是如果你有需要在一个事务中使用很多不同表的查询(例如查询一个有很多子表的父表),你可能需要提高这个值。这个参数只能在服务器启动时设置。 + + + + 运行备库时,必须将此参数设置为大于或等于主库上的值。否则,备库上将不允许查询。 + + + + + + + max_pred_locks_per_transaction (integer) + + max_pred_locks_per_transaction配置参数 + + + + + + 共享谓词锁表跟踪在max_pred_locks_per_transaction * ( + ) 个对象(如表)上的锁。因此,在任何一个时刻,只有不超过这么多个可区分对象能够被锁住。这个参数控制为每个事务分配的对象锁的平均数量。只要所有事务的锁都能放入锁表中,单个事务就可以锁住更多对象。这是能被锁住的行数,那个值是没有限制的。默认值 64 已经在测试中被证明通常是足够的,但是如果你有会在单个可串行化事务中访问许多不同表的客户端,则可能需要提高这个值。这个参数只能在服务器启动时设置。 + + + + + + + + + + 版本和平台兼容性 + + + 以前的 PostgreSQL 版本 + + + + + + array_nulls (boolean) + + array_nulls配置参数 + + + + + + 这个参数控制数组输入解析器是否把未用引号的NULL识别为一个值为空值的数组元素。默认为on,允许输入包含空值的数组值。但是PostgreSQL 8.2 之前的版本不支持数组中的空值,并且因此将把NULL当作指定一个值为字符串NULL的正常数组元素。为了向后兼容那些要求旧行为的应用,这个变量可以被设置为off。 + + + + 注意即使这个变量为off也能够创建包含空值的数组值。 + + + + + + backslash_quote (enum) + 字符串反斜线引号 + + backslash_quote配置参数 + + + + + 这个参数控制字符串字面量中的单引号是否能够用\'来表示。首选的 SQL 标准的方法是将其双写(''),但是PostgreSQL在历史上也接受\'。不过使用\'容易导致安全风险,因为在某些客户端字符集编码中,有多字节字符的最后一个字节在数值上等价于 ASCII 的\。如果客户端代码没有做到正确转义,那么就可能遭到 SQL 注入攻击。如果服务器拒绝看起来带有被反斜线转义的单引号的查询,那么就可以避免这种风险。backslash_quote的可用值是on(总是允许\')、off(总是拒绝)以及safe_encoding(只有客户端编码不允许在多字节字符中存在 ASCII \时允许)。safe_encoding是默认设置。 + + + + 注意在符合标准的字符串字面量中,\就表示\。这个参数只影响不符合标准的字面量的处理,包括转义字符串语法(E'...')。 + + + + + + default_with_oids (boolean) + + default_with_oids 配置参数 + + + + + 此参数控制在既未指定WITH OIDS也未指定WITHOUT OIDS时, + CREATE TABLECREATE TABLE AS是否在新建表中包含 OID 列。 + 它也决定SELECT INTO创建的表是否包含 OID。 + 此参数默认为off;在PostgreSQL 8.0 及更早版本中,默认为on。 + + + + 在用户表中使用 OID 已被弃用,因此大多数安装应让此变量保持禁用状态。 + 需要为特定表使用 OID 的应用应在创建表时指定WITH OIDS。 + 可以启用此变量,以兼容不遵循这种做法的旧应用。 + + + + + + + escape_string_warning (boolean) + 字符串转义警告 + + escape_string_warning配置参数 + + + + + + 打开时,如果在普通字符串字面量中('...'语法)出现了 一个反斜线(\)并且standard_conforming_strings为关闭,那么就会发出一个警告。默认值是on。 + + + + 希望使用反斜线作为转义符的应用应该被修改来使用转义字符串语法(E'...'),因为按照 SQL 标准,普通字符串现在默认将反斜线视作一个普通字符。这个变量可以被启用来帮助定位需要被更改的代码。 + + + + + + lo_compat_privileges (boolean) + + lo_compat_privileges配置参数 + + + + + 在PostgreSQL 9.0之前的版本中,大对象没有访问权限,因此始终可以被所有用户读取和写入。 + 将此变量设置为on会禁用新的权限检查,以保持与之前版本的兼容性。默认值为off。 + 只有超级用户能更改这个设置。 + + + 设置此变量不会禁用所有与大对象相关的安全检查 — 只禁用那些在PostgreSQL 9.0 中默认行为发生变化的检查。 + 例如,无论此设置如何,lo_import()lo_export()都需要超级用户权限。 + + + + + + operator_precedence_warning (boolean) + + operator_precedence_warning 配置参数 + + + + + 开启时,对于任何可能因操作符优先级的变化而在PostgreSQL 9.4 之后改变含义的构造,解析器都会发出警告。 + 这有助于审查应用,检查优先级变化是否破坏了某些行为;但不应在生产环境中一直开启,因为它也会对一些完全有效、符合标准的 SQL 代码发出警告。 + 默认值为off。 + + + + 更多信息参见。 + + + + + + + quote_all_identifiers (boolean) + + quote_all_identifiers配置参数 + + + + + + 当数据库产生 SQL 时,强制所有标识符被引号包围,即使它们(当前)不是关键字。这将影响EXPLAIN的输出以及pg_get_viewdef等函数的结果。另请参阅选项。 + + + + + + sql_inheritance (boolean) + + sql_inheritance配置参数 + + 继承 + + + + 这个设置控制不带修饰的表引用是否被认为包括继承子表。默认值为on,即子表被包括在内(因此,默认假定有*后缀)。如果设为off,子表不被包括(因此,假定有ONLY前缀)。SQL 标准要求包括子表,因此off设置不符合标准,但提供它是为了与 7.1 之前版本的PostgreSQL兼容。更多信息见。 + + + + 将sql_inheritance关闭已被弃用,因为这种行为已被发现既容易出错又违背 SQL 标准。本手册其他地方对继承行为的讨论通常都假定它为on。 + + + + + + + standard_conforming_strings (boolean) + 字符串符合标准 + + standard_conforming_strings配置参数 + + + + + + 控制普通字符串字面量('...')是否按照 SQL 标准把反斜线当普通文本。从PostgreSQL 9.1 开始,默认值为on(之前的发行中默认值为off)。应用可以检查这个参数来判断字符串字面量如何被处理。这个参数的存在也可以被当做转义字符串语法(E'...')被支持的标志。如果一个应用希望反斜线被当做转义字符,应该使用转义字符串语法()。 + + + + + + + synchronize_seqscans (boolean) + + synchronize_seqscans配置参数 + + + + + + 它允许对大型表的顺序扫描与其他扫描同步,因此并发扫描可以在几乎相同的时刻读取相同的块,这样可以分担 I/O 负载。当启用这个参数时,一个扫描可能会从表的中间开始并且之后绕回到开头以覆盖所有的行,这样可以与已在进行中的扫描活动同步。对于没有ORDER BY子句的查询,这样的扫描可能会在返回行的顺序中造成不可预料的改变。将这个参数设置为off以保证 8.3 之前的行为(顺序扫描总是从表的起始处开始)。默认值是on。 + + + + + + + + + 平台和客户端兼容性 + + + + + transform_null_equals (boolean) + IS NULL + + transform_null_equals配置参数 + + + + + + 当打开时,形为expr = NULL(或NULL = expr)的表达式将被当做expr IS NULL, 也就是说,如果expr计算结果为空值则返回真,否则返回假。正确的 SQL 标准兼容的expr = NULL行为总是返回空值(未知)。因此这个参数默认为off。 + + + + 不过,在Microsoft Access里的过滤表单生成的查询似乎使用expr = NULL来测试空值,因此,如果你使用这个接口访问数据库,你可能想把这个选项打开。因为expr = NULL形式的表达式总是返回空值(使用 SQL 标准解释),它们不是非常有用并且在普通应用中也不常见,因此这个选项实际上没有什么危害。但是新用户常常对涉及空值的表达式语义感到困惑,因此这个选项默认为关闭。 + + + + 请注意这个选项只影响= NULL形式,而不影响其它比较操作符或者其它与一些涉及等值操作符的表达式在计算上等效的其他表达式(例如IN)。因此,这个选项不能普遍修复错误的程序写法。 + + + + 相关信息请见。 + + + + + + + + + + 错误处理 + + + + + exit_on_error (boolean) + + exit_on_error配置参数 + + + + + 如果为true,任何错误将中止当前会话。默认情况下,这个值被设置为false,这样只有 FATAL 错误(致命)将中止会话。 + + + + + + restart_after_crash (boolean) + + restart_after_crash配置参数 + + + + + 当被设置为true(默认值),PostgreSQL将在一次后端崩溃后自动重新初始化。 + 让这个值设置为true通常是将数据库可用性最大化的最佳方法。但是在某些环境中,例如PostgreSQL被集群软件调用时,禁用重启可能很有用,这样集群软件可以得到控制并且采取它认为适当的行动。 + + + + 这个参数只能在postgresql.conf文件中或通过服务器命令行进行设置。 + + + + + + data_sync_retry (boolean) + + data_sync_retry配置参数 + + + + + 如果设置为false(默认值),PostgreSQL在将修改后的数据文件刷盘到文件系统失败时,将引发PANIC级错误。 + 这样会导致数据库服务器崩溃。这个参数只能在服务器启动时被设置。 + + + 在某些操作系统上,回写失败后,内核页面缓存中的数据状态未知。 在某些情况下,它可能已被完全遗忘,因此重试不安全;第二次尝试可能报告为成功,而事实上数据已丢失。在此类情形下,避免数据丢失的唯一方法是在报告任何故障后从WAL中恢复,最好是在调查了故障的根本原因并更换了任何有故障的硬件之后。 + + + 如果设置为true,PostgreSQL将报告错误,但会继续运行,以便可以在以后的检查点中重试数据刷盘操作。 + 只有在调查清楚操作系统在回写失败时如何处理缓冲数据之后,才应将其设置为true。 + + + + + + + + + + 预置选项 + + + 下列参数是只读的,在编译或安装 PostgreSQL 时确定。 + 因此,它们未列入示例 postgresql.conf 文件。 + 这些选项报告 PostgreSQL 行为的各个方面,某些应用(特别是管理前端)可能对此感兴趣。 + + + + + + + block_size (integer) + + block_size配置参数 + + + + + + 报告一个磁盘块的大小。它由编译服务器时BLCKSZ的值确定。默认值是 8192 字节。有些配置变量的含义(例如)会被block_size影响。详见。 + + + + + + + data_checksums (boolean) + + data_checksums配置参数 + + + + + + 报告对这个集簇是否启用了数据校验和。详见。 + + + + + + + debug_assertions (boolean) + + debug_assertions 配置参数 + + + + + + 报告PostgreSQL是否在构建时启用了断言。 + 如果在构建PostgreSQL时定义了宏 + USE_ASSERT_CHECKING(例如通过 + configure选项), + 则会报告已启用。默认情况下,PostgreSQL是在未启用断言的情况下构建的。 + + + + + + + integer_datetimes (boolean) + + integer_datetimes配置参数 + + + + + + 报告PostgreSQL是否在编译时启用了对 64 位整数日期和时间的支持。构建PostgreSQL时配置--disable-integer-datetimes可以禁用它。默认值是on。 + + + + + + + + lc_collate (string) + + lc_collate 配置参数 + + + + + 报告对文本数据进行排序所使用的区域设置。更多信息参见。 + 此值在创建数据库时确定。 + + + + + + + + lc_ctype (string) + + lc_ctype 配置参数 + + + + + 报告决定字符分类的区域设置。更多信息参见。 + 此值在创建数据库时确定。通常它与lc_collate相同,但特殊应用可能将它设置为不同的值。 + + + + + + + max_function_args (integer) + + max_function_args配置参数 + + + + + + 报告函数参数的最大数量。它由编译服务器时的FUNC_MAX_ARGS值决定。默认值是 100 个参数。 + + + + + + + max_identifier_length (integer) + + max_identifier_length配置参数 + + + + + + 报告标识符的最大长度。它由编译服务器时的NAMEDATALEN值减一决定。NAMEDATALEN的默认值是 64;因此max_identifier_length的默认值是 63 字节,在使用多字节编码时,这可能不足 63 个字符。 + + + + + + + max_index_keys (integer) + + max_index_keys配置参数 + + + + + + 报告索引键的最大数目。它由编译服务器时的INDEX_MAX_KEYS值决定。默认值是 32 个键。 + + + + + + + segment_size (integer) + + segment_size配置参数 + + + + + + 报告一个文件段中可以存储的块(页)的数量。由编译服务器时的RELSEG_SIZE值决定。一个段文件的最大尺寸(以字节计)等于segment_size乘以block_size,默认是 1GB。 + + + + + + + server_encoding (string) + + server_encoding配置参数 + + 字符集 + + + + + 报告数据库的编码(字符集)。这是在数据库被创建时决定的。通常,客户端只需要关心的值。 + + + + + + + server_version (string) + + server_version配置参数 + + + + + + 报告服务器的版本号。它是由编译服务器时的PG_VERSION值决定的。 + + + + + + + server_version_num (integer) + + server_version_num配置参数 + + + + + + 以整数形式报告服务器的版本号。它是由编译服务器时的PG_VERSION_NUM值决定的。 + + + + + + + wal_block_size (integer) + + wal_block_size配置参数 + + + + + + 报告一个 WAL 磁盘块的尺寸。由编译服务器时的XLOG_BLCKSZ值决定。默认是 8192 字节。 + + + + + + wal_segment_size (integer) + + wal_segment_size配置参数 + + + + + 报告 WAL 段文件中的块(页)数。WAL 段文件以字节计的总大小等于wal_segment_size乘以wal_block_size; + 默认总大小为 16MB。更多信息参见。 + + + + + + + + + + 自定义选项 + + + 这个特性允许附加模块(例如过程语言)向PostgreSQL添加系统通常不认识的参数。这样便能以标准方式配置扩展模块。 + + + + 自定义选项的名称由两部分组成:扩展名称和参数名本身,中间用句点分隔,类似于 SQL 中的限定名。例如plpgsql.variable_conflict。 + + + + 因为自定义选项可能需要在尚未加载相关扩展模块的进程中设置, + PostgreSQL将接受任何两部分参数名称的设置。 + 这些变量被视为占位符,在定义它们的模块加载之前没有任何功能。 + 当加载扩展模块时,它将添加其变量定义并根据这些定义转换任何占位符值。 + 如果存在以其扩展名称开头的任何未识别的占位符,将发出警告。 + + + + + 开发者选项 + + + 下列参数用于PostgreSQL源代码的开发工作,在某些情况下也用于辅助恢复严重损坏的数据库。 + 没有理由在生产数据库中使用它们。因此,它们被排除在示例postgresql.conf文件之外。 + 请注意,许多参数需要特殊的源代码编译标志才能起作用。 + + + + + + allow_system_table_mods (boolean) + + allow_system_table_mods配置参数 + + + + + 允许修改系统表的结构。此参数由initdb使用。 + 此参数只能在服务器启动时设置。 + + + + + + + ignore_system_indexes (boolean) + + ignore_system_indexes配置参数 + + + + + + 读取系统表时忽略系统索引(但是修改系统表时依然同时更新索引)。这在从被破坏的系统索引中恢复数据时有用。这个参数在会话开始之后不能被更改。 + + + + + + post_auth_delay (integer) + + post_auth_delay配置参数 + + + + + 如果非零,则在新服务器进程启动并执行认证过程之后,延迟指定的秒数。 + 这旨在给开发者一个机会,用调试器附加到服务器进程上。 + 此参数在会话开始之后不能更改。 + + + + + + pre_auth_delay (integer) + + pre_auth_delay配置参数 + + + + + 如果非零,则在新服务器进程刚刚派生之后、执行认证过程之前,延迟指定的秒数。 + 这旨在给开发者一个机会,用调试器附加到服务器进程上,跟踪认证过程中的异常行为。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + trace_notify (boolean) + + trace_notify配置参数 + + + + + + 为LISTENNOTIFY命令生成大量调试输出。必须是DEBUG1或者更低才能把这种输出分别发送到客户端或者服务器日志。 + + + + + + + + trace_recovery_messages (enum) + + trace_recovery_messages 配置参数 + + + + + 启用原本不会记录的恢复相关调试输出。此参数允许用户覆盖的常规设置,但仅针对特定消息。 + 它旨在用于调试热备。有效值为DEBUG5DEBUG4、 + DEBUG3DEBUG2DEBUG1LOG。 + 默认值LOG完全不影响是否记录消息的决定。其他值会让该优先级或更高优先级的恢复相关调试消息 + 按LOG优先级记录;对于log_min_messages的常见设置,这会无条件地将它们发送到服务器日志。 + 此参数只能在postgresql.conf文件中或在服务器命令行上设置。 + + + + + + + trace_sort (boolean) + + trace_sort配置参数 + + + + + + 如果开启,输出排序操作中的资源使用信息。只有在编译PostgreSQL时定义了TRACE_SORT宏, 这个参数才可用(不过,当前在默认情况下就定义了TRACE_SORT)。 + + + + + + trace_locks (boolean) + + trace_locks配置参数 + + + + + + 如果开启,发出锁使用情况的信息。被转储信息中包括锁操作的类型、锁的类型和 被锁或被解锁对象的唯一标识符。同样包括的还有已经授予这个对象的锁类型的位掩码和 等待这个对象的锁类型的位掩码。还会转储每种锁类型已授予的锁数、等待的锁数,以及它们的总数。一个日志文件输出的示例如下: + +LOG: LockAcquire: new: lock(0xb7acd844) id(24688,24696,0,0,0,1) + grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0 + wait(0) type(AccessShareLock) +LOG: GrantLock: lock(0xb7acd844) id(24688,24696,0,0,0,1) + grantMask(2) req(1,0,0,0,0,0,0)=1 grant(1,0,0,0,0,0,0)=1 + wait(0) type(AccessShareLock) +LOG: UnGrantLock: updated: lock(0xb7acd844) id(24688,24696,0,0,0,1) + grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0 + wait(0) type(AccessShareLock) +LOG: CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1) + grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0 + wait(0) type(INVALID) + + 被转储结构体的详细信息可以在src/include/storage/lock.h中找到。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + trace_lwlocks (boolean) + + trace_lwlocks配置参数 + + + + + + 如果开启,发出轻量级锁的使用信息。轻量级锁主要是为了提供对共享内存数据结构的互斥访问。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + trace_userlocks (boolean) + + trace_userlocks配置参数 + + + + + + 如果开启,发出关于用户锁使用的信息。与trace_locks的输出一样,但只用于咨询锁。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + trace_lock_oidmin (integer) + + trace_lock_oidmin配置参数 + + + + + + 如果设置,不会跟踪 OID 小于此值的表上的锁(用于避免在系统表上的输出)。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + trace_lock_table (integer) + + trace_lock_table配置参数 + + + + + + 无条件地跟踪此表(OID)上的锁。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + debug_deadlocks (boolean) + + debug_deadlocks配置参数 + + + + + + 如果设置,当死锁超时发生时,转储所有当前锁的信息。 + + + + 只有在编译PostgreSQL时定义了LOCK_DEBUG宏, 这个参数才可用。 + + + + + + log_btree_build_stats (boolean) + + log_btree_build_stats配置参数 + + + + + + 如果设置,会记录 B-树操作上的系统资源使用情况统计(内存和 CPU)。 + + + + 只有在编译PostgreSQL时定义了BTREE_BUILD_STATS宏, 这个参数才可用。 + + + + + + + + wal_debug (boolean) + + wal_debug配置参数 + + + + + + 如果被打开,WAL 相关的调试输出将被发出。只有在编译PostgreSQL时定义了WAL_DEBUG宏的情况下,这个参数才可用。 + + + + + + + ignore_checksum_failure (boolean) + + ignore_checksum_failure配置参数 + + + + + + 只有当被启用时才有效。 + + + + 在读取过程中检测到校验和失败通常会导致PostgreSQL报告错误,中止当前事务。 + 将ignore_checksum_failure设置为 on 会使系统忽略失败(但仍报告警告),并继续处理。 + 这种行为可能导致崩溃、传播或隐藏损坏,或引发其他严重问题。 + 但是,如果块首部仍然正常,它可能允许你跳过错误,检索表中可能仍然存在的未损坏元组。 + 如果首部损坏,即使启用此选项也会报告错误。默认设置为off。 + 只有超级用户才能更改此设置。 + + + + + + + zero_damaged_pages (boolean) + + zero_damaged_pages配置参数 + + + + + + 检测到损坏的页面头通常会导致PostgreSQL报告错误,中止当前事务。 + 将zero_damaged_pages设置为on会导致系统报告警告,将内存中的损坏页面清零,并继续处理。 + 这种行为会破坏数据,即损坏页面上的所有行。但是,它确实允许你跳过错误,并从表中可能存在的未损坏页面中检索行。 + 如果由于硬件或软件错误而发生损坏,这对于恢复数据很有用。通常在放弃从表的损坏页面恢复数据的希望之前,不应将其设置为on。 + 清零的页面不会强制写入磁盘,因此建议在再次关闭此参数之前重新创建表或索引。默认设置为off。 + 只有超级用户能更改这个设置。 + + + + + + + 短选项 + + + 为了方便起见,系统中还为一些参数提供了单字母的命令行选项开关。它们在中描述。其中一些选项是由于历史原因而存在,它们以单字母选项的形式存在,并不一定表示鼓励频繁使用这些选项。 + + + + 短选项对照 + + + + + 短选项 + 等效于 + + + + + + + shared_buffers = x + + + + log_min_messages = DEBUGx + + + + datestyle = euro + + + + + , , , + , , , + , + + + enable_bitmapscan = off, + enable_hashjoin = off, + enable_indexscan = off, + enable_mergejoin = off, + enable_nestloop = off, + enable_indexonlyscan = off, + enable_seqscan = off, + enable_tidscan = off + + + + + fsync = off + + + + listen_addresses = x + + + + listen_addresses = '*' + + + + unix_socket_directories = x + + + + ssl = on + + + + max_connections = x + + + + allow_system_table_mods = on + + + + port = x + + + + ignore_system_indexes = on + + + + log_statement_stats = on + + + + work_mem = x + + + + , , + log_parser_stats = on, + log_planner_stats = on, + log_executor_stats = on + + + + post_auth_delay = x + + + +
+ +
+
diff --git a/zh/9.6/contacts.sgml b/zh/9.6/contacts.sgml new file mode 100644 index 00000000..b32d862d --- /dev/null +++ b/zh/9.6/contacts.sgml @@ -0,0 +1,26 @@ + + + +联系方式 + + + diff --git a/zh/9.6/contrib-spi.sgml b/zh/9.6/contrib-spi.sgml new file mode 100644 index 00000000..25ce1d44 --- /dev/null +++ b/zh/9.6/contrib-spi.sgml @@ -0,0 +1,141 @@ + + + + spi + + + SPI + 示例 + + + + spi 模块提供了若干使用 + 服务器编程接口(SPI)和触发器的实用示例。 + 虽然这些函数本身也有一定的独立用途,但把它们作为可按自身需求修改的示例来看更有价值。 + 这些函数足够通用,可以用于任何表,但在创建触发器时必须指定表名和字段名 + (如下所述)。 + + + + 下文描述的每组函数都作为一个可单独安装的扩展提供。 + + + + refint — 用于实现引用完整性的函数 + + check_primary_key()check_foreign_key()用于检查外键约束。(当然,这项功能早已被内置的外键机制取代,不过该模块作为示例仍然有用。) + + + check_primary_key() 检查引用表。使用时,在引用其他表的表上 + 创建一个使用该函数的 BEFORE INSERT OR UPDATE 触发器。 + 触发器参数依次为:引用表中构成外键的列名、被引用表名,以及被引用表中构成 + 主键/唯一键的列名。 + 若要处理多个外键,请为每个引用分别创建一个触发器。 + + + + check_foreign_key() 检查被引用表。使用时,在被其他表引用的表上 + 创建一个使用该函数的 BEFORE DELETE OR UPDATE 触发器。 + 触发器参数依次为:该函数需要检查的引用表数量、发现引用键时采取的动作 + (cascade 表示删除引用行, + restrict 表示若存在引用键则中止事务, + setnull 表示将引用键字段设为空)、 + 被引用表中构成主键/唯一键的列名,然后是引用表名和列名 + (按第一个参数指定的引用表数量重复提供)。请注意,主键/唯一键列应标记为 + NOT NULL,并具有唯一索引。 + + + + refint.example 中有示例。 + + + + + timetravel — 实现时间旅行的函数 + + 很久以前,PostgreSQL曾内置时间旅行功能,为每个元组保存插入和删除时间。可以使用这些函数模拟这一功能。要使用这些函数,必须为表添加两个abstime类型的列,以存储元组插入的时间(start_date)和更改/删除的时间(stop_date): +CREATE TABLE mytab ( + ... ... + start_date abstime, + stop_date abstime + ... ... +); +这些列可以任意命名,不过在这里我们将它们称为 start_date 和 stop_date。 + + 插入新行时,start_date 通常应设置为当前时间,stop_date 应设置为infinity。如果插入的数据在这些列中包含空值,触发器会自动将其替换为上述值。一般来说,只有在重新载入转储数据时,才应向这些列中插入显式的非空数据。 + + stop_date 等于infinity的元组当前有效,可以修改。stop_date 为有限值的元组则不能再修改,触发器会阻止这种操作。(如果需要这样做,可以按下文所示关闭时间旅行。) + + 对于可修改的行,更新时只会将被更新元组的 stop_date 改为当前时间,并插入一个包含修改后数据的新元组。新元组的 start_date 将设置为当前时间,stop_date 设置为infinity + + 删除操作实际上不会删除元组,而只会将其 stop_date 设置为当前时间。 + + 要查询当前有效的元组,请在查询的 WHERE 条件中加入stop_date = 'infinity'。(你可能希望将其放入视图中。)类似地,通过为 start_date 和 stop_date 设置合适的条件,也可以查询在过去任何时间有效的元组。 + + timetravel()是支持此行为的通用触发器函数。在每个使用时间旅行的表上,使用此函数创建一个BEFORE INSERT OR UPDATE OR DELETE触发器。指定两个触发器参数:start_date 和 stop_date 列的实际名称。还可以选择指定一到三个额外参数,它们必须引用text类型的列。触发器会在 INSERT 时将当前用户名存入其中的第一列,在 UPDATE 时存入第二列,在 DELETE 时存入第三列。 + + set_timetravel()允许为表打开或关闭时间旅行。set_timetravel('mytab', 1)会为表mytab打开 TT。set_timetravel('mytab', 0)会为表mytab关闭 TT。两种情况都会报告原来的状态。在 TT 关闭时,可以自由修改 start_date 和 stop_date 列。请注意,开关状态仅作用于当前数据库会话;新会话总是从所有表的 TT 都打开的状态开始。 + + get_timetravel()返回表的 TT 状态,而不改变它。 + + timetravel.example中有一个示例。 + + + + autoinc — 用于字段自动递增的函数 + + autoinc()是一个将序列的下一个值存入整数字段的触发器。它与内置的序列列功能有一些重叠,但并不相同:autoinc()会覆盖插入时试图为该字段指定其他值的操作,而且还可以选择在更新时也递增该字段。 + + + 使用时,创建一个使用该函数的 BEFORE INSERT + (或者可选的 BEFORE INSERT OR UPDATE)触发器。 + 指定两个触发器参数:要修改的整型列名,以及提供这些值的序列对象名。 + (实际上,如果你希望更新多个自动递增列,也可以指定任意多组这样的名称对。) + + + + autoinc.example 中有一个示例。 + + + + + + insert_username — 用于跟踪谁修改了表的函数 + + + insert_username() 是一个将当前用户名存入文本字段的触发器。 + 它有助于跟踪表中某一特定行最后一次是由谁修改的。 + + + + 使用时,创建一个使用该函数的 BEFORE INSERT 和/或 + UPDATE 触发器。指定一个触发器参数:要修改的文本列名。 + + + + insert_username.example 中有一个示例。 + + + + + + moddatetime — 用于跟踪最后修改时间的函数 + + + moddatetime() 是一个将当前时间存入 timestamp 字段的触发器。 + 它有助于跟踪表中某一特定行最后一次被修改的时间。 + + + + 使用时,创建一个使用该函数的 BEFORE UPDATE 触发器。 + 指定一个触发器参数:要修改的列名。该列必须是 + timestamptimestamp with time zone 类型。 + + + + moddatetime.example 中有一个示例。 + + + + + diff --git a/zh/9.6/contrib.sgml b/zh/9.6/contrib.sgml new file mode 100644 index 00000000..0a493cfa --- /dev/null +++ b/zh/9.6/contrib.sgml @@ -0,0 +1,145 @@ + + + + 额外提供的模块与扩展 + + + 本附录与下一附录包含有关 + PostgreSQL 发行版中 + contrib 目录中可选组件的信息。 + 这些组件包括移植工具、分析实用程序,以及不属于 PostgreSQL 核心系统的 + 插件功能。之所以将它们单独提供,主要是因为它们面向的受众有限, + 或者实验性太强,不适合作为主源码树的一部分。但这并不影响它们的实用性。 + + + + 本附录介绍位于 contrib 中的扩展以及其他服务器插件模块库。 + 介绍实用程序。 + + + 从源码发行版构建时,除非构建“world”目标(见),否则这些组件不会自动构建。可以运行以下命令来构建并安装所有组件: +make +make install +这些命令应在已配置好的源码树的contrib目录中运行;若只想构建并安装某个选定的模块,则可在该模块的子目录中执行同样的命令。许多模块都带有回归测试,可以运行以下命令来执行: +make check +这是在安装前执行的命令;也可以运行 +make installcheck +,此时需要已有一个PostgreSQL服务器正在运行。 + + + 如果你使用的是预打包版本的 PostgreSQL, + 这些组件通常会作为单独的子包提供,例如 + postgresql-contrib。 + + + 许多模块提供新的用户定义函数、操作符或类型。安装代码后,要使用其中某个模块,就需要在数据库系统中注册新的 SQL 对象。在PostgreSQL9.1 及后续版本中,这可通过执行命令完成。在一个新建的数据库中,你可以直接执行: +CREATE EXTENSION module_name; +该命令必须由数据库超级用户执行。它只会在当前数据库中注册新的 SQL 对象,因此需要在每个希望使用该模块功能的数据库中运行它。另一种做法是在数据库template1中运行它,这样该扩展默认会被复制到随后创建的数据库中。 + + 许多模块允许你将其对象安装到所选的模式中。要这样做,请在CREATE EXTENSION命令中加入SCHEMA schema_name。默认情况下,这些对象会被放入当前的创建目标模式,而该模式默认是public + + 如果你的数据库是从PostgreSQL9.1 之前的版本通过转储和重新载入升级而来的,并且其中一直在使用该模块的 9.1 之前版本,则应改为执行: +CREATE EXTENSION module_name FROM unpackaged; +这会将该模块在 9.1 之前版本中的对象更新为一个正规的扩展对象。今后该模块的更新将由管理。有关扩展更新的更多信息,请参见。 + + + 但请注意,其中有些模块并不是这种意义上的扩展,而是通过其他方式加载到服务器中,例如借助。详情见各模块文档。 + + &adminpack; + &auth-delay; + &auto-explain; + &bloom; + &btree-gin; + &btree-gist; + &chkpass; + &citext; + &cube; + &dblink; + &dict-int; + &dict-xsyn; + &earthdistance; + &file-fdw; + &fuzzystrmatch; + &hstore; + &intagg; + &intarray; + &isn; + &lo; + <ree; + &pageinspect; + &passwordcheck; + &pgbuffercache; + &pgcrypto; + &pgfreespacemap; + &pgprewarm; + &pgrowlocks; + &pgstatstatements; + &pgstattuple; + &pgtrgm; + &pgvisibility; + &postgres-fdw; + &seg; + &sepgsql; + &contrib-spi; + &sslinfo; + &tablefunc; + &tcn; + &test-decoding; + &tsearch2; + &tsm-system-rows; + &tsm-system-time; + &unaccent; + &uuid-ossp; + &xml2; + + + + + + + 额外提供的程序 + + + 本附录与前一个附录包含有关 + PostgreSQL 发行版中 + contrib 目录内模块的信息。 + 关于 contrib 部分的一般性信息,以及 + contrib 中服务器扩展和插件的具体信息,请参见 + 。 + + + + 本附录介绍位于 contrib 中的实用程序。无论是通过 + 源码还是打包系统安装,它们安装后都会出现在 + PostgreSQL 安装的 bin + 目录中,并可像其他任何程序一样使用。 + + + + 客户端应用 + + + 本节介绍 contrib 中的 + PostgreSQL 客户端应用程序。 + 无论数据库服务器位于何处,都可以从任何位置运行它们。 + 另见 ,其中介绍了属于核心 + PostgreSQL 发行版一部分的客户端应用程序。 + + + &oid2name; + &vacuumlo; + + + + 服务器应用 + + 本节介绍contrib中与PostgreSQL服务器相关的应用程序。它们通常运行在数据库服务器所在的主机上。另见,其中介绍了作为核心PostgreSQL发行版一部分的服务器应用程序。 + + &pgstandby; + + diff --git a/zh/9.6/cube.sgml b/zh/9.6/cube.sgml new file mode 100644 index 00000000..b398121f --- /dev/null +++ b/zh/9.6/cube.sgml @@ -0,0 +1,510 @@ + + + + cube — 多维立方体数据类型 + + + cube(扩展) + + + + 该模块实现了用于表示多维立方体的cube数据类型。 + + + + 语法 + + + 展示了cube类型合法的外部表示。 + xy等表示浮点数。 + + + + cube 外部表示 + + + + 外部语法 + 含义 + + + + + + x + 一个一维点 + (或者长度为零的一维区间) + + + + (x) + 同上 + + + x1,x2,...,xn + n 维空间中的一个点,在内部表示为零体积立方体 + + + + (x1,x2,...,xn) + 同上 + + + (x),(y) + 一个从x开始、到y结束的一维区间, + 反过来也可以;顺序无关紧要 + + + + [(x),(y)] + 同上 + + + (x1,...,xn),(y1,...,yn) + 一个 n 维立方体,由一对对角相对的角点表示 + + + + [(x1,...,xn),(y1,...,yn)] + 同上 + + + +
+ + + 立方体的两个对角点以何种顺序输入都无关紧要。必要时, + cube函数会自动交换这些值,以创建统一的 + 左下角 — 右上角内部表示。当两个角点重合时, + cube只存储其中一个角点,并附带一个is point标志, + 以避免浪费空间。 + + + + 输入时会忽略空白字符,因此 + [(x),(y)]与 + [ ( x ), ( y ) ] + 相同。 + +
+ + + 精度 + + + 值在内部以 64 位浮点数存储。这意味着有效数字超过大约 16 位的数值会被截断。 + + + + + 用法 + + 展示了为cube类型提供的操作符。 + + + cube 操作符 + + + + 操作符 + 结果 + + 描述 + + + + + + + a = b + boolean + 立方体 a 和 b 相同。 + + + + a && b + boolean + 立方体 a 和 b 重叠。 + + + + a @> b + boolean + 立方体 a 包含立方体 b。 + + + + a <@ b + boolean + 立方体 a 包含在立方体 b 中。 + + + + a < b + boolean + 立方体 a 小于立方体 b。 + + + + a <= b + boolean + 立方体 a 小于或等于立方体 b。 + + + + a > b + boolean + 立方体 a 大于立方体 b。 + + + + a >= b + boolean + 立方体 a 大于或等于立方体 b。 + + + + a <> b + boolean + 立方体 a 不等于立方体 b。 + + + + a -> n + float8 + 获取立方体的第 n 个坐标(从 1 开始计数)。 + + + + a ~> n + float8 + 按以下方式获取立方体的第 n 个坐标:n = 2 * k - 1 表示第 k 维的下界,n = 2 * k 表示第 k 维的上界。该操作符专为支持 KNN-GiST 而设计。 + + + + a <-> b + float8 + a 和 b 之间的欧几里得距离。 + + + + a <#> b + float8 + a 和 b 之间的出租车距离(L-1 度量)。 + + + + a <=> b + float8 + a 和 b 之间的切比雪夫距离(L-无穷度量)。 + + + + +
+ + (在 PostgreSQL 8.2 之前,包含操作符 @><@ 分别称为 @~。这些名称仍然可用,但已弃用,最终将被删除。请注意,旧名称与核心几何数据类型以前采用的约定正好相反!) + + 标量排序操作符(<>= 等)除了排序之外,在实际用途上没有太大意义。这些操作符首先比较第一个坐标;如果相等,再比较第二个坐标,依此类推。它们主要用于支持cube的 B-树索引操作符类,例如,当你希望在cube列上设置 UNIQUE 约束时可能会有用。 + + + cube模块还为cube值提供了一个 GiST 索引操作符类。 + cube GiST 索引可用于在WHERE子句中使用 + =&&@> + 和<@操作符搜索值。 + + + + 此外,cube GiST 索引还可用于在ORDER BY子句中借助度量操作符 + <-><#>和 + <=>查找最近邻。例如,三维点 (0.5, 0.5, 0.5) + 的最近邻可以用下面的查询高效地找到: + +SELECT c FROM test ORDER BY c <-> cube(array[0.5,0.5,0.5]) LIMIT 1; + + + + + ~>操作符也可以这样使用,以便高效地检索按选定坐标排序后的前几个值。 + 例如,要获得按第一个坐标(左下角)升序排列的前几个立方体,可以使用下面的查询: + +SELECT c FROM test ORDER BY c ~> 1 LIMIT 5; + + 要获得按右上角第一个坐标降序排列的二维立方体,可以使用: + +SELECT c FROM test ORDER BY c ~> 3 DESC LIMIT 5; + + + + + 展示了可用函数。 + + + + cube 函数 + + + + 函数 + 结果 + + 描述 + + 示例 + + + + + + cube(float8) + cube + + 创建一个一维立方体,其两个坐标相同。 + + + cube(1) == '(1)' + + + + + cube(float8, float8) + cube + + 创建一个一维立方体。 + + + cube(1,2) == '(1),(2)' + + + + + cube(float8[]) + cube + + 根据数组定义的坐标创建一个零体积立方体。 + + + cube(ARRAY[1,2]) == '(1,2)' + + + + + cube(float8[], float8[]) + cube + + 创建一个立方体,其右上角和左下角坐标由这两个数组定义,这两个数组必须等长。 + + + cube(ARRAY[1,2], ARRAY[3,4]) == '(1,2),(3,4)' + + + + + + cube(cube, float8) + cube + + 通过向现有立方体增加一个维度来创建新的立方体,新坐标的两个端点取相同的值。 + 这可用于根据计算得到的值逐步构建立方体。 + + + cube('(1,2),(3,4)'::cube, 5) == '(1,2,5),(3,4,5)' + + + + + cube(cube, float8, float8) + cube + + 通过向现有立方体增加一个维度来创建新的立方体。这可用于根据计算得到的值逐步构建立方体。 + + + cube('(1,2),(3,4)'::cube, 5, 6) == '(1,2,5),(3,4,6)' + + + + + cube_dim(cube) + integer + + 返回立方体的维数。 + + + cube_dim('(1,2),(3,4)') == '2' + + + + + cube_ll_coord(cube, integer) + float8 + 返回立方体左下角的第 n 个坐标值。 + + cube_ll_coord('(1,2),(3,4)', 2) == '2' + + + + + cube_ur_coord(cube, integer) + float8 + 返回立方体右上角的第 n 个坐标值。 + + cube_ur_coord('(1,2),(3,4)', 2) == '4' + + + + + cube_is_point(cube) + boolean + + 如果立方体是一个点,也就是定义它的两个角相同,则返回真。 + + + + + + + cube_distance(cube, cube) + float8 + + 返回两个立方体之间的距离。如果两个立方体都是点,这就是普通的距离函数。 + + + + + + + cube_subset(cube, integer[]) + cube + + 根据数组中给出的维度索引列表,从现有立方体创建一个新立方体。 + 可用于提取单个维度的端点、删除维度,或者按需要重新排列维度。 + + cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[2]) == '(3),(7)' cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[3,2,1,1]) == '(5,3,1,1),(8,7,6,6)' + + + + cube_union(cube, cube) + cube + + 生成两个立方体的并集。 + + + + + + + cube_inter(cube, cube) + cube + + 生成两个立方体的交集。 + + + + + + + cube_enlarge(c cube, r double, n integer) + cube + 将立方体至少 n 个维度的大小增加指定半径 r。如果半径为负,则改为缩小立方体。所有已定义的维度都会按半径 r 改变。左下角坐标减小 r,右上角坐标增大 r。如果某个左下角坐标增大到超过对应的右上角坐标(这只可能在 r < 0 时发生),则将两个坐标都设为它们的平均值。如果 n 大于已定义维度数且立方体正在扩大(r > 0),则添加额外维度,使总数达到 n;额外坐标的初始值使用 0。此函数对于创建点周围的边界框、以搜索附近点很有用。 + cube_enlarge('(1,2),(3,4)', 0.5, 3) == '(0.5,1.5,-0.5),(3.5,4.5,0.5)' + + + +
+
+ + + 默认规则 + + + 下面这个并集: + + +select cube_union('(0,5,2),(2,3,1)', '0'); +cube_union +------------------- +(0, 0, 0),(2, 5, 2) +(1 row) + + + + 并不违背常识,下面这个交集也是如此: + + + +select cube_inter('(0,-1),(1,1)', '(-2),(2)'); +cube_inter +------------- +(0, 0),(1, 0) +(1 row) + + + + 在所有对不同维度立方体执行的二元操作中,都假定维度较低的那个是一个笛卡尔投影, + 也就是说,在字符串表示中省略的坐标位置上补 0。上面的例子等价于: + + + +cube_union('(0,5,2),(2,3,1)','(0,0,0),(0,0,0)'); +cube_inter('(0,-1),(1,1)','(-2,0),(2,0)'); + + + + 下面的包含谓词使用的是点语法,但实际上第二个参数在内部表示为一个 box。 + 这种语法使我们无须单独定义点类型以及用于 (box,point) 谓词的函数。 + + + +select cube_contains('(0,0),(1,1)', '0.5,0.5'); +cube_contains +-------------- +t +(1 row) + + + + + 注意 + + + 有关用法示例,请参见回归测试sql/cube.sql。 + + + + 为了避免用户轻易把事情弄坏,立方体的维数上限被设为 100。 + 如果需要更大的值,可在cubedata.h中修改该限制。 + + + + + 致谢 + + + 原作者:Gene Selkov, Jr. selkovjr@mcs.anl.gov, + 阿贡国家实验室数学与计算机科学部。 + + + + 我首先要感谢 Joe Hellerstein 教授 + (), + 他为我阐明了 GiST + () 的要旨; + 也感谢他曾经的学生 Andy Dong 为 Illustra 编写了示例。 + 我同样感谢过去和现在所有的 Postgres 开发者, + 他们使我得以创造自己的世界并在其中不受打扰地生活。 + 我还要感谢阿贡实验室以及美国能源部,多年来始终如一地支持我的数据库研究。 + + + + Bruno Wolff III bruno@wolff.to 在 2002 年 8 月和 9 月 + 对该软件包做了一些小更新,包括将精度从单精度改为双精度,并增加了一些新函数。 + + + + Joshua Reich josh@root.net 在 2006 年 7 月做了进一步更新。 + 这些更新包括加入cube(float8[], float8[]), + 并清理代码,使其使用 V1 调用协议而不是已废弃的 V0 协议。 + + + +
diff --git a/zh/9.6/custom-scan.sgml b/zh/9.6/custom-scan.sgml new file mode 100644 index 00000000..083a5a25 --- /dev/null +++ b/zh/9.6/custom-scan.sgml @@ -0,0 +1,225 @@ + + + + 编写自定义扫描提供者 + + + 自定义扫描提供者 + 处理器 + + + + PostgreSQL 支持一组实验性功能,旨在允许扩展模块向系统中添加新的扫描类型。与外部数据包装器不同,后者只负责了解如何扫描其自身的外部表,而自定义扫描提供者则可以为系统中的任何关系提供一种替代扫描方法。通常,编写自定义扫描提供者的动机,是为了能够使用某些核心系统尚不支持的优化,例如缓存或某种形式的硬件加速。本章概述如何编写新的自定义扫描提供者。 + + + + 实现一种新的自定义扫描类型分为三个步骤。首先,在规划期间,需要生成表示使用所提策略进行扫描的访问路径。其次,如果规划器从这些访问路径中选出一条,作为扫描某个特定关系的最优策略,就必须将该访问路径转换为计划。最后,还必须能够执行该计划,并生成与针对同一关系的其他访问路径原本会生成的结果相同的结果。 + + + + 创建自定义扫描路径 + + 自定义扫描提供者通常通过设置下面的钩子,为基表添加路径。核心代码为该关系生成所有可生成的访问路径之后,会调用此钩子(Gather 路径除外,它们会在本次调用之后生成,以便使用钩子添加的部分路径): +typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root, + RelOptInfo *rel, + Index rti, + RangeTblEntry *rte); +extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook; + + + + 虽然此钩子函数可用于检查、修改或删除核心系统生成的路径,但自定义扫描提供者通常只生成CustomPath对象,并将它们添加到rel,所用函数是add_path。自定义扫描提供者负责初始化CustomPath对象,其声明如下: +typedef struct CustomPath +{ + Path path; + uint32 flags; + List *custom_paths; + List *custom_private; + const CustomPathMethods *methods; +} CustomPath; + + + + + path 必须像其他任何路径一样完成初始化,包括行数估计、启动代价和总代价,以及该路径提供的排序顺序。flags 是一个位掩码,如果自定义路径支持反向扫描,就应包含 CUSTOMPATH_SUPPORT_BACKWARD_SCAN;如果支持标记和恢复,就应包含 CUSTOMPATH_SUPPORT_MARK_RESTORE。这两种能力都是可选的。可选字段 custom_paths 是该自定义路径节点所使用的 Path 节点列表;规划器会把它们转换成 Plan 节点。custom_private 可用于存储自定义路径的私有数据。私有数据应以 nodeToString 能够处理的形式存储,这样尝试打印该自定义路径的调试例程才能按预期工作。methods 必须指向一个实现了所需自定义路径方法的对象(通常是静态分配的),目前只有一种此类方法。 + + + + 自定义扫描提供者也可以提供连接路径。与基本关系一样,这种路径也必须产生与它所替代连接通常会产生的相同输出。为此,连接提供者应设置下列钩子,并在该钩子函数内部为连接关系创建 CustomPath 路径。 + +typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); +extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; + + + 对于同一个连接关系,这个钩子会针对不同的内外关系组合重复调用;如何尽量减少重复工作,是该钩子的职责。 + + + + 自定义扫描路径回调 + + + +Plan *(*PlanCustomPath) (PlannerInfo *root, + RelOptInfo *rel, + CustomPath *best_path, + List *tlist, + List *clauses, + List *custom_plans); + + 将自定义路径转换为完成的计划。返回值通常是一个 CustomScan 对象,回调函数必须负责分配并初始化该对象。详见 。 + + + + + + 创建自定义扫描计划 + + + 在完成的计划树中,自定义扫描使用下述结构体表示: + +typedef struct CustomScan +{ + Scan scan; + uint32 flags; + List *custom_plans; + List *custom_exprs; + List *custom_private; + List *custom_scan_tlist; + Bitmapset *custom_relids; + const CustomScanMethods *methods; +} CustomScan; + + + + + scan 必须像其他任何扫描一样完成初始化,包括估计代价、目标列表、限定条件等。flags 是一个位掩码,其含义与 CustomPath 中相同。custom_plans 可用于存储子 Plan 节点。custom_exprs 应当用于存储那些需要由 setrefs.csubselect.c 修正的表达式树,而 custom_private 则应用于存放仅供自定义扫描提供者自身使用的其他私有数据。扫描基本关系时,custom_scan_tlist 可以为 NIL,表示该自定义扫描返回的扫描元组与该基本关系的行类型一致。否则,它就是描述实际扫描元组的目标列表。对于连接,必须提供 custom_scan_tlist;如果自定义扫描提供者能够计算某些非 Var 表达式,则在扫描场景下也可以提供该字段。custom_relids 由核心代码设置为该扫描节点所处理关系(范围表索引)的集合;除非该扫描是在替代一个连接,否则它将只有一个成员。methods 必须指向一个实现了所需自定义扫描方法的对象(通常是静态分配的),下文会进一步说明。 + + + + 当 CustomScan 扫描单个关系时,scan.scanrelid 必须是待扫描表的范围表索引。当它替代的是一个连接时,scan.scanrelid 应为零。 + + + + 计划树必须能够通过 copyObject 复制,因此存放在 custom 字段中的所有数据都必须由该函数能够处理的节点组成。此外,自定义扫描提供者不能像对 CustomPathCustomScanState 那样,用一个包含 CustomScan 的更大结构体替代该结构体本身。 + + + + 自定义扫描计划回调 + + +Node *(*CreateCustomScanState) (CustomScan *cscan); + + 为该 CustomScan 分配一个 CustomScanState。实际分配的空间往往会比普通 CustomScanState 所需的更大,因为很多提供者都希望把它作为第一个字段嵌入到一个更大的结构体中。返回的值必须适当地设置节点标签和 methods,但在这个阶段,其他字段应当保留为零;待 ExecInitCustomScan 完成基本初始化之后,将调用 BeginCustomScan 回调,让自定义扫描提供者有机会执行其他所需操作。 + + + + + + 执行自定义扫描 + + + 在执行 CustomScan 时,其执行状态由 CustomScanState 表示,其声明如下: + +typedef struct CustomScanState +{ + ScanState ss; + uint32 flags; + const CustomExecMethods *methods; +} CustomScanState; + + + + + ss 的初始化方式与其他任何扫描状态相同,不过如果该扫描针对的是连接而不是基本关系,那么 ss.ss_currentRelation 会保留为 NULL。flags 是一个位掩码,其含义与 CustomPathCustomScan 中相同。methods 必须指向一个实现了所需自定义扫描状态方法的对象(通常是静态分配的),下文会进一步说明。通常,CustomScanState 实际上会是一个更大的结构体,并把上述结构体嵌为其第一个成员;它不需要支持 copyObject。 + + + + 自定义扫描执行回调 + + + +void (*BeginCustomScan) (CustomScanState *node, + EState *estate, + int eflags); + + 完成所提供 CustomScanState 的初始化。标准字段已经由 ExecInitCustomScan 初始化,但任何私有字段都应在这里初始化。 + + + + +TupleTableSlot *(*ExecCustomScan) (CustomScanState *node); + + 取出下一个扫描元组。如果仍有元组剩余,它应当使用当前扫描方向中的下一个元组填充 ps_ResultTupleSlot,然后返回该元组槽。否则,应返回 NULL 或一个空槽。 + + + + +void (*EndCustomScan) (CustomScanState *node); + + 清理与 CustomScanState 关联的任何私有数据。这个方法是必需的,但如果没有相关数据,或者相关数据会被自动清理,则它不需要做任何事情。 + + + + +void (*ReScanCustomScan) (CustomScanState *node); + + 将当前扫描倒回到开始处,并准备重新扫描该关系。 + + + + +void (*MarkPosCustomScan) (CustomScanState *node); + + 保存当前扫描位置,以便之后可由 RestrPosCustomScan 回调恢复。这个回调是可选的,只需在设置了 CUSTOMPATH_SUPPORT_MARK_RESTORE 标志时提供。 + + + + +void (*RestrPosCustomScan) (CustomScanState *node); + + 恢复由 MarkPosCustomScan 回调保存的先前扫描位置。这个回调是可选的,只需在设置了 CUSTOMPATH_SUPPORT_MARK_RESTORE 标志时提供。 + + + + +Size (*EstimateDSMCustomScan) (CustomScanState *node, + ParallelContext *pcxt); + + 估算并行操作所需的动态共享内存数量。这可能会高于实际使用量,但绝不能低于实际使用量。返回值的单位是字节。这个回调是可选的,只需在该自定义扫描提供者支持并行执行时提供。 + + + + +void (*InitializeDSMCustomScan) (CustomScanState *node, + ParallelContext *pcxt, + void *coordinate); + + 初始化并行操作所需的动态共享内存;coordinate 指向一块已分配的空间,其大小等于 EstimateDSMCustomScan 的返回值。这个回调是可选的,只需在该自定义扫描提供者支持并行执行时提供。 + + + + +void (*InitializeWorkerCustomScan) (CustomScanState *node, + shm_toc *toc, + void *coordinate); + + 基于领导者通过 InitializeDSMCustomScan 建立的共享状态,初始化并行工作者的自定义状态。这个回调是可选的,只需在该自定义扫描提供者支持并行执行时提供。 + + + + + +void (*ExplainCustomScan) (CustomScanState *node, + List *ancestors, + ExplainState *es); + + 为自定义扫描计划节点的 EXPLAIN 输出附加信息。这个回调是可选的。即使没有这个回调,存储在 ScanState 中的公共数据,例如目标列表和扫描关系,也会被显示;但该回调允许显示额外的私有状态。 + + + + diff --git a/zh/9.6/datatype.sgml b/zh/9.6/datatype.sgml new file mode 100644 index 00000000..b99127f5 --- /dev/null +++ b/zh/9.6/datatype.sgml @@ -0,0 +1,4183 @@ + + + + 数据类型 + + + 数据类型 + + + + 类型 + 数据类型 + + + + PostgreSQL 提供了丰富的原生数据类型。 + 用户可以使用命令向 + PostgreSQL添加新类型。 + + + + 展示了所有内置的通用数据类型。 + 别名列中列出的多数可选名称,都是 + PostgreSQL出于历史原因在内部使用的名称。 + 此外,还有一些内部使用或已废弃的类型也可用,但未在此列出。 + + + + 数据类型 + + + + 名字 + 别名 + 描述 + + + + + + bigint + int8 + 有符号的8字节整数 + + + + bigserial + serial8 + 自动递增的8字节整数 + + + + bit [ (n) ] + + 定长位串 + + + + bit varying [ (n) ] + varbit [ (n) ] + 变长位串 + + + + boolean + bool + 逻辑布尔值(真/假) + + + + box + + 平面上的矩形框 + + + + bytea + + 二进制数据(字节数组 + + + + character [ (n) ] + char [ (n) ] + 定长字符串 + + + + character varying [ (n) ] + varchar [ (n) ] + 变长字符串 + + + + cidr + + IPv4或IPv6网络地址 + + + + circle + + 平面上的圆 + + + + date + + 日历日期(年、月、日) + + + + double precision + float8 + 双精度浮点数(8 字节) + + + + inet + + IPv4或IPv6主机地址 + + + + integer + int, int4 + 有符号4字节整数 + + + + interval [ fields ] [ (p) ] + + 时间段 + + + + json + + 文本 JSON 数据 + + + + jsonb + + 二进制 JSON 数据,已分解 + + + + line + + 平面上的无限长的线 + + + + lseg + + 平面上的线段 + + + + macaddr + + MAC(Media Access Control)地址 + + + + money + + 货币数量 + + + + numeric [ (p, + s) ] + decimal [ (p, + s) ] + 可选择精度的精确数字 + + + + path + + 平面上的几何路径 + + + + pg_lsn + + PostgreSQL日志序列号类型 + + + + point + + 平面上的几何点 + + + + polygon + + 平面上的封闭几何路径 + + + + real + float4 + 单精度浮点数(4字节) + + + + smallint + int2 + 有符号2字节整数 + + + + smallserial + serial2 + 自动递增的2字节整数 + + + + serial + serial4 + 自动递增的4字节整数 + + + + text + + 变长字符串 + + + + time [ (p) ] [ without time zone ] + + 一天中的时间(无时区) + + + + time [ (p) ] with time zone + timetz + 一天中的时间,包括时区 + + + + timestamp [ (p) ] [ without time zone ] + + 日期和时间(无时区) + + + + timestamp [ (p) ] with time zone + timestamptz + 日期和时间,包括时区 + + + + tsquery + + 文本搜索查询 + + + + tsvector + + 文本搜索文档 + + + + txid_snapshot + + 用户级事务 ID 快照 + + + + uuid + + 通用唯一标识符 + + + + xml + + XML 数据 + + + +
+ + + 兼容性 + + 下列类型(或它们的这些拼写形式)由SQL规定:bigintbitbit varyingbooleancharcharacter varyingcharactervarchardatedouble precisionintegerintervalnumericdecimalrealsmallinttime(有时区或无时区)、timestamp(有时区或无时区)、xml。 + + + + + 每种数据类型都有一种由其输入和输出函数决定的外部表示。 + 许多内置类型的外部格式都很直观。不过,也有一些类型是 + PostgreSQL所特有的,例如几何路径; + 还有一些类型可能存在多种可能的格式,例如日期/时间类型。 + 有些输入和输出函数并不可逆,也就是说,输出函数的结果与原始 + 输入相比可能会丢失精度。 + + + + 数字类型 + + + 数据类型 + 数字 + + + + 数字类型包括 2、4、8 字节的整数,4、8 字节的浮点数, + 以及可选精度的小数。 + 列出了所有可用类型。 + + + + 数字类型 + + + + 名字 + 存储尺寸 + 描述 + 范围 + + + + + + smallint + 2字节 + 小范围整数 + -32768 到 +32767 + + + integer + 4字节 + 整数的典型选择 + -2147483648 到 +2147483647 + + + bigint + 8字节 + 大范围整数 + -9223372036854775808 到 +9223372036854775807 + + + + decimal + 可变 + 用户指定精度,精确 + 最高小数点前131072位,以及小数点后16383位 + + + numeric + 可变 + 用户指定精度,精确 + 最高小数点前131072位,以及小数点后16383位 + + + + real + 4字节 + 可变精度,不精确 + 6位十进制精度 + + + double precision + 8字节 + 可变精度,不精确 + 15位十进制精度 + + + + smallserial + 2字节 + 自动递增的小整数 + 1到32767 + + + + serial + 4字节 + 自动递增的整数 + 1到2147483647 + + + + bigserial + 8字节 + 自动递增的大整数 + 1到9223372036854775807 + + + +
+ + + 数字类型常量的语法在里描述。数字类型有一整套对应的数学操作符和函数。相关信息请参考 。下面的几节详细描述这些类型。 + + + + 整数类型 + + + 整数 + + + + smallint + + + + bigint + + + + int4 + 整数 + + + + int2 + smallint + + + + int8 + bigint + + + + 类型 smallintinteger 和 + bigint 用于存储不同范围的整数,也就是没有小数部分的数。 + 试图存储超出允许范围的值会导致错误。 + + + + 常用的类型是integer,因为它提供了在范围、存储空间和性能之间的最佳平衡。一般只有在磁盘空间紧张的时候才使用 smallint类型。而只有在integer的范围不够的时候才使用bigint。 + + + + SQL只声明了整数类型integer(或int)、smallintbigint。类型int2int4int8都是扩展,也在许多其它SQL数据库系统中使用。 + + + + + + 任意精度数值 + + + 数字(数据类型) + + + + 任意精度数字 + + + + decimal + numeric + + + + 类型numeric可以存储非常多位的数字。我们特别建议将它用于货币金额和其它要求计算准确的数量。numeric值的计算在可能的情况下会得到准确的结果,例如加法、减法、乘法。不过,numeric类型上的算术运算比整数类型或者下一节描述的浮点数类型要慢很多。 + + + 下面使用如下术语:numeric精度是整个数中有效数字的总数,即小数点两侧的数字位数之和。numeric小数位数是小数部分中十进制数字的数量,即小数点右侧的位数。因此,数值 23.5141 的精度为 6,小数位数为 4。整数可以视为小数位数为零。 + + 可以配置numeric列的最大精度和最大小数位数。要声明numeric类型的列,使用以下语法: +NUMERIC(precision, scale) +精度必须为正数,小数位数必须为零或正数。也可以使用: +NUMERIC(precision) +这会选择小数位数为 0。指定: +NUMERIC +而不指定任何精度或小数位数,会创建一个可存储任意精度和小数位数数值的列,直到达到实现对精度的限制。这种列不会把输入值强制转换为某个特定的小数位数,而声明了小数位数的numeric列会将输入值强制转换为该小数位数。(SQL标准要求默认小数位数为 0,即强制转换为整数精度。我们认为这没有多少用处。如果你关心可移植性,请始终显式指定精度和小数位数。) + + + 在类型声明中显式指定精度时,所允许的最大值为 1000;未指定精度的NUMERIC中所述限制的约束。 + + + + 如果要存储的值的小数位数大于该列声明的小数位数,系统会把该值 + 舍入到指定的小数位数。然后,如果小数点左侧的位数超过了声明的 + 精度减去声明的小数位数,就会报错。 + + + 数值在物理上存储时不会保留多余的前导零或尾随零。因此,列上 + 声明的精度和小数位数只是最大值,而不是固定分配的空间 + (从这个意义上说,numeric更像 + varchar(n),而不像 + char(n))。实际存储需求 + 是每四个十进制数字组占两个字节,再加上 3 到 8 字节的开销。 + + + + NaN + 非数字 + + + + 非数字 + 数字(数据类型) + + + 除了普通数值外,numeric类型还允许特殊值NaN,表示非数。对NaN的任何运算都会产生另一个NaN。在 SQL 命令中将该值写为常量时,必须加上引号,例如UPDATE table SET x = 'NaN'。输入时,字符串NaN的识别不区分大小写。 + + + + 在大多数非数字概念的实现中,NaN + 都被认为不等于任何其他数值(包括 NaN 本身)。 + 为了让 numeric 值能够排序并用于基于树的索引, + PostgreSQLNaN + 值视为彼此相等,并且大于所有非 NaN 值。 + + + + + 类型decimalnumeric是等效的。两种类型都是SQL标准的一部分。 + + + + 进行舍入时,numeric类型在遇到恰好处于中间的值时, + 会朝远离零的方向舍入;而(在大多数机器上) + realdouble precision + 类型会把这类值舍入到最近的偶数。例如: + + +SELECT x, + round(x::numeric) AS num_round, + round(x::double precision) AS dbl_round +FROM generate_series(-3.5, 3.5, 1) as x; + x | num_round | dbl_round +------+-----------+----------- + -3.5 | -4 | -4 + -2.5 | -3 | -2 + -1.5 | -2 | -2 + -0.5 | -1 | -0 + 0.5 | 1 | 0 + 1.5 | 2 | 2 + 2.5 | 3 | 2 + 3.5 | 4 | 4 +(8 rows) + + + + + + + 浮点类型 + + + real + + + + 双精度 + + + + float4 + real + + + + float8 + 双精度 + + + + 浮点 + + + 数据类型realdouble precision是不精确的、变精度的数字类型。实际上,在底层处理器、操作系统和编译器支持的范围内,这两种类型通常实现了IEEE 754 二进制浮点算术标准(分别对应单精度和双精度)。 + + + 所谓不精确,是指某些值无法被精确转换为内部格式,只能以近似值 + 存储,因此存储并再取出一个值时,可能会看到轻微差异。如何处理 + 这类误差以及它们在计算中如何传播,是数学和计算机科学中的一个 + 独立领域,这里不再展开,只强调以下几点: + + + + 如果你要求准确的存储和计算(例如计算货币金额),应使用numeric类型。 + + + + + + 如果你想用这些类型进行任何重要的复杂计算,尤其是依赖边界情形 + (无穷大、下溢)特定行为的计算,那么应该仔细评估其实现。 + + + + + + 比较两个浮点值是否相等,未必总能得到符合预期的结果。 + + + + + + 在大多数平台上,real类型的范围至少为 1E-37 到 1E+37,精度至少为 6 位十进制数字。double precision类型的范围通常约为 1E-307 到 1E+308,精度至少为 15 位数字。过大或过小的值会导致错误。如果输入数字的精度过高,可能会发生舍入。过于接近零且无法表示为与零不同的数值,会导致下溢错误。 + + + 设置控制浮点值转换为文本输出时所包含的额外有效数字位数。使用默认值0时,输出在 PostgreSQL 支持的每个平台上都相同。增大该值会使输出更精确地表示所存储的值,但可能不具有可移植性。 + + + + 非数字 + double precision + + + 除了普通数值外,浮点类型还有几个特殊值: +Infinity +-Infinity +NaN +它们分别表示 IEEE 754 的特殊值无穷大负无穷大非数字。(在浮点运算不遵循 IEEE 754 的机器上,这些值可能无法按预期工作。)在 SQL 命令中将这些值写为常量时,必须加上引号,例如UPDATE table SET x = 'Infinity'。输入时,这些字符串的识别不区分大小写。 + + + + IEEE 754 规定,NaN 不应与任何其他浮点值 + (包括NaN)相等。为了允许浮点值被排序, + 并可用于基于树的索引,PostgreSQL + 将NaN视为彼此相等,并且大于所有非 + NaN值。 + + + + + PostgreSQL 也支持 SQL 标准记法 + float 和 + float(p) 来指定不精确数值 + 类型。这里,p 指定可接受的最小 + 二进制精度位数。 + PostgreSQL 将 + float(1)float(24) 视为选择 + real 类型,而 float(25) 到 + float(53) 则选择 double precision。 + p 超出允许范围会报错。未指定精度的 + float 视为 double precision。 + + + + 假定realdouble precision的尾数分别恰好有 24 位和 53 位,对于 IEEE 标准浮点实现而言是正确的。在非 IEEE 平台上可能会有少许偏差,但为简单起见,所有平台都使用相同的p取值范围。 + + + + + + serial 类型 + + + smallserial + + + + serial + + + + bigserial + + + + serial2 + + + + serial4 + + + + serial8 + + + + auto-increment + serial + + + + sequence + and serial type + + + + smallserialserial 和 + bigserial 并不是真正的数据类型,它们只是为了创建 + 唯一标识符列而提供的记法便利(类似于其他一些数据库支持的 + AUTO_INCREMENT 属性)。在当前实现中,下列语句: + + +CREATE TABLE tablename ( + colname SERIAL +); + + + 等价于以下语句: + + +CREATE SEQUENCE tablename_colname_seq; +CREATE TABLE tablename ( + colname integer NOT NULL DEFAULT nextval('tablename_colname_seq') +); +ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname; + + + 这样就创建了一个整数列,并把它的默认值设置为从序列生成器中取值。 + 同时还会加上NOT NULL约束,以确保不能插入空值。 + (在大多数情况下,你可能还会希望再加上UNIQUE + 或PRIMARY KEY约束,以防意外插入重复值,但这 + 不会自动发生。)最后,该序列会被标记为属于该列, + 这样当列或表被删除时,序列也会随之删除。 + + + + + 因为 smallserialserial 和 + bigserial 是用序列实现的,所以即使没有删除任何 + 行,列中出现的值序列也可能存在空洞或缺口。 + 即使包含该值的行从未成功插入表中,从序列分配出去的值仍会被 + 视为已使用。例如,如果插入事务回滚,就会 + 发生这种情况。详细信息见 + 中的 nextval()。 + + + + + 要向 serial 列插入序列中的下一个值,应指定让serial列 + 使用其默认值。这既可以通过在 INSERT 语句的 + 列表中省略该列来实现,也可以通过使用 DEFAULT + 关键字来实现。 + + + + 类型名 serialserial4 是等价的: + 二者都会创建 integer 列。类型名 + bigserialserial8 的工作方式相同, + 只是它们创建的是 bigint 列。如果预计表在其生命 + 周期内会使用超过 231 个标识符, + 就应使用 bigserial。类型名 + smallserialserial2 也同理, + 只是它们创建的是 smallint 列。 + + + + 为 serial 列创建的序列会在其所属列被删除时自动删除。 + 你也可以在不删除该列的情况下删除该序列,但这会强制移除该列的默认值 + 表达式。 + + +
+ + + 货币类型 + + + money 类型以固定的小数精度存储货币金额; + 参见 。小数精度由数据库的 + 设置决定。表中显示的范围假定 + 有两位小数。输入支持多种格式,包括整数字面量和浮点数字面量,以及典型的 + 货币格式,例如 '$1,000.00'。输出通常也采用 + 后一种形式,但会受到区域设置影响。 + + + + 货币类型 + + + + 名字 + 存储尺寸 + 描述 + 范围 + + + + + money + 8 字节 + 货币额 + -92233720368547758.08 到 +92233720368547758.07 + + + +
+ + + 由于该数据类型的输出依赖区域设置,因此在 + lc_monetary 设置不同的数据库之间装入 + money 数据时,可能无法正常工作。为了避免这类问题, + 在将转储恢复到新数据库之前,应确保新数据库的 + lc_monetary 设置与原数据库相同或等效。 + + + + 数据类型numericintbigint的值可以转换为money。从数据类型realdouble precision的转换可以通过先转换为numeric来实现,例如: + +SELECT '12.34'::float8::numeric::money; + + 但是,我们不推荐这样做。浮点数不应该被用来处理货币,因为浮点数可能会有圆整错误。 + + + + 一个money值可以在不损失精度的情况下转换为numeric。转换到其他类型可能会丢失精度,并且必须采用两个阶段完成: + +SELECT '52093.89'::money::numeric::float8; + + + + + 一个 money 值除以一个整数值时,会朝零方向截去小数 + 部分。要得到圆整结果,可以除以一个浮点值,或者在除法前先把 + money 转换为 numeric,再在除法后转换回 + money(如果要避免精度丢失风险,后一种做法更好)。 + 当一个 money 值被另一个 money 值除时, + 结果是 double precision(即一个纯数字,而不是金额), + 因为在除法中货币单位被约掉了。 + +
+ + + + 字符类型 + + + 字符串 + 数据类型 + + + + string + 字符串 + + + + 字符 + + + + character varying + + + + text + + + + char + + + + varchar + + + + 字符类型 + + + + 名字 + 描述 + + + + + character varying(n), varchar(n) + 有长度限制的变长 + + + character(n), char(n) + 定长,空白填充 + + + text + 无限长度的变长 + + + +
+ + + 显示了在PostgreSQL里可用的一般用途的字符类型。 + + + + SQL 定义了两种主要字符类型: + character varying(n) 和 + character(n),其中 + n 是正整数。这两种类型都可以存储长度 + 最多为 n 个字符(而不是字节)的字符串。 + 试图把更长的字符串存入这些类型的列时会报错,除非超出的字符全部 + 都是空格,在这种情况下字符串会被截断到最大长度。(这一多少有些 + 奇怪的例外是 SQL 标准要求的。) + 如果要存储的字符串短于声明长度,character 类型的值 + 会用空格填充;character varying 类型则只是简单地 + 存储较短的字符串。 + + + + 如果显式把一个值转换为 + character varying(n) 或 + character(n),那么超长的值会 + 被截断为 n 个字符而不会报错。 + (这同样是 SQL 标准要求的。) + + + + varchar(n) 和 + char(n) 分别是 + character varying(n) 和 + character(n) 的别名。 + character 若不带长度说明则等同于 + character(1)。如果 character varying + 不带长度说明,则该类型接受任意长度的字符串。这是 + PostgreSQL 的扩展。 + + + + 此外,PostgreSQL 还提供 + text 类型,用于存储任意长度的字符串。虽然 + text 类型不在 SQL 标准中, + 但其他若干 SQL 数据库管理系统也提供了它。 + + + + character 类型的值在物理存储时会在右侧用空格填充到 + 指定宽度 n,并且也会按这种形式显示。 + 但是,在比较两个 character 值时,尾随空格在语义上 + 被视为不重要并会被忽略。在空白字符有区分意义的排序规则中,这种 + 行为可能产生意外结果;例如, + SELECT 'a '::CHAR(2) collate "C" < + E'a\n'::CHAR(2) 会返回真,即使 + C 区域设置认为空格大于换行符。把 + character 值转换为其他字符串类型之一时,尾随空格会 + 被移除。请注意,在 character varying 和 + text 值中,以及进行模式匹配(即 + LIKE 和正则表达式)时,尾随空格 + 在语义上是有意义的。 + + + + 可以存储在这些数据类型中的字符由数据库字符集确定,该数据库字符集在创建数据库时选择。无论特定的字符集是什么,都无法存储代码为零的字符(有时称为NUL)。有关更多信息,请参阅。 + + + + 短字符串(最长 126 字节)的存储需求是 1 个字节,再加上实际 + 字符串本身;对 character 而言,这其中还包括填充的 + 空格。更长的字符串则需要 4 个字节的额外开销,而不是 1 个字节。 + 长字符串会被系统自动压缩,因此在磁盘上的实际占用可能更小。 + 非常长的值还会被存储在后台表中,以免影响对较短列值的快速访问。 + 无论如何,可存储的最长字符串大约为 1 GB。(数据类型声明中 + n 允许的最大值比这还小。更改这个限制 + 并没有意义,因为在多字节字符编码下,字符数和字节数可能差异很大。 + 如果你想存储没有明确上限的长字符串,应使用 text + 或未指定长度的 character varying,而不是随意给出 + 一个长度上限。) + + + + + 这三种类型之间没有性能差别,除了使用空白填充类型时会占用更多存储 + 空间,以及在写入带长度约束的列时需要少量额外 CPU 周期来检查长度。 + 虽然在某些其他数据库系统中,character(n) + 可能有一定性能优势,但在 PostgreSQL + 中并不存在这种优势。事实上,由于额外的存储开销, + character(n) 通常反而是三者中最慢的。 + 大多数情况下,应优先使用 text 或 + character varying。 + + + + + 关于字符串常量的语法,请参见 + ;关于可用的操作符和函数, + 请参见 。 + + + + 使用字符类型 + + +CREATE TABLE test1 (a character(4)); +INSERT INTO test1 VALUES ('ok'); +SELECT a, char_length(a) FROM test1; -- + + a | char_length +------+------------- + ok | 2 + + +CREATE TABLE test2 (b varchar(5)); +INSERT INTO test2 VALUES ('ok'); +INSERT INTO test2 VALUES ('good '); +INSERT INTO test2 VALUES ('too long'); +ERROR: value too long for type character varying(5) +INSERT INTO test2 VALUES ('too long'::varchar(5)); -- explicit truncation +SELECT b, char_length(b) FROM test2; + + b | char_length +-------+------------- + ok | 2 + good | 5 + too l | 5 + + + + + + 函数char_length中讨论。 + + + + + + + PostgreSQL 中还有两种固定长度字符类型, + 如 所示。name + 类型用于在内部系统目录中存储标识符,并非供一般用户使用。 + 它的长度目前定义为 64 字节(63 个可用字符加结束符),但在 C + 源代码中应使用常量 NAMEDATALEN 来引用。这个长度是在 + 编译时设定的(因此可以针对特殊用途调整);默认最大长度在未来版本中可能会变化。 + 类型 "char"(注意带引号)不同于 char(1),因为它只使用 + 1 个字节存储。它在系统目录中被用作一种简单的枚举类型。 + + + + 特殊字符类型 + + + + 名字 + 存储尺寸 + 描述 + + + + + "char" + 1字节 + 单字节内部类型 + + + name + 64字节 + 用于对象名的内部类型 + + + +
+ +
+ + + 二进制数据类型 + + + 二进制数据 + + + + bytea + + + + bytea数据类型允许存储二进制串,参见。 + + + + 二进制数据类型 + + + + 名字 + 存储尺寸 + 描述 + + + + + bytea + 1或4字节外加真正的二进制串 + 变长二进制串 + + + +
+ + + 二进制串是八位组(或字节)的序列。二进制串与字符串有两点区别。 + 首先,二进制串明确允许存储值为零的字节以及其他不可打印 + 的字节(通常指十进制范围 32 到 126 之外的字节)。而字符串不允许 + 零字节,也不允许那些按数据库所选字符集编码看属于非法的其他字节值 + 或字节序列。其次,对二进制串的操作处理的是实际字节,而字符串的处理 + 则取决于区域设置。简单地说,二进制串适合存储程序员视为裸字节 + 的数据,而字符串适合存储文本。 + + + + bytea类型支持两种用于输入和输出的格式:十六进制格式和PostgreSQL的历史的转义格式。在输入时这两种格式总是会被接受。输出格式则取决于配置参数,其默认值为十六进制(注意十六进制格式是在PostgreSQL 9.0中被引入的,早期的版本和某些工具无法理解它)。 + + + + SQL标准定义了一种不同的二进制串类型, 叫做BLOB或者BINARY LARGE OBJECT。其输入格式和bytea不同,但是提供的函数和操作符大多一样。 + + + + <type>bytea</type>的十六进制格式 + + + 十六进制格式把二进制数据编码为每字节两个十六进制 + 数字,最高有效半字节在前。整个串以前缀 \x + 开头(以便与转义格式区分)。在某些上下文中,这个开头的反斜线 + 可能需要通过双写进行转义(见 + )。作为输入时,十六进制数字 + 可以使用大写或小写,并且在两个数字组成的一组之间允许出现空白 + (但组内以及起始的 \x 序列中不能有空白)。 + 十六进制格式与大量外部应用和协议兼容,并且通常比转义格式转换得 + 更快,因此更推荐使用。 + + + 例如: +SELECT '\xDEADBEEF'; + + + + + + <type>bytea</type>的转义格式 + + + 转义格式是 bytea 类型在 + PostgreSQL 中的传统格式。它采用把 + 二进制串表示为 ASCII 字符序列的方式,同时把那些不能表示为 + ASCII 字符的字节转换为特殊的转义序列。如果从应用角度看,把字节 + 当作字符表示是合理的,那么这种表示法会比较方便。但在实际中它 + 往往让人困惑,因为它模糊了二进制串和字符串之间的区别,而且所选 + 用的转义机制也比较笨拙。因此,对大多数新应用来说,最好避免使用 + 这种格式。 + + + + 在转义格式中输入 bytea 值时,某些字节值 + 必须转义,而所有字节值都 + 可以转义。通常,转义一个字节的方法是把它写成 + 三位八进制值,并在前面加一个反斜线。反斜线本身 + (十进制字节值 92)也可以写成双反斜线。 + 展示了必须转义的字符,并给出 + 了可用的替代转义序列。 + + + + <type>bytea</type>字面量中需要转义的字节 + + + + 十进制字节值 + 描述 + 转义输入表示 + 示例 + 十六进制表示 + + + + + + 0 + 0字节 + '\000' + SELECT '\000'::bytea; + \x00 + + + + 39 + 单引号 + '''''\047' + SELECT ''''::bytea; + \x27 + + + + 92 + 反斜线 + '\\''\134' + SELECT '\\'::bytea; + \x5c + + + + 0到31和127到255 + 不可打印的字节 + '\xxx'(八进制值) + SELECT '\001'::bytea; + \x01 + + + + +
+ + + 是否必须转义这些不可打印字节,会因区域设置 + 不同而有所差异。在某些情况下,你可以不转义它们。 + + + + 如 所示,单引号必须成对 + 写出的原因,在于这对 SQL 命令中的任何字符串常量都成立。通用的 + 字符串常量解析器会去掉最外层单引号,并把任意成对的单引号缩减为 + 一个数据字符。因此,bytea 输入函数实际只会看到 + 一个单引号,并将其视为普通数据字符。不过, + bytea 输入函数会把反斜线视为特殊字符,而 + 中展示的其他行为也是由 + 这个函数实现的。 + + + + 在某些上下文中,反斜线必须比上面显示的再多写一倍,因为通用的 + 字符串常量解析器也会把成对反斜线缩减为一个数据字符; + 参见 。 + + + + Bytea字节默认以hex格式输出。如果把改为escape, + 不可打印字节会被转换为等价的三位八进制值,并在前面加一个反斜线。大多数可打印字节以客户端字符集中相应的标准表示输出,例如: +SET bytea_output = 'escape'; + +SELECT 'abc \153\154\155 \052\251\124'::bytea; + bytea +---------------- + abc klm *\251T +十进制值为 92 的字节(反斜线)在输出中会被双写。详情见。 + + + + <type>bytea</type>输出转义字节 + + + + 十进制字节值 + 描述 + 转义的输出表示 + 示例 + 输出结果 + + + + + + + 92 + 反斜线 + \\ + SELECT '\134'::bytea; + \\ + + + + 0到31和127到255 + 不可打印的字节 + \xxx(八进制值) + SELECT '\001'::bytea; + \001 + + + + 32到126 + 可打印的字节 + 客户端字符集表示 + SELECT '\176'::bytea; + ~ + + + + +
+ + + 取决于你所使用的 PostgreSQL 前端, + 在转义和反转义 bytea 串时可能还需要做额外工作。 + 例如,如果你的接口会自动转换换行和回车,那么你可能还需要对它们 + 进行转义。 + +
+
+ + + + 日期/时间类型 + + + date + + + time + + + 不带时区的时间 + + + 带时区的时间 + + + timestamp + + + timestamptz + + + 带时区的时间戳 + + + 不带时区的时间戳 + + + 间隔 + + + 时间跨度 + + + + PostgreSQL 支持完整的一组 + SQL 日期和时间类型,如 + 所示。这些数据类型上可用的 + 操作见 。日期按格里高利历 + 计算,即使是在该历法启用之前的年份也是如此(更多信息见 + )。 + + + + 日期/时间类型 + + + + 名字 + 存储尺寸 + 描述 + 最小值 + 最大值 + 解析度 + + + + + timestamp [ (p) ] [ without time zone ] + 8字节 + 包括日期和时间(无时区) + 4713 BC + 294276 AD + 1微秒 / 14位 + + + timestamp [ (p) ] with time zone + 8字节 + 包括日期和时间,有时区 + 4713 BC + 294276 AD + 1微秒 / 14位 + + + date + 4字节 + 日期(没有一天中的时间) + 4713 BC + 5874897 AD + 1日 + + + time [ (p) ] [ without time zone ] + 8字节 + 一天中的时间(无日期) + 00:00:00 + 24:00:00 + 1微秒 / 14位 + + + time [ (p) ] with time zone + 12字节 + 仅一天中的时间,带时区 + + 00:00:00+1559 + 24:00:00-1559 + 1微秒 / 14位 + + + interval [ fields ] [ (p) ] + 16字节 + 时间间隔 + -178000000年 + 178000000年 + 1微秒 / 14位 + + + +
+ + + + SQL 要求仅写 timestamp 时,应等效于 + timestamp without time zone,而 + PostgreSQL 也遵循这种行为。 + timestamptz 被接受为 + timestamp with time zone 的简写,这是 + PostgreSQL 的扩展。 + + + + + timetimestampinterval + 都接受一个可选精度值 p,用来指定在秒字段中 + 保留多少位小数。默认情况下,对精度没有显式上界。 + 对于 timestampinterval 类型, + p 的允许范围是 0 到 6。 + + + + + 当 timestamp 值存储为八字节整数(目前的默认方式)时, + 在整个取值范围内都可以得到微秒精度。而当 timestamp + 值改为存储为双精度浮点数(一个已废弃的编译期选项)时,有效精度上界 + 可能小于 6。timestamp 值存储为相对于 2000-01-01 午夜 + 之前或之后的秒数。当 timestamp 值采用浮点数实现时, + 对于距 2000-01-01 几年之内的日期可以达到微秒精度,但对更远的日期 + 精度会下降。注意,使用浮点日期时间可以表示比上表所列更大的 + timestamp 取值范围:从公元前 4713 年直到公元 5874897 年。 + + + + 同一个编译期选项还决定 timeinterval 值 + 存储为浮点数还是八字节整数。在浮点存储的情况下,大的 + interval 值会随着间隔长度的增大而降低精度。 + + + + + 对于 time 类型,采用八字节整数存储时, + p 的允许范围是 0 到 6;采用浮点存储时, + 允许范围是 0 到 10。 + + + + interval 类型还有一个附加选项,可以通过写出下面这些 + 短语之一来限制所存储字段的集合: + +YEAR +MONTH +DAY +HOUR +MINUTE +SECOND +YEAR TO MONTH +DAY TO HOUR +DAY TO MINUTE +DAY TO SECOND +HOUR TO MINUTE +HOUR TO SECOND +MINUTE TO SECOND + + 注意,如果同时指定了 fields 和 + p,那么 + fields 必须包含 + SECOND,因为精度只作用于秒。 + + + + time with time zone 类型由 SQL 标准定义,但其定义具有 + 一些会让人怀疑其实用性的特性。在大多数情况下, + datetime、 + timestamp without time zone 和 + timestamp with time zone 的组合,就足以提供任何应用 + 所需的完整日期/时间功能。 + + + 类型abstimereltime是内部使用的低精度类型。不推荐在应用中使用这些类型;这些内部类型可能在将来的版本中消失。 + + + 日期/时间输入 + + + 日期和时间输入几乎接受任何合理的格式,包括 ISO 8601、 + 与 SQL 兼容的格式、传统 + POSTGRES 格式等。对于某些格式, + 日期输入中日、月、年的顺序可能存在歧义,因此支持指定这些字段的 + 预期顺序。将 参数设置为 + MDY,表示采用月-日-年解释; + 设置为 DMY 表示采用日-月-年解释; + 而 YMD 表示采用年-月-日解释。 + + + + PostgreSQL 在处理日期/时间输入方面, + 比 SQL 标准要求得更灵活。有关日期/时间输入的 + 精确解析规则,以及可识别的文本字段(包括月份、星期几和时区), + 请参见 。 + + + + 请记住,任何日期或时间字面值输入都必须像文本字符串一样用单引号 + 括起来。更多信息请参见 + 。 + SQL 要求使用下列语法: + +type [ (p) ] 'value' + + 其中 p 是可选的精度说明,给出秒字段中 + 保留的小数位数。精度可用于 time、 + timestampinterval 类型, + 允许的取值范围见上文。如果在常量声明中没有指定 + 精度,则默认采用该字面值本身的精度。 + + + + 日期 + + + date + + + + 显示了date类型可能的输入方式。 + + + + 日期输入 + + + + 示例 + 描述 + + + + + 1999-01-08 + ISO 8601; 任何模式下的1月8日 + (推荐格式) + + + January 8, 1999 + 在任何datestyle输入模式下都无歧义 + + + 1/8/1999 + MDY模式中的1月8日;DMY模式中的8月1日 + + + 1/18/1999 + MDY模式中的1月18日;在其他模式中被拒绝 + + + 01/02/03 + MDY模式中的2003年1月2日; + DMY模式中的2003年2月1日; + YMD模式中的2001年2月3日 + + + + 1999-Jan-08 + 任何模式下的1月8日 + + + Jan-08-1999 + 任何模式下的1月8日 + + + 08-Jan-1999 + 任何模式下的1月8日 + + + 99-Jan-08 + YMD模式中的1月8日,否则错误 + + + 08-Jan-99 + 1月8日,除了在YMD模式中错误 + + + Jan-08-99 + 1月8日,除了在YMD模式中错误 + + + 19990108 + ISO 8601; 任何模式中的1999年1月8日 + + + 990108 + ISO 8601; 任何模式中的1999年1月8日 + + + 1999.008 + 年和一年中的日子 + + + J2451187 + 儒略日期 + + + January 8, 99 BC + 公元前99年 + + + +
+
+ + + 时间 + + + time + + + 无时区的时间 + + + 带时区的时间 + + + + 一天中的时间类型包括 + time [ (p) ] without time zone + 和 + time [ (p) ] with time zone。 + 单独写 time 等效于 + time without time zone。 + + + + 这些类型的有效输入由一个一天中的时间,加上一个可选时区组成 + (见 和 + )。如果在 + time without time zone 的输入中指定了时区,它会被 + 静默忽略。你也可以指定一个日期,但它同样会被忽略,除非你使用了 + 涉及夏令时规则的时区名称,例如 + America/New_York。在这种情况下,必须指定日期, + 以便确定应适用标准时间还是夏令时。相应的时区偏移会被记录到 + time with time zone 值中。 + + + 时间输入 + + + + 示例 + 描述 + + + + + 04:05:06.789 + ISO 8601 + + + 04:05:06 + ISO 8601 + + + 04:05 + ISO 8601 + + + 040506 + ISO 8601 + + + 04:05 AM + 和04:05一样,AM并不影响值 + + + 04:05 PM + 和16:05一样,输入的小时必须为 <= 12 + + + 04:05:06.789-8 + ISO 8601,时区以 UTC 偏移表示 + + + 04:05:06-08:00 + ISO 8601,时区以 UTC 偏移表示 + + + 04:05-08:00 + ISO 8601,时区以 UTC 偏移表示 + + + 040506-08 + ISO 8601,时区以 UTC 偏移表示 + + + 040506+0730 + ISO 8601,以分数小时形式给出 UTC 偏移 + + + 040506+07:30:00 + UTC偏移量指定为秒(ISO 8601中不允许) + + + 04:05:06 PST + 缩写指定的时区 + + + 2003-04-12 04:05:06 America/New_York + 全名指定的时区 + + + +
+ + + 时区输入 + + + + 示例 + 描述 + + + + + PST + 缩写(太平洋标准时间) + + + America/New_York + 完整时区名 + + + PST8PDT + POSIX风格的时区声明 + + + -8:00:00 + PST的UTC偏移 + + + -8:00 + PST的UTC偏移量(ISO 8601扩展格式) + + + -800 + PST的UTC偏移量(ISO 8601基本格式) + + + -8 + PST的UTC偏移量(ISO 8601基本格式) + + + zulu + UTC的军方缩写 + + + z + zulu的缩写形式(也在ISO 8601中) + + + +
+ + + 关于如何指定时区,参见 。 + +
+ + + 时间戳 + + + timestamp + + + + 带时区的时间戳 + + + + 无时区的时间戳 + + + 时间戳类型的有效输入由日期与时间拼接而成,其后可以跟时区,再后可以跟ADBC。(或者,AD/BC可以出现在时区之前,但这不是首选顺序。)因此: +1999-01-08 04:05:06 +以及: +1999-01-08 04:05:06 -8:00 +都是有效值,遵循ISO8601 标准。此外,也支持下面这种常用格式: +January 8 04:05:06 1999 PST + + + 按照SQL标准,timestamp without time zonetimestamp with time zone字面量的区别在于,时间后是否有+-符号及其后的时区偏移。因此,按照该标准,TIMESTAMP '2004-10-19 10:23:54'timestamp without time zone,而TIMESTAMP '2004-10-19 10:23:54+02'timestamp with time zone。 + PostgreSQL在确定字符串字面量的类型之前,从不检查其内容,因此会把上述两者都视为timestamp without time zone。为确保字面量被视为timestamp with time zone,应为它显式指定正确类型:TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'若字面量已经被确定为timestamp without time zonePostgreSQL会静默忽略任何时区标记。也就是说,所得值来自输入值中的日期/时间字段,不会根据时区调整。 + + 对于timestamp with time zone,内部存储的值始终使用 UTC(协调世界时,传统上称为格林尼治标准时间,GMT)。对于显式指定时区的输入值,会使用该时区适当的偏移将它转换为 UTC。如果输入字符串中没有说明时区,则假定它属于系统参数指定的时区,并使用timezone时区的偏移将它转换为 UTC。 + + + 当输出一个 timestamp with time zone 值时,它总会从 + UTC 转换到当前 timezone 时区,并显示为该时区的 + 本地时间。若要查看其他时区的时间,可以修改 + timezone,或者使用 + AT TIME ZONE 构造(见 + )。 + + + + 在 timestamp without time zone 和 + timestamp with time zone 之间转换时,通常假定 + timestamp without time zone 值应被解释为,或输出为, + timezone 本地时间。要为该转换指定不同的时区, + 可以使用 AT TIME ZONE。 + + + + + 特殊值 + + + time + constants + + + + date + constants + + + + 为了方便起见,PostgreSQL 支持若干特殊的 + 日期/时间输入值,如 + 所示。infinity-infinity + 在系统内部有特殊表示,并且输出时会保持不变;其余值则只是记法上的 + 简写,在读取时会被转换成普通日期/时间值。(特别是, + now 及相关字符串在被读取后会立刻转换成某个 + 特定的时间值。)这些值在 SQL 命令中作为常量使用时,都必须用 + 单引号括起来。 + + + + 特殊日期/时间输入 + + + + 输入串 + 合法类型 + 描述 + + + + + epoch + date, timestamp + 1970-01-01 00:00:00+00(Unix系统时间0) + + + infinity + date, timestamp + 晚于所有其他时间戳 + + + -infinity + date, timestamp + 早于所有其他时间戳 + + + now + date, time, timestamp + 当前事务的开始时间 + + + today + date, timestamp + 今天午夜(00:00 + + + tomorrow + date, timestamp + 明天午夜(00:00 + + + yesterday + date, timestamp + 昨天午夜(00:00 + + + allballs + time + 00:00:00.00 UTC + + + +
+ + + 以下与 SQL 兼容的函数也可用于获取相应数据 + 类型的当前时间值:CURRENT_DATE、 + CURRENT_TIME、 + CURRENT_TIMESTAMPLOCALTIME + 和 LOCALTIMESTAMP。(参见 + 。)请注意,这些是 SQL + 函数,不会在日期/时间输入字符串中被识别。 + + + + + 虽然输入字符串 nowtoday、 + tomorrowyesterday + 可用于交互式 SQL 命令,但当命令被保存以便稍后执行时,例如在预备 + 语句、视图和函数定义中,它们的行为可能令人意外。这些字符串可能在 + 读取时就被转换成某个具体时间值,而该值即使后来已经过时,也会继续 + 被使用。在这类上下文中,应改用某个 SQL 函数。例如, + CURRENT_DATE + 1 比 + 'tomorrow'::date 更安全。 + + + +
+
+ + + 日期/时间输出 + + + date + 输出格式 + formatting + + + + time + 输出格式 + formatting + + + + 日期/时间类型的输出格式可以设为四种样式之一:ISO 8601、 + SQL(Ingres)、传统的 + POSTGRES(Unix + date 格式)或 German。默认是 + ISO 格式。(SQL 标准要求 + 使用 ISO 8601 格式。之所以存在名为 SQL 的输出格式, + 只是历史原因。) + 展示了各种输出样式的示例。datetime + 类型的输出通常只包含示例中的日期部分或时间部分。不过, + POSTGRES 样式对纯日期值仍会采用 + ISO 格式输出。 + + + + 日期/时间输出风格 + + + + 风格声明 + 描述 + 示例 + + + + + ISO + ISO 8601, SQL标准 + 1997-12-17 07:37:16-08 + + + SQL + 传统样式 + 12/17/1997 07:37:16.00 PST + + + Postgres + 原始样式 + Wed Dec 17 07:37:16 1997 PST + + + German + 地区样式 + 17.12.1997 07:37:16.00 PST + + + +
+ + + + ISO 8601规定使用大写字母T来分隔日期和时间。 + PostgreSQL在输入时接受该格式,但在输出时使用空格而不是T,如上所示。 + 这是为了可读性和与RFC 3339以及其他一些数据库系统的一致性。 + + + + + SQL和POSTGRES风格中,如果DMY域顺序被指定,“日”将出现在“月”之前,否则“月”出现在“日”之前(有关该设置如何影响输入值的解释,请参考)。给出了示例。 + + + + 日期顺序习惯 + + + + datestyle设置 + 输入顺序 + 示例输出 + + + + + SQL, DMY + // + 17/12/1997 15:37:16.00 CET + + + SQL, MDY + // + 12/17/1997 07:37:16.00 PST + + + Postgres, DMY + // + Wed 17 Dec 07:37:16 1997 PST + + + +
+ + + 在 ISO 样式中,时区总是显示为相对于 UTC 的 + 有符号数字偏移,格林尼治以东的时区使用正号。如果偏移是整小时, + 就显示为 hh;如果是整分钟,则显示为 + hh:mm; + 否则显示为 + hh:mm:ss。 + (第三种情况在任何现代时区标准下都不可能出现,但在处理早于标准化 + 时区采用之前的时间戳时可能会看到。)在其他日期样式中,如果当前 + 时区有通用的字母缩写,就会显示该缩写;否则会以 ISO 8601 基本 + 格式的有符号数字偏移显示 + (hh 或 + hhmm)。 + + + 用户可以通过 SET datestyle 命令、 + postgresql.conf 配置文件中的 + 参数,或者服务器端或客户端上的 + PGDATESTYLE 环境变量来选择日期/时间样式。 + + + + 格式化函数to_char(见)也可以作为一个更灵活的方式来格式化日期/时间输出。 + +
+ + + 时区 + + + time zone + + + + 时区及其约定不仅受地球几何形状影响,也受政治决定影响。世界各地的 + 时区在 20 世纪逐渐趋于标准化,但仍然容易发生任意变化,尤其是夏令时 + 规则方面。PostgreSQL 使用广泛采用的 + IANA(Olson)时区数据库来获取历史时区规则信息。对于未来时间,则 + 假定某个时区最新已知的规则会无限期地持续下去。 + + + + PostgreSQL 努力在典型用法上与 + SQL 标准定义保持兼容。不过, + SQL 标准在日期和时间类型及其能力方面存在一些 + 奇怪的混搭。两个显而易见的问题是: + + + + + 尽管 date 类型不能有关联的时区, + time 类型却可以。但现实世界中的时区如果不同时关联 + 日期和时间,几乎没有意义,因为偏移量可能会随着夏令时切换而在 + 一年中发生变化。 + + + + + + 默认时区被指定为相对于 UTC 的一个固定数值 + 偏移。因此,在跨越 DST 边界做日期/时间 + 算术时,根本无法适应夏令时变化。 + + + + + + + + 为了克服这些困难,我们建议在使用时区时采用同时包含日期和时间的 + 日期/时间类型。我们建议使用 + time with time zone 类型(尽管 + PostgreSQL 出于兼容旧应用以及遵循 + SQL 标准的考虑而支持它)。 + PostgreSQL 对于任何只包含日期或时间的 + 类型,都会假定其使用本地时区。 + + + + 在系统内部,所有带时区的日期和时间都以 UTC + 存储。显示给客户端之前,它们会被转换为由 + 配置参数指定的本地时间。 + + + + PostgreSQL允许使用三种不同形式指定时区: + + + 完整时区名称,例如 America/New_York。 + 识别到的时区名称列在 + pg_timezone_names 视图中(见 + )。 + PostgreSQL 为此使用广泛采用的 IANA + 时区数据,因此同样的时区名称通常也会被其他软件识别。 + + + + + 时区缩写,例如 PST。与完整时区名称不同,这种 + 指定方式只定义了一个特定的 UTC 偏移,而完整时区名称还可能 + 隐含一套夏令时切换规则。识别到的缩写列在 + pg_timezone_abbrevs 视图中(见 + )。你不能把 + 或 + 配置参数设置为时区缩写, + 但可以在日期/时间输入值中以及与 + AT TIME ZONE 操作符一起使用缩写。 + + + + + 除了时区名称和缩写之外, + PostgreSQL 还接受 POSIX 风格的 + 时区说明,见 。 + 这个选项通常不如使用具名时区更合适,但如果没有可用的 IANA + 时区条目,它可能就是必需的。 + + + 简而言之,缩写和完整名称的区别是:缩写表示特定的 UTC 偏移,而许多完整名称隐含本地夏令时规则,因此有两个可能的 UTC 偏移。例如,2014-06-04 12:00 America/New_York表示纽约当地时间的中午,在该日期使用的是东部夏令时间(UTC-4)。因此,2014-06-04 12:00 EDT指定相同的时刻。但是,2014-06-04 12:00 EST指定东部标准时间(UTC-5)的中午,而不管该日期是否名义上实行夏令时。 + + + 更复杂的是,一些司法辖区在不同时间使用同一时区缩写来表示不同的 + UTC 偏移;例如在莫斯科,MSK 在某些年份表示 + UTC+3,在另一些年份则表示 UTC+4。PostgreSQL + 会按照该缩写在所给日期上的含义(或最近一次的含义)来解释这类缩写; + 但与上面的 EST 例子一样,这并不一定等同于该日期的 + 当地民用时间。 + + + 在所有情况下,时区名称和缩写的识别都不区分大小写。(这与 8.2 之前的PostgreSQL版本不同;那些版本在某些上下文中区分大小写,在另一些上下文中则不区分。) + + + 时区名称和缩写并不是硬编码在服务器中的;它们来自安装目录下 + .../share/timezone/ 和 + .../share/timezonesets/ 子目录中的数据 + (见 )。 + + + + 配置参数可以在 + postgresql.conf 文件中设置,也可以通过 + 中说明的其他标准方式设置。 + 另外,还有一些特殊的设置方法: + + + + + SQL 命令 SET TIME ZONE + 用于设置会话的时区。它是 SET TIMEZONE TO + 的另一种写法,语法上更符合 SQL 规范。 + + + + + + PGTZ 环境变量会被 + libpq 客户端用于在连接到服务器时 + 发送一条 SET TIME ZONE 命令。 + + + + + + + + 间隔输入 + + + interval + + + + interval值可以使用下列语法书写: + + +@ quantity unit quantity unit... direction + + + 其中quantity是一个数字(很可能是有符号的); + unitmicrosecond、 + millisecondsecond、 + minutehourday、 + weekmonthyear、 + decadecenturymillennium + 或它们的缩写或复数形式; + direction 可以是 ago + 或为空。at 符号(@)只是可选的噪声。不同单位 + 的数量会按适当的符号规则隐式相加。ago 会将 + 所有字段取反。如果 被设置为 + postgres_verbose,该语法也会用于间隔输出。 + + + + 日、小时、分钟和秒的数量也可以不写显式单位标记。例如, + '1 12:59:10' 会被读作 + '1 day 12 hours 59 min 10 sec'。同样,年和月的 + 组合也可以用一个连字符表示,例如 + '200-10' 会被读作 + '200 years 10 months'。(事实上,这些较短形式正是 + SQL 标准唯一允许的形式,并且在 + IntervalStyle 被设置为 + sql_standard 时也用于输出。) + + + + 间隔值也可以写成 ISO 8601 时间间隔,使用标准第 4.4.3.2 节的 + 带标志符的格式,或第 4.4.3.3 节的 + 替代格式。带标志符的格式如下: + +P quantity unit quantity unit ... T quantity unit ... + + 字符串必须以 P 开头,并且可以包含一个 + T 来引出一天中时间单位。可用的单位缩写见 + 。单位可以省略, + 也可以按任意顺序出现,但小于一天的单位必须出现在 + T 之后。特别是,M 的含义 + 取决于它是在 T 之前还是之后。 + + + + ISO 8601 间隔单位缩写 + + + + 缩写 + 含义 + + + + + Y + + + + M + 月(在日期部分中) + + + W + + + + D + + + + H + 小时 + + + M + 分钟 (在时间部分中) + + + S + + + + +
+ + + 如果使用替代格式: + +P years-months-days T hours:minutes:seconds + + 串必须以P开始,并且一个T分隔间隔的日期和时间部分。其值按照类似于 ISO 8601日期的数字给出。 + + + + 当编写带有 fields 说明的间隔常量, + 或者把字符串赋给一个定义时带有 fields + 说明的间隔列时,未标记数量的解释方式取决于 + fields。例如, + INTERVAL '1' YEAR 会被读作 1 年,而 + INTERVAL '1' 表示 1 秒。此外,位于 + fields 说明所允许的最小字段 + 右侧的字段值会被静默丢弃。例如,写 + INTERVAL '1 day 2:03:04' HOUR TO MINUTE + 会导致秒字段被丢弃,而不是日字段。 + + + + 根据 SQL 标准,一个间隔值的所有字段都必须带有 + 相同符号,因此一个前导负号会作用于所有字段;例如间隔字面值 + '-1 2:03:04' 中的负号会同时作用于日、小时、 + 分钟和秒。PostgreSQL 允许各字段具有 + 不同符号,并且传统上认为文本表示中的每个字段都带有各自独立的符号, + 因而在这个例子里,小时、分钟和秒部分会被视为正值。如果 + IntervalStyle 被设置为 + sql_standard,则会把前导符号视为作用于所有字段 + (但前提是没有出现额外符号);否则将采用传统的 + PostgreSQL 解释。为了避免混淆,我们建议 + 只要有任何字段为负值,就为每个字段都显式写出符号。 + + + 字段值可以带小数部分,例如'1.5 weeks''01:02:03.45'。不过,由于时间间隔内部仅存储三种整数单位(月、日、微秒),小数单位必须转入更小的单位。大于月的单位的小数部分会被截断为整数个月,例如'1.5 years'会变成'1 year 6 mons'。周和日的小数部分会按每月 30 天、每天 24 小时计算为整数天和微秒,例如'1.75 months'会变成1 mon 22 days 12:00:00。输出时,只有秒会显示小数部分。 + + + 展示了一些有效interval输入的示例。 + + + + 间隔输入 + + + + 示例 + 描述 + + + + + 1-2 + SQL标准格式:1年2个月 + + + 3 4:05:06 + SQL标准格式:3日4小时5分钟6秒 + + + 1 year 2 months 3 days 4 hours 5 minutes 6 seconds + 传统Postgres格式:1年2个月3日4小时5分钟6秒钟 + + + P1Y2M3DT4H5M6S + 带标志符的ISO 8601 格式:含义同上 + + + P0001-02-03T04:05:06 + ISO 8601 的替代格式:含义同上 + + + +
+ + 在内部,interval值以月、日和微秒存储。这是因为一个月的天数各不相同,而涉及夏令时调整时,一天可能有 23 或 25 小时。月和日字段为整数,微秒字段则可以存储小数秒。由于时间间隔通常由常量字符串或timestamp相减创建,这种存储方式在大多数情况下都很有效,但可能产生意外结果: +SELECT EXTRACT(hours from '80 minutes'::interval); + date_part +----------- + 1 + +SELECT EXTRACT(days from '80 hours'::interval); + date_part +----------- + 0 +函数justify_daysjustify_hours可用于调整超出正常范围的日和小时。 + +
+ + + 间隔输出 + + + interval + 输出格式 + formatting + + + + 间隔类型的输出格式可以通过 SET intervalstyle + 命令设置为以下四种风格之一:sql_standard、 + postgrespostgres_verbose + 或 iso_8601。默认值是 + postgres 格式。 + 展示了每种输出风格的 + 示例。 + + + + 如果间隔值满足 SQL 标准的限制条件(仅有年-月或仅有日-时间,且 + 不混合正负分量),sql_standard 风格会生成符合 + SQL 标准的间隔字面值输出。否则,输出将表现为一个标准的年-月字面值 + 串后跟一个日-时间字面值串,并显式加上符号,以消除正负混合间隔的 + 歧义。 + + + + postgres 样式的输出与 + PostgreSQL 8.4 之前版本在 + 参数设为 ISO + 时的输出一致。 + + + + postgres_verbose 样式的输出与 + PostgreSQL 8.4 之前版本在 + DateStyle 参数设为非 ISO + 输出时的结果一致。 + + + + iso_8601 风格的输出符合 ISO 8601 标准 + 4.4.3.2 节描述的带标志符格式。 + + + + 间隔输出风格示例 + + + + 风格声明 + 年-月间隔 + 日-时间间隔 + 混合间隔 + + + + + sql_standard + 1-2 + 3 4:05:06 + -1-2 +3 -4:05:06 + + + postgres + 1 year 2 mons + 3 days 04:05:06 + -1 year -2 mons +3 days -04:05:06 + + + postgres_verbose + @ 1 year 2 mons + @ 3 days 4 hours 5 mins 6 secs + @ 1 year 2 mons -3 days 4 hours 5 mins 6 secs ago + + + iso_8601 + P1Y2M + P3DT4H5M6S + P-1Y-2M3DT-4H-5M-6S + + + +
+ +
+ +
+ + + 布尔类型 + + + Boolean + 数据类型 + + + + true + + + + false + + + + PostgreSQL提供标准的SQL类型boolean,参见boolean可以有多个状态:true(真)false(假)和第三种状态unknown(未知),未知状态由SQL空值表示。 + + + + 布尔数据类型 + + + + 名字 + 存储尺寸 + 描述 + + + + + boolean + 1字节 + 状态为真或假 + + + +
+ + + 在 SQL 查询中,布尔常量可以用 SQL 关键字 TRUE、 + FALSENULL 表示。 + + + + boolean 类型的输入函数接受以下表示状态的 + 字符串: + + true + yes + on + 1 + + 以及以下表示状态的字符串: + + false + no + off + 0 + + 这些字符串的唯一前缀也同样可以接受,例如 t 或 + n。前导或尾随空白会被忽略,并且大小写不敏感。 + + + + boolean 类型的输出函数总是产生 t + 或 f,如 + 所示。 + + + + 使用<type>boolean</type>类型 + + +CREATE TABLE test1 (a boolean, b text); +INSERT INTO test1 VALUES (TRUE, 'sic est'); +INSERT INTO test1 VALUES (FALSE, 'non est'); +SELECT * FROM test1; + a | b +---+--------- + t | sic est + f | non est + +SELECT * FROM test1 WHERE a; + a | b +---+--------- + t | sic est + + + + + 在 SQL 查询中,最好使用关键字 TRUE 和 + FALSE 来书写布尔常量(这与 + SQL 兼容)。不过,你也可以使用遵循 + 中所述通用字符串 + 常量语法的字符串来表示,例如 'yes'::boolean。 + + + + 注意,语法分析器会自动将 TRUE 和 + FALSE 理解为 boolean 类型,但 + NULL 不会,因为它可以是任意类型。因此在某些上下文中, + 你可能需要显式将 NULL 转换为 + boolean,例如 NULL::boolean。 + 反过来,如果语法分析器能够判定某个字符串字面量必定是 + boolean 类型,则也可以省略这类转换。 + +
+ + + 枚举类型 + + + 数据类型 + enumerated (enum) + + + + enumerated types + + + + 枚举(enum)类型是由一个静态、值的有序集合构成的数据类型。它们等效于很多编程语言所支持的enum类型。枚举类型的一个示例可以是一周中的日期,或者一个数据的状态值集合。 + + + + 枚举类型的声明 + + + 可以使用命令创建枚举类型,例如: + + +CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + + + 创建之后,该 enum 类型就可以像任何其他类型一样在表定义和函数 + 定义中使用: + +CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); +CREATE TABLE person ( + name text, + current_mood mood +); +INSERT INTO person VALUES ('Moe', 'happy'); +SELECT * FROM person WHERE current_mood = 'happy'; + name | current_mood +------+-------------- + Moe | happy +(1 row) + + + + + + 排序 + + + enum 类型中各值的顺序,就是创建该类型时列出的顺序。枚举支持 + 所有标准比较操作符以及相关聚合函数。例如: + + +INSERT INTO person VALUES ('Larry', 'sad'); +INSERT INTO person VALUES ('Curly', 'ok'); +SELECT * FROM person WHERE current_mood > 'sad'; + name | current_mood +-------+-------------- + Moe | happy + Curly | ok +(2 rows) + +SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood; + name | current_mood +-------+-------------- + Curly | ok + Moe | happy +(2 rows) + +SELECT name +FROM person +WHERE current_mood = (SELECT MIN(current_mood) FROM person); + name +------- + Larry +(1 row) + + + + + + 类型安全性 + + + 每一种枚举数据类型都是独立的并且不能和其他枚举类型相比较。看这样一个示例: + + +CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); +CREATE TABLE holidays ( + num_weeks integer, + happiness happiness +); +INSERT INTO holidays(num_weeks,happiness) VALUES (4, 'happy'); +INSERT INTO holidays(num_weeks,happiness) VALUES (6, 'very happy'); +INSERT INTO holidays(num_weeks,happiness) VALUES (8, 'ecstatic'); +INSERT INTO holidays(num_weeks,happiness) VALUES (2, 'sad'); +ERROR: invalid input value for enum happiness: "sad" +SELECT person.name, holidays.num_weeks FROM person, holidays + WHERE person.current_mood = holidays.happiness; +ERROR: operator does not exist: mood = happiness + + + + + 如果你确实需要做这类事情,可以编写自定义操作符,或者在查询中 + 添加显式类型转换: + + +SELECT person.name, holidays.num_weeks FROM person, holidays + WHERE person.current_mood::text = holidays.happiness::text; + name | num_weeks +------+----------- + Moe | 4 +(1 row) + + + + + + + 实现细节 + + + 枚举标签是大小写敏感的,因此'happy''HAPPY'是不同的。标签中的空格也是有意义的。 + + + + 尽管枚举类型的主要目的是用于值的静态集合,但也有方法在现有枚举类型中增加新值和重命名值(见)。不能从枚举类型中去除现有的值,也不能更改这些值的排序顺序,如果要那样做可以删除并且重建枚举类型。 + + + + 一个枚举值在磁盘上占据4个字节。一个枚举值的文本标签的长度受限于NAMEDATALEN设置,该设置被编译在PostgreSQL中,在标准编译下它表示最多63字节。 + + + + 从内部枚举值到文本标签的翻译被保存在系统目录pg_enum中。可以直接查询该目录。 + + + + + + + 几何类型 + + + 几何数据类型表示二维的空间物体。展示了PostgreSQL中可以用的几何类型。 + + + + 几何类型 + + + + 名字 + 存储尺寸 + 描述 + 表示 + + + + + point + 16字节 + 平面上的点 + (x,y) + + + line + 32字节 + 无限直线 + {A,B,C} + + + lseg + 32字节 + 有限线段 + ((x1,y1),(x2,y2)) + + + box + 32字节 + 矩形框 + ((x1,y1),(x2,y2)) + + + path + 16+16n字节 + 封闭路径(类似于多边形) + ((x1,y1),...) + + + path + 16+16n字节 + 开放路径 + [(x1,y1),...] + + + polygon + 40+16n字节 + 多边形(类似于封闭路径) + ((x1,y1),...) + + + circle + 24字节 + + <(x,y),r>(中心点和半径) + + + +
+ + + 我们提供了丰富的函数和操作符来进行各种几何操作,例如缩放、平移、 + 旋转以及计算相交等,详见 。 + + + + + + + point + + + + 点是几何类型的基本二维构造块。用下面的语法描述point类型的值: + + +( x , y ) + x , y + + + 其中xy分别是坐标,都是浮点数。 + + + + 点使用第一种语法输出。 + + + + + 线 + + + line + + + + 线由线性方程Ax + By + C = 0 + 表示,其中AB不能同时为零。类型line + 的值采用以下形式输入和输出: + +{ A, B, C } + + + 另外,还可以用下列任一形式输入: + + +[ ( x1 , y1 ) , ( x2 , y2 ) ] +( ( x1 , y1 ) , ( x2 , y2 ) ) + ( x1 , y1 ) , ( x2 , y2 ) + x1 , y1 , x2 , y2 + + + 其中 + (x1,y1) + 和 + (x2,y2) + 是线上不同的两点。 + + + + + 线段 + + + lseg + + + + 线段 + + + + 线段用一对线段的端点来表示。lseg类型的值用下面的语法声明: + + +[ ( x1 , y1 ) , ( x2 , y2 ) ] +( ( x1 , y1 ) , ( x2 , y2 ) ) + ( x1 , y1 ) , ( x2 , y2 ) + x1 , y1 , x2 , y2 + + + 其中(x1,y1) + 和 + (x2,y2) + 是线段的端点。 + + + + 线段使用第一种语法输出。 + + + + + 方框 + + + box (data type) + + + + rectangle + + + + 方框用其对角的点对表示。box类型的值使用下面的语法指定: + + +( ( x1 , y1 ) , ( x2 , y2 ) ) + ( x1 , y1 ) , ( x2 , y2 ) + x1 , y1 , x2 , y2 + + + 其中(x1,y1) + 和 + (x2,y2) + 是方框的对角点。 + + + + 方框使用第二种语法输出。 + + + + 在输入时可以提供任意两个对角,但是值将根据需要被按顺序记录为右上角和左下角。 + + + + + 路径 + + + path (data type) + + + + 路径由一系列连接的点组成。路径可能是开放的,也就是认为列表中第一个点和最后一个点没有被连接起来;也可能是封闭的,这时认为第一个和最后一个点被连接起来。 + + + + path类型的值用下面的语法声明: + + +[ ( x1 , y1 ) , ... , ( xn , yn ) ] +( ( x1 , y1 ) , ... , ( xn , yn ) ) + ( x1 , y1 ) , ... , ( xn , yn ) + ( x1 , y1 , ... , xn , yn ) + x1 , y1 , ... , xn , yn + + + 其中的点是组成路径的线段的端点。方括弧([])表示一个开放的路径,圆括弧(())表示一个封闭的路径。如第三种到第五种语法所示,当最外面的圆括号被忽略时,路径将被假定为封闭。 + + + + 路径的输出使用第一种或第二种语法。 + + + + + 多边形 + + + polygon + + + 多边形由点的列表(多边形的顶点)表示。多边形与闭合路径非常相似,但存储方式不同,并且有自己的支持例程。 + + + polygon类型的值用下列语法声明: + + +( ( x1 , y1 ) , ... , ( xn , yn ) ) + ( x1 , y1 ) , ... , ( xn , yn ) + ( x1 , y1 , ... , xn , yn ) + x1 , y1 , ... , xn , yn + + + 其中的点是组成多边形边界的线段的端点。 + + + + 多边形的输出使用第一种语法。 + + + + + + + + circle + + + + 圆由一个圆心和一个半径代表。circle类型的值用下面的语法指定: + + +< ( x , y ) , r > +( ( x , y ) , r ) + ( x , y ) , r + x , y , r + + + 其中(x,y)是圆心,而r是圆的半径。 + + + + 圆的输出用第一种语法。 + + + +
+ + + 网络地址类型 + + + network + 数据类型 + + + + PostgreSQL 提供了用于存储 IPv4、IPv6 + 和 MAC 地址的数据类型,如 + 所示。使用这些数据类型来存储网络地址,比使用纯文本类型更好, + 因为它们提供了输入错误检查以及专门的操作符和函数 + (见 )。 + + + + 网络地址类型 + + + + 名字 + 存储尺寸 + 描述 + + + + + + cidr + 7或19字节 + IPv4和IPv6网络 + + + + inet + 7或19字节 + IPv4和IPv6主机以及网络 + + + + macaddr + 6字节 + MAC地址 + + + + +
+ + + 在对 inetcidr 数据类型排序时,IPv4 + 地址总是排在 IPv6 地址之前,包括那些封装在 IPv6 地址中或映射到 + IPv6 地址中的 IPv4 地址,例如 ::10.2.3.4 或 + ::ffff:10.4.3.2。 + + + + + <type>inet</type> + + + inet(数据类型) + + + + inet 在一个字段中保存 IPv4 或 IPv6 主机地址, + 以及可选的子网信息。子网由主机地址中包含的网络地址位数 + (即网络掩码)表示。如果网络掩码为 32 且地址是 + IPv4,那么该值表示的不是子网,而只是单个主机。在 IPv6 中, + 地址长度为 128 位,因此 128 位表示一个唯一的主机地址。请注意, + 如果你只想接受网络地址,应使用 cidr 类型,而不是 + inet。 + + + + 该类型的输入格式为 + address/y,其中 + address 是 IPv4 或 + IPv6 地址,而 y + 是网络掩码中的位数。如果省略 + /y 部分,则 IPv4 + 的网络掩码取 32,IPv6 的网络掩码取 128,因此该值表示单个主机。 + 显示时,如果 /y + 部分表示的是单个主机,则它不会被显示出来。 + + + + + <type>cidr</type> + + + cidr + + + + cidr 类型保存 IPv4 或 IPv6 网络说明。输入和输出格式 + 遵循无类别域间路由(CIDR)约定。指定网络的格式为 + address/y,其中 + address 是网络最低地址的 + IPv4 或 IPv6 表示,而 + y 是网络掩码中的位数。 + 如果省略 y,则会按照旧式 + 的分类网络编号系统假设来计算,但至少会大到足以覆盖输入中写出的 + 所有八位组。若指定的网络地址在所给网络掩码右侧仍有置位比特,则会 + 报错。 + + + + 展示了一些示例。 + + + + <type>cidr</type>类型输入示例 + + + + cidr输入 + cidr输出 + abbrev(cidr) + + + + + 192.168.100.128/25 + 192.168.100.128/25 + 192.168.100.128/25 + + + 192.168/24 + 192.168.0.0/24 + 192.168.0/24 + + + 192.168/25 + 192.168.0.0/25 + 192.168.0.0/25 + + + 192.168.1 + 192.168.1.0/24 + 192.168.1/24 + + + 192.168 + 192.168.0.0/24 + 192.168.0/24 + + + 128.1 + 128.1.0.0/16 + 128.1/16 + + + 128 + 128.0.0.0/16 + 128.0/16 + + + 128.1.2 + 128.1.2.0/24 + 128.1.2/24 + + + 10.1.2 + 10.1.2.0/24 + 10.1.2/24 + + + 10.1 + 10.1.0.0/16 + 10.1/16 + + + 10 + 10.0.0.0/8 + 10/8 + + + 10.1.2.3/32 + 10.1.2.3/32 + 10.1.2.3/32 + + + 2001:4f8:3:ba::/64 + 2001:4f8:3:ba::/64 + 2001:4f8:3:ba::/64 + + + 2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128 + 2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128 + 2001:4f8:3:ba:2e0:81ff:fe22:d1f1 + + + ::ffff:1.2.3.0/120 + ::ffff:1.2.3.0/120 + ::ffff:1.2.3/120 + + + ::ffff:1.2.3.0/128 + ::ffff:1.2.3.0/128 + ::ffff:1.2.3.0/128 + + + +
+
+ + + <type>inet</type> vs. <type>cidr</type> + + + inetcidr 两种数据类型的本质区别在于: + inet 接受网络掩码右侧仍含有非零位的值,而 + cidr 不接受。 + + + + + 如果你不喜欢 inetcidr 值的输出 + 格式,可以试试 hosttext + 和 abbrev 函数。 + + + + + + <type>macaddr</type> + + + macaddr(数据类型) + + + + MAC地址 + macaddr + + + + macaddr类型存储 MAC 地址,也就是以太网卡硬件地址 (尽管 MAC 地址还用于其它用途)。可以接受下列格式的输入: + + + '08:00:2b:01:02:03' + '08-00-2b-01-02-03' + '08002b:010203' + '08002b-010203' + '0800.2b01.0203' + '0800-2b01-0203' + '08002b010203' + + + 这些示例指定的都是同一个地址。十六进制数字 + af 可使用大小写任意 + 形式。输出总是采用上面展示的第一种形式。 + + + + IEEE标准802-2001指定所示的第二种形式(带连字符)作为MAC地址的 + 规范形式,并指定第一种形式(带冒号)作为位反转表示法,因此 + 08-00-2b-01-02-03 = 01:00:4D:08:04:0C。这种约定现在被广泛 + 忽略,仅适用于过时的网络协议(如Token Ring)。PostgreSQL不提供 + 位反转的功能,所有接受的格式都使用规范的LSB顺序。 + + + + 剩下的五种输入格式不属于任何标准。 + + + + +
+ + + 位串类型 + + + 位串 + 数据类型 + + + 位串是由 1 和 0 组成的字符串,可用于存储或可视化位掩码。SQL 有两种位类型:bit(n)bit varying(n),其中n是正整数。 + + + bit 类型的数据长度必须与 + n 完全一致;试图存储更短或更长的位串 + 都会报错。bit varying 数据则是长度上限为 + n 的变长类型,更长的串会被拒绝。 + 不带长度的 bit 等效于 bit(1), + 而不带长度说明的 bit varying 表示长度不受限。 + + + + + 如果显式地将一个位串值转换为 + bit(n),它会在右侧被截断 + 或补零,直到长度恰好为 n 位, + 且不会报错。类似地,如果显式地将一个位串值转换为 + bit varying(n),而其长度 + 超过 n 位,则会在右侧被截断。 + + + + + 关于位串常量的语法信息,请参见 + 。位逻辑操作符和字符串操作 + 函数也可用;参见 。 + + + + 使用位串类型 + + +CREATE TABLE test (a BIT(3), b BIT VARYING(5)); +INSERT INTO test VALUES (B'101', B'00'); +INSERT INTO test VALUES (B'10', B'101'); + +ERROR: bit string length 2 does not match type bit(3) + +INSERT INTO test VALUES (B'10'::bit(3), B'101'); +SELECT * FROM test; + + a | b +-----+----- + 101 | 00 + 100 | 101 + + + + + + 一个 bit 串值每 8 位需要 1 个字节,再加上 5 或 8 个字节的额外 + 开销,具体取决于串的长度。(不过,长值可能会被压缩或移到行外存储, + 与 中对字符串的说明相同。) + + + + + 文本搜索类型 + + + 全文搜索 + 数据类型 + + + + 文本搜索 + 数据类型 + + + + PostgreSQL 提供了两种专为支持全文搜索而 + 设计的数据类型。所谓全文搜索,是指在一组自然语言 + 文档中查找最匹配某个 + 查询的文档。tsvector 类型以 + 适合文本搜索的优化形式表示文档,tsquery 类型则表示 + 文本查询。关于这一功能的详细解释见 ; + 相关函数和操作符的概览见 。 + + + + <type>tsvector</type> + + + tsvector(数据类型) + + + 一个tsvector值是由互不相同的词位组成的有序列表。词位是经过规范化以合并同一个词的不同变体的词(详情见)。排序和去重会在输入时自动完成,如本例所示: +SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector; + tsvector +---------------------------------------------------- + 'a' 'and' 'ate' 'cat' 'fat' 'mat' 'on' 'rat' 'sat' +要表示包含空白或标点符号的词位,请用引号括住它们: +SELECT $$the lexeme ' ' contains spaces$$::tsvector; + tsvector +------------------------------------------- + ' ' 'contains' 'lexeme' 'spaces' 'the' +(本例和下一个示例使用美元符号引用的字符串字面量,以免在字面量中双写引号造成混淆。)内嵌的引号和反斜线必须双写: +SELECT $$the lexeme 'Joe''s' contains a quote$$::tsvector; + tsvector +------------------------------------------------ + 'Joe''s' 'a' 'contains' 'lexeme' 'quote' 'the' +还可以给词位附加整数形式的位置,如下所示: +SELECT 'a:1 fat:2 cat:3 sat:4 on:5 a:6 mat:7 and:8 ate:9 a:10 fat:11 rat:12'::tsvector; + tsvector +------------------------------------------------------------------------------- + 'a':1,6,10 'and':8 'ate':9 'cat':3 'fat':2,11 'mat':7 'on':5 'rat':12 'sat':4 +位置通常表示原词在文档中的位置。位置信息可用于邻近度排序。位置值的范围为 1 到 16383;更大的数值会被静默设为 16383。同一词位的重复位置会被丢弃。 + + 具有位置的词位还可以标记一个权重,它可以是A, + BCD。 + D是默认值,因此不会在输出中显示: +SELECT 'a:1A fat:2B,4C cat:5D'::tsvector; + tsvector +---------------------------- + 'a':1A 'cat':5 'fat':2B,4C +权重通常用来反映文档结构,例如为标题中的词和正文中的词采用不同标记。文本搜索排名函数可以为不同的权重标记分配不同优先级。 + + + 必须认识到,tsvector 类型本身并不会执行任何词语 + 规范化;它假定输入的词已经按照应用需求完成规范化。例如: + + +SELECT 'The Fat Rats'::tsvector; + tsvector +-------------------- + 'Fat' 'Rats' 'The' + + + 对于大多数英文全文搜索应用来说,上述词会被视为尚未规范化,但 + tsvector 并不在意。原始文档文本通常应先经过 + to_tsvector,以按搜索需要对词语进行规范化: + + +SELECT to_tsvector('english', 'The Fat Rats'); + to_tsvector +----------------- + 'fat':2 'rat':3 + + + 更多细节仍请参见 。 + + + + + + <type>tsquery</type> + + + tsquery(数据类型) + + + + tsquery 值存储要搜索的词位,并可用布尔操作符 + &(AND)、|(OR)和 + !(NOT)将它们组合起来,也可使用短语搜索 + 操作符 <->(FOLLOWED BY)。此外, + FOLLOWED BY 还有一种变体 + <N>,其中 + N 是整数常量,用于指定被搜索的两个词位 + 之间的距离。<-> 等效于 + <1>。 + + + + 可以使用圆括号强制指定这些操作符的分组方式。若没有圆括号, + !(NOT)的绑定最紧,其次是 + <->(FOLLOWED BY),再其次是 + &(AND),最后是 |(OR)。 + + + + 以下是一些示例: + + +SELECT 'fat & rat'::tsquery; + tsquery +--------------- + 'fat' & 'rat' + +SELECT 'fat & (rat | cat)'::tsquery; + tsquery +--------------------------- + 'fat' & ( 'rat' | 'cat' ) + +SELECT 'fat & rat & ! cat'::tsquery; + tsquery +------------------------ + 'fat' & 'rat' & !'cat' + + + + + 可选地,tsquery 中的词位可以用一个或多个权重字母 + 标注,这会限制它们只匹配在 tsvector 中带有这些 + 权重之一的词位: + + +SELECT 'fat:ab & cat'::tsquery; + tsquery +------------------ + 'fat':AB & 'cat' + + + + + 此外,tsquery 中的词位还可以带上 + * 标签来指定前缀匹配: + +SELECT 'super:*'::tsquery; + tsquery +----------- + 'super':* + + 这个查询将匹配 tsvector 中任何以 + super 开头的词位。 + + + + 引号的使用规则与前面介绍 tsvector 时相同;同样, + 与 tsvector 一样,任何需要的词语规范化都必须在 + 转换为 tsquery 类型之前完成。to_tsquery + 函数很适合用来实现这种规范化: + + +SELECT to_tsquery('Fat:ab & Cats'); + to_tsquery +------------------ + 'fat':AB & 'cat' + + + 请注意,to_tsquery 会像处理其他词一样处理 + 前缀,这意味着下面的比较会返回真: + + +SELECT to_tsvector( 'postgraduate' ) @@ to_tsquery( 'postgres:*' ); + ?column? +---------- + t + + 因为 postgres 会被词干化为 + postgr: + +SELECT to_tsvector( 'postgraduate' ), to_tsquery( 'postgres:*' ); + to_tsvector | to_tsquery +---------------+------------ + 'postgradu':1 | 'postgr':* + + 因而它能够匹配其带前缀的后继形式 + postgraduate。 + + + + + + + + <acronym>UUID</acronym>类型 + + + UUID + + + + uuid 数据类型用于存储由 + RFC 4122、 + ISO/IEC 9834-8:2005 及相关标准定义的通用唯一标识符(UUID)。 + (有些系统把这种数据类型称为全局唯一标识符,或 GUID, + GUID。)这种标识符是一个 + 128 位的量,由某种算法生成,该算法被设计为使同一算法在已知宇宙中 + 被其他人生成出相同标识符的概率极低。因此,对于分布式系统而言, + 这类标识符能比序列生成器提供更好的唯一性保证,因为序列生成器仅在 + 单个数据库内唯一。 + + + + UUID 写作一串小写十六进制数字,并用连字符分隔成若干组: + 先是 8 位一组,接着是三个 4 位组,最后是一个 12 位组。总共 + 32 个十六进制数字表示 128 个二进制位。标准形式的 UUID + 例如: + +a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 + + PostgreSQL 也接受其他输入形式:可以使用 + 大写字母、用花括号包围标准格式、忽略部分或全部连字符,或者在任意 + 4 位分组后额外加上连字符。例如: + +A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11 +{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11} +a0eebc999c0b4ef8bb6d6bb9bd380a11 +a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11 +{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11} + + 输出总是采用标准形式。 + + + PostgreSQL为 UUID 提供存储和比较函数,但核心数据库不包含生成 UUID 的函数,因为没有一种算法适合所有应用。模块提供实现多种标准算法的函数。模块也提供生成随机 UUID 的函数。或者,可以由客户端应用或通过服务器端函数调用的其他库来生成 UUID。 + + + + <acronym>XML</acronym>类型 + + + XML + + + + xml 数据类型可用于存储 XML 数据。与把 XML 数据存储在 + text 字段中相比,它的优势在于会检查输入值的良构性,并且提供了可执行类型安全操作的支持函数;见 + 。使用该数据类型要求构建时使用 + configure --with-libxml。 + + + + xml 类型既可以存储 XML 标准所定义的良构 + 文档,也可以存储 内容 片段;后者是参照 + XQuery 和 XPath 数据模型中更宽松的 + 文档节点 + 概念来定义的。粗略地说,这意味着内容片段可以拥有多个顶层元素或字符 + 节点。表达式 + xmlvalue IS DOCUMENT + 可用于判断某个特定的 xml 值是完整文档,还是仅仅是一个 + 内容片段。 + + + + 创建XML值 + 要生成xml类型的值,可以对字符数据使用函数xmlparsexmlparse + +XMLPARSE ( { DOCUMENT | CONTENT } value) +例如:Manual...') +XMLPARSE (CONTENT 'abcbarfoo') +]]>按照 SQL 标准,这是将字符串转换为 XML 值的唯一方式,不过也可以使用以下 PostgreSQL 特有语法:bar' +'bar'::xml +]]> + + + 即使输入值指定了文档类型声明(DTD),xml 类型也不会 + 根据 DTD 来验证输入值DTD。 + 目前也没有内置支持可依据其他 XML 模式语言(如 XML Schema) + 来执行验证。 + + + 相反的操作是把xml值转换为字符串,这使用函数xmlserializexmlserialize + +XMLSERIALIZE ( { DOCUMENT | CONTENT } value AS type ) + + type可以是charactercharacter varyingtext(或这些类型之一的别名)。同样,按照 SQL 标准,这是在xml类型和字符类型之间转换的唯一方式,不过 PostgreSQL 也允许直接对值进行类型转换。 + + + 当字符串值在不经过 XMLPARSE 或 + XMLSERIALIZE 的情况下与 xml 类型互相转换时, + 选择 DOCUMENT 还是 CONTENT + 由会话配置参数 XML option + XML option 决定,可以使用 + 标准命令设置: + +SET XML OPTION { DOCUMENT | CONTENT }; + + 或使用更接近 PostgreSQL 风格的语法 + +SET xmloption TO { DOCUMENT | CONTENT }; + + 默认值是 CONTENT,因此允许所有形式的 XML 数据。 + + + + + + 编码处理 + + 在客户端、服务器以及其间传输的 XML 数据上处理多种字符编码时, + 必须格外小心。使用文本模式向服务器发送查询并把查询结果返回给 + 客户端时(这是通常使用的模式),PostgreSQL 会将客户端与 + 服务器之间传输的所有字符数据转换为目标端的字符编码,参见 + 。这也包括表示 XML 值的字符串,如上例 + 所示。这通常意味着,由于字符数据在客户端和服务器之间传输时可能被 + 转换为其他编码,XML 数据中包含的编码声明可能会失效,因为内嵌的 + 编码声明本身并不会被修改。为处理这种情况,表示 + xml 类型输入值的字符串中所包含的编码声明会被 + 忽略,其内容被假定为当前服务器编码。因此, + 为了正确处理,客户端发出的 XML 数据字符串必须采用当前客户端编码。 + 客户端负责在将文档发送给服务器之前把它们转换为当前客户端编码, + 或适当调整客户端编码。输出时,xml 类型值不会带有 + 编码声明,而客户端应假定所有数据都采用当前客户端编码。 + + + + 若使用二进制模式把查询参数传给服务器并把查询结果返回客户端, + 则不会执行编码转换,因此情况有所不同。在这种情况下,XML 数据中的 + 编码声明会被识别;如果缺少编码声明,则该数据会被假定为 UTF-8 + (由于 XML 标准的要求,请注意 PostgreSQL 不支持 UTF-16)。 + 输出时,除非客户端编码是 UTF-8(此时编码声明会被省略),否则 + 数据会带有一个指明客户端编码的编码声明。 + + + + 不言而喻,在 PostgreSQL 中处理 XML + 数据时,如果 XML 数据编码、客户端编码和服务器编码三者相同, + 不仅更高效,也更不容易出错。由于 XML 数据在内部以 UTF-8 处理, + 如果服务器编码也是 UTF-8,则处理效率最高。 + + + + + 当服务器编码不是 UTF-8 时,某些与 XML 相关的函数在非 ASCII + 数据上可能完全无法工作。尤其是 xpath(),这是一个已知问题。 + + + + + + 访问XML值 + + + xml 数据类型有些特殊,因为它不提供任何比较操作符。 + 这是因为对 XML 数据并不存在良定义且通用的比较算法。其结果是, + 你无法通过把某个 xml 值与搜索值比较来检索行。 + 因此,XML 值通常应伴随一个独立的键字段,例如 ID。另一种比较 + XML 值的办法,是先把它们转换成字符串;但请注意,字符串比较对 + XML 的比较需求通常帮助不大。 + + + + 由于 xml 数据类型没有可用的比较操作符,因此无法直接 + 在这种类型上创建索引。如果需要在 XML 中快速搜索,可行方案包括: + 将表达式转换为字符串类型后为其建立索引,或者为某个 XPath 表达式 + 建立索引。当然,实际查询也必须相应调整为使用该被索引的表达式。 + + + + PostgreSQL 的文本搜索功能也可用于加速 + XML 数据的全文搜索。不过,目前 PostgreSQL 发行版中仍缺少所需的 + 预处理支持。 + + + + + &json; + + &array; + + &rowtypes; + + &rangetypes; + + + 对象标识符类型 + + + 对象标识符 + 数据类型 + + + + oid + + + + regproc + + + + regprocedure + + + + regoper + + + + regoperator + + + + regclass + + + + regtype + + + + regconfig + + + + regdictionary + + + + xid + + + + cid + + + + tid + + + 对象标识符(OID)在PostgreSQL内部被用作若干系统表的主键。除非建表时指定WITH OIDS,或启用配置变量,否则不会向用户创建的表添加 OID。类型oid表示一个对象标识符。还有若干oid的别名类型:regprocregprocedureregoperregoperatorregclassregtyperegroleregnamespaceregconfigregdictionary给出了概要说明。 + + + oid 类型目前实现为无符号 4 字节整数。因此, + 在大型数据库中,它不足以提供数据库范围内的唯一性,甚至在大型的 + 单个表中也无法保证唯一性。 + 因此,不推荐把用户创建表的 OID 列用作主键。OID 最好仅用于引用系统表。 + + + oid 类型本身除比较之外几乎没有其他操作。不过,它可以 + 转换为整数,然后再用标准整数操作符进行处理。(这样做时要注意 + 有符号与无符号可能造成的混淆。) + + + + OID 别名类型除了专门的输入和输出例程外,没有自己的操作。这些 + 例程能够接受并显示系统对象的符号名称,而不是 oid + 类型所使用的原始数值。别名类型简化了对象 OID 值的查找。例如, + 要查看与表 mytable 相关的 + pg_attribute 行,可以写成: + +SELECT * FROM pg_attribute WHERE attrelid = 'mytable'::regclass; + + 而不是: + +SELECT * FROM pg_attribute + WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'mytable'); + + 虽然这样看起来也不算太糟,但其实仍然过于简化。如果不同模式中有 + 多个名为 mytable 的表,就必须写一个复杂得多 + 的子查询来选出正确的 OID。regclass 的输入转换器会 + 按照模式路径设置来处理表查找,因此能够自动完成 + 正确的事情。同样,把表的 OID 转换成 + regclass,也很适合用来以符号形式显示数值 OID。 + + + + 对象标识符类型 + + + + 名字 + 引用 + 描述 + 值示例 + + + + + + + oid + 任意 + 数字形式的对象标识符 + 564182 + + + + regproc + pg_proc + 函数名称 + sum + + + + regprocedure + pg_proc + 函数与参数类型 + sum(int4) + + + + regoper + pg_operator + 操作符名称 + + + + + + regoperator + pg_operator + 带参数类型的操作符 + *(integer,integer)-(NONE,integer) + + + + regclass + pg_class + 关系名称 + pg_type + + + + regtype + pg_type + 数据类型名称 + integer + + + + regrole + pg_authid + 角色名 + smithee + + + + regnamespace + pg_namespace + 命名空间名称 + pg_catalog + + + + regconfig + pg_ts_config + 文本搜索配置 + english + + + + regdictionary + pg_ts_dict + 文本搜索字典 + simple + + + +
+ + 对于按名字空间分组的对象,所有 OID 别名类型都接受模式限定名称;如果不加限定就无法在当前搜索路径中找到对象,输出时也会显示模式限定名称。regprocregoper别名类型只接受唯一的(未重载的)输入名称,因此用途有限;对于大多数用途,regprocedureregoperator更合适。对于regoperator,通过将未使用的操作数写为NONE来标识一元操作符。 + + + 大多数 OID 别名类型还有一个附加特性,就是会创建依赖关系。如果 + 这些类型的常量出现在存储的表达式中(例如列默认表达式或视图), + 它就会对被引用对象建立依赖。例如,如果某列的默认表达式是 + nextval('my_seq'::regclass), + PostgreSQL 就会知道该默认表达式依赖于 + 序列 my_seq,因此系统在删除该序列之前,必须先 + 移除该默认表达式。 + (regrole 是这一特性的例外:该类型的常量不允许出现在 + 存储表达式中。) + + + + OID 别名类型并不完全遵循事务隔离规则。规划器也会把它们视为简单常量,这可能导致次优的规划。 + + + 系统使用的另一种标识符类型是xid,即事务(缩写为xact)标识符。这是系统列xminxmax的数据类型。事务标识符是 32 位的量。 + + + 系统使用的第三种标识符类型是 cid,也就是命令标识符。 + 这是系统列 cmin 和 + cmax 的数据类型。命令标识符同样是 32 位量。 + + + + 系统使用的最后一种标识符类型是 tid,也就是元组标识符 + (行标识符)。这是系统列 ctid 的数据 + 类型。一个元组 ID 是一对值(块号、块内元组索引),用来标识该行在 + 其所属表中的物理位置。 + + + + (系统列的更多说明见 。) + +
+ + + <acronym>pg_lsn</acronym> 类型 + + + pg_lsn + + + + pg_lsn 数据类型可用于存储 LSN(日志序列号)数据。 + LSN 是指向 XLOG 中某个位置的指针。该类型是 + XLogRecPtr 的一种表示形式,也是 + PostgreSQL 的内部系统类型之一。 + + + + 在内部,LSN 是一个 64 位整数,表示预写式日志流中的字节位置。 + 它打印为两个最多 8 位的十六进制数,中间以斜线分隔,例如 + 16/B374D848pg_lsn 类型支持 + 标准比较操作符,如 = 和 + >。两个 LSN 可以用 - + 操作符相减,结果是这两个预写式日志位置之间相隔的字节数。 + + + + 伪类型 + + + record + + + + any + + + + anyelement + + + + anyarray + + + + anynonarray + + + + anyenum + + + + anyrange + + + + void + + + + trigger + + + + event_trigger + + + + pg_ddl_command + + + + language_handler + + + + fdw_handler + + + + index_am_handler + + + + tsm_handler + + + + cstring + + + + internal + + + + opaque + + + + PostgreSQL 的类型系统中包含一些特殊用途 + 的条目,统称为 伪类型。伪类型不能作为列 + 数据类型使用,但可以用于声明函数的参数类型或结果类型。每一种 + 伪类型都适用于这样的场景:函数的行为并不对应于简单地接受或返回 + 某个特定 SQL 数据类型的值。 + 列出了现有的伪类型。 + + + + 伪类型 + + + + 名字 + 描述 + + + + + + any + 表示一个函数可以接受任意输入数据类型。 + + + + anyelement + 表示一个函数可以接受任意数据类型(参见)。 + + + + anyarray + 表示一个函数可以接受任意数组数据类型(参见 + + + + anynonarray + 表示一个函数可以接受任意非数组数据类型(参见)。 + + + + anyenum + 表示一个函数可以接受任意枚举数据类型(参见)。 + + + + anyrange + 表示一个函数可以接受任意范围数据类型(参见)。 + + + + cstring + 表示一个函数接受或返回一个以空字符结尾的 C 字符串。 + + + + internal + 表示一个函数接受或返回一个服务器内部数据类型。 + + + + language_handler + 表示过程语言调用处理器被声明为返回 language_handler + + + + fdw_handler + 表示外部数据包装器处理器被声明为返回 fdw_handler + + + + index_am_handler + 表示索引访问方法处理器被声明为返回 index_am_handler + + + + tsm_handler + 表示表采样方法处理器被声明为返回 tsm_handler + + + + record + 标识一个接受或返回未指定行类型的函数。 + + + + trigger + 触发器函数被声明为返回trigger. + + + + event_trigger + 事件触发器函数被声明为返回event_trigger. + + + + pg_ddl_command + 标识一种对事件触发器可用的 DDL 命令的表达。 + + + + void + 表示一个函数不返回值。 + + + + opaque + 一个已废弃的类型名称,以前用于上述所有用途。 + + + +
+ + + 用 C 编写的函数(无论是内置的还是动态装载的)都可以声明为接受或 + 返回这些伪类型中的任意一种。函数作者必须自行确保,当伪类型被用作 + 参数类型时,该函数的行为仍然安全。 + + + 用过程语言编写的函数,只有在其实现语言允许的情况下才能使用伪类型。目前,大多数过程语言都禁止把伪类型用作参数类型,而只允许把voidrecord用作结果类型(如果函数被用作触发器或事件触发器,则也允许triggerevent_trigger)。有些语言还支持使用anyelementanyarrayanynonarrayanyenumanyrange实现多态函数。 + + + internal 伪类型用于声明那些只应由数据库系统内部调用、 + 而不应被 SQL 查询直接调用的函数。如果一个函数 + 至少有一个 internal 类型参数,那么它就不能从 + SQL 中调用。为了保持这一限制的类型安全性, + 必须遵守这样一条编码规则:除非函数至少有一个 + internal 类型参数,否则不要声明任何返回 + internal 的函数。 + + +
+ +
diff --git a/zh/9.6/datetime.sgml b/zh/9.6/datetime.sgml new file mode 100644 index 00000000..3e82e64e --- /dev/null +++ b/zh/9.6/datetime.sgml @@ -0,0 +1,623 @@ + + + + 日期/时间支持 + + + PostgreSQL 对所有日期/时间输入都使用一个内部的启发式解析器。日期和时间以字符串形式输入,并被拆分为不同的字段,同时初步判断每个字段中可能包含哪类信息。每个字段都会被解释,并被赋予数值、忽略或拒绝。解析器内部为所有文本字段都维护了查找表,包括月份、星期几以及时区。 + + + + 本附录包含关于这些查找表内容的信息,并描述了解析器解码日期和时间时所使用的步骤。 + + + + 日期/时间输入解释 + + + 日期/时间输入字符串按照下面的过程进行解码。 + + + + + + 将输入字符串拆分为词元,并把每个词元归类为字符串、时间、时区或数字。 + + + + + + 如果数字词元包含冒号(:),则它是一个时间字符串。把其后的所有数字和冒号都包含进来。 + + + + + + 如果数字词元包含连字符(-)、斜杠(/)或两个以上的点(.),则它是一个日期字符串,并且可能带有文本形式的月份。如果已经见过日期词元,则它会被解释为时区名称(例如 America/New_York)。 + + + + + + 如果词元仅由数字组成,那么它要么是单个字段,要么是 ISO 8601 的拼接式日期(例如,19990113 表示 1999 年 1 月 13 日)或时间(例如,141516 表示 14:15:16)。 + + + + + + 如果词元以加号(+)或减号(-)开头,那么它要么是数字时区,要么是特殊字段。 + + + + + + + + 如果词元是一个纯字母字符串,则将其与可能的字符串匹配: + + + + + + 先看该词元是否匹配任何已知的时区缩写。这些缩写由 中描述的配置设置决定。 + + + + + + 如果没有找到,则搜索内部表,将该词元识别为特殊字符串(例如 today)、星期名(例如 Thursday)、月份名(例如 January)或噪声词(例如 aton)。 + + + + + + 如果仍然没有找到,则抛出错误。 + + + + + + + + 当词元是一个数字或数字字段时: + + + + + + 如果有八位或六位数字,并且之前还没有读取到其他日期字段,则将其解释为拼接式日期(例如 19990118990118)。其解释方式分别为 YYYYMMDDYYMMDD。 + + + + + + 如果词元是三位数字,并且已经读取到了年份,则将其解释为一年中的第几天。 + + + + + + 如果是四位或六位数字,并且已经读取到了年份,则将其解释为时间(HHMMHHMMSS)。 + + + + + + 如果有三位或更多位数字,并且尚未找到任何日期字段,则将其解释为年份(这会强制其余日期字段按 yy-mm-dd 的顺序解释)。 + + + + + + 否则,假定日期字段顺序遵循 DateStyle 设置:mm-dd-yy、dd-mm-yy 或 yy-mm-dd。如果发现月份字段或日字段超出范围,则抛出错误。 + + + + + + + + 如果指定了 BC,则将年份取反并加一后再用于内部存储。(格里高利历中没有 0 年,因此从数值上说,公元前 1 年会变成年 0。) + + + + + 如果没有指定 BC,并且年份字段的长度为两位,则将年份调整为四位。如果该字段小于 70,则加上 2000,否则加上 1900。 格里高利历公元 1-99 年可以使用带前导零的四位数字输入(例如,0099 表示公元 99 年)。 + + + + + + + 处理无效或有歧义的时间戳 + + + 通常,如果一个日期/时间字符串在语法上有效,但包含超出范围的字段值,就会抛出错误。例如,指定 2 月 31 日的输入会被拒绝。 + + + + 在夏令时转换期间,看似有效的时间戳字符串有可能表示一个不存在的时间戳,或者表示一个有歧义的时间戳。这类情况不会被拒绝;系统通过确定应采用哪个 UTC 偏移来消除歧义。例如,假设 参数被设置为 America/New_York,请看: + +=> SELECT '2018-03-11 02:30'::timestamptz; + timestamptz +------------------------ + 2018-03-11 03:30:00-04 +(1 row) + + 因为那一天在该时区是春季拨快的转换日,所以并不存在民用时间 2:30AM;时钟从 2AM EST 直接跳到 3AM EDT。PostgreSQL 将给定时间按标准时间(UTC-5)来解释,因此显示为 3:30AM EDT(UTC-4)。 + + + + 相反,考虑秋季回拨转换期间的行为: + +=> SELECT '2018-11-04 02:30'::timestamptz; + timestamptz +------------------------ + 2018-11-04 02:30:00-05 +(1 row) + + 在那一天,2:30AM 有两种可能的解释;先有一次 2:30AM EDT,随后一小时后回拨到标准时间,于是又出现一次 2:30AM EST。再次,PostgreSQL 将给定时间按标准时间(UTC-5)来解释。我们可以通过显式指定夏令时来强制: + +=> SELECT '2018-11-04 02:30 EDT'::timestamptz; + timestamptz +------------------------ + 2018-11-04 01:30:00-05 +(1 row) + + 这个时间戳既可以合法地显示为 2:30 UTC-4,也可以显示为 1:30 UTC-5;时间戳输出代码选择了后者。 + + + + 在这类情况下适用的精确规则是:如果一个无效时间戳看起来落在夏令时前向跳转转换之内,则为它分配该时区在转换前刚刚生效的 UTC 偏移;如果一个有歧义的时间戳可能落在回拨转换前后任一侧,则为它分配该时区在转换后刚刚生效的 UTC 偏移。在大多数时区,这等价于说拿不准时优先采用标准时间的解释。 + + + + 在任何情况下,都可以显式指定与时间戳相关联的 UTC 偏移,方法是使用数字形式的 UTC 偏移,或者使用对应于固定 UTC 偏移的时区缩写。上述规则只在需要为 UTC 偏移会变化的时区推断 UTC 偏移时才适用。 + + + + + + 日期/时间关键字 + + + 展示了哪些词元会被识别为月份名称。 + + + + 月份名称 + + + + 月份 + 缩写 + + + + + January + Jan + + + February + Feb + + + March + Mar + + + April + Apr + + + May + + + + June + Jun + + + July + Jul + + + August + Aug + + + September + Sep, Sept + + + October + Oct + + + November + Nov + + + December + Dec + + + +
+ + + 展示了哪些词元会被识别为星期名称。 + + + + 星期名称 + + + + 星期 + 缩写 + + + + + Sunday + Sun + + + Monday + Mon + + + Tuesday + Tue, Tues + + + Wednesday + Wed, Weds + + + Thursday + Thu, Thur, Thurs + + + Friday + Fri + + + Saturday + Sat + + + +
+ + + 展示了用于各种修饰用途的词元。 + + + + 日期/时间字段修饰符 + + + + 标识符 + 说明 + + + + + AM + 时间早于 12:00 + + + AT + 忽略 + + + JULIAN, JD, J + 下一个字段是儒略日 + + + ON + 忽略 + + + PM + 时间为 12:00 或之后 + + + T + 下一个字段是时间 + + + +
+
+ + + 日期/时间配置文件 + + + 时区 + 输入缩写 + + + 由于时区缩写没有得到良好的标准化,PostgreSQL 提供了一种定制服务器所接受缩写集合的方法。运行时参数 决定当前使用的缩写集合。任何数据库用户都可以修改此参数,但它的可选值由数据库管理员控制;这些值实际上是安装目录中 .../share/timezonesets/ 下的配置文件名。管理员可以通过在该目录中添加或修改文件,制定本地时区缩写策略。 + + + 如果文件名完全由字母组成,那么 timezone_abbreviations 可以被设置为 .../share/timezonesets/ 中找到的任意文件名。(禁止在 timezone_abbreviations 中使用非字母字符,不仅可以防止读取目标目录之外的文件,也能防止读取编辑器备份文件和其他无关文件。) + + + 时区缩写文件可以包含空行,以及以下字符开头的注释:#。非注释行必须采用以下格式之一: +zone_abbreviation offset +zone_abbreviation offset D +zone_abbreviation time_zone_name +@INCLUDE file_name +@OVERRIDE + + + + zone_abbreviation 就是要定义的缩写。offset 是一个整数,表示相对于 UTC 的等效偏移秒数,正数表示格林尼治以东,负数表示以西。例如,-18000 表示格林尼治以西五小时,即北美东海岸标准时间。D 表示该时区名称代表当地夏令时间,而非标准时间。 + + 也可以给出 time_zone_name,引用 IANA 时区数据库中定义的时区名。系统会查阅该时区的定义,判断其中是否正在或曾经使用该缩写;如果是,则采用合适的含义:即正在确定其值的时间戳所处时刻使用的含义;如果该时刻并未使用此缩写,则取此前最近使用的含义;如果只在该时刻之后使用过,则取最早的含义。这对于处理含义曾经发生变化的缩写至关重要。也允许用一个并未出现该缩写的时区名来定义缩写;此时,使用缩写就等价于写出该时区名。 + + + + 如果某个缩写相对 UTC 的偏移从未改变过,那么定义它时最好使用简单的整数 offset,因为这类缩写的处理成本远低于那些需要查阅时区定义的缩写。 + + + + @INCLUDE 语法允许包含 .../share/timezonesets/ 目录中的另一个文件。包含可以嵌套,但深度有限。 + + @OVERRIDE 语法表示文件中后续的条目可以覆盖之前的条目(通常是从被包含文件中取得的条目)。如果没有此指令,同一时区缩写的冲突定义会被视为错误。 + + + 在未经修改的安装中,Default 文件包含世界上大多数地区中所有互不冲突的时区缩写。另外还为澳大利亚和印度这两个地区提供了 AustraliaIndia 文件:这些文件会先包含 Default 文件,然后按需添加或修改缩写。 + + + + 作为参考,标准安装还包含 Africa.txtAmerica.txt 等文件,其中包含 IANA 时区数据库中已知正在使用的每个时区缩写的信息。可以按需把这些文件中找到的时区名称定义复制并粘贴到自定义配置文件中。注意,这些文件不能直接作为 timezone_abbreviations 设置来引用,因为它们的名称中带有点号。 + + + + + 如果在读取时区缩写集时发生错误,则不会应用新值,而会保留旧集合。如果该错误发生在数据库启动期间,则数据库启动会失败。 + + + + + + 配置文件中定义的时区缩写会覆盖 PostgreSQL 内置的非时区含义。例如,Australia 配置文件定义了 SAT(南澳大利亚标准时间)。当该文件处于活动状态时,SAT 将不会被识别为 Saturday 的缩写。 + + + + + + 如果你修改了 .../share/timezonesets/ 中的文件,就需要自行做好备份 — 普通的数据库转储不会包含这个目录。 + + + + + + + <acronym>POSIX</acronym> 时区规范 + + + 时区 + POSIX 风格规范 + + + + PostgreSQL 可以接受按照 POSIX 标准为 TZ 环境变量规定的规则编写的时区规范。POSIX 时区规范不足以处理真实世界时区历史的复杂性,但有时仍有使用它们的理由。 + + + + POSIX 时区规范的形式为 + +STD offset DST dstoffset , rule + + (为便于阅读,这里把字段之间的空格写了出来,但实际使用时不应包含空格。)各字段含义如下: + + + + STD 是标准时间所使用的时区缩写。 + + + + + offset 是该时区标准时间相对 UTC 的偏移。 + + + + + DST 是夏令时所使用的时区缩写。如果省略这个字段以及其后的字段,则该时区使用固定的 UTC 偏移,没有夏令时规则。 + + + + + dstoffset 是夏令时相对 UTC 的偏移。这个字段通常会被省略,因为它默认比标准时间的 offset 少一小时,而这通常正是正确的设置。 + + + + + rule 定义夏令时何时生效的规则,如下所述。 + + + + + + + 在这种语法中,时区缩写可以是由字母组成的字符串,如 EST;也可以是用尖括号括起来的任意字符串,如 <UTC-05>。注意,这里给出的时区缩写只用于输出,而且即便如此,也只在某些时间戳输出格式中使用。时间戳输入中可识别的时区缩写按 中说明的方式确定。 + + + + 偏移字段指定与 UTC 的时差,可以写成小时,并可选地带分钟和秒。其格式为 hh:mm:ss,并可选带前导符号(+-)。正号用于格林尼治以西的时区。(注意,这与 PostgreSQL 其他地方使用的 ISO-8601 符号约定正好相反。)hh 可以是一位或两位数字;mmss(如果使用)必须是两位数字。 + + + + 夏令时转换 rule 的格式为 + +dstdate / dsttime , stddate / stdtime + + (同前,实际使用时不应包含空格。)dstdatedsttime 字段定义夏令时何时开始,而 stddatestdtime 定义标准时间何时开始。(在某些情况下,尤其是在赤道以南的时区中,前者在一年中可能晚于后者。)日期字段具有下列格式之一: + + + n + + + 一个普通整数表示一年中的第几天,从 0 开始计,到 364 为止;闰年则到 365 为止。 + + + + + Jn + + + 在这种形式中,n 从 1 计到 365,即使存在 2 月 29 日也不计入。(因此,发生在 2 月 29 日的转换无法用这种方式指定。不过,2 月之后的日期无论是否闰年,编号都相同,所以对于固定日期上的转换,这种形式通常比普通整数形式更有用。) + + + + + Mm.n.d + + + 这种形式指定一个总是发生在同一个月份、同一个星期几的转换。m 表示月份,范围是 1 到 12。n 指定由 d 标识的星期几在该月中第 n 次出现。n 是 1 到 4 之间的数字,或者是 5,表示该月中该星期几最后一次出现(可能是第四次,也可能是第五次)。d 是 0 到 6 之间的数字,其中 0 表示星期日。例如,M3.2.0 表示3 月的第二个星期日。 + + + + + + + + + M 格式足以描述许多常见的夏令时转换规则。但请注意,这些变体都无法处理夏令时法规的变更,因此在实践中,要正确解释过去的时间戳,就需要依赖命名时区中保存的历史数据(位于 IANA 时区数据库中)。 + + + + + 转换规则中的时间字段格式与前面描述的偏移字段相同,只是它们不能包含符号。它们定义了本地当前时间切换到另一种时间的时刻。如果省略,则默认值为 02:00:00。 + + + 如果给出了夏令时缩写,却省略了转换 rule 字段,PostgreSQL 会尝试通过查阅 IANA 时区数据库中的 posixrules 文件来确定转换时间。该文件的格式与完整时区条目相同,但这里只使用其转换时间规则,不使用其 UTC 偏移。通常,该文件与 US/Eastern 文件内容相同,因此 POSIX 风格的时区说明会遵循美国的夏令时规则。如有需要,可以替换 posixrules 文件来调整此行为。 + + + IANA 已弃用查阅 posixrules 文件的功能,它可能在未来被移除。此功能有一个缺陷:无法将夏令时规则应用于 2038 年之后的日期;在该功能消失前,这个缺陷也不太可能得到修复。 + + + 如果 posixrules 文件不存在,则回退为使用规则 M3.2.0,M11.1.0。这对应美国截至 2020 年的做法:在三月的第二个星期日向前调时钟,在十一月的第一个星期日向后调时钟,两次转换都发生在当时当地时间凌晨 2 点。 + + 例如,CET-1CEST,M3.5.0,M10.5.0/3 描述了巴黎当前(截至 2020 年)的计时做法。它表示标准时间缩写为 CET,比 UTC 提前一小时(位于其东侧);夏令时间缩写为 CEST,隐含比 UTC 提前两小时;夏令时于三月最后一个星期日凌晨 2 点 CET 开始,于十月最后一个星期日凌晨 3 点 CEST 结束。 + + 四个时区名 EST5EDTCST6CDTMST7MDTPST8PDT 看起来像 POSIX 时区说明,但实际上会作为命名时区处理,因为 IANA 时区数据库中出于历史原因存在这些名称的文件。这意味着,这些时区名能够给出正确的美国历史夏令时转换,即使普通 POSIX 说明因缺少合适的 posixrules 文件而无法做到。 + + + 需要注意的是,POSIX 风格时区规范很容易拼错,因为系统不会检查时区缩写是否合理。例如,SET TIMEZONE TO FOOBAR0 也能工作,从而使系统实际上使用了一个相当古怪的 UTC 缩写。 + + + + + + 历法的历史 + + + 格里高利历 + + + + SQL 标准指出,日期时间字面量的定义中,日期时间值受格里高利历中日期和时间自然规则的约束PostgreSQL 遵循 SQL 标准的做法,即便对于该历法尚未实际使用之前的年份,也完全按照格里高利历计算日期。这一规则称为 外推格里高利历。 + + + + 儒略历由 Julius Caesar 于公元前 45 年引入。在 1582 年各国开始改用格里高利历之前,它在西方世界一直被广泛使用。在儒略历中,回归年近似为 365 1/4 天 = 365.25 天。这会在大约 128 年里累计 1 天的误差。 + + + + 不断累积的历法误差促使教皇格里高利十三世按照特伦托会议的指示改革历法。在格里高利历中,回归年近似为 365 + 97 / 400 天 = 365.2425 天。因此,大约要经过 3300 年,回归年与格里高利历之间才会累积出 1 天的偏差。 + + + + 365+97/400 这一近似值是通过每 400 年设置 97 个闰年来实现的,具体规则如下: + + + + 每个能被 4 整除的年份都是闰年。 + + + 不过,每个能被 100 整除的年份都不是闰年。 + + + 但是,每个能被 400 整除的年份仍然是闰年。 + + + + 因此,1700、1800、1900、2100 和 2200 都不是闰年,但 1600、2000 和 2400 是闰年。 + + 相比之下,较早的儒略历规定所有能被 4 整除的年份都是闰年。 + + + + 1582 年 2 月发布的教皇诏书规定,应从 1582 年 10 月删去 10 天,使 10 月 15 日紧接在 10 月 4 日之后。这一改革在意大利、波兰、葡萄牙和西班牙得到执行。其他天主教国家不久后也跟进,但新教国家不愿改变,而希腊正教国家直到 20 世纪初才改用格里高利历。 + + 大不列颠及其属地(包括今天的美国)在 1752 年实施了这项改革。因此,1752 年 9 月 2 日之后紧接着就是 1752 年 9 月 14 日。 + + 这就是为什么提供 cal 程序的 Unix 系统会产生如下输出: + + +$ cal 9 1752 + September 1752 + S M Tu W Th F S + 1 2 14 15 16 +17 18 19 20 21 22 23 +24 25 26 27 28 29 30 + + + 不过,当然,这个历法只对大不列颠及其属地有效,对其他地方并不适用。由于尝试跟踪不同地方在不同时间实际采用的历法既困难又容易引起混淆,PostgreSQL 并不这样做,而是对所有日期都遵循格里高利历规则,尽管这种做法在历史上并不准确。 + + + + 世界上不同地区发展出了不同的历法,其中许多都早于格里高利体系。 + + 例如,中国历法的起源可以追溯到公元前 14 世纪。传说黄帝在公元前 2637 年发明了这种历法。 + + 中华人民共和国在民用方面使用格里高利历。中国历法则用于确定节日。 + + + + + + 儒略日 + + + 儒略日 + + + 儒略日系统是一种为天数编号的方法。它与儒略历并无关系,只是名称容易让人误以为二者有关。儒略日系统由法国学者 Joseph Justus Scaliger(1540-1609)发明,其名称大概取自他的父亲、意大利学者 Julius Caesar Scaliger(1484-1558)。 + + 在儒略日系统中,每一天都有一个连续编号,从 JD 0 开始(有时称为起始儒略日)。JD 0 对应儒略历公元前 4713 年 1 月 1 日,或格里高利历公元前 4714 年 11 月 24 日。儒略日计数最常用于天文学家标记夜间观测,因此一天从 UTC 正午开始,到下一个 UTC 正午结束,而不是从午夜到午夜:JD 0 表示从 UTC 公元前 4714 年 11 月 24 日正午,到 UTC 公元前 4714 年 11 月 25 日正午的 24 小时。 + + + 虽然 PostgreSQL 支持在日期输入和输出中使用儒略日记法(并且也会在某些内部日期/时间计算中使用儒略日),但它并不遵循日期从正午算到正午这一约定。PostgreSQL 将儒略日视为从本地午夜到本地午夜,与普通日期相同。 + + + 不过,这一定义也提供了在需要时获得天文学定义的方法:在以下时区中进行运算:UTC+12。例如, +=> SELECT extract(julian from '2021-06-23 7:00:00-04'::timestamptz at time zone 'UTC+12'); + date_part +-------------------- + 2459388.9583333335 +(1 row) +=> SELECT extract(julian from '2021-06-23 8:00:00-04'::timestamptz at time zone 'UTC+12'); + date_part +----------- + 2459389 +(1 row) +=> SELECT extract(julian from date '2021-06-23'); + date_part +----------- + 2459389 +(1 row) + + + + +
diff --git a/zh/9.6/dblink.sgml b/zh/9.6/dblink.sgml new file mode 100644 index 00000000..7c65b0eb --- /dev/null +++ b/zh/9.6/dblink.sgml @@ -0,0 +1,1959 @@ + + + + dblink + + + dblink + + + + dblink 是一个支持在数据库会话内连接到其他 PostgreSQL 数据库的模块。 + + + + 另请参见 ,它使用更现代且更符合标准的基础设施提供了基本相同的功能。 + + + + + dblink_connect + + + + dblink_connect + 3 + + + + dblink_connect + 打开到远程数据库的持久连接 + + + + +dblink_connect(text connstr) returns text +dblink_connect(text connname, text connstr) returns text + + + + + 描述 + + + dblink_connect() 建立到远程 + PostgreSQL 数据库的连接。要联系的服务器和数据库通过标准的 + libpq 连接字符串标识。可以为该连接指定一个名称。 + 可同时打开多个命名连接,但同一时间只允许存在一个未命名连接。该连接会一直保留到被关闭或数据库会话结束。 + + + + 连接字符串也可以是现有外部服务器的名称。定义该外部服务器时,建议使用外部数据包装器 + dblink_fdw。请参见下面的示例,以及 + 和 + 。 + + + + + + 参数 + + + + connname + + + 用于该连接的名称;如果省略,则打开一个未命名连接,并替换现有的未命名连接。 + + + + + + connstr + + libpq-风格的连接信息字符串,例如 + hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres + password=mypasswd options=-csearch_path=。 + 详见。 + 此外,也可以是外部服务器的名称。 + + + + + + + + 返回值 + + + 返回状态,总是 OK(因为任何错误都会导致该函数抛出错误,而不是返回)。 + + + + + 注解 + + + 如果不受信任的用户能够访问一个尚未采用模式的安全使用方式的数据库,则应在每个会话开始时从 + search_path 中移除公共可写模式。例如,可以把 + options=-csearch_path= 加到 connstr + 中。这个注意事项并非 dblink 所特有;它适用于每一种执行任意 SQL 命令的接口。 + + + + 只有超级用户才能使用 dblink_connect 创建不使用密码认证的连接。 + 如果非超级用户需要这种能力,请改用 + dblink_connect_u。 + + + + 选择包含等号的连接名称并不明智,因为这会导致在其他 + dblink 函数中与连接信息字符串产生混淆风险。 + + + + + 示例 + + +SELECT dblink_connect('dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_connect('myconn', 'dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +-- FOREIGN DATA WRAPPER 功能 +-- 注意:要使本例正常工作,本地连接必须要求密码认证。 +-- 否则,dblink_connect() 将报告以下错误: +-- ---------------------------------------------------------------------- +-- ERROR: password is required +-- DETAIL: Non-superuser cannot connect if the server does not request a password. +-- HINT: Target server's authentication method must be changed. + +CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression'); + +CREATE USER regress_dblink_user WITH PASSWORD 'secret'; +CREATE USER MAPPING FOR regress_dblink_user SERVER fdtest OPTIONS (user 'regress_dblink_user', password 'secret'); +GRANT USAGE ON FOREIGN SERVER fdtest TO regress_dblink_user; +GRANT SELECT ON TABLE foo TO regress_dblink_user; + +\set ORIGINAL_USER :USER +\c - regress_dblink_user +SELECT dblink_connect('myconn', 'fdtest'); + dblink_connect +---------------- + OK +(1 row) + +SELECT * FROM dblink('myconn', 'SELECT * FROM foo') AS t(a int, b text, c text[]); + a | b | c +----+---+--------------- + 0 | a | {a0,b0,c0} + 1 | b | {a1,b1,c1} + 2 | c | {a2,b2,c2} + 3 | d | {a3,b3,c3} + 4 | e | {a4,b4,c4} + 5 | f | {a5,b5,c5} + 6 | g | {a6,b6,c6} + 7 | h | {a7,b7,c7} + 8 | i | {a8,b8,c8} + 9 | j | {a9,b9,c9} + 10 | k | {a10,b10,c10} +(11 rows) + +\c - :ORIGINAL_USER +REVOKE USAGE ON FOREIGN SERVER fdtest FROM regress_dblink_user; +REVOKE SELECT ON TABLE foo FROM regress_dblink_user; +DROP USER MAPPING FOR regress_dblink_user SERVER fdtest; +DROP USER regress_dblink_user; +DROP SERVER fdtest; + + + + + + + dblink_connect_u + + + + dblink_connect_u + 3 + + + + dblink_connect_u + 不安全地打开到远程数据库的持久连接 + + + + +dblink_connect_u(text connstr) returns text +dblink_connect_u(text connname, text connstr) returns text + + + + + 描述 + + + dblink_connect_u() 与 + dblink_connect() 相同,不同之处在于它允许非超级用户使用任意认证方法进行连接。 + + + + 如果远程服务器选择的认证方法不涉及密码,则可能发生身份冒充以及随之而来的权限提升,因为该会话看起来会像是由运行本地 + PostgreSQL 服务器的那个用户发起的。 + 此外,即使远程服务器确实要求密码,密码也有可能来自服务器环境,例如属于服务器用户的 + ~/.pgpass 文件。这不仅带来身份冒充的风险,也可能将密码暴露给不可信的远程服务器。 + 因此,dblink_connect_u() 在初始安装时会撤销 + PUBLIC 的全部权限,从而除了超级用户之外无法调用它。 + 在某些情况下,可能适合向被认为可信的特定用户授予 + dblink_connect_u()EXECUTE 权限,但必须谨慎操作。还建议服务器用户的任何 + ~/.pgpass 文件不要包含指定通配主机名的记录。 + + + + 更多细节请参见 dblink_connect()。 + + + + + + + dblink_disconnect + + + + dblink_disconnect + 3 + + + + dblink_disconnect + 关闭到远程数据库的持久连接 + + + + +dblink_disconnect() returns text +dblink_disconnect(text connname) returns text + + + + + 描述 + + + dblink_disconnect() 关闭先前由 + dblink_connect() 打开的连接。不带参数的形式会关闭未命名连接。 + + + + + 参数 + + + + connname + + + 要关闭的命名连接名称。 + + + + + + + + 返回值 + + + 返回状态,总是 OK(因为任何错误都会导致该函数抛出错误,而不是返回)。 + + + + + 示例 + + +SELECT dblink_disconnect(); + dblink_disconnect +------------------- + OK +(1 row) + +SELECT dblink_disconnect('myconn'); + dblink_disconnect +------------------- + OK +(1 row) + + + + + + + dblink + + + + dblink + 3 + + + + dblink + 在远程数据库中执行查询 + + + + +dblink(text connname, text sql [, bool fail_on_error]) returns setof record +dblink(text connstr, text sql [, bool fail_on_error]) returns setof record +dblink(text sql [, bool fail_on_error]) returns setof record + + + + + 描述 + + + dblink 在远程数据库中执行查询(通常是 + SELECT,但也可以是任意返回行的 SQL 语句)。 + + + + 当提供两个 text 参数时,首先会将第一个参数作为持久连接名称查找;如果找到,就在该连接上执行命令。如果没有找到,则把第一个参数视为像 + dblink_connect 那样的连接信息字符串,并且只在本命令执行期间建立所指明的连接。 + + + + + 参数 + + + + connname + + + 要使用的连接名称;省略该参数则使用未命名连接。 + + + + + + connstr + + + 如前面对 dblink_connect 所描述的连接信息字符串。 + + + + + + sql + + + 要在远程数据库中执行的 SQL 查询,例如 + select * from foo。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数不返回任何行。 + + + + + + + + 返回值 + + + 该函数返回查询产生的行。由于 dblink 可以与任意查询一起使用,因此它被声明为返回 + record,而不是指定某个特定的列集。这意味着必须在调用查询中指定预期的列集 — 否则 + PostgreSQL 无法知道应该期待什么。以下是一个示例: + + +SELECT * + FROM dblink('dbname=mydb options=-csearch_path=', + 'select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text) + WHERE proname LIKE 'bytea%'; + + + FROM 子句中的 别名 部分必须指定该函数将返回的列名和类型。(在别名中指定列名实际上是标准 SQL 语法,但指定列类型则是 + PostgreSQL 的扩展。)这样系统才能在尝试执行该函数之前,就知道 + * 应展开成什么,以及 WHERE 子句中的 + proname 指的是什么。运行时,如果远程数据库返回的实际查询结果与 + FROM 子句中列出的列数不同,就会抛出错误。不过,列名不必匹配, + dblink 也不要求类型完全一致。只要返回的数据字符串可以作为 + FROM 子句中声明的列类型的有效输入,它就会成功。 + + + + + 注解 + + + 将预定义查询用于 dblink 的一种方便方式是创建视图。这样就可以把列类型信息隐藏在视图中,而不必在每个查询里都重复写出。例如, + + +CREATE VIEW myremote_pg_proc AS + SELECT * + FROM dblink('dbname=postgres options=-csearch_path=', + 'select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text); + +SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%'; + + + + + 示例 + + +SELECT * FROM dblink('dbname=postgres options=-csearch_path=', + 'select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%'; + proname | prosrc +------------+------------ + byteacat | byteacat + byteaeq | byteaeq + bytealt | bytealt + byteale | byteale + byteagt | byteagt + byteage | byteage + byteane | byteane + byteacmp | byteacmp + bytealike | bytealike + byteanlike | byteanlike + byteain | byteain + byteaout | byteaout +(12 rows) + +SELECT dblink_connect('dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT * FROM dblink('select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%'; + proname | prosrc +------------+------------ + byteacat | byteacat + byteaeq | byteaeq + bytealt | bytealt + byteale | byteale + byteagt | byteagt + byteage | byteage + byteane | byteane + byteacmp | byteacmp + bytealike | bytealike + byteanlike | byteanlike + byteain | byteain + byteaout | byteaout +(12 rows) + +SELECT dblink_connect('myconn', 'dbname=regression options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT * FROM dblink('myconn', 'select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%'; + proname | prosrc +------------+------------ + bytearecv | bytearecv + byteasend | byteasend + byteale | byteale + byteagt | byteagt + byteage | byteage + byteane | byteane + byteacmp | byteacmp + bytealike | bytealike + byteanlike | byteanlike + byteacat | byteacat + byteaeq | byteaeq + bytealt | bytealt + byteain | byteain + byteaout | byteaout +(14 rows) + + + + + + + dblink_exec + + + + dblink_exec + 3 + + + + dblink_exec + 在远程数据库中执行命令 + + + + +dblink_exec(text connname, text sql [, bool fail_on_error]) returns text +dblink_exec(text connstr, text sql [, bool fail_on_error]) returns text +dblink_exec(text sql [, bool fail_on_error]) returns text + + + + + 描述 + + + dblink_exec 在远程数据库中执行命令(即任何不返回行的 SQL 语句)。 + + + + 当提供两个 text 参数时,首先会将第一个参数作为持久连接名称查找;如果找到,就在该连接上执行命令。如果没有找到,则把第一个参数视为像 + dblink_connect 那样的连接信息字符串,并且只在本命令执行期间建立所指明的连接。 + + + + + 参数 + + + + connname + + + 要使用的连接名称;省略该参数则使用未命名连接。 + + + + + + connstr + + + 如前面对 dblink_connect 所描述的连接信息字符串。 + + + + + + sql + + + 要在远程数据库中执行的 SQL 命令,例如 + insert into foo values(0, 'a', '{"a0","b0","c0"}')。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数的返回值会被设为 ERROR。 + + + + + + + + 返回值 + + + 返回状态,可以是命令的状态字符串,也可以是 ERROR。 + + + + + 示例 + + +SELECT dblink_connect('dbname=dblink_test_standby'); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_exec('insert into foo values(21, ''z'', ''{"a0","b0","c0"}'');'); + dblink_exec +----------------- + INSERT 943366 1 +(1 row) + +SELECT dblink_connect('myconn', 'dbname=regression'); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_exec('myconn', 'insert into foo values(21, ''z'', ''{"a0","b0","c0"}'');'); + dblink_exec +------------------ + INSERT 6432584 1 +(1 row) + +SELECT dblink_exec('myconn', 'insert into pg_class values (''foo'')',false); +NOTICE: sql error +DETAIL: ERROR: null value in column "relnamespace" violates not-null constraint + + dblink_exec +------------- + ERROR +(1 row) + + + + + + + dblink_open + + + + dblink_open + 3 + + + + dblink_open + 在远程数据库中打开游标 + + + + +dblink_open(text cursorname, text sql [, bool fail_on_error]) returns text +dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) returns text + + + + + 描述 + + + dblink_open() 在远程数据库中打开游标。随后可以使用 + dblink_fetch()dblink_close() 操作该游标。 + + + + + 参数 + + + + connname + + + 要使用的连接名称;省略该参数则使用未命名连接。 + + + + + + cursorname + + + 要分配给该游标的名称。 + + + + + + sql + + + 要在远程数据库中执行的 SELECT 语句,例如 + select * from pg_class。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数的返回值会被设为 ERROR。 + + + + + + + + 返回值 + + + 返回状态,可以是 OKERROR。 + + + + + 注解 + + + 由于游标只能在事务中持续存在,如果远程端当前尚未处于事务中, + dblink_open 会在远程端启动一个显式事务块(BEGIN)。 + 当执行匹配的 dblink_close 时,该事务会再次关闭。注意,如果在 + dblink_opendblink_close 之间用 + dblink_exec 修改了数据,随后又发生错误,或者在调用 + dblink_close 之前使用了 dblink_disconnect,所做的更改将会丢失,因为该事务会被中止。 + + + + + 示例 + + +SELECT dblink_connect('dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_open('foo', 'select proname, prosrc from pg_proc'); + dblink_open +------------- + OK +(1 row) + + + + + + + dblink_fetch + + + + dblink_fetch + 3 + + + + dblink_fetch + 返回远程数据库中已打开游标的行 + + + + +dblink_fetch(text cursorname, int howmany [, bool fail_on_error]) returns setof record +dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error]) returns setof record + + + + + 描述 + + + dblink_fetch 从先前由 + dblink_open 建立的游标中提取行。 + + + + + 参数 + + + + connname + + + 要使用的连接名称;省略该参数则使用未命名连接。 + + + + + + cursorname + + + 要从中提取数据的游标名称。 + + + + + + howmany + + + 要检索的最大行数。从当前游标位置开始,向前提取接下来的 + howmany 行。一旦游标到达末尾,就不会再产生更多行。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数不返回任何行。 + + + + + + + + 返回值 + + + 该函数返回从游标提取的行。要使用此函数,需要像前面讨论 + dblink 时那样,指定预期的列集。 + + + + + 注解 + + + 如果 FROM 子句中指定的返回列数与远程游标实际返回的列数不一致,就会抛出错误。在这种情况下,远程游标仍会像没有发生该错误一样前进相同的行数。远程 + FETCH 完成之后,本地查询中发生的任何其他错误也是如此。 + + + + + 示例 + + +SELECT dblink_connect('dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_open('foo', 'select proname, prosrc from pg_proc where proname like ''bytea%'''); + dblink_open +------------- + OK +(1 row) + +SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text); + funcname | source +----------+---------- + byteacat | byteacat + byteacmp | byteacmp + byteaeq | byteaeq + byteage | byteage + byteagt | byteagt +(5 rows) + +SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text); + funcname | source +-----------+----------- + byteain | byteain + byteale | byteale + bytealike | bytealike + bytealt | bytealt + byteane | byteane +(5 rows) + +SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text); + funcname | source +------------+------------ + byteanlike | byteanlike + byteaout | byteaout +(2 rows) + +SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text); + funcname | source +----------+-------- +(0 rows) + + + + + + + dblink_close + + + + dblink_close + 3 + + + + dblink_close + 关闭远程数据库中的游标 + + + + +dblink_close(text cursorname [, bool fail_on_error]) returns text +dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text + + + + + 描述 + + + dblink_close 关闭先前由 + dblink_open 打开的游标。 + + + + + 参数 + + + + connname + + + 要使用的连接名称;省略该参数则使用未命名连接。 + + + + + + cursorname + + + 要关闭的游标名。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数的返回值会被设为 ERROR。 + + + + + + + + 返回值 + + + 返回状态,可以是 OKERROR。 + + + + + 注解 + + + 如果 dblink_open 启动了显式事务块,并且这是此连接上最后一个仍然打开的游标,dblink_close 将发出对应的 + COMMIT。 + + + + + 示例 + + +SELECT dblink_connect('dbname=postgres options=-csearch_path='); + dblink_connect +---------------- + OK +(1 row) + +SELECT dblink_open('foo', 'select proname, prosrc from pg_proc'); + dblink_open +------------- + OK +(1 row) + +SELECT dblink_close('foo'); + dblink_close +-------------- + OK +(1 row) + + + + + + + dblink_get_connections + + + + dblink_get_connections + 3 + + + + dblink_get_connections + 返回所有打开的命名 dblink 连接的名称 + + + + +dblink_get_connections() returns text[] + + + + + 描述 + + + dblink_get_connections 返回包含所有已打开命名 + dblink 连接名称的数组。 + + + + + 返回值 + + 返回包含连接名称的文本数组;如果没有连接,则返回 NULL。 + + + + 示例 + + +SELECT dblink_get_connections(); + + + + + + + dblink_error_message + + + + dblink_error_message + 3 + + + + dblink_error_message + 获取命名连接上的最后一条错误消息 + + + + +dblink_error_message(text connname) returns text + + + + + 描述 + + + dblink_error_message 获取给定连接上最近一次的远程错误消息。 + + + + + 参数 + + + + connname + + + 要使用的连接名称。 + + + + + + + + 返回值 + + + 返回最后一条错误消息;如果该连接没有发生错误,则返回 OK。 + + + + + 注解 + + + 在由 dblink_send_query 发起异步查询时,与该连接关联的错误消息在读取服务器响应消息之前可能不会更新。这通常意味着应该先调用 + dblink_is_busydblink_get_result,再调用 + dblink_error_message,以便看到异步查询产生的任何错误。 + + + + + 示例 + + +SELECT dblink_error_message('dtest1'); + + + + + + + dblink_send_query + + + + dblink_send_query + 3 + + + + dblink_send_query + 向远程数据库发送异步查询 + + + + +dblink_send_query(text connname, text sql) returns int + + + + + 描述 + + + dblink_send_query 发送要异步执行的查询,也就是不会立即等待结果。该连接上不能已经有正在进行的异步查询。 + + + + 异步查询成功发送后,可以用 dblink_is_busy 检查完成状态,并最终通过 dblink_get_result 收集结果。也可以使用 + dblink_cancel_query 尝试取消活动中的异步查询。 + + + + + 参数 + + + + connname + + + 要使用的连接名称。 + + + + + + sql + + + 要在远程数据库中执行的 SQL 语句,例如select * from pg_class。 + + + + + + + + 返回值 + + + 如果查询已成功发送,则返回 1;否则返回 0。 + + + + + 示例 + + +SELECT dblink_send_query('dtest1', 'SELECT * FROM foo WHERE f1 < 3'); + + + + + + + dblink_is_busy + + + + dblink_is_busy + 3 + + + + dblink_is_busy + 检查连接是否正忙于异步查询 + + + + +dblink_is_busy(text connname) returns int + + + + + 描述 + + + dblink_is_busy 测试是否有异步查询正在进行。 + + + + + 参数 + + + + connname + + + 要检查的连接名称。 + + + + + + + + 返回值 + + + 如果连接正忙则返回 1,否则返回 0。如果此函数返回 0,则可以保证 + dblink_get_result 不会阻塞。 + + + + + 示例 + + +SELECT dblink_is_busy('dtest1'); + + + + + + + dblink_get_notify + + + + dblink_get_notify + 3 + + + + dblink_get_notify + 检索连接上的异步通知 + + + + +dblink_get_notify() returns setof (notify_name text, be_pid int, extra text) +dblink_get_notify(text connname) returns setof (notify_name text, be_pid int, extra text) + + + + + 描述 + + + dblink_get_notify 检索未命名连接上的通知;如果指定了命名连接,则检索该连接上的通知。要通过 dblink 接收通知,必须先使用 + dblink_exec 执行 LISTEN。详见 + 。 + + + + + + 参数 + + + + connname + + + 要检索通知的命名连接名称。 + + + + + + + + 返回值 + 返回 setof (notify_name text, be_pid int, extra text);如果没有,则返回空集。 + + + + 示例 + + +SELECT dblink_exec('LISTEN virtual'); + dblink_exec +------------- + LISTEN +(1 row) + +SELECT * FROM dblink_get_notify(); + notify_name | be_pid | extra +-------------+--------+------- +(0 rows) + +NOTIFY virtual; +NOTIFY + +SELECT * FROM dblink_get_notify(); + notify_name | be_pid | extra +-------------+--------+------- + virtual | 1229 | +(1 row) + + + + + + + dblink_get_result + + + + dblink_get_result + 3 + + + + dblink_get_result + 获取异步查询结果 + + + + +dblink_get_result(text connname [, bool fail_on_error]) returns setof record + + + + + 描述 + + + dblink_get_result 收集此前用 + dblink_send_query 发送的异步查询结果。如果该查询尚未完成, + dblink_get_result 将等待直至其完成。 + + + + + 参数 + + + + connname + + + 要使用的连接名称。 + + + + + + fail_on_error + + + 如果为真(省略时默认),则连接远程端抛出的错误也会在本地抛出。如果为假,远程端错误会在本地报告为一个 NOTICE,并且该函数不返回任何行。 + + + + + + + + 返回值 + + + 对于异步查询(即返回行的 SQL 语句),该函数返回查询产生的行。要使用此函数,需要像前面讨论 + dblink 时那样,指定预期的列集。 + + + + 对于异步命令(即不返回行的 SQL 语句),该函数返回一个只有一个 text 列的单行,其中包含命令的状态字符串。即便如此,仍需要在调用查询的 + FROM 子句中指定结果只有一个 text 列。 + + + + + 注解 + + + 如果 dblink_send_query 返回 1,则必须调用此函数。每发送一个查询都必须调用一次,并且还要额外再调用一次以获得空集结果,之后该连接才能再次使用。 + + + + 当使用 dblink_send_query 和 + dblink_get_result 时,dblink 会在向本地查询处理器返回任何结果行之前,先获取整个远程查询结果。如果查询返回大量行,这可能导致本地会话暂时出现内存膨胀。对于此类查询,最好用 + dblink_open 将其作为游标打开,然后每次提取可管理数量的行。另一种做法是使用普通的 + dblink(),它会将大型结果集溢写到磁盘,从而避免内存膨胀。 + + + + + 示例 + + +contrib_regression=# SELECT dblink_connect('dtest1', 'dbname=contrib_regression'); + dblink_connect +---------------- + OK +(1 row) + +contrib_regression=# SELECT * FROM +contrib_regression-# dblink_send_query('dtest1', 'select * from foo where f1 < 3') AS t1; + t1 +---- + 1 +(1 row) + +contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]); + f1 | f2 | f3 +----+----+------------ + 0 | a | {a0,b0,c0} + 1 | b | {a1,b1,c1} + 2 | c | {a2,b2,c2} +(3 rows) + +contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]); + f1 | f2 | f3 +----+----+---- +(0 rows) + +contrib_regression=# SELECT * FROM +contrib_regression-# dblink_send_query('dtest1', 'select * from foo where f1 < 3; select * from foo where f1 > 6') AS t1; + t1 +---- + 1 +(1 row) + +contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]); + f1 | f2 | f3 +----+----+------------ + 0 | a | {a0,b0,c0} + 1 | b | {a1,b1,c1} + 2 | c | {a2,b2,c2} +(3 rows) + +contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]); + f1 | f2 | f3 +----+----+--------------- + 7 | h | {a7,b7,c7} + 8 | i | {a8,b8,c8} + 9 | j | {a9,b9,c9} + 10 | k | {a10,b10,c10} +(4 rows) + +contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]); + f1 | f2 | f3 +----+----+---- +(0 rows) + + + + + + + dblink_cancel_query + + + + dblink_cancel_query + 3 + + + + dblink_cancel_query + 取消命名连接上的任何活动查询 + + + + +dblink_cancel_query(text connname) returns text + + + + + 描述 + + + dblink_cancel_query 尝试取消命名连接上正在进行的任何查询。注意,这不一定会成功(例如远程查询可能已经结束)。取消请求只会提高该查询很快失败的概率。仍必须完成正常的查询协议,例如调用 + dblink_get_result。 + + + + + 参数 + + + + connname + + + 要使用的连接名称。 + + + + + + + + 返回值 + + + 如果取消请求已发送,则返回 OK;如果失败,则返回错误消息文本。 + + + + + 示例 + + +SELECT dblink_cancel_query('dtest1'); + + + + + + + dblink_get_pkey + + + + dblink_get_pkey + 3 + + + + dblink_get_pkey + 返回关系主键字段的位置和字段名 + + + + + +dblink_get_pkey(text relname) returns setof dblink_pkey_results + + + + + 描述 + + + dblink_get_pkey 提供有关本地数据库中某个关系主键的信息。这有时有助于生成要发送到远程数据库的查询。 + + + + + 参数 + + + + relname + + + 本地关系的名称,例如 foo 或 + myschema.mytab。如果该名称是大小写混合的或包含特殊字符,则应包含双引号,例如 + "FooBar";如果没有引号,字符串将被折叠为小写形式。 + + + + + + + + 返回值 + + + 为每个主键字段返回一行;如果该关系没有主键,则不返回任何行。结果行类型定义如下: + + +CREATE TYPE dblink_pkey_results AS (position int, colname text); + + + position 列只是从 1 到 N 递增;它表示该字段在主键中的序号,而不是在表列中的序号。 + + + + + 示例 + + +CREATE TABLE foobar ( + f1 int, + f2 int, + f3 int, + PRIMARY KEY (f1, f2, f3) +); +CREATE TABLE + +SELECT * FROM dblink_get_pkey('foobar'); + position | colname +----------+--------- + 1 | f1 + 2 | f2 + 3 | f3 +(3 rows) + + + + + + + dblink_build_sql_insert + + + + dblink_build_sql_insert + 3 + + + + dblink_build_sql_insert + + 使用本地元组构造 INSERT 语句,并用提供的替代值替换主键字段值 + + + + + +dblink_build_sql_insert(text relname, + int2vector primary_key_attnums, + integer num_primary_key_atts, + text[] src_pk_att_vals_array, + text[] tgt_pk_att_vals_array) returns text + + + + + 描述 + + + dblink_build_sql_insert 可用于将本地表有选择地复制到远程数据库。它根据主键从本地表中选取一行,然后构建一条会复制该行的 SQL + INSERT 命令,但主键值会替换为最后一个参数中的值。(若要精确复制该行,只需为最后两个参数指定相同的值。) + + + + + 参数 + + + + relname + + + 本地关系的名称,例如 foo 或 + myschema.mytab。如果该名称是大小写混合的或包含特殊字符,则应包含双引号,例如 + "FooBar";如果没有引号,字符串将被折叠为小写形式。 + + + + + + primary_key_attnums + + + 主键字段的属性编号(从 1 开始),例如 1 2。 + + + + + + num_primary_key_atts + + + 主键字段数量。 + + + + + + src_pk_att_vals_array + + + 用于查找本地元组的主键字段值。每个字段都以文本形式表示。如果没有本地行具有这些主键值,则抛出错误。 + + + + + + tgt_pk_att_vals_array + + + 放入结果 INSERT 命令中的主键字段值。每个字段都以文本形式表示。 + + + + + + + + 返回值 + + 以文本形式返回所请求的 SQL 语句。 + + + + 注解 + + + 从 PostgreSQL 9.0 起, + primary_key_attnums 中的属性编号会被解释为逻辑列号,对应于列在 + SELECT * FROM relname 中的位置。早期版本将这些编号解释为物理列位置。如果在表的生命周期中,指示列左侧的某些列已被删除,两者就会有差异。 + + + + + 示例 + + +SELECT dblink_build_sql_insert('foo', '1 2', 2, '{"1", "a"}', '{"1", "b''a"}'); + dblink_build_sql_insert +-------------------------------------------------- + INSERT INTO foo(f1,f2,f3) VALUES('1','b''a','1') +(1 row) + + + + + + + dblink_build_sql_delete + + + + dblink_build_sql_delete + 3 + + + + dblink_build_sql_delete + 使用提供的主键字段值构造 DELETE 语句 + + + + + +dblink_build_sql_delete(text relname, + int2vector primary_key_attnums, + integer num_primary_key_atts, + text[] tgt_pk_att_vals_array) returns text + + + + + 描述 + + + dblink_build_sql_delete 可用于将本地表有选择地复制到远程数据库。它会构建一条 SQL + DELETE 命令,用来删除具有给定主键值的行。 + + + + + 参数 + + + + relname + + + 本地关系的名称,例如 foo 或 + myschema.mytab。如果该名称是大小写混合的或包含特殊字符,则应包含双引号,例如 + "FooBar";如果没有引号,字符串将被折叠为小写形式。 + + + + + + primary_key_attnums + + + 主键字段的属性编号(从 1 开始),例如 1 2。 + + + + + + num_primary_key_atts + + + 主键字段数量。 + + + + + + tgt_pk_att_vals_array + + + 用于结果 DELETE 命令中的主键字段值。每个字段都以文本形式表示。 + + + + + + + + 返回值 + + 以文本形式返回所请求的 SQL 语句。 + + + + 注解 + + + 从 PostgreSQL 9.0 起, + primary_key_attnums 中的属性编号会被解释为逻辑列号,对应于列在 + SELECT * FROM relname 中的位置。早期版本将这些编号解释为物理列位置。如果在表的生命周期中,指示列左侧的某些列已被删除,两者就会有差异。 + + + + + 示例 + + +SELECT dblink_build_sql_delete('"MyFoo"', '1 2', 2, '{"1", "b"}'); + dblink_build_sql_delete +--------------------------------------------- + DELETE FROM "MyFoo" WHERE f1='1' AND f2='b' +(1 row) + + + + + + + dblink_build_sql_update + + + + dblink_build_sql_update + 3 + + + + dblink_build_sql_update + 使用本地元组构造 UPDATE 语句,并用提供的替代值替换主键字段值 + + + + + +dblink_build_sql_update(text relname, + int2vector primary_key_attnums, + integer num_primary_key_atts, + text[] src_pk_att_vals_array, + text[] tgt_pk_att_vals_array) returns text + + + + + 描述 + + + dblink_build_sql_update 可用于将本地表有选择地复制到远程数据库。它根据主键从本地表中选取一行,然后构建一条会复制该行的 SQL + UPDATE 命令,但主键值会替换为最后一个参数中的值。(若要精确复制该行,只需为最后两个参数指定相同的值。)UPDATE 命令总是对该行的所有字段赋值 — 它与 + dblink_build_sql_insert 的主要区别在于,前者假定目标行已经存在于远程表中。 + + + + + 参数 + + + + relname + + + 本地关系的名称,例如 foo 或 + myschema.mytab。如果该名称是大小写混合的或包含特殊字符,则应包含双引号,例如 + "FooBar";如果没有引号,字符串将被折叠为小写形式。 + + + + + + primary_key_attnums + + + 主键字段的属性编号(从 1 开始),例如 1 2。 + + + + + + num_primary_key_atts + + + 主键字段数量。 + + + + + + src_pk_att_vals_array + + + 用于查找本地元组的主键字段值。每个字段都以文本形式表示。如果没有本地行具有这些主键值,则抛出错误。 + + + + + + tgt_pk_att_vals_array + + + 用于结果 UPDATE 命令中的主键字段值。每个字段都以文本形式表示。 + + + + + + + + 返回值 + + 以文本形式返回所请求的 SQL 语句。 + + + + 注解 + + + 从 PostgreSQL 9.0 起, + primary_key_attnums 中的属性编号会被解释为逻辑列号,对应于列在 + SELECT * FROM relname 中的位置。早期版本将这些编号解释为物理列位置。如果在表的生命周期中,指示列左侧的某些列已被删除,两者就会有差异。 + + + + + 示例 + + +SELECT dblink_build_sql_update('foo', '1 2', 2, '{"1", "a"}', '{"1", "b"}'); + dblink_build_sql_update +------------------------------------------------------------- + UPDATE foo SET f1='1',f2='b',f3='1' WHERE f1='1' AND f2='b' +(1 row) + + + + + diff --git a/zh/9.6/ddl.sgml b/zh/9.6/ddl.sgml new file mode 100644 index 00000000..d931381f --- /dev/null +++ b/zh/9.6/ddl.sgml @@ -0,0 +1,2608 @@ + + + + 数据定义 + + + 本章介绍如何创建用于保存数据的数据库结构。在关系数据库中,原始数据存储在表中,因此本章的大部分内容都在说明如何创建和修改表,以及有哪些特性可用于控制表中存储的数据。随后,我们将讨论如何把表组织到模式中,以及如何向表分配权限。最后,我们会简要介绍一些同样会影响数据存储的特性,例如继承、视图、函数和触发器。 + + + + + 表基础 + + + table + + + + row + + + + column + + + + 关系数据库中的表很像纸面上的表格:它由行和列组成。列的数量和顺序是固定的,每一列都有名称。行的数量则是可变的,它反映了某一时刻存储了多少数据。SQL 不对表中行的顺序作任何保证。读取表时,除非显式要求排序,否则行会以未指定的顺序出现,参见。此外,SQL 不会为行分配唯一标识符,因此一个表中可能出现多行完全相同的记录。这是 SQL 所依据的数学模型带来的结果,但通常并不理想。本章稍后将看到如何处理这个问题。 + + + + 每一列都有一种数据类型。数据类型限制了可以赋给该列的可能值集合,并为列中存储的数据赋予语义,从而使其能够用于计算。例如,声明为数值类型的列不能接受任意文本字符串,存储在这类列中的数据可以参与数学运算。相比之下,声明为字符串类型的列几乎能接受任何种类的数据,但并不适合用于数学计算,尽管仍可进行字符串拼接等其他操作。 + + + + PostgreSQL提供了相当丰富的一组内置数据类型,适用于许多应用场景。用户也可以定义自己的数据类型。大多数内置数据类型的名称和语义都很直观,因此我们把详细说明留到。一些常用的数据类型包括:表示整数的integer、表示可能带小数部分数值的numeric、表示字符串的text、表示日期的date、表示一天中时间值的time,以及同时包含日期和时间的timestamp。 + + + + table + creating + + + + 要创建一个表,可使用名副其实的命令。在该命令中,至少要指定新表的名称、各列的名称以及每列的数据类型。例如: + +CREATE TABLE my_first_table ( + first_column text, + second_column integer +); + + 这会创建一个名为my_first_table的表,它有两列。第一列名为first_column,数据类型为text;第二列名为second_column,类型为integer。表名和列名遵循中解释的标识符语法。类型名通常也是标识符,但也有少数例外。注意,列列表以逗号分隔,并用圆括号括起来。 + + + + 当然,前面的例子相当刻意。通常,你会为表和列起能体现其所存储数据含义的名称。下面看一个更实际的例子: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric +); + + (numeric类型可以存储小数部分,这对金额很常见。) + + + + + + 在创建许多相互关联的表时,最好为表和列选择一致的命名模式。例如,表名可以使用单数名词或复数名词,两种做法都各有支持者。 + + + + + 一个表能包含的列数是有限的。根据列类型不同,这个上限在 250 到 1600 之间。不过,定义一个接近这个数量的表极不常见,而且其设计往往值得商榷。 + + + + table + removing + + + + 如果不再需要某个表,可以使用命令删除它。例如: + +DROP TABLE my_first_table; +DROP TABLE products; + + 尝试删除不存在的表会报错。不过,在 SQL 脚本文件中,人们常常在创建每个表之前无条件地尝试删除它,并忽略可能出现的错误消息,这样脚本无论表是否存在都能工作。(如果愿意,也可以使用DROP TABLE IF EXISTS变体来避免错误消息,但这不是标准 SQL。) + + + + 如果我们需要修改一个已经存在的表,请参考本章稍后的。 + + + + 利用到目前为止所讨论的工具,我们已经可以创建功能完备的表。本章其余内容关注如何向表定义添加特性,以确保数据完整性、安全性或使用便利。如果你现在就急着向表中填充数据,可以先跳到,稍后再回来阅读本章其余内容。 + + + + + + 默认值 + + + default value + + + + 列可以被赋予默认值。当创建新行时,如果没有为某些列指定值,这些列就会填入各自的默认值。数据操作命令也可以显式要求把某列设为其默认值,而不必知道默认值究竟是什么。(数据操作命令详见。) + + + + null valuedefault value + 如果没有显式指定默认值,则默认值是空值。这是合理的,因为空值表示未知数据。 + + + + 在表定义中,默认值写在列的数据类型之后。例如: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric DEFAULT 9.99 +); + + + + + 默认值也可以是表达式。每次需要插入默认值时都会对其求值(不是在创建表时)。一个常见例子是让timestamp列的默认值为CURRENT_TIMESTAMP,这样它会被设为插入该行时的时间。另一个常见例子是为每一行生成一个序列号。在PostgreSQL中,这通常可以这样实现: + +CREATE TABLE products ( + product_no integer DEFAULT nextval('products_product_no_seq'), + ... +); + + 其中nextval()函数从序列对象(见)中依次取值。这种写法非常常见,因此还有一种专门的简写形式: + +CREATE TABLE products ( + product_no SERIAL, + ... +); + + SERIAL简写会在进一步讨论。 + + + + + 约束 + + + constraint + + + + 数据类型是一种限制能够存储在表中数据类别的方法。但是对于很多应用来说,它们提供的约束太粗糙。例如,一个包含产品价格的列应该只接受正值。但是没有任何一种标准数据类型只接受正值。另一个问题是我们可能需要根据其他列或行来约束一个列中的数据。例如,在一个包含产品信息的表中,对于每个产品编号应该只有一行。 + + + + 为此,SQL 允许我们在列和表上定义约束。约束让我们能够按照需要控制表中的数据。如果用户试图在列中存储违反约束的数据,就会报错。即使该值来自默认值定义,这条规则也同样适用。 + + + + 检查约束 + + + check constraint + + + + constraint + check + + + + 检查约束是最通用的约束类型。它允许我们指定某一列中的值必须满足一个布尔(真值)表达式。例如,要要求产品价格为正值,可以使用: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric CHECK (price > 0) +); + + + + + 如你所见,约束定义和默认值定义一样都写在数据类型之后。默认值和约束的先后顺序没有影响。检查约束由关键字CHECK以及其后放在圆括号中的表达式组成。检查约束表达式应当涉及被约束的列,否则这个约束就没有太大意义。 + + + + constraint + name + + + + 我们也可以为约束单独指定一个名称。这样可以让错误消息更清晰,也便于在需要修改约束时引用它。语法如下: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric CONSTRAINT positive_price CHECK (price > 0) +); + + 要指定一个命名约束,在约束名标识符前写关键字CONSTRAINT,再在其后写约束定义即可。(如果没有用这种方式指定约束名,系统会为你选择一个。) + + + + 一个检查约束也可以引用多个列。例如我们存储一个普通价格和一个打折后的价格,而我们希望保证打折后的价格低于普通价格: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric CHECK (price > 0), + discounted_price numeric CHECK (discounted_price > 0), + CHECK (price > discounted_price) +); + + + + + 前两个约束看起来很相似。第三个则使用了一种新语法。它并没有依附在一个特定的列,而是作为一个独立的项出现在逗号分隔的列列表中。列定义和这种约束定义可以以混合的顺序出现在列表中。 + + + + 我们将前两个约束称为列约束,而第三个约束为表约束,因为它独立于任何一个列定义。列约束也可以写成表约束,但反过来不行,因为一个列约束只能引用它所依附的那一个列(PostgreSQL并不强制要求这个规则,但是如果我们希望表定义能够在其他数据库系统中工作,那就应该遵循它)。上述示例也可以写成: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric, + CHECK (price > 0), + discounted_price numeric, + CHECK (discounted_price > 0), + CHECK (price > discounted_price) +); + + 甚至是: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric CHECK (price > 0), + discounted_price numeric, + CHECK (discounted_price > 0 AND price > discounted_price) +); + + 这只是口味的问题。 + + + + 表约束也可以用列约束相同的方法来指定名称: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric, + CHECK (price > 0), + discounted_price numeric, + CHECK (discounted_price > 0), + CONSTRAINT valid_discount CHECK (price > discounted_price) +); + + + + + null value + with check constraints + + + + 需要注意的是,当检查表达式的值为真或空值时,检查约束就被视为满足。由于当任一操作数为空时,大多数表达式都会计算为空值,所以检查约束不会阻止受约束列中出现空值。要确保某列不包含空值,可以使用下一节介绍的非空约束。 + + + + + PostgreSQL不支持引用除了正在检查的新行或更新行之外的表数据的CHECK约束。 + 虽然违反此规则的CHECK约束在简单测试中可能有效,但无法保证数据库不会达到约束条件为假的状态 + (由于其他行的后续更改)。这将导致数据库转储和恢复失败。即使完整的数据库状态与约束一致,恢复也可能失败, + 因为行未按满足约束的顺序加载。如果可能的话,使用UNIQUEEXCLUDE或 + FOREIGN KEY约束来表示跨行和跨表的限制。 + + + + 如果你需要的是在插入行时针对其他行做一次性检查,而不是持续维护一致性保证, + 可以使用自定义触发器来实现。(这种方法避免了 + 转储/恢复问题,因为pg_dump在恢复数据之后才重新安装触发器, + 因此在转储/恢复期间不会强制执行检查。) + + + + + + PostgreSQL假定CHECK约束的条件是不可变的,也就是说,对于同一输入行它们始终会给出相同的结果。 + 正是这个假设,才使得只需要在插入或更新行时检查CHECK约束,而不必在其他时间检查。 + (上面关于不引用其他表数据的警告实际上是此限制的特殊情况。) + + + + 一种常见的破坏这种假设的方式,是在CHECK表达式中引用用户定义函数, + 然后改变该函数的行为。PostgreSQL不会禁止这样做, + 但它不会注意到表中是否有行现在违反了CHECK约束。 + 这将导致后续的数据库转储和恢复操作失败。 + 处理这种变化的推荐方法是删除约束(使用ALTER TABLE), + 调整函数定义,然后重新添加约束,从而重新检查所有表行。 + + + + + + 非空约束 + + + 非空约束 + + + + constraint + NOT NULL + + + 非空约束只是指定某列不能取空值。语法示例如下: +CREATE TABLE products ( + product_no integer NOT NULL, + name text NOT NULL, + price numeric +); + + + + 非空约束总是写成列约束。从功能上看,非空约束等价于创建检查约束CHECK (column_name IS NOT NULL),但在PostgreSQL中创建显式的非空约束效率更高。缺点是无法为以这种方式创建的非空约束显式指定名称。 + + 当然,一列可以有多个约束。只需将约束逐个写出: +CREATE TABLE products ( + product_no integer NOT NULL, + name text NOT NULL, + price numeric NOT NULL CHECK (price > 0) +); +顺序并不重要,也不一定决定检查约束的顺序。 + + NOT NULL约束有一个反面形式:NULL约束。这并不表示该列必须为空值,那显然毫无用处。它只是显式选择列可以为空的默认行为。NULL约束不属于 SQL 标准,因此不应在需要可移植性的应用中使用。(它之所以被加入PostgreSQL,只是为了兼容某些其他数据库系统。)不过,有些用户喜欢它,因为它让在脚本文件中切换该约束变得比较容易。例如,可以从下面的定义开始: +CREATE TABLE products ( + product_no integer NULL, + name text NULL, + price numeric NULL +); +然后在需要的地方插入NOT关键字。 + + + + 在大多数数据库设计中,多数列都应标记为非空。 + + + + + + 唯一约束 + + + unique constraint + + + + constraint + unique + + + + 唯一约束保证某一列或某一组列中保存的数据在整个表的所有行之间都是唯一的。写成列约束时的语法是: + +CREATE TABLE products ( + product_no integer UNIQUE, + name text, + price numeric +); + + 写成表约束时则是: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric, + UNIQUE (product_no) +); + + + + + 要为一组列定义一个唯一约束,把它写作一个表级约束,列名用逗号分隔: + +CREATE TABLE example ( + a integer, + b integer, + c integer, + UNIQUE (a, c) +); + + 这指定这些列的组合值在整个表的范围内是唯一的,但其中任意一列的值并不需要是(一般也不是)唯一的。 + + + + 我们可以按通常的方式为唯一约束指定名称: + +CREATE TABLE products ( + product_no integer CONSTRAINT must_be_different UNIQUE, + name text, + price numeric +); + + + + + 增加一个唯一约束会在约束中列出的列或列组上自动创建一个唯一 B-树索引。只覆盖某些行的唯一性限制不能写成唯一约束,但可以通过创建唯一的部分索引来强制这种限制。 + + + + null value + with unique constraints + + + 通常情况下,如果表中有多行,且约束所包含的所有列的值都相等,就违反了唯一约束。不过,在这种比较中,两个空值从不被视为相等。这意味着即使存在唯一约束,也可以存储重复行,只要其中至少一个受约束列含有空值。此行为符合 SQL 标准,但我们听说其他 SQL 数据库可能不遵循此规则。因此,开发需要可移植性的应用时要小心。 + + + + + 主键 + + + primary key + + + + constraint + primary key + + + + 一个主键约束表示可以用作表中行的唯一标识符的一个列或者一组列。这要求那些值都是唯一的并且非空。因此,下面的两个表定义接受相同的数据: + +CREATE TABLE products ( + product_no integer UNIQUE NOT NULL, + name text, + price numeric +); + + + +CREATE TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); + + + + + 主键也可以包含多于一个列,其语法和唯一约束相似: + +CREATE TABLE example ( + a integer, + b integer, + c integer, + PRIMARY KEY (a, c) +); + + + + + 增加一个主键会自动在主键中列出的列或列组上创建一个唯一 B-树索引,并强制这些列被标记为NOT NULL。 + + + + 一个表最多只能有一个主键(可以有任意数量的唯一和非空约束,它们可以达到和主键几乎一样的功能,但只能有一个被标识为主键)。关系数据库理论要求每一个表都要有一个主键。但PostgreSQL中并未强制要求这一点,但是最好能够遵循它。 + + + + 主键对于文档和客户端应用都是有用的。例如,一个允许修改行值的 GUI 应用可能需要知道一个表的主键,以便能唯一地标识行。如果定义了主键,数据库系统也有多种方法来利用主键。例如,主键定义了外键要引用的默认目标列。 + + + + + 外键 + + + foreign key + + + + constraint + foreign key + + + + referential integrity + + + + 一个外键约束指定一列(或一组列)中的值必须匹配出现在另一个表中某些行的值。我们说这维持了两个关联表之间的引用完整性。 + + + + 例如我们有一个使用过多次的产品表: + +CREATE TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); + + 让我们假设我们还有一个存储这些产品订单的表。我们希望保证订单表中只包含真正存在的产品的订单。因此我们在订单表中定义一个引用产品表的外键约束: + +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products (product_no), + quantity integer +); + + 现在就不可能创建包含不存在于产品表中的product_no值(非空)的订单。 + + + + 我们说在这种情况下,订单表是引用表而产品表是被引用表。相应地,也有引用和被引用列的说法。 + + + + 我们也可以把上述命令简写为: + +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + product_no integer REFERENCES products, + quantity integer +); + + 因为如果缺少列的列表,则被引用表的主键将被用作被引用列。 + + + + 你可以按常规方式为外键约束指定自己的名称。 + + + + 一个外键也可以约束和引用一组列。照例,它需要被写成表约束的形式。下面是一个示例: + +CREATE TABLE t1 ( + a integer PRIMARY KEY, + b integer, + c integer, + FOREIGN KEY (b, c) REFERENCES other_table (c1, c2) +); + + 当然,被约束列的数量和类型应该匹配被引用列的数量和类型。 + + + + 外键 + 自引用 + + + 有时,让外键约束的其它表是同一个表会很有用;这称为自引用外键。例如,如果希望表中的行代表树结构的节点,可以写成: +CREATE TABLE tree ( + node_id integer PRIMARY KEY, + parent_id integer REFERENCES tree, + name text, + ... +); +顶层节点的parent_id为 NULL,而非 NULL 的parent_id条目则受到约束,必须引用该表中的有效行。 + + 一个表可以有多个外键约束。这可用于实现表之间的多对多关系。假设有产品表和订单表,现在希望一个订单可以包含多个产品(上面的结构不允许这样做)。可以使用如下表结构: +CREATE TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); + +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + shipping_address text, + ... +); + +CREATE TABLE order_items ( + product_no integer REFERENCES products, + order_id integer REFERENCES orders, + quantity integer, + PRIMARY KEY (product_no, order_id) +); +注意,最后一个表中的主键与外键有重叠。 + + + CASCADE + 外键动作 + + + + RESTRICT + 外键动作 + + + 我们知道,外键不允许创建与任何产品都无关的订单。但如果先创建了引用某产品的订单,随后又删除该产品,会怎样呢?SQL 也允许处理这种情况。直观来看,有以下几种选择: + 不允许删除被引用的产品 + 同时删除订单 + 其他处理方式? + + + + 为说明这一点,在上面的多对多关系示例中实行如下策略:如果有人要删除仍被订单引用(通过order_items)的产品,则不允许删除。如果有人删除订单,则同时删除订单项: +CREATE TABLE products ( + product_no integer PRIMARY KEY, + name text, + price numeric +); + +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + shipping_address text, + ... +); + +CREATE TABLE order_items ( + product_no integer REFERENCES products ON DELETE RESTRICT, + order_id integer REFERENCES orders ON DELETE CASCADE, + quantity integer, + PRIMARY KEY (product_no, order_id) +); + + + + 限制删除和级联删除是最常见的两种选项。RESTRICT阻止删除被引用的行。NO ACTION表示检查约束时若仍存在引用行,就会报错;未指定任何选项时,这是默认行为。(这两种选项的根本区别在于,NO ACTION允许将检查延迟到事务稍后进行,而RESTRICT不允许。)CASCADE指定当被引用行被删除时,引用它的行也应自动删除。另有两个选项:SET NULLSET DEFAULT。它们会在被引用行删除时,分别把引用行中的引用列设为空值或其默认值。注意,这并不能让你免于遵守其他约束。例如,如果某个动作指定了SET DEFAULT,但默认值本身不满足外键约束,则该操作仍会失败。 + + ON DELETE类似,还有ON UPDATE,当被引用列发生变化(更新)时就会触发。可用动作相同。在这种情况下,CASCADE表示应把被引用列更新后的值复制到引用行中。 + + 正常情况下,如果引用行的任意一个引用列为空,它就不需要满足外键约束。如果在外键声明中加入MATCH FULL,引用行只有在所有引用列都为空时才不需要满足约束(因此空值和非空值混合的情况必定不满足MATCH FULL约束)。如果不希望引用行能够避开外键约束,应将引用列声明为NOT NULL + + 外键必须引用作为主键或组成唯一约束的列。这意味着被引用列总是具有索引(即主键或唯一约束的底层索引),因此可以高效检查引用行是否存在匹配项。由于从被引用表中DELETE行或UPDATE被引用列时,需要扫描引用表以查找匹配旧值的行,因此通常也建议为引用列建立索引。由于这并不总是必需,而且索引方式也有很多种,因此声明外键约束时不会自动在引用列上创建索引。 + + + 更多关于更新和删除数据的信息请见。外键约束的语法描述请参考。 + + + + + + 排他约束 + + + exclusion constraint + + + + constraint + exclusion + + + + 排他约束保证:对任意两行,若对指定列或表达式使用指定操作符进行比较,则这些操作符比较中至少有一个会返回假或空值。语法如下: + +CREATE TABLE circles ( + c circle, + EXCLUDE USING gist (c WITH &&) +); + + + + + 详见CREATE + TABLE ... CONSTRAINT ... EXCLUDE。 + + + + 增加一个排他约束会自动创建约束声明中指定类型的索引。 + + + + + + 系统列 + + + 每个表都有几个由系统隐式定义的系统列。因此,这些名称不能用作用户定义列的名称。(注意,这些限制与名称是否为关键字无关;给名称加引号也不能绕过这些限制。)你其实不必关心这些列的细节,只需要知道它们存在即可。 + + + + column + system column + + + + + oid + + OID行的对象标识符(对象 ID)。只有使用WITH OIDS创建表,或创建时设置了配置变量,才存在此列。该列的类型是oid(与列同名);关于该类型的更多信息,参见 + + + + + tableoid + + + tableoid + + + + 包含该行的表的 OID。对于从继承层次(见)中查询数据的场景,这一列尤其方便,因为如果没有它,就很难判断一行究竟来自哪个具体表。tableoid可以与pg_classoid列连接,以取得表名。 + + + + + + xmin + + + + xmin + + + + 插入该行版本的事务 ID。(行版本是某一行的一个具体状态;对同一逻辑行的每次更新都会创建一个新的行版本。) + + + + + + cmin + + + + cmin + + + + 插入事务中的命令标识符(从0开始)。 + + + + + + xmax + + + + xmax + + + + 删除事务的标识(事务 ID);对于未删除的行版本则为 0。对于一个可见的行版本,该列也可能是非零值。这通常表示删除事务尚未提交,或者一次删除尝试被回滚了。 + + + + + + cmax + + + + cmax + + + + 删除事务中的命令标识符,或者为0。 + + + + + + ctid + + + ctid + + + + 行版本在其表中的物理位置。注意尽管ctid可以被用来非常快速地定位行版本,但是一个行的ctid会在被更新或者被VACUUM FULL移动时改变。因此,ctid不适合用作长期行标识符。应使用 OID,或者更好地使用用户定义的序列号,来标识逻辑行。 + + + + + + OID 是 32 位的量,由整个集簇共用的单个计数器分配。在大型或长期运行的数据库中,该计数器可能发生回卷。因此,除非采取了确保唯一性的措施,否则不应假定 OID 唯一。如果需要标识表中的行,强烈建议使用序列生成器。不过,只要采取一些额外的预防措施,也可以使用 OID: + + 对于使用 OID 标识行的每个表,都应在其 OID 列上创建唯一约束。有这样的唯一约束(或唯一索引)时,系统会确保不生成与已有行相同的 OID。(当然,这只有在表的行数小于 232(40 亿)时才有可能;实际上,表的大小最好远小于此值,否则性能可能受影响。) + + + 绝不能假定 OID 在不同表之间也唯一;如果需要数据库范围的标识符,应将tableoid与行 OID 组合使用。 + + + 当然,相关表必须以WITH OIDS创建。从PostgreSQL 8.1 开始,默认值是WITHOUT OIDS + + + + + + 事务 ID也是 32 位的量。在一个长期运行的数据库中,事务 ID 可能会回卷。只要采取适当的维护措施,这并不是致命问题,详见。不过,从长期来看(超过十亿个事务)依赖事务 ID 的唯一性是不明智的。 + + + + 命令标识符也是 32 位的量。这为单个事务中的SQL命令数设置了一个硬上限: + 232(40 亿)。在实践中,这个限制并不是问题 — 注意,这里限制的是SQL命令的数量,而不是处理的行数。另外,只有真正修改数据库内容的命令才会消耗命令标识符。 + + + + + 修改表 + + + table + modifying + + + + 创建表之后,如果你意识到自己犯了错误,或者应用需求发生了变化,可以删掉表再重新创建。但如果表中已经有数据,或者该表已被其他数据库对象引用(例如被外键约束引用),这样做就不方便了。因此,PostgreSQL提供了一组命令来修改现有表。注意,这在概念上不同于修改表中保存的数据:这里关注的是修改表的定义,也就是表的结构。 + + + + 利用这些命令,我们可以: + + + 增加列 + + + 移除列 + + + 增加约束 + + + 移除约束 + + + 修改默认值 + + + 修改列数据类型 + + + 重命名列 + + + 重命名表 + + + + 所有这些动作都由命令执行,其参考页面中包含更详细的信息。 + + + + 增加列 + + + column + adding + + + + 要添加一列,可以使用这样的命令: + +ALTER TABLE products ADD COLUMN description text; + + 新列最初会填入给定的默认值(如果没有指定DEFAULT子句,则为填入空值)。 + + + + 也可以同时为该列定义约束,使用常规语法: + +ALTER TABLE products ADD COLUMN description text CHECK (description <> ''); + + 实际上,凡是在CREATE TABLE中可用于列描述的选项,在这里都可以使用。不过要记住,默认值必须满足给定约束,否则ADD就会失败。另一种做法是先把新列正确填好,再在之后添加约束(见下文)。 + + + + 添加带默认值的列需要更新表中的每一行(以存储新列值)。不过,如果没有指定默认值,PostgreSQL就可以避免物理更新。因此,如果打算为该列填入的大多数值都不是默认值,最好先添加不带默认值的列,用UPDATE填入正确的值,再按下面所述添加所需的默认值。 + + + + + + 移除列 + + + column + removing + + + + 要删除一列,使用如下命令: + +ALTER TABLE products DROP COLUMN description; + + 该列中的数据会消失,涉及该列的表约束也会被删除。不过,如果该列被其他表的外键约束引用,PostgreSQL不会静默删除该约束。你可以通过添加CASCADE来授权删除所有依赖该列的对象: + +ALTER TABLE products DROP COLUMN description CASCADE; + + 关于这个操作背后的一般性机制请见。 + + + + + 增加约束 + + + constraint + adding + + + 添加约束时使用表约束语法。例如: +ALTER TABLE products ADD CHECK (name <> ''); +ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no); +ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups; +非空约束不能写成表约束,添加时应使用以下语法: +ALTER TABLE products ALTER COLUMN product_no SET NOT NULL; + + + + 系统会立即检查约束,因此只有表中的数据满足约束,才能将其添加。 + + + + 移除约束 + + + constraint + removing + + + 要删除约束,需要知道它的名称。如果曾为它指定名称,这很容易;否则系统会分配一个自动生成的名称,需要将其查出来。psql的命令\d + tablename对此很有帮助;其他接口也可能提供检查表细节的方式。然后执行以下命令: +ALTER TABLE products DROP CONSTRAINT some_name; +(如果处理的是自动生成的约束名,例如$2,别忘了加上双引号,使其成为有效标识符。) + + + 和删除列一样,如果要删除某些其他对象所依赖的约束,也需要加上CASCADE。例如,外键约束就依赖于被引用列上的唯一约束或主键约束。 + + + 除非空约束之外,所有类型的约束都可以用相同方式删除。要删除非空约束,使用: +ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL; +(请记住,非空约束没有名称。) + + + + 更改列的默认值 + + + default value + changing + + + + 要为一个列设置一个新默认值,使用命令: + +ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77; + + 注意这不会影响任何表中已经存在的行,它只是为未来的INSERT命令改变了默认值。 + + + + 要移除任何默认值,使用: + +ALTER TABLE products ALTER COLUMN price DROP DEFAULT; + + 这等同于将默认值设置为空值。相应的,试图删除一个未被定义的默认值并不会引发错误,因为默认值已经被隐式地设置为空值。 + + + + + 修改列的数据类型 + + + column data type + changing + + + + 为了将一个列转换为一种不同的数据类型,使用如下命令: + +ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2); + + 只有当列中的每一个项都能通过一个隐式类型转换为新的类型时该操作才能成功。如果需要一种更复杂的转换,应该加上一个USING子句来指定应该如何把旧值转换为新值。 + + + + PostgreSQL将尝试把列的默认值转换为新类型,其他涉及到该列的任何约束也是一样。但是这些转换可能失败或者产生奇特的结果。因此最好在修改类型之前先删除该列上所有的约束,然后在修改完类型后重新加上相应修改过的约束。 + + + + + 重命名列 + + + column + renaming + + + + 要重命名一个列: + +ALTER TABLE products RENAME COLUMN product_no TO product_number; + + + + + + 重命名表 + + + table + renaming + + + + 要重命名一个表: + +ALTER TABLE products RENAME TO items; + + + + + + + 权限 + + + privilege + + + + permission + privilege + + + + owner + + + + GRANT + + + + REVOKE + + + + 一旦一个对象被创建,它会被分配一个所有者。所有者通常是执行创建语句的角色。对于大部分类型的对象,初始状态下只有所有者(或者超级用户)能够对该对象做任何事情。为了允许其他角色使用它,必须分配权限。 + + + + 有不同种类的权限:SELECTINSERTUPDATE、 + DELETETRUNCATEREFERENCES、 + TRIGGERCREATECONNECT、 + TEMPORARYEXECUTEUSAGE。 + 适用于特定对象的权限取决于对象的类型(表、函数等)。 + 关于PostgreSQL支持的不同权限类型的完整信息,请参阅参考页。 + 后续的各节和各章还将展示这些权限是如何使用的。 + + + 修改或销毁对象的权利始终只属于该对象的拥有者。 + + 可以使用适合对象类型的ALTER命令,将对象分配给新的拥有者,例如。超级用户始终可以这样做;普通角色只有同时是对象的当前拥有者(或拥有者角色的成员),并且是新拥有者角色的成员,才能这样做。 + + 分配权限使用GRANT命令。例如,如果joe是一个已有角色,并且accounts是一个已有表,则可以用以下命令授予更新该表的权限: +GRANT UPDATE ON accounts TO joe; +ALL代替某个具体权限,会授予与该对象类型相关的全部权限。 + + 特殊的角色PUBLIC可用于向系统中的每个角色授予权限。此外,当数据库用户很多时,可以设置角色来协助管理权限 — 详见 + + 要撤销权限,使用名称贴切的REVOKE命令: +REVOKE ALL ON accounts FROM PUBLIC; +对象拥有者的特殊权限(即执行DROPGRANTREVOKE等操作的权利)始终隐含在拥有者身份中,不能授予或撤销。不过,对象拥有者可以选择撤销自己的普通权限,例如将表设为对自己和其他人都只读。 + + 通常,只有对象的拥有者(或超级用户)可以授予或撤销对象上的权限。不过,也可以在授予权限时附带授权选项,使接收者有权进一步将该权限授予其他人。如果之后撤销了授权选项,所有从该接收者直接或经过授权链取得此权限的用户都会失去权限。详见参考页。 + + + + 行安全性策略 + + + row-level security + + + + policy + + + + 除了可通过使用的 SQL 标准权限系统之外,表还可以拥有行安全性策略,用来按用户限制普通查询可以返回哪些行,以及数据修改命令可以插入、更新或删除哪些行。这一特性也称为行级安全性。默认情况下,表没有任何策略,因此如果某个用户按照 SQL 权限系统拥有访问该表的权限,那么表中的所有行对查询或更新来说都是同等可用的。 + + + + 当在表上启用行安全性时(使用ALTER TABLE ... ENABLE ROW LEVEL SECURITY),所有针对该表选择行或修改行的普通访问都必须得到某条行安全性策略的允许。(不过,表拥有者通常不受行安全性策略约束。)如果该表没有任何策略,则会采用一条默认拒绝策略,也就是说所有行都不可见,也不能被修改。作用于整张表的操作,例如TRUNCATEREFERENCES,不受行安全性约束。 + + + + 行安全性策略可以针对特定命令、特定角色,或者同时针对两者。策略可以指定适用于ALL命令,或者适用于SELECTINSERTUPDATEDELETE。一条策略也可以分配给多个角色,并且正常的角色成员关系与继承规则同样适用。 + + + + 要指定根据某条策略哪些行可见或可修改,需要提供一个返回布尔结果的表达式。对每一行来说,在计算来自用户查询的任何条件或函数之前,都会先计算这个表达式。(这条规则的唯一例外是leakproof函数,它们被保证不会泄露信息;优化器可能会选择在行安全性检查之前应用这类函数。)表达式结果不是true的行不会被处理。你还可以分别指定独立的表达式,以便独立控制哪些行可见,以及哪些行允许被修改。策略表达式作为查询的一部分运行,并使用执行该查询的用户的权限;不过,可以借助安全性定义者函数访问调用用户本来无权访问的数据。 + + + + 超级用户以及带有BYPASSRLS属性的角色在访问表时总是会绕过行安全性系统。表拥有者通常也会绕过行安全性,不过表拥有者可以通过ALTER TABLE ... FORCE ROW LEVEL SECURITY选择让自己也受行安全性约束。 + + + + 启用或禁用行安全性,以及向表添加策略,始终都只属于表拥有者的权限。 + + + + 策略的创建可以使用命令,策略的修改 + 可以使用命令,而策略的删除可以使用 + 命令。要为一个给定表启用或者禁用行 + 安全性,可以使用命令。 + + + + 每条策略都有一个名称,而且同一张表可以定义多条策略。由于策略是表级对象,因此同一张表上的每条策略都必须具有唯一名称。不同的表则可以拥有同名策略。 + + + + 当多条策略适用于某个给定查询时,它们会通过OR组合在一起,因此只要任一策略允许访问某个行,该行就是可访问的。这类似于某个角色拥有其所属全部角色权限的规则。 + + + + 作为一个简单的示例,这里是如何在account关系上 + 创建一条策略以允许只有managers角色的成员能访问行, + 并且只能访问它们账户的行: + + + +CREATE TABLE accounts (manager text, company text, contact_email text); + +ALTER TABLE accounts ENABLE ROW LEVEL SECURITY; + +CREATE POLICY account_managers ON accounts TO managers + USING (manager = current_user); + + + + 上面的策略会隐式提供一个与其USING子句相同的WITH CHECK子句,因此该约束既作用于命令选中的行(也就是说,经理不能SELECTUPDATEDELETE属于其他经理的现有行),也作用于命令修改的行(也就是说,不能通过INSERTUPDATE创建属于其他经理的行)。 + + + + 如果没有指定角色或者使用了特殊的用户名PUBLIC, + 则该策略适用于系统上所有的用户。要允许所有用户访问users + 表中属于他们自己的行,可以使用一条简单的策略: + + + +CREATE POLICY user_policy ON users + USING (user_name = current_user); + + + + 这个示例的效果和前一个类似。 + + + + 为了对增加到表中的行使用与可见行不同的策略,可以组合多条策略。这一对策略将允许所有用户查看users表中的所有行,但只能修改他们自己的行: + + + +CREATE POLICY user_sel_policy ON users + FOR SELECT + USING (true); +CREATE POLICY user_mod_policy ON users + USING (user_name = current_user); + + + + 在SELECT命令中,这两条策略会使用OR组合,最终效果就是可以选中所有行。在其他命令类型中,只有第二条策略适用,因此效果和之前相同。 + + + + 也可以用ALTER TABLE命令禁用行安全性。禁用行安全性 + 不会移除定义在表上的任何策略,它们只是被简单地忽略。然后该表中的所有 + 行都是可见的并且可修改,服从于标准的 SQL 权限系统。 + + + + 下面是一个更大的示例,展示这项特性如何用于生产环境。表 + passwd模拟了一个 Unix 密码文件: + + + +-- Simple passwd-file based example +CREATE TABLE passwd ( + user_name text UNIQUE NOT NULL, + pwhash text, + uid int PRIMARY KEY, + gid int NOT NULL, + real_name text NOT NULL, + home_phone text, + extra_info text, + home_dir text NOT NULL, + shell text NOT NULL +); + +CREATE ROLE admin; -- Administrator +CREATE ROLE bob; -- Normal user +CREATE ROLE alice; -- Normal user + +-- Populate the table +INSERT INTO passwd VALUES + ('admin','xxx',0,0,'Admin','111-222-3333',null,'/root','/bin/dash'); +INSERT INTO passwd VALUES + ('bob','xxx',1,1,'Bob','123-456-7890',null,'/home/bob','/bin/zsh'); +INSERT INTO passwd VALUES + ('alice','xxx',2,1,'Alice','098-765-4321',null,'/home/alice','/bin/zsh'); + +-- Be sure to enable row level security on the table +ALTER TABLE passwd ENABLE ROW LEVEL SECURITY; + +-- Create policies +-- Administrator can see all rows and add any rows +CREATE POLICY admin_all ON passwd TO admin USING (true) WITH CHECK (true); +-- Normal users can view all rows +CREATE POLICY all_view ON passwd FOR SELECT USING (true); +-- Normal users can update their own records, but +-- limit which shells a normal user is allowed to set +CREATE POLICY user_mod ON passwd FOR UPDATE + USING (current_user = user_name) + WITH CHECK ( + current_user = user_name AND + shell IN ('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/tcsh') + ); + +-- Allow admin all normal rights +GRANT SELECT, INSERT, UPDATE, DELETE ON passwd TO admin; +-- Users only get select access on public columns +GRANT SELECT + (user_name, uid, gid, real_name, home_phone, extra_info, home_dir, shell) + ON passwd TO public; +-- Allow users to update certain columns +GRANT UPDATE + (pwhash, real_name, home_phone, extra_info, shell) + ON passwd TO public; + + + + 对于任意安全性设置来说,重要的是测试并确保系统的行为符合预期。 + 使用上述的示例,下面展示了权限系统工作正确: + + + +-- admin can view all rows and fields +postgres=> set role admin; +SET +postgres=> table passwd; + user_name | pwhash | uid | gid | real_name | home_phone | extra_info | home_dir | shell +-----------+--------+-----+-----+-----------+--------------+------------+-------------+----------- + admin | xxx | 0 | 0 | Admin | 111-222-3333 | | /root | /bin/dash + bob | xxx | 1 | 1 | Bob | 123-456-7890 | | /home/bob | /bin/zsh + alice | xxx | 2 | 1 | Alice | 098-765-4321 | | /home/alice | /bin/zsh +(3 rows) + +-- Test what Alice is able to do +postgres=> set role alice; +SET +postgres=> table passwd; +ERROR: permission denied for relation passwd +postgres=> select user_name,real_name,home_phone,extra_info,home_dir,shell from passwd; + user_name | real_name | home_phone | extra_info | home_dir | shell +-----------+-----------+--------------+------------+-------------+----------- + admin | Admin | 111-222-3333 | | /root | /bin/dash + bob | Bob | 123-456-7890 | | /home/bob | /bin/zsh + alice | Alice | 098-765-4321 | | /home/alice | /bin/zsh +(3 rows) + +postgres=> update passwd set user_name = 'joe'; +ERROR: permission denied for relation passwd +-- Alice is allowed to change her own real_name, but no others +postgres=> update passwd set real_name = 'Alice Doe'; +UPDATE 1 +postgres=> update passwd set real_name = 'John Doe' where user_name = 'admin'; +UPDATE 0 +postgres=> update passwd set shell = '/bin/xx'; +ERROR: new row violates WITH CHECK OPTION for "passwd" +postgres=> delete from passwd; +ERROR: permission denied for relation passwd +postgres=> insert into passwd (user_name) values ('xxx'); +ERROR: permission denied for relation passwd +-- Alice can change her own password; RLS silently prevents updating other rows +postgres=> update passwd set pwhash = 'abc'; +UPDATE 1 + + + + + 参照完整性检查,例如唯一约束或主键约束以及外键引用,总是会绕过行级安全性, + 以确保数据完整性得到维护。在设计模式和行级策略时必须小心, + 避免通过这类参照完整性检查形成隐蔽通道并泄露信息。 + + + + 在某些场景下,确保没有应用行安全性很重要。例如在做备份时,如果行安全性静默地导致某些行被从备份中省略,那将是灾难性的。在这种情况下,可以把配置参数设置为off。这本身并不会绕过行安全性;它的作用是,只要某个查询结果本会被策略过滤,就直接抛出错误。这样就可以调查并修复出错原因。 + + + + 在上面的示例中,策略表达式只考虑了要被访问的行中的当前值。这是最简 + 单并且表现最好的情况。如果可能,最好设计行安全性应用以这种方式工作。 + 如果需要参考其他行或者其他表来做出策略决定,可以在策略表达式中通过 + 使用子SELECT或包含SELECT的函数 + 来实现。不过要注意这类访问可能会导致竞争条件,在不小心的情况下这可能 + 会导致信息泄露。作为一个示例,考虑下面的表设计: + + + +-- definition of privilege groups +CREATE TABLE groups (group_id int PRIMARY KEY, + group_name text NOT NULL); + +INSERT INTO groups VALUES + (1, 'low'), + (2, 'medium'), + (5, 'high'); + +GRANT ALL ON groups TO alice; -- alice is the administrator +GRANT SELECT ON groups TO public; + +-- definition of users' privilege levels +CREATE TABLE users (user_name text PRIMARY KEY, + group_id int NOT NULL REFERENCES groups); + +INSERT INTO users VALUES + ('alice', 5), + ('bob', 2), + ('mallory', 2); + +GRANT ALL ON users TO alice; +GRANT SELECT ON users TO public; + +-- table holding the information to be protected +CREATE TABLE information (info text, + group_id int NOT NULL REFERENCES groups); + +INSERT INTO information VALUES + ('barely secret', 1), + ('slightly secret', 2), + ('very secret', 5); + +ALTER TABLE information ENABLE ROW LEVEL SECURITY; + +-- a row should be visible to/updatable by users whose security group_id is +-- greater than or equal to the row's group_id +CREATE POLICY fp_s ON information FOR SELECT + USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user)); +CREATE POLICY fp_u ON information FOR UPDATE + USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user)); + +-- we rely only on RLS to protect the information table +GRANT ALL ON information TO public; + + + + 现在假设alice想修改slightly secret这条信息,但认为mallory不应该看到这一行的新内容,因此她这样做: + + + +BEGIN; +UPDATE users SET group_id = 1 WHERE user_name = 'mallory'; +UPDATE information SET info = 'secret from mallory' WHERE group_id = 2; +COMMIT; + + + + 这看起来是安全的;似乎不存在mallory能够看到secret from mallory这个字符串的窗口。不过,这里存在一个竞争条件。如果mallory正在并发执行例如: + +SELECT * FROM information WHERE group_id = 2 FOR UPDATE; + + 并且她的事务处于READ COMMITTED模式,那么她就有可能看到 + secret from mallory。这种情况会在她的事务恰好在alice之后到达 + information中的那一行时发生。它会阻塞并等待 + alice的事务提交,然后由于FOR UPDATE子句而取到更新后的行内容。 + 但是,对于来自users的隐式SELECT, + 它不会取到更新后的行,因为该子SELECT没有 + FOR UPDATE;相反,users中的那一行是用查询开始时取得的快照读取的。 + 因此,策略表达式测试的是mallory权限级别的旧值,并允许她看到更新后的行。 + + + + 有多种方法能解决这个问题。一种简单的答案是在行安全性策略中的 + 子SELECT里使用SELECT ... FOR SHARE。 + 不过,这要求在被引用表(这里是users)上授予 + UPDATE权限给受影响的用户,这可能不是我们想要的( + 但是另一条行安全性策略可能被应用来阻止它们实际使用这个权限,或者 + 子SELECT可能被嵌入到一个安全性定义者函数中)。 + 还有,在被引用的表上过多并发地使用行共享锁可能会导致性能问题, + 特别是表更新比较频繁时。另一种解决方案(如果被引用表上的更新 + 不频繁就可行)是在更新被引用表时对它取一个ACCESS EXCLUSIVE锁, + 这样就没有并发事务能够检查旧的行值了。或者我们可以在提交对被引用表的更新 + 之后、在做依赖于新安全性情况的更改之前等待所有并发事务结束。 + + + + 更多细节请见 + 和。 + + + + + + 模式 + + + schema + + + + 一个PostgreSQL数据库集簇中包含一个或更多命名的数据库。 + 角色和一些其他对象类型被整个集簇共享,连接到服务器的客户端只能访问单个数据库中的数据,在连接请求中指定的那一个。 + + + + + + 一个集簇的用户并不必拥有访问集簇中每一个数据库的权限。 + 角色名的共享意味着不可能在同一个集簇中出现重名的不同角色,例如两个数据库中都有叫joe的用户。 + 但系统可以被配置为只允许joe访问某些数据库。 + + + + + 一个数据库包含一个或多个命名的模式,模式中又包含表。 + 模式还包含其他类型的命名对象,包括数据类型、函数和操作符。 + 在同一个模式中,同一类型的两个对象不能有相同的名称。 + 此外,表、序列、索引、视图、物化视图和外部表共享同一个名字空间, + 因此例如当它们位于同一模式中时,索引和表就必须具有不同的名称。 + 相同的对象名可以在不同模式中重复使用而不发生冲突;例如, + schema1myschema都可以包含名为mytable的表。 + 与数据库不同,模式并不是被严格隔离的:只要拥有相应权限,用户就可以访问其所连接数据库中任意模式里的对象。 + + + + 下面是一些使用模式的原因: + + + + + 允许多个用户使用一个数据库并且不会互相干扰。 + + + + + + 将数据库对象组织成逻辑组以便更容易管理。 + + + + + + 第三方应用的对象可以放在独立的模式中,这样它们就不会与其他对象的名称发生冲突。 + + + + 模式类似于操作系统层的目录,但是模式不能嵌套。 + + + + 创建模式 + + + schema + creating + + + + 要创建一个模式,可使用命令,并且给出选择的模式名称。例如: + +CREATE SCHEMA myschema; + + + + + qualified name + + + + name + qualified + + + + 在一个模式中创建或访问对象,需要使用由模式名和表名构成的限定名,模式名和表名之间以点号分隔: + +schema.table + + 在任何需要一个表名的地方都可以这样用,包括表修改命令和后续章节要讨论的数据访问命令(为了简洁我们在这里只谈到表,但是这种方式对其他类型的命名对象同样有效,例如类型和函数)。 + + + 实际上,也可以使用更通用的语法 +database.schema.table +,但目前这只是为了形式上符合 SQL 标准。如果写出数据库名称,它必须与当前连接的数据库相同。 + + + 因此,如果要在一个新模式中创建一个表,可用: + +CREATE TABLE myschema.mytable ( + ... +); + + + + + schema + removing + + + + 要删除一个为空的模式(其中的所有对象已经被删除),可用: + +DROP SCHEMA myschema; + + 要删除一个模式以及其中包含的所有对象,可用: + +DROP SCHEMA myschema CASCADE; + + 有关于此的更一般的机制请参见。 + + + + 我们常常希望创建一个由其他人所拥有的模式(因为这是将用户动作限制在良定义的名字空间中的方法之一)。其语法是: + +CREATE SCHEMA schema_name AUTHORIZATION user_name; + + 我们甚至可以省略模式名称,在此种情况下模式名称将会使用用户名,参见。 + + + + 以pg_开头的模式名被保留用于系统目的,所以不能被用户所创建。 + + + + + + 公共模式 + + + schema + public + + + + 在前面的小节中,我们创建表时都没有指定模式名。默认情况下,这些表(以及其他对象)会自动放入名为public的模式中。每个新数据库都包含这样一个模式。因此,下面两条命令是等效的: + +CREATE TABLE products ( ... ); + + 以及: + +CREATE TABLE public.products ( ... ); + + + + + + + 模式搜索路径 + + + search path + + + + unqualified name + + + + name + unqualified + + + + 限定名写起来很繁琐,而且通常最好不要把某个特定模式名硬编码到应用中。因此,表通常通过非限定名来引用,也就是只写表名。系统会沿着一条搜索路径来决定该名称指的是哪个表;搜索路径就是要查找的一组模式列表。搜索路径中第一个匹配的表会被视为目标表。如果搜索路径中没有任何匹配,就会报错,即使数据库的其他模式中存在同名表也是如此。 + + + + 在不同模式中创建命名相同的对象的能力使得编写每次都准确引用相同对象的查询变得复杂。这也使得用户有可能更改其他用户查询的行为,不管是出于恶意还是无意。由于未经限定的名称在查询中以及在PostgreSQL内部的广泛使用,在search_path中增加一个模式实际上是信任所有在该模式中具有CREATE权限的用户。在你运行一个普通查询时,如果恶意用户可以在搜索路径的模式中创建对象,那么他们将能够控制并执行任意SQL函数的对象,而这些事情就像是你在执行一样。 + + + + schema + current + + + + 搜索路径中的第一个模式被称为当前模式。除了是第一个被搜索的模式外,如果CREATE TABLE命令没有指定模式名,它将是新创建表所在的模式。 + + + + search_path配置参数 + + + + 要显示当前搜索路径,可使用下面的命令: + +SHOW search_path; + + 在默认设置下这将返回: + + search_path +-------------- + "$user", public + + 第一个元素说明一个和当前用户同名的模式会被搜索。如果不存在这个模式,该项将被忽略。第二个元素指向我们已经见过的公共模式。 + + + + 搜索路径中第一个存在的模式,是创建新对象时的默认位置。这就是默认情况下对象会被创建在公共模式中的原因。当对象在其他任何未限定模式的上下文中被引用时(表修改、数据修改或查询命令),系统会沿搜索路径查找,直到找到匹配对象为止。因此,在默认配置中,任何非限定访问仍然只能指向公共模式。 + + + + 要把新模式放在搜索路径中,我们可以使用: + +SET search_path TO myschema,public; + + (我们在这里省略了$user,因为我们并不立即需要它)。然后我们可以删除该表而无需使用模式进行限定: + +DROP TABLE mytable; + + 同样,由于myschema是路径中的第一个元素,新对象默认也会创建在其中。 + + + + 我们也可以这样写: + +SET search_path TO myschema; + + 这样一来,在没有显式限定时,我们就不再能访问公共模式了。公共模式本身并无特殊之处,只是默认存在而已;它同样可以被删除。 + + + + 其他操作模式搜索路径的方法请见。 + + + + 搜索路径对于数据类型名称、函数名称和操作符名称的作用与表名一样。数据类型和函数名称可以使用和表名完全相同的限定方式。如果我们需要在一个表达式中写一个限定的操作符名称,我们必须写成一种特殊的形式: + +OPERATOR(schema.operator) + + 这是为了避免句法歧义。例如: + +SELECT 3 OPERATOR(pg_catalog.+) 4; + + 实际上我们通常都会依赖于搜索路径来查找操作符,因此没有必要去写如此“丑陋”的东西。 + + + + + 模式和权限 + + + privilege + for schemas + + + + 默认情况下,用户无法访问他们不拥有的模式中的任何对象。要允许这样做,模式的所有者必须授予该模式上的USAGE权限。 + 要允许用户使用模式中的对象,可能需要授予其他权限,适用于该对象。 + + + 也可以允许用户在他人的模式中创建对象。为此,需要授予该模式上的CREATE权限。注意,默认情况下,每个人都具有CREATEUSAGE权限,它们作用于模式public。这使所有能够连接到某个数据库的用户都可以在它的public模式中创建对象。一些使用方式要求撤销此权限: +REVOKE CREATE ON SCHEMA public FROM PUBLIC; +(第一个public是模式名,第二个public表示每个用户。前一种用法是标识符,后一种是关键字,因此大小写不同;请回顾。) + + + + + + 系统目录模式 + + + system catalog + schema + + + + 除public和用户创建的模式之外,每一个数据库还包括一个pg_catalog模式,它包含了系统表和所有内置的数据类型、函数以及操作符。pg_catalog总是搜索路径的一个有效部分。如果没有在路径中显式地包括该模式,它将在路径中的模式之前被搜索。这保证了内置的名称总是能被找到。然而,如果我们希望用用户定义的名称重载内置的名称,可以显式的将pg_catalog放在搜索路径的末尾。 + + + + 由于系统表名以pg_开头,因此最好避免使用这样的名称,以免将来某个版本定义出与你的表同名的系统表。系统表会继续遵循以pg_开头的约定,因此只要用户避免使用pg_前缀,它们就不会与未经限定的用户表名发生冲突。 + + + + + 使用方式 + + 模式可以用多种方式组织数据。模式的安全使用方式可防止不受信任的用户改变其他用户查询的行为。如果数据库未采用安全的模式使用方式,希望安全查询该数据库的用户应在每个会话开始时采取保护措施。具体而言,应在每个会话开始时将search_path设为空字符串,或者以其他方式移除非超级用户可写的模式,使它们不再出现在以下路径中:search_path。默认配置很容易支持以下几种使用方式: + + + 将普通用户限制在各自私有的模式中。为此,执行REVOKE CREATE ON SCHEMA public FROM PUBLIC,并为每个用户创建一个与其同名的模式。请记住,默认搜索路径以$user开头,它会解析为用户名。因此,如果每个用户都有单独的模式,他们默认访问的就是自己的模式。在已经有不受信任用户登录过的数据库中采用这种方式后,应考虑检查 public 模式中是否存在与pg_catalog模式中的对象同名的对象。除非不受信任的用户是数据库拥有者或持有CREATEROLE权限,否则这是一种安全的模式使用方式;如果存在上述情况,则没有安全的模式使用方式。 + + + + + + 通过修改postgresql.conf,或执行ALTER ROLE ALL SET search_path = "$user",将 public 模式从默认搜索路径中移除。每个人仍可在 public 模式中创建对象,但只有限定名才会选中这些对象。虽然限定的表引用没有问题,但调用 public 模式中的函数会不安全或不可靠。如果要在 public 模式中创建函数或扩展,请改用第一种方式。否则,它和第一种方式一样,除非不受信任的用户是数据库拥有者或持有CREATEROLE权限,否则就是安全的。 + + + + 保持默认设置。所有用户都会隐式访问 public 模式。这模拟了根本没有模式可用的情况,使从不支持模式的环境迁移过来时更加平滑。不过,这绝不是一种安全的方式。它只适用于数据库只有单个用户,或只有少数彼此信任的用户的场景。 + + + + + + 对于任何一种模式,如果要安装共享应用 + (所有人都要使用的表、第三方提供的附加函数等),可以把它们放进单独的模式中。 + 记得授予适当的权限,以便其他用户能够访问它们。这样,用户既可以通过带模式名的限定名来引用这些附加对象, + 也可以按自己的需要把这些附加模式加入搜索路径。 + + + + + + 可移植性 + + + 在 SQL 标准中,不存在同一模式中的对象由不同用户拥有这一概念。 + 此外,有些实现不允许创建与其所有者名称不同的模式。 + 事实上,在那些只实现了标准中基本模式支持的数据库系统中,模式和用户这两个概念几乎是等价的。 + 因此,很多用户认为限定名实际上就是 + user_name.table_name。 + 如果你为每个用户都创建一个独立模式,PostgreSQL的行为实际上也会是这样。 + + + + 同样,SQL 标准中也没有public模式这一概念。为了尽可能符合标准,你不应使用public模式。 + + + + 当然,某些SQL数据库系统可能根本没有实现模式,或者提供(很可能是有限制地)允许跨数据库访问的命名空间。如果需要使用这样的系统,为了获得最好的可移植性,最好不要使用模式。 + + + + + + 继承 + + + inheritance + + + + table + inheritance + + + + PostgreSQL实现了表继承,这对数据库设计者来说是一种有用的工具(SQL:1999及其后的版本定义了一种类型继承特性,但和这里介绍的继承有很大的不同)。 + + + + 让我们从一个示例开始:假设我们要为城市建立一个数据模型。每个州有很多城市,但只有一个首府。我们希望能够快速检索任意特定州的首府城市。这可以通过创建两个表来实现:一个用于州首府,另一个用于非首府城市。然而,当我们想要查询某个城市的数据,而不关心它是不是首府时,会发生什么?继承特性将有助于解决这个问题。我们可以将capitals表定义为继承自cities表: + + +CREATE TABLE cities ( + name text, + population float, + elevation int -- in feet +); + +CREATE TABLE capitals ( + state char(2) +) INHERITS (cities); + + + 在这种情况下,capitals继承了它的父表cities的所有列。州首府还有一个额外的列state用来表示它所属的州。 + + + + 在PostgreSQL中,一个表可以从0个或者多个其他表继承,而对一个表的查询则可以引用一个表的所有行或者该表的所有行加上它所有的后代表。 + 默认情况是后一种行为。例如,下面的查询将查找所有高度高于500尺的城市的名称,包括州首府: + + +SELECT name, elevation + FROM cities + WHERE elevation > 500; + + + 对于来自PostgreSQL教程(见)的示例数据,它将返回: + + + name | elevation +-----------+----------- + Las Vegas | 2174 + Mariposa | 1953 + Madison | 845 + + + + + 另一方面,下面的查询将找到所有高度超过 500 尺且不是州首府的城市: + + +SELECT name, elevation + FROM ONLY cities + WHERE elevation > 500; + + name | elevation +-----------+----------- + Las Vegas | 2174 + Mariposa | 1953 + + + + + 这里的ONLY关键词指示查询只被应用于cities上,而其他在继承层次中位于cities之下的其他表都不会被该查询涉及。很多我们已经讨论过的命令(如SELECTUPDATEDELETE)都支持ONLY关键词。 + + + + 我们也可以在表名后写上一个*来显式地将后代表包括在查询范围内: + + +SELECT name, elevation + FROM cities* + WHERE elevation > 500; + + + 写*并非必需,因为这种行为就是默认的(除非你更改了配置选项的设置)。不过,写*或许有助于强调将会搜索额外的表。 + + + + 在某些情况下,我们可能希望知道一个特定行来自于哪个表。每个表中的系统列tableoid可以告诉我们行来自于哪个表: + + +SELECT c.tableoid, c.name, c.elevation +FROM cities c +WHERE c.elevation > 500; + + + 将会返回: + + + tableoid | name | elevation +----------+-----------+----------- + 139793 | Las Vegas | 2174 + 139793 | Mariposa | 1953 + 139798 | Madison | 845 + + + (如果重新生成这个结果,可能会得到不同的OID数字。)通过与pg_class进行连接可以看到实际的表名: + + +SELECT p.relname, c.name, c.elevation +FROM cities c, pg_class p +WHERE c.elevation > 500 AND c.tableoid = p.oid; + + + 将会返回: + + + relname | name | elevation +----------+-----------+----------- + cities | Las Vegas | 2174 + cities | Mariposa | 1953 + capitals | Madison | 845 + + + + + 得到同样效果的另一种方法,是使用regclass伪类型, + 它会以符号形式打印表的 OID: + + +SELECT c.tableoid::regclass, c.name, c.elevation +FROM cities c +WHERE c.elevation > 500; + + + + + 继承不会自动地将来自INSERTCOPY命令的数据传播到继承层次中的其他表中。在我们的示例中,下面的INSERT语句将会失败: + +INSERT INTO cities (name, population, elevation, state) +VALUES ('Albany', NULL, NULL, 'NY'); + + 我们也许会希望数据能以某种方式被路由到capitals表中,但这不会发生:INSERT总是向指定的表中插入。在某些情况下,可以通过使用一个规则(见)将插入动作重定向。但是这对上面的情况并没有帮助,因为cities表根本就不包含state列,因而这个命令会在触发规则之前就被拒绝。 + + + + 父表上的所有检查约束和非空约束都将自动被它的后代所继承,除非显式地指定了NO INHERIT子句。其他类型的约束(唯一、主键和外键约束)则不会被继承。 + + + + 一个表可以从超过一个的父表继承,在这种情况下它拥有父表们所定义的列的并集。任何定义在子表上的列也会被加入到其中。如果在这个集合中出现重名列,那么这些列将被合并,这样在子表中只会有一个这样的列。重名列能被合并的前提是这些列必须具有相同的数据类型,否则会导致错误。可继承的检查约束和非空约束会以类似的方式被合并。例如,如果合并成一个合并列的任一列定义被标记为非空,则该合并列会被标记为非空。如果检查约束的名称相同,则他们会被合并,但如果它们的条件不同则合并会失败。 + + + + 表继承通常在创建子表时建立,即通过语句中的INHERITS子句。已经创建好的表也可以通过INHERIT变体再增加一个新的父表关系。要这么做,新子表必须已经包含与父表同名且数据类型相同的列。子表还必须包含与父表相同的检查约束和检查表达式。类似地,也可以使用ALTER TABLENO INHERIT变体,从子表中移除一条继承链接。动态添加和移除继承链接可用于实现表分区(见)。 + + + + 一种创建将来要用作子表的新表的方法,是在CREATE + TABLE中使用LIKE子句。这样会创建一个与源表具有相同列的新表。如果源表上定义了任何CHECK约束,可以使用LIKEINCLUDING CONSTRAINTS选项,让新表也包含与父表相同的约束。 + + + + 当有任何一个子表存在时,父表不能被删除。当子表的列或者检查约束继承于父表时,它们也不能被删除或修改。如果希望移除一个表和它的所有后代,一种简单的方法是使用CASCADE选项删除父表(见)。 + + + + 会把列的数据定义或检查约束上的任何变化沿着继承层次向下传播。同样,删除被其他表依赖的列只能使用CASCADE选项。对于同名列的合并与拒绝,ALTER TABLE遵循与CREATE TABLE相同的规则。 + + + 继承查询只对父表进行访问权限检查。因此,例如授予cities表上的UPDATE权限,就意味着通过cities访问时,也有权更新capitals表中的行。这保留了数据(也)位于父表中的表象。不过,未经额外授权,不能直接更新capitals表。这条规则有两个例外:TRUNCATELOCK TABLE。无论直接处理子表,还是对父表执行这些命令并递归处理子表,都会检查子表的权限。 + + 类似地,在继承查询期间,父表的行安全策略(见)会应用于来自子表的行。子表自己的策略(如果有)只会在查询中明确指定该子表时应用;此时,其父表的任何策略都会被忽略。 + + + 外部表(见)也可以是继承层次 + 中的一部分,即可以作为父表也可以作为子表,就像常规表一样。如果 + 一个外部表是继承层次的一部分,那么任何不被该外部表支持的操作也 + 不被整个层次所支持。 + + + + 注意事项 + + + 注意,并非所有 SQL 命令都能作用于继承层次。用于数据查询、数据修改或模式修改的命令(例如SELECTUPDATEDELETE、大多数ALTER TABLE变体,但不包括INSERTALTER TABLE ... RENAME)通常默认包含子表,并支持使用ONLY记法将其排除。用于数据库维护和调优的命令(例如REINDEXVACUUM)通常只作用于独立的物理表,不支持沿继承层次递归。各条命令的具体行为都记录在相应参考页中()。 + + + + 继承特性的一个严重限制是,索引(包括唯一约束)和外键约束只作用于单个表,而不作用于其继承子表。对于外键约束的引用端和被引用端,这一点都成立。因此,沿用上面的示例: + + + + + 如果我们把cities.name声明为UNIQUEPRIMARY KEY,这并不能阻止capitals表中出现与cities中城市同名的行。而且这些重复行默认还会出现在针对cities的查询结果中。事实上,默认情况下capitals根本没有唯一约束,因此它可以包含多行同名记录。你当然可以给capitals添加唯一约束,但这仍无法阻止相对于cities的重复。 + + + + + + 相似地,如果我们指定cities.name REFERENCES某个其他表,该约束不会自动地传播到capitals。在此种情况下,我们可以变通地在capitals上手工创建一个相同的REFERENCES约束。 + + + + + + 如果让另一个表的某列REFERENCES cities(name),那么该表可以包含城市名称,但不能包含首府名称。对于这种情况,并没有什么好的变通办法。 + + + + + 这些不足未来可能会在某个发行版中修复,但在此期间,在决定继承是否适合你的应用时,仍需要非常小心。 + + + + + + + 分区 + + + partitioning + + + + table + partitioning + + + + PostgreSQL支持基本的表分区。本节介绍为什么以及如何把分区作为数据库设计的一部分来实现。 + + + + 概述 + + 分区是指将逻辑上的一个大表拆分成较小的物理部分。分区可以带来以下好处: + + + 在某些情况下查询性能能够显著提升,特别是当那些访问压力大的行在一个分区或者少数几个分区时。分区替代了索引的前导列,从而减小索引大小,使索引中被大量使用的部分更有可能容纳在内存中。 + + + + + + 当查询或更新访问单个分区的很大一部分时,可以通过使用该分区的顺序扫描来提高性能,而不是使用索引,这将需要分散在整个表中的随机访问读取。 + + + + + + 如果在分区设计中考虑到了这种使用模式,就可以通过添加或移除分区来完成批量加载和删除。ALTER TABLE NO INHERITDROP TABLE都比批量操作快得多。这些命令还完全避免了批量DELETE所导致的VACUUM开销。 + + + + + + 很少使用的数据可以被迁移到便宜且较慢的存储介质上。 + + + 通常只有表本身非常大时,这些好处才值得考虑。表从多大开始受益于分区,取决于具体应用;不过,一条经验法则是表的大小应超过数据库服务器的物理内存。 + + + 目前,PostgreSQL通过表继承来支持分区。每个分区都必须作为单个父表的子表创建。父表本身通常为空;它的存在只是为了表示整个数据集。在尝试设置分区之前,你应当先熟悉继承(见)。 + + + + 在PostgreSQL中可以实现以下几种分区形式: + + + + 范围分区 + + + 按照某个键列或一组列定义的范围对表进行分区,不同分区所分配的值范围互不重叠。例如,可以按日期范围,或特定业务对象的标识符范围分区。 + + + + + 列表分区 + + + 通过明确列出每个分区包含哪些键值来对表进行分区。 + + + + + + + + 实现分区 + + + 要设置一个分区表,可以按以下步骤操作: + + + + 创建所有分区都将继承的表。 + + + 这个表将不包含数据。不要在这个表上定义任何检查约束,除非想让它们等同地应用到所有分区上。在这个表上定义索引或者唯一约束也没有意义。 + + + + + + 创建若干表,每个都从主表继承。通常,这些表不会在从主表继承的列集合之外增加任何列。 + + + + 我们将这些子表称为分区,尽管它们在各方面都是普通的PostgreSQL表(或者,也可能是外部表)。 + + + + + + 为各个分区表添加表约束,定义每个分区中允许的键值。 + + + + 典型示例如下: + +CHECK ( x = 1 ) +CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' )) +CHECK ( outletID >= 100 AND outletID < 200 ) + + 确保约束保证不同分区所允许的键值互不重叠。常见错误是设置如下范围约束: + +CHECK ( outletID BETWEEN 100 AND 200 ) +CHECK ( outletID BETWEEN 200 AND 300 ) + + 这是错误的,因为无法明确键值 200 属于哪个分区。 + + + + 注意,范围分区和列表分区在语法上没有区别;这些术语只是描述性的。 + + + + + + 对于每个分区,在键列上创建索引,以及其他所需的索引。(键索引不是严格必需的,但在大多数场景中是有帮助的。如果希望键值唯一,则应始终为每个分区创建唯一或主键约束。) + + + + + + 可以选择定义一个触发器或规则,把插入到主表的数据重定向到适当的分区。 + + + + + + 确认配置参数在postgresql.conf中没有被禁用,否则查询将无法按预期得到优化。 + + + + + + + + 例如,假设正在为一家大型冰淇淋公司构建数据库。该公司每天测量最高温度,并统计各个地区的冰淇淋销量。从概念上看,我们需要如下表: + + +CREATE TABLE measurement ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +); + + + 我们知道,大多数查询只会访问最近一周、一个月或一个季度的数据,因为该表主要用于为管理层生成在线报表。为减少需要存储的旧数据量,决定只保留最近 3 年的数据,并在每月月初删除最早一个月的数据。 + + + + 在这种情况下,可以利用分区来帮助满足测量表的各种不同需求。按照上面概述的步骤,可以这样设置分区: + + + + + + + 主表就是measurement表,完全按上面的方式声明。 + + + + + + 然后,为每个活动月份创建一个分区: + + +CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement); +CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement); +... +CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement); +CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement); +CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement); + + + 每个分区本身都是完整的表,但它们从measurement表继承其定义。 + + + + 这解决了我们的一个问题:删除旧数据。每个月,我们只需对最旧的子表执行DROP TABLE,并为新月份的数据创建一个新的子表。 + + + + + + 我们必须提供互不重叠的表约束。与其像上面那样只创建分区表,表创建脚本其实应该是: + + + CREATE TABLE measurement_y2006m02 ( + CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' ) + ) INHERITS (measurement); + CREATE TABLE measurement_y2006m03 ( + CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' ) + ) INHERITS (measurement); + ... + CREATE TABLE measurement_y2007m11 ( + CHECK ( logdate >= DATE '2007-11-01' AND logdate < DATE '2007-12-01' ) + ) INHERITS (measurement); + CREATE TABLE measurement_y2007m12 ( + CHECK ( logdate >= DATE '2007-12-01' AND logdate < DATE '2008-01-01' ) + ) INHERITS (measurement); + CREATE TABLE measurement_y2008m01 ( + CHECK ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' ) + ) INHERITS (measurement); + + + + + + + 我们可能还需要在键列上创建索引: + + + CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate); + CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate); +... + CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate); + CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate); + CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate); + + + 我们此时选择不添加更多索引。 + + + + + + 我们希望应用程序能够执行INSERT INTO measurement ...,并使数据被重定向到适当的分区表。可以通过在主表上附加合适的触发器函数来实现这一点。如果数据只添加到最新的分区,可以使用非常简单的触发器函数: + + + CREATE OR REPLACE FUNCTION measurement_insert_trigger() + RETURNS TRIGGER AS $$ + BEGIN + INSERT INTO measurement_y2008m01 VALUES (NEW.*); + RETURN NULL; + END; + $$ + LANGUAGE plpgsql; + + 创建函数后,创建一个调用该触发器函数的触发器: +CREATE TRIGGER insert_measurement_trigger + BEFORE INSERT ON measurement + FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger(); +必须每月重新定义触发器函数,使其始终指向当前分区。不过,触发器定义无需更新。 + + + 我们可能希望插入数据时,由服务器自动定位应添加该行的分区。这可以通过更复杂的触发器函数实现,例如: +CREATE OR REPLACE FUNCTION measurement_insert_trigger() +RETURNS TRIGGER AS $$ +BEGIN + IF ( NEW.logdate >= DATE '2006-02-01' AND + NEW.logdate < DATE '2006-03-01' ) THEN + INSERT INTO measurement_y2006m02 VALUES (NEW.*); + ELSIF ( NEW.logdate >= DATE '2006-03-01' AND + NEW.logdate < DATE '2006-04-01' ) THEN + INSERT INTO measurement_y2006m03 VALUES (NEW.*); + ... + ELSIF ( NEW.logdate >= DATE '2008-01-01' AND + NEW.logdate < DATE '2008-02-01' ) THEN + INSERT INTO measurement_y2008m01 VALUES (NEW.*); + ELSE + RAISE EXCEPTION 'Date out of range. Fix the measurement_insert_trigger() function!'; + END IF; + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +触发器定义与之前相同。注意,每个IF测试必须与其分区的CHECK约束完全一致。 + + + 当该函数比单月形式更加复杂时,并不需要频繁地更新它,因为可以在需要的时候提前加入分支。 + + + + + 在实践中,如果大部分插入都会进入最新的分区,最好先检查它。为了简洁,我们为触发器的检查采用了和本例中其他部分一致的顺序。 + + + + + + + + 如我们所见,一个复杂的分区方案可能需要大量的DDL。在上面的示例中,我们可能每个月创建一个新分区,因此编写一个脚本来自动生成所需要的DDL可能会更好。 + + + + + 分区管理 + + + 通常,最初定义表时建立的分区集合并不会保持不变。常见的需求是删除旧数据分区,并定期为新数据添加新分区。分区最重要的优势之一,恰恰在于它允许通过操纵分区结构,而不是物理地移动大量数据,来近乎瞬时地完成这种原本非常痛苦的任务。 + + + + 移除旧数据最简单的选择,是直接删除不再需要的分区: + +DROP TABLE measurement_y2006m02; + + 由于不必逐条删除每条记录,这可以非常快速地删除数百万条记录。 + + + + 另一种通常更可取的选择,是将该分区从分区表中移除,但保留它作为独立表的可访问性: + +ALTER TABLE measurement_y2006m02 NO INHERIT measurement; + + 这样在删除数据之前,还可以对其执行进一步的操作。例如,这常常是使用COPYpg_dump或类似工具备份数据的好时机。也可能是把数据聚合成更小格式、执行其他数据处理或运行报告的好时机。 + + + + 类似地,我们可以添加一个新分区来处理新数据。可以像上面创建初始分区那样,在分区表中创建一个空分区: + + +CREATE TABLE measurement_y2008m02 ( + CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' ) +) INHERITS (measurement); + + + 作为替代,有时更方便的做法是在分区结构之外创建新表,之后再把它变成正式的分区。这允许数据在出现在分区表中之前先被载入、检查和转换: + + +CREATE TABLE measurement_y2008m02 + (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS); +ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02 + CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' ); +\copy measurement_y2008m02 from 'measurement_y2008m02' +-- possibly some other data preparation work +ALTER TABLE measurement_y2008m02 INHERIT measurement; + + + + + + 分区和约束排除 + + + constraint exclusion + + + + 约束排除是一种查询优化技术,可提高按上述方式定义的分区表的性能。例如: +SET constraint_exclusion = on; +SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; +如果没有约束排除,上述查询会扫描measurement表的每个分区。启用约束排除后,规划器会检查每个分区的约束,并尝试证明该分区不需要扫描,因为它不可能包含满足查询WHERE子句的行。如果规划器能够证明这一点,就会将该分区排除在查询计划之外。 + + 可以使用EXPLAIN命令,显示启用constraint_exclusion时的计划与禁用时的计划之间的差异。对于这种表结构,典型的未优化计划如下: +SET constraint_exclusion = off; +EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; + + QUERY PLAN +----------------------------------------------------------------------------------------------- + Aggregate (cost=158.66..158.68 rows=1 width=0) + -> Append (cost=0.00..151.88 rows=2715 width=0) + -> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) + -> Seq Scan on measurement_y2006m02 measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) + -> Seq Scan on measurement_y2006m03 measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) +... + -> Seq Scan on measurement_y2007m12 measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) + -> Seq Scan on measurement_y2008m01 measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) +某些或全部分区可能使用索引扫描,而不是全表顺序扫描,但这里的重点是,为回答该查询,根本不需要扫描较旧的分区。启用约束排除后,可以得到代价明显更低、结果却相同的计划: +SET constraint_exclusion = on; +EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; + QUERY PLAN +----------------------------------------------------------------------------------------------- + Aggregate (cost=63.47..63.48 rows=1 width=0) + -> Append (cost=0.00..60.75 rows=1086 width=0) + -> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) + -> Seq Scan on measurement_y2008m01 measurement (cost=0.00..30.38 rows=543 width=0) + Filter: (logdate >= '2008-01-01'::date) + + + + + 注意,约束排除只由CHECK约束驱动,与是否存在索引无关。因此,不必在键列上定义索引。是否需要为某个分区创建索引,取决于预期查询扫描该分区时通常会扫描其中的大部分,还是仅扫描一小部分。后一种情况下索引会有帮助,前一种情况下则不会。 + + + + 的默认(也是推荐的)设置实际上既不是on也不是off,而是一种被称为partition的中间设置,这会导致该技术仅被应用于可能访问分区表的查询。on设置导致规划器检查所有查询中的CHECK约束,甚至是那些不太可能受益的简单查询。 + + + + + + 备选分区方法 + + + 将插入重定向到适当分区表的另一种方法,是在主表上设置规则来代替触发器。例如: + + +CREATE RULE measurement_insert_y2006m02 AS +ON INSERT TO measurement WHERE + ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' ) +DO INSTEAD + INSERT INTO measurement_y2006m02 VALUES (NEW.*); +... +CREATE RULE measurement_insert_y2008m01 AS +ON INSERT TO measurement WHERE + ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' ) +DO INSTEAD + INSERT INTO measurement_y2008m01 VALUES (NEW.*); + + + 规则的开销明显高于触发器,但这种开销每个查询只支付一次,而不是每行一次,因此在批量插入时,这种方法可能有优势。不过,在大多数情况下,触发器方法的性能更好。 + + + + 注意COPY会忽略规则。如果想要使用COPY插入数据,则需要拷贝到正确的分区表而不是主表中。COPY会引发触发器,因此在使用触发器方法时可以正常使用它。 + + + + 规则方法的另一个缺点是,如果规则集合无法覆盖插入日期,则没有简单的方法能够强制产生错误,数据将会无声无息地进入到主表中。 + + + + 也可以使用UNION ALL视图来安排分区,而不是使用表继承。例如: + + +CREATE VIEW measurement AS + SELECT * FROM measurement_y2006m02 +UNION ALL SELECT * FROM measurement_y2006m03 +... +UNION ALL SELECT * FROM measurement_y2007m11 +UNION ALL SELECT * FROM measurement_y2007m12 +UNION ALL SELECT * FROM measurement_y2008m01; + + + 但是,重新创建视图的需要给数据集分区的添加和删除增加了一个额外步骤。实践中,与使用继承相比,这种方法没有什么可取之处。 + + + + + + 注意事项 + + + 分区表有以下注意事项: + + + + 没有自动方式验证所有CHECK约束是否互斥。与手工逐个编写相比,通过代码生成分区并创建和/或修改相关对象更安全。 + + + + + + 这里展示的方案假定行的分区键列值永不改变,或者至少不会改变到必须把该行移入另一个分区的程度。由于CHECK约束的存在,试图那样做的UPDATE将会失败。如果需要处理这种情况,可以在分区表上放置合适的更新触发器,但这会让整个结构的管理复杂得多。 + + + + + + 如果手动执行VACUUMANALYZE命令,不要忘记需要在每个分区上分别运行。例如,以下命令: +ANALYZE measurement; +只会处理主表。 + + + + + + 带有ON CONFLICT子句的INSERT语句不太可能按照预期工作,因为只有在指定的目标关系上发生唯一违背时才会采取ON CONFLICT动作,而不是在其子关系上。 + + + + + + + + 约束排除有以下注意事项: + + + + + 只有查询的WHERE子句包含常量(或者外部提供的参数)时,约束排除才能有效果。例如,针对一个非不可变函数(如CURRENT_TIMESTAMP)的比较不能被优化,因为规划器不知道该函数的值在运行时会落到哪个分区中。 + + + + + + 保持分区约束尽量简单,否则规划器可能无法证明哪些分区不需要访问。如前面的示例所示,对列表分区使用简单的等值条件,对范围分区使用简单的范围测试。一条很好的经验法则是:分区约束应只包含分区列与常量之间使用 B-树可索引操作符的比较。 + + + + + 约束排除期间会检查主表的所有分区上的全部约束,因此大量分区很可能显著增加查询规划时间。使用这些技术进行分区,大概在不超过一百个分区时效果较好;不要尝试使用成千上万个分区。 + + + + + + + + + + + 外部数据 + + + foreign data + + + + foreign table + + + + user mapping + + + + PostgreSQL实现了部分的SQL/MED规定,允许我们使用普通SQL查询来访问位于PostgreSQL之外的数据。这种数据被称为外部数据(注意这种用法不要和外键混淆,后者是数据库中的一种约束)。 + + + + 外部数据可以借助外部数据包装器来访问。外部数据包装器是一个库,它可以与外部数据源通信,并隐藏连接数据源以及从中获取数据的细节。在contrib模块中有一些外部数据包装器,参见。其他类型的外部数据包装器可以在第三方产品中找到。如果这些现有的外部数据包装器都不能满足需要,也可以自行编写,参见。 + + + + 要访问外部数据,我们需要创建一个外部服务器对象,它根据其支持的外部数据包装器所使用的一组选项定义如何连接到某个特定的外部数据源。然后还需要创建一个或多个外部表,用于定义远程数据的结构。外部表在查询中可以像普通表一样使用,但它在 PostgreSQL 服务器中并不存储数据。每次使用外部表时,PostgreSQL都会要求外部数据包装器从外部数据源获取数据;而在更新命令的场景下,则要求其把数据发送到外部数据源。 + + + + 访问远程数据可能需要向外部数据源进行认证。这些信息可以通过一个用户映射提供,它会基于当前的PostgreSQL角色提供附加数据,例如用户名和密码。 + + + + 更多信息请见 + 、 + 、 + 、 + 、以及 + 。 + + + + + 其他数据库对象 + + + 表是关系数据库结构中的核心对象,因为它们承载着数据。 + 但它们并不是数据库中唯一存在的对象。还可以创建许多其他种类的对象, + 以便让数据的使用和管理更加高效或方便。本章不会讨论这些对象, + 但这里给出一个列表,让你了解有哪些可能性: + + + + + + + 视图 + + + + + + 函数和操作符 + + + + + + + 数据类型和域 + + + + + + + 触发器和重写规则 + + + + + + 这些主题的详细信息请见。 + + + + + 依赖跟踪 + + + CASCADE + with DROP + + + + RESTRICT + with DROP + + + + 当我们创建一个涉及到很多具有外键约束、视图、触发器、函数等的表的复杂数据库结构时,我们隐式地创建了一张对象之间的依赖关系网。例如,具有一个外键约束的表依赖于它所引用的表。 + + + + 为了保证整个数据库结构的完整性,PostgreSQL确保我们无法删除仍然被其他对象依赖的对象。例如,尝试删除中的产品表会导致一个如下的错误消息,因为有订单表依赖于产品表: + +DROP TABLE products; + +ERROR: cannot drop table products because other objects depend on it +DETAIL: constraint orders_product_no_fkey on table orders depends on table products +HINT: Use DROP ... CASCADE to drop the dependent objects too. + + 该错误消息包含了一个有用的提示:如果我们不想一个一个去删除所有的依赖对象,我们可以执行: + +DROP TABLE products CASCADE; + + 这样所有的依赖对象将被移除,同样依赖于它们的任何对象也会被递归删除。在这种情况下,订单表不会被移除,但是它的外键约束会被移除。之所以在这里会停下,是因为没有什么依赖着外键约束(如果希望检查DROP ... CASCADE会干什么,运行不带CASCADEDROP并阅读DETAIL输出)。 + + + + PostgreSQL中几乎所有DROP命令都支持指定CASCADE。当然,可能出现的依赖关系形态会随着对象类型不同而变化。你也可以写RESTRICT来代替CASCADE,从而得到默认行为,也就是阻止删除任何仍被其他对象依赖的对象。 + + + + 根据 SQL 标准,DROP命令必须指定RESTRICTCASCADE。实际上,没有数据库系统强制执行这条规则,但不同系统的默认行为是RESTRICT还是CASCADE,各有不同。 + + + + 如果一个DROP命令列出了多个对象,只有在存在指定对象构成的组之外的依赖关系时才需要CASCADE。例如,如果发出命令DROP TABLE tab1, tab2且存在从tab2tab1的外键引用,那么就不需要CASCADE即可成功执行。 + + + 对于用户定义函数,PostgreSQL会跟踪与函数外部可见属性有关的依赖,例如参数和结果类型,但不会跟踪那些只有检查函数体才能得知的依赖。例如,考虑以下情况: +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', + 'green', 'blue', 'purple'); + +CREATE TABLE my_colors (color rainbow, note text); + +CREATE FUNCTION get_color_note (rainbow) RETURNS text AS + 'SELECT note FROM my_colors WHERE color = $1' + LANGUAGE SQL; +(关于 SQL 语言函数的说明,请参阅。)PostgreSQL会知道get_color_note函数依赖于rainbow类型:删除该类型将迫使系统删除函数,因为它的参数类型将不再有定义。但是,PostgreSQL不会认为get_color_note依赖于my_colors表,因此不会在删除该表时删除函数。这种做法有缺点,也有好处。即使表不存在,函数在某种意义上仍然有效,尽管执行它会报错;创建一个同名新表后,函数就可以重新工作。 + + + diff --git a/zh/9.6/dfunc.sgml b/zh/9.6/dfunc.sgml new file mode 100644 index 00000000..6f0a3281 --- /dev/null +++ b/zh/9.6/dfunc.sgml @@ -0,0 +1,250 @@ + + + + 编译和链接动态装载函数 + + + 在你能够使用以 C 编写的 PostgreSQL 扩展函数之前, + 必须以特殊方式对它们进行编译和链接,以生成一个可由服务器动态装载的文件。 + 更准确地说,需要创建一个共享库共享库 + + + + 若想了解本节未涵盖的信息,你应阅读操作系统的文档,特别是 C 编译器 + cc 和链接编辑器 ld 的手册页。 + 此外,PostgreSQL 源代码在 + contrib 目录中包含若干可用的示例。 + 不过,如果你依赖这些示例,就会使你的模块依赖于 PostgreSQL + 源代码是否可用。 + + + + 创建共享库通常与链接可执行文件类似:先把源文件编译为目标文件, + 再把目标文件链接在一起。目标文件需要以位置无关代码 + (PIC)形式生成。 + PIC + 从概念上讲,这意味着当它们被可执行文件装载时,可以放在内存中的任意位置。 + (面向可执行文件的目标文件通常不会这样编译。) + 链接共享库的命令中也包含一些特殊标志,用来把它与链接可执行文件的命令区分开来 + (至少理论上如此,某些系统上的实际做法要丑陋得多)。 + + + + 在下面的示例中,我们假定你的源代码位于文件 foo.c 中, + 并将创建共享库 foo.so。除非另有说明, + 中间目标文件名为 foo.o。共享库可以包含多个目标文件, + 但这里我们只使用一个。 + + + + + + + + FreeBSD + FreeBSD共享库 + + + + 生成 PIC 的编译器选项是 。 + 创建共享库时使用的编译器选项是 。 + +gcc -fPIC -c foo.c +gcc -shared -o foo.so foo.o + + 这从 FreeBSD 3.0 版起适用。 + + + + + + + HP-UX + HP-UX共享库 + + + + 生成 PIC 的系统编译器选项是 。 + 使用 GCC 时则是 。 + 用于共享库的链接器选项是 。因此: + +cc +z -c foo.c + + 或者: + +gcc -fPIC -c foo.c + + 然后: + +ld -b -o foo.sl foo.o + + HP-UX 使用 + .sl 作为共享库扩展名,这与大多数其他系统不同。 + + + + + + + Linux + Linux共享库 + + + + 生成 PIC 的编译器选项是 。 + 创建共享库的编译器选项是 。完整示例如下: + +cc -fPIC -c foo.c +cc -shared -o foo.so foo.o + + + + + + + + OS X + OS X共享库 + + + + 下面是一个示例。它假定开发者工具已经安装。 + +cc -c foo.c +cc -bundle -flat_namespace -undefined suppress -o foo.so foo.o + + + + + + + + NetBSD + NetBSD共享库 + + + + 生成 PIC 的编译器选项是 。 + 对于 ELF 系统,使用带有 选项的编译器来链接共享库。 + 对于较早的非 ELF 系统,则使用 ld -Bshareable。 + +gcc -fPIC -c foo.c +gcc -shared -o foo.so foo.o + + + + + + + + OpenBSD + OpenBSD共享库 + + + + 生成 PIC 的编译器选项是 。 + 使用 ld -Bshareable 来链接共享库。 + +gcc -fPIC -c foo.c +ld -Bshareable -o foo.so foo.o + + + + + + + + Solaris + Solaris共享库 + + + + 生成 PIC 的编译器选项,对于 Sun 编译器是 , + 而 则用于 GCC。 + 链接共享库时,两种编译器都可以使用 ,或者也可以改用 + ,但这只适用于 GCC。 + +cc -KPIC -c foo.c +cc -G -o foo.so foo.o + + 或者 + +gcc -fPIC -c foo.c +gcc -G -o foo.so foo.o + + + + + + + + UnixWare + UnixWare共享库 + + + + 生成 PIC 的编译器选项,对于 SCO 编译器是 ,而 则用于 GCC。 + 链接共享库时,编译器选项对于 SCO 编译器是 , + 对于 GCC 则是 。 + +cc -K PIC -c foo.c +cc -G -o foo.so foo.o + + 或者 + +gcc -fpic -c foo.c +gcc -shared -o foo.so foo.o + + + + + + + + + + 如果这些内容对你来说过于复杂,可以考虑使用 + + GNU Libtool, + 它通过统一接口隐藏了平台差异。 + + + + + 生成的共享库文件随后就可以装载到 PostgreSQL 中。 + 在向 CREATE FUNCTION 命令指定文件名时, + 必须给出共享库文件名,而不是中间目标文件名。 + 请注意,系统标准的共享库扩展名(通常是 .so.sl) + 可以在 CREATE FUNCTION 命令中省略,并且通常也应省略,以获得最佳可移植性。 + + + + 关于服务器期望在何处找到共享库文件,请回头参见 + 。 + + + + + diff --git a/zh/9.6/dict-int.sgml b/zh/9.6/dict-int.sgml new file mode 100644 index 00000000..df327be2 --- /dev/null +++ b/zh/9.6/dict-int.sgml @@ -0,0 +1,78 @@ + + + + dict_int — 用于整数的示例全文搜索词典 + + + dict_int + + + + dict_int是一个全文搜索附加词典模板的示例。 + 引入这个示例词典是为了控制整数(有符号和无符号)的索引, + 使这类数字能够被索引,同时又避免唯一词的数量过度增长, + 因为那会严重影响搜索性能。 + + + + 配置 + + 该词典接受两个选项: + + + + + maxlen参数指定整数词中允许的最大数字位数。 + 默认值为 6。 + + + + + rejectlong参数指定超长整数应被截断还是忽略。 + 如果rejectlongfalse(默认值), + 词典将返回该整数的前maxlen位数字。 + 如果rejectlongtrue,词典会将超长整数视为停用词, + 因此不会为其建立索引。注意,这也意味着无法搜索这类整数。 + + + + + + + 用法 + + + 安装dict_int扩展后,会创建一个文本搜索模板 + intdict_template以及一个基于该模板、使用默认参数的词典 + intdict。你可以修改这些参数,例如: + + +mydb# ALTER TEXT SEARCH DICTIONARY intdict (MAXLEN = 4, REJECTLONG = true); +ALTER TEXT SEARCH DICTIONARY + + + 或者基于该模板创建新的词典。 + + + + 要测试该词典,可以尝试: + + +mydb# select ts_lexize('intdict', '12345678'); + ts_lexize +----------- + {123456} + + + 但在实际使用中,通常需要像所述那样, + 将它包含到某个文本搜索配置中。可能类似如下: + + +ALTER TEXT SEARCH CONFIGURATION english + ALTER MAPPING FOR int, uint WITH intdict; + + + + + + diff --git a/zh/9.6/dict-xsyn.sgml b/zh/9.6/dict-xsyn.sgml new file mode 100644 index 00000000..30a3f1fd --- /dev/null +++ b/zh/9.6/dict-xsyn.sgml @@ -0,0 +1,138 @@ + + + + dict_xsyn — 示例同义词全文检索词典 + + + dict_xsyn + + + + dict_xsyn(扩展同义词词典)是全文检索的一个附加词典模板示例。 + 这种词典类型会将词替换为其同义词组,因此可以使用某个词的任一同义词来搜索该词。 + + + + 配置 + + dict_xsyn词典接受以下选项: + + + + matchorig控制词典是否接受原词。默认为true。 + + + + + matchsynonyms控制词典是否接受同义词。默认为false。 + + + + + keeporig控制词典输出中是否包含原词。默认为true。 + + + + + keepsynonyms控制词典输出中是否包含同义词。默认为true。 + + + + + rules是包含同义词列表的文件的基本名。 + 该文件必须存放在$SHAREDIR/tsearch_data/ + (其中$SHAREDIR表示PostgreSQL安装的共享数据目录)中。 + 文件名必须以.rules结尾 + (这一后缀不应写入rules参数)。 + + + + + 规则文件具有下列格式: + + + + + 每一行表示某个单词的一组同义词,且该单词位于行首。 + 同义词之间用空白分隔,例如: + +word syn1 syn2 syn3 + + + + + + 井号(#)是注释分隔符。它可以出现在一行中的任意位置, + 该行余下的内容都会被跳过。 + + + + + + 示例可参见安装在$SHAREDIR/tsearch_data/中的 + xsyn_sample.rules。 + + + + + 用法 + + + 安装dict_xsyn扩展会使用默认参数创建一个全文检索模板 + xsyn_template和一个基于它的词典xsyn。 + 你可以修改这些参数,例如: + + +mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=false); +ALTER TEXT SEARCH DICTIONARY + + + 或者基于该模板创建新的词典。 + + + + 要测试该词典,可以尝试: + + +mydb=# SELECT ts_lexize('xsyn', 'word'); + ts_lexize +----------------------- + {syn1,syn2,syn3} + +mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=true); +ALTER TEXT SEARCH DICTIONARY + +mydb=# SELECT ts_lexize('xsyn', 'word'); + ts_lexize +----------------------- + {word,syn1,syn2,syn3} + +mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=false, MATCHSYNONYMS=true); +ALTER TEXT SEARCH DICTIONARY + +mydb=# SELECT ts_lexize('xsyn', 'syn1'); + ts_lexize +----------------------- + {syn1,syn2,syn3} + +mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=true, MATCHORIG=false, KEEPSYNONYMS=false); +ALTER TEXT SEARCH DICTIONARY + +mydb=# SELECT ts_lexize('xsyn', 'syn1'); + ts_lexize +----------------------- + {word} + + + 但实际使用通常需要把它纳入中描述的全文检索配置。 + 可能会像这样: + + +ALTER TEXT SEARCH CONFIGURATION english + ALTER MAPPING FOR word, asciiword WITH xsyn, english_stem; + + + + + + diff --git a/zh/9.6/diskusage.sgml b/zh/9.6/diskusage.sgml new file mode 100644 index 00000000..56746571 --- /dev/null +++ b/zh/9.6/diskusage.sgml @@ -0,0 +1,109 @@ + + + + 监控磁盘使用情况 + + + 本章讨论如何监控PostgreSQL数据库系统的磁盘使用情况。 + + + + 确定磁盘使用情况 + + + 磁盘使用情况 + + + + 每个表都有一个主堆磁盘文件,其中存储了大部分数据。如果该表有任何值可能较宽的列,还可能会有一个与该表关联的TOAST文件,用于存储宽到无法方便地放入主表中的值(见)。如果存在,还会在TOAST表上有一个有效索引。也可能会有与基表关联的索引。每个表和索引都存储在单独的磁盘文件中 — 如果文件会超过 1 GB,则可能分成多个文件。这些文件的命名约定见。 + + + + 可以通过三种方式监控磁盘空间:使用中列出的 SQL 函数,使用模块,或者手工检查系统目录。SQL 函数最容易使用,通常也是推荐的方法。本节其余部分展示如何通过检查系统目录来完成此事。 + + + + 在最近进行过清理或分析 的数据库上使用psql时,可以发出查询以查看任意表的磁盘使用情况: + +SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer'; + + pg_relation_filepath | relpages +----------------------+---------- + base/16384/16806 | 60 +(1 row) + + 每个页通常为 8 KB。(请记住,relpages只会由VACUUMANALYZE以及少数 DDL 命令(如CREATE INDEX)更新。)如果你想直接检查该表的磁盘文件,文件路径名就很有用。 + + + + 要显示TOAST表使用的空间,可使用如下查询: + +SELECT relname, relpages +FROM pg_class, + (SELECT reltoastrelid + FROM pg_class + WHERE relname = 'customer') AS ss +WHERE oid = ss.reltoastrelid OR + oid = (SELECT indexrelid + FROM pg_index + WHERE indrelid = ss.reltoastrelid) +ORDER BY relname; + + relname | relpages +----------------------+---------- + pg_toast_16806 | 0 + pg_toast_16806_index | 1 + + + + 也可以轻松显示索引大小: +SELECT c2.relname, c2.relpages +FROM pg_class c, pg_class c2, pg_index i +WHERE c.relname = 'customer' AND + c.oid = i.indrelid AND + c2.oid = i.indexrelid +ORDER BY c2.relname; + + relname | relpages +----------------------+---------- + customer_id_indexdex | 26 + + + + + 利用这些信息,你可以很容易地找出最大的表和索引: + +SELECT relname, relpages +FROM pg_class +ORDER BY relpages DESC; + + relname | relpages +----------------------+---------- + bigtable | 3290 + customer | 3144 + + + + + + 磁盘已满故障 + + + 数据库管理员最重要的磁盘监控任务,是确保磁盘不会被写满。数据磁盘写满不会导致数据损坏,但可能会阻止有用的活动继续进行。如果保存 WAL 文件的磁盘被写满,则数据库服务器可能会 panic 并随后关闭。 + + + + 如果无法通过删除其他内容来释放磁盘上的额外空间,可以利用表空间将部分数据库文件移到其他文件系统上。有关更多信息,请参见。 + + + + + 有些文件系统在接近写满时性能会明显变差,因此不要等到磁盘完全写满才采取措施。 + + + + + 如果你的系统支持按用户设置磁盘配额,那么数据库自然也会受到运行服务器的那个用户所拥有配额的限制。超出配额会带来与磁盘空间完全耗尽相同的不良影响。 + + + diff --git a/zh/9.6/dml.sgml b/zh/9.6/dml.sgml new file mode 100644 index 00000000..7ffbddc8 --- /dev/null +++ b/zh/9.6/dml.sgml @@ -0,0 +1,278 @@ + + + + 数据操纵 + + + 本章内容仍然相当不完整。 + + + + 前一章讨论了如何创建用于保存数据的表和其他结构。现在该把数据填入表中了。本章介绍如何插入、更新和删除表数据。下一章则终于会讲到如何从数据库中取回那些久寻不得的数据。 + + + + 插入数据 + + + 插入 + + + + INSERT + + + + 创建表时,其中不包含任何数据。在数据库真正派上用场之前,首先要做的就是插入数据。数据一次插入一行。你也可以在一条命令中插入多行,但不能插入不完整的行。即使只知道部分列值,也必须创建完整的一行。 + + + + 要创建新行,可使用 + 命令。该命令需要提供表名和列值。例如,考虑中的 products 表: + +CREATE TABLE products ( + product_no integer, + name text, + price numeric +); + + 插入一行的示例命令如下: + +INSERT INTO products VALUES (1, 'Cheese', 9.99); + + 数据值按列在表中出现的顺序列出,并以逗号分隔。通常,数据值会是字面量(常量),但也允许使用标量表达式。 + + + + 上述语法的缺点在于你必须知道表中列的顺序。为了避免这一点,你也可以显式列出列。例如,下面两条命令的效果都与上面的命令相同: + +INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', 9.99); +INSERT INTO products (name, price, product_no) VALUES ('Cheese', 9.99, 1); + + 许多用户认为,总是列出列名是一种好习惯。 + + + + 如果你没有所有列的值,可以省略其中一些列。在这种情况下,这些列将填入各自的缺省值。例如: + +INSERT INTO products (product_no, name) VALUES (1, 'Cheese'); +INSERT INTO products VALUES (1, 'Cheese'); + + 第二种形式是PostgreSQL + 的一个扩展。它从左到右用给出的值填充各列,其余列则取缺省值。 + + + + 为求清晰,你也可以显式请求缺省值,既可以针对单个列,也可以针对整行: + +INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', DEFAULT); +INSERT INTO products DEFAULT VALUES; + + + + + 你可以在一条命令中插入多行: + +INSERT INTO products (product_no, name, price) VALUES + (1, 'Cheese', 9.99), + (2, 'Bread', 1.99), + (3, 'Milk', 2.99); + + + + + 也可以插入一个查询的结果(它可能返回零行、一行或多行): + +INSERT INTO products (product_no, name, price) + SELECT product_no, name, price FROM new_products + WHERE release_date = 'today'; + + 这使你能够利用 SQL 查询机制的全部能力(见)来计算要插入的行。 + + + + + 当需要一次插入大量数据时,可以考虑使用 + 命令。 + 它不像 + 命令那样灵活,但效率更高。关于如何改善批量装载性能的更多信息,请参阅。 + + + + + + 更新数据 + + + 更新 + + + + UPDATE + + + + 修改数据库中已有的数据称为更新。你可以更新单独的行、表中的所有行,或者所有行中的一个子集。每一列都可以单独更新;其他列不受影响。 + + + + 要更新现有的行,可使用 + 命令。这需要三部分信息: + + + 要更新的表名和列名 + + + + 列的新值 + + + + 要更新哪一行(或哪些行) + + + + + + 回想一下中的内容,SQL 通常并不为行提供唯一标识符。因此,并不总能直接指定要更新哪一行。取而代之的是,你要指定一行必须满足哪些条件才会被更新。只有当表中存在主键时(不论你是否声明了它),才能通过选择一个匹配主键的条件来可靠地定位单独的行。图形化数据库访问工具正是依赖这一事实来允许你逐行更新。 + + + + 例如,这条命令把所有价格为 5 的产品的价格更新为 10: + +UPDATE products SET price = 10 WHERE price = 5; + + 这可能更新零行、一行或多行。尝试执行一个匹配不到任何行的更新并不算错误。 + + + + 让我们仔细看看这条命令。首先是关键字 + UPDATE,后面跟着表名。和平常一样, + 表名可以用模式限定;否则就在搜索路径中查找。接着是关键字 + SET,后面跟着列名、一个等号以及新的列值。新列值可以是任意标量表达式,而不仅仅是常量。例如,如果你想把所有产品的价格提高 10%,可以使用: + +UPDATE products SET price = price * 1.10; + + 如你所见,用于新值的表达式也可以引用该行中的现有值。我们还省略了WHERE子句。如果省略它,就意味着更新表中的所有行。如果给出了它,则只有匹配 + WHERE 条件的那些行才会被更新。请注意, + SET 子句中的等号表示赋值,而 + WHERE 子句中的等号表示比较,但这不会造成歧义。当然, + WHERE 条件不必是等值测试。还有许多其他操作符可用(见)。不过,该表达式必须计算出一个布尔结果。 + + + + 你可以在一条UPDATE命令中更新多个列,只需在 + SET子句中列出多个赋值。例如: + +UPDATE mytable SET a = 5, b = 3, c = 1 WHERE a > 0; + + + + + + 删除数据 + + + 删除 + + + + DELETE + + + + 到目前为止,我们已经解释了如何向表中添加数据以及如何修改数据。接下来要讨论的是如何删除不再需要的数据。正如添加数据只能按整行进行一样,从表中删除数据也只能按整行进行。前一节已经说明,SQL 不提供直接寻址单独行的方法。因此,只能通过指定待删除行必须满足的条件来删除它们。如果表中有主键,那么你可以指定精确的一行;但你也可以删除匹配某个条件的一组行,或者一次删除表中的所有行。 + + + 可以使用命令删除行;其语法与UPDATE命令非常相似。例如,要删除 products 表中所有价格为 10 的行,可以使用: +DELETE FROM products WHERE price = 10; + + + + + 如果你只是写: + +DELETE FROM products; + + 那么表中的所有行都会被删除!程序员须当心。 + + + + + 从被修改的行中返回数据 + + + RETURNING + + + + INSERT + RETURNING + + + + UPDATE + RETURNING + + + + DELETE + RETURNING + + + + 有时候,在修改行的同时取得这些行中的数据会很有用。 + INSERTUPDATE、 + DELETE 命令都支持可选的 + RETURNING 子句。使用 + RETURNING 可以避免额外执行一次数据库查询来收集这些数据;当原本难以可靠地识别被修改的行时,这一点尤其有价值。 + + + + RETURNING 子句允许的内容与 + SELECT 命令的输出列表相同 + (见)。它可以包含命令目标表的列名, + 也可以包含使用这些列的值表达式。一个常见的简写是 + RETURNING *,它按顺序选择目标表的全部列。 + + + + 在 INSERT 中,RETURNING + 默认可用的数据是插入后的行。对于简单的插入,这并没有太大用处, + 因为它只是重复客户端提供的数据。但在依赖计算得出的缺省值时,它就会非常方便。例如,当使用 + serial + 列提供唯一标识符时,RETURNING 可以返回分配给新行的 ID: + +CREATE TABLE users (firstname text, lastname text, id serial primary key); + +INSERT INTO users (firstname, lastname) VALUES ('Joe', 'Cool') RETURNING id; + + RETURNING 子句配合 + INSERT ... SELECT 也非常有用。 + + + + 在 UPDATE 中,RETURNING + 默认可用的数据是被修改行的新内容。例如: + +UPDATE products SET price = price * 1.10 + WHERE price <= 99.99 + RETURNING name, price AS new_price; + + + + + 在 DELETE 中,RETURNING + 默认可用的数据是被删除行的内容。例如: + +DELETE FROM products + WHERE obsoletion_date = 'today' + RETURNING *; + + + + 如果目标表上有触发器(),RETURNING可取得的数据就是触发器修改后的行。因此,检查由触发器计算的列,也是RETURNING的另一个常见用途。 + + + diff --git a/zh/9.6/docguide.sgml b/zh/9.6/docguide.sgml new file mode 100644 index 00000000..880a7ffb --- /dev/null +++ b/zh/9.6/docguide.sgml @@ -0,0 +1,994 @@ + + + + 文档 + + + PostgreSQL有四种主要的文档格式: + + + + + 纯文本,用于安装前信息 + + + + + HTML,用于在线浏览和参考 + + + + + PDF 或 PostScript,用于打印 + + + + + 手册页,用于快速参考。 + + + + + 另外,若干纯文本README文件可以在 + PostgreSQL源代码树各处找到,用于说明各种实现方面的问题。 + + + + HTML文档和手册页属于标准发行包的一部分,并会默认安装。 + PDF 和 PostScript 格式的文档可单独下载。 + + + + DocBook + 文档源码使用DocBook编写,这是一种表面上类似于HTML的标记语言。这两种语言都是标准通用标记语言SGML)的应用,后者本质上是一种描述其他语言的语言。下文会同时使用 DocBook 和SGML这两个术语,但从技术上讲,它们不能互换。 + + + + DocBook允许作者指定技术文档的结构和内容,而无需操心展示细节。 + 文档样式定义这些内容如何被渲染成若干最终形式之一。DocBook 由 + + OASIS group维护。 + 官方 DocBook 站点提供了很好的入门和参考文档,以及一本可供在线阅读的完整 + O'Reilly 图书。NewbieDoc Docbook Guide对初学者非常有帮助。 + FreeBSD Documentation Project也使用 DocBook,并提供了一些很有价值的信息, + 其中包括若干值得参考的风格指南。 + + + + + + 工具集 + + 以下工具用于处理文档。其中一些可能是可选的,具体见说明。 + + DocBook DTD + + 这是 DocBook 自身的定义。我们当前使用 4.2 版;不能使用更高或更低的版本。你需要 DocBook DTD 的SGML变体,但要构建手册页,还需要同一版本的XML变体。 + + + + + ISO 8879 字符实体 + + DocBook 需要这些实体,但由于它们由 ISO 维护,因此单独分发。 + + + + + DocBook DSSSL Stylesheets + + + 它们包含将 DocBook 源文件转换成其他格式(例如 + HTML)的处理指令。 + + + + + + DocBook XSL Stylesheets + + + 这是另一种将 DocBook 转换为其他格式的样式表。我们目前用它生成手册页,以及可选的 HTMLHelp。你也可以用这套工具链生成 HTML 或 PDF 输出,但 PostgreSQL 官方发行版为此使用 DSSSL 样式表。 + + + + 当前要求的最低版本是 1.74.0。 + + + + + + OpenJade + + 这是处理 SGML 的基础软件包。它包含一个SGML解析器、一个DSSSL处理器(即使用 DSSSL 样式表将SGML转换为其他格式的程序),以及若干相关工具。Jade现在由 OpenJade 小组维护,而不再由 James Clark 维护。 + + + + + Libxml2,用于xmllint + + + 这个库及其所含的xmllint工具用于处理 XML。许多开发者可能已经安装了 + Libxml2,因为构建 PostgreSQL 代码时也会用到它。不过请注意, + xmllint可能需要通过单独的子包安装。 + + + + + + Libxslt,用于xsltproc + + + 这是与 XSLT 样式表配合使用的处理工具(就像jade是与 DSSSL 样式表配合使用的处理工具一样)。 + + + + + + JadeTeX + + + 如果你愿意,还可以安装JadeTeX,用 + TeX作为Jade的格式化后端。 + JadeTeX可以创建 PostScript 或 + PDF文件(后者带书签)。 + + + + 不过,JadeTeX的输出质量不如 + RTF后端。问题特别多的地方是表格,以及各种 + 垂直和水平间距的瑕疵。另外,也没有机会对结果进行手工润色。 + + + + + + + + 我们已经记录了多种安装处理文档所需工具的方法,下面将加以介绍。 + 这些工具也可能还有其他打包发行形式。请将软件包状态报告到文档邮件列表, + 我们会把这些信息补充到这里。 + + + + 在 Fedora、RHEL 及其衍生版上安装 + + 要安装所需软件包,请使用: +yum install docbook-dtds docbook-style-dsssl docbook-style-xsl libxslt openjade + + + + + + 在 FreeBSD 上安装 + + FreeBSD Documentation Project 本身就大量使用 DocBook,因此 FreeBSD 提供了完整的文档工具ports也就不足为奇了。要在 FreeBSD 上构建文档,需要安装以下 ports。 + + textproc/docbook-sgml + + + textproc/docbook-xml + + + textproc/docbook-xsl + + + textproc/dsssl-docbook-modular + + + textproc/libxslt + + + textproc/openjade + + + + + + /usr/ports/print 中的一些内容 + (texjadetex)可能也值得安装。 + + + 更多关于 FreeBSD 文档工具的信息,请参阅FreeBSD Documentation Project 的说明 + + + + Debian 软件包 + + 完整的文档工具软件包也适用于Debian GNU/Linux。只需使用以下命令即可安装: +apt-get install docbook docbook-dsssl docbook-xsl libxml2-utils openjade1.3 opensp xsltproc + + + + + + OS X + + 如果使用 MacPorts,以下命令即可完成配置: +sudo port install docbook-dsssl docbook-sgml-4.2 docbook-xml-4.2 docbook-xsl libxslt openjade opensp + + + + + + 从源码手动安装 + + 手动安装 DocBook 工具的过程有些复杂,因此如果有预构建的软件包,就使用它们。这里只描述一种标准配置,使用合理且标准的安装路径,不使用任何花哨功能。有关细节,应查阅各软件包的文档,并阅读SGML入门资料。 + + + 安装 OpenJade + + + + + OpenJade 的安装采用 GNU 风格的./configure; make; make install构建过程。详细信息可在 OpenJade 源码发行包中找到。简要来说: +./configure --enable-default-catalog=/usr/local/share/sgml/catalog +make +make install +务必记住默认目录文件的存放位置,后面会用到它。也可以不指定,但这样以后每次使用jade时,都必须设置环境变量SGML_CATALOG_FILES,使其指向该文件。(如果 OpenJade 已经安装,而你希望在本地安装工具链的其余部分,也可以采用这种方法。) + + + + + 有用户报告,使用 OpenJade 1.4devel 构建 PDF 时会遇到段错误,并出现类似下面的消息: + +openjade:./stylesheet.dsl:664:2:E: flow object not accepted by port; only display flow objects accepted +make: *** [postgres-A4.tex-pdf] Segmentation fault + + 降级到 OpenJade 1.3 应该可以消除此错误。 + + + + + + + + 此外,还应该把dsssl目录中的文件 + dsssl.dtdfot.dtd、 + style-sheet.dtdcatalog + 安装到某个位置,例如 + /usr/local/share/sgml/dsssl。最简单的做法 + 可能是复制整个目录: + +cp -R dsssl /usr/local/share/sgml + + + + + + + 最后,创建文件 + /usr/local/share/sgml/catalog,并向其中加入 + 这一行: + +CATALOG "dsssl/catalog" + + (这是对安装到中的文件的相对路径引用。如果你选择了不同的安装布局,务必作相应调整。) + + + + + + + 安装<productname>DocBook</productname> <acronym>DTD</acronym>工具包 + + + + 获取DocBook V4.2 发行包 + + + + 创建目录/usr/local/share/sgml/docbook-4.2并切换到该目录。(具体位置无关紧要,但这个位置在本文所采用的布局中是合理的。) +$ mkdir /usr/local/share/sgml/docbook-4.2 +$ cd /usr/local/share/sgml/docbook-4.2 + + + + + + 解压归档包: +$ unzip -a ...../docbook-4.2.zip +(归档包会将文件解压到当前目录。) + + + + 编辑文件/usr/local/share/sgml/catalog(或者安装时告诉 jade 的其他位置),并加入类似下面的一行: +CATALOG "docbook-4.2/docbook.cat" + + + + + + 下载ISO 8879 字符实体归档包,将其解压,然后把文件放入存放 DocBook 文件的同一目录: +$ cd /usr/local/share/sgml/docbook-4.2 +$ unzip ...../ISOEnts.zip + + + + + + 在包含 DocBook 和 ISO 文件的目录中运行以下命令: +perl -pi -e 's/iso-(.*).gml/ISO\1/g' docbook.cat +(这会修正 DocBook 目录文件中使用的名称与 ISO 字符实体文件的实际名称之间的混淆。) + + + + + + 安装 DocBook <acronym>DSSSL</acronym> 样式表 + + + 要安装样式表,先解压发行包,再把它移到合适的位置,例如 + /usr/local/share/sgml。(归档会自动创建一个 + 子目录。) + +$ gunzip docbook-dsssl-1.xx.tar.gz +$ tar -C /usr/local/share/sgml -xf docbook-dsssl-1.xx.tar + + + + + /usr/local/share/sgml/catalog 中通常的目录项 + 也可以加上: + +CATALOG "docbook-dsssl-1.xx/catalog" + + 由于样式表变化相当频繁,而且有时尝试其他版本是有益的, + PostgreSQL不使用这个目录项。关于如何改为选择 + 样式表,参见。 + + + + + 安装<productname>JadeTeX</productname> + + + 要安装和使用JadeTeX,你需要可用的 + TeXLaTeX2e安装, + 包括受支持的tools和 + graphics宏包、Babel、 + AMS 字体和 + AMS-LaTeX、 + PSNFSS扩展及 + 35 种字体配套包、用于生成 + PostScriptdvips程序, + 以及宏包fancyhdr、 + hyperrefminitoc、 + urlot2enc。所有这些 + 都可以在邻近的 + CTAN 站点上找到。 + TeX基础系统的安装远远超出了本介绍的范围。 + 任何能运行TeX的系统都应该有可用的二进制软件包。 + + + + 在将JadeTeX用于 + PostgreSQL文档源之前,你需要增大 + TeX内部数据结构的尺寸。相关细节可以在 + JadeTeX的安装说明中找到。 + + + + 完成之后,就可以安装JadeTeX: + +$ gunzip jadetex-xxx.tar.gz +$ tar xf jadetex-xxx.tar +$ cd jadetex +$ make install +$ mktexlsr + + 最后两条需要以root身份执行。 + + + + + + + 通过<command>configure</command>检测 + + 在构建文档之前,需要运行configure脚本,就像构建PostgreSQL程序本身时一样。检查运行即将结束时的输出,它应该类似于: + +checking for onsgmls... onsgmls +checking for openjade... openjade +checking for DocBook V4.2... yes +checking for DocBook stylesheets... /usr/share/sgml/docbook/stylesheet/dsssl/modular +checking for collateindex.pl... /usr/bin/collateindex.pl +checking for xsltproc... xsltproc +checking for osx... osx + +如果onsgmlsnsgmls都没有找到,后续一些测试会被跳过。nsgmls是 Jade 软件包的一部分。如果没有自动找到这些程序,可以向 configure 传递环境变量JADENSGMLS以指明它们的位置。如果没有找到DocBook V4.2,则说明 DocBook DTD 工具包没有安装到 Jade 能找到的位置,或者目录文件没有正确设置。请参阅上面的安装提示。DocBook 样式表会在若干个较为标准的位置中查找,但如果你把它们放在其他地方,则应该设置环境变量DOCBOOKSTYLE指向该位置,然后重新运行configure + + + + + + 构建文档 + + + 一切设置妥当后,切换到doc/src/sgml目录,并运行后续各小节中介绍的某个命令来构建文档。 + (记得使用 GNU make。) + + + + HTML + + + 要构建文档的HTML版本: + +doc/src/sgml$ make html + + 这也是默认目标。输出位于子目录html中。 + + + + 若要使用postgresql.org上所使用的样式表, + 而不是默认的简单样式来生成 HTML 文档,请使用: + +doc/src/sgml$ make STYLE=website html + + + + + 要创建合适的索引,构建过程可能会经历多个相同的阶段。如果你不关心索引, + 只想校对输出,可以使用draft: + +doc/src/sgml$ make draft + + + + + 要把文档构建为单个 HTML 页面,使用: + +doc/src/sgml$ make postgres.html + + + + + + 手册页 + + + 我们使用 DocBook XSL 样式表将DocBook + refentry页面转换为适合手册页的 *roff 输出。 + 与HTML版本类似,手册页也以 tar 归档包形式分发。 + 要创建手册页,请使用以下命令: + +cd doc/src/sgml +make man + + + + + + 通过<application>JadeTeX</application>生成打印输出 + + + 如果你想使用JadeTex生成文档的可打印版本, + 可以使用下列命令之一: + + + + + 以 A4 格式通过DVI生成 PostScript: + +doc/src/sgml$ make postgres-A4.ps + + 以美国信纸格式: + +doc/src/sgml$ make postgres-US.ps + + + + + + + 生成PDF: + +doc/src/sgml$ make postgres-A4.pdf + + 或: + +doc/src/sgml$ make postgres-US.pdf + + (当然也可以从 PostScript 生成PDF版本,但直接 + 生成PDF会带有超链接和其他增强特性。) + + + + + + + 使用 JadeTeX 构建 PostgreSQL 文档时,你很可能需要增大 TeX 的一些内部 + 参数。这些参数可以在文件texmf.cnf中设置。撰写本文时, + 以下设置可用: + +hash_extra.jadetex = 200000 +hash_extra.pdfjadetex = 200000 +pool_size.jadetex = 2000000 +pool_size.pdfjadetex = 2000000 +string_vacancies.jadetex = 150000 +string_vacancies.pdfjadetex = 150000 +max_strings.jadetex = 300000 +max_strings.pdfjadetex = 300000 +save_size.jadetex = 15000 +save_size.pdfjadetex = 15000 + + + + + + + 超宽文本 + + + 有时文本会超出打印边距,在极端情况下甚至超出打印页面,例如未折行的 + 文本、过宽的表格。过宽的文本会在 TeX 日志输出文件(例如 + postgres-US.logpostgres-A4.log) + 中产生Overfull hbox消息。一英寸有 72 个点,因此任何报告为 + 超出 72 点以上宽度的内容都可能放不进打印页面(假定边距为一英寸)。要找到 + 导致溢出的SGML文本,可在溢出消息上方找到提到的第一个 + 页码,例如[50 ###](第 50 页),然后在PDF + 文件中查看其后一页(例如第 51 页),看到溢出文本后相应调整 + SGML即可。 + + + + + 通过<acronym>RTF</acronym>生成打印输出 + + + 你也可以把PostgreSQL文档转换为 + RTF并用办公套件做一些小的格式修正,从而生成可打印版本。 + 视具体办公套件的能力而定,随后可以把文档转换为 PostScript 或 + PDF。下面的过程以Applixware + 为例说明。 + + + + + 当前版本的PostgreSQL文档似乎会触发 OpenJade + 的某个错误,或者超出其大小限制。如果RTF版本的构建 + 过程长时间挂起且输出文件大小仍为 0,你可能是遇到了这个问题。 + (不过请记住,正常构建也需要 5 到 10 分钟,所以不要太早中止。) + + + + + <productname>Applixware</productname> <acronym>RTF</acronym> 清理 + + + OpenJade没有为正文文本指定默认样式。过去, + 这个未确诊的问题导致目录生成过程极其漫长。不过,在 + Applixware方面的大力帮助下,症状已得到诊断, + 并且有了可用的变通方法。 + + + + + 输入以下命令生成RTF版本: + +doc/src/sgml$ make postgres.rtf + + + + + + + 修复 RTF 文件,使其正确指定所有样式,特别是默认样式。如果文档包含 + refentry小节,还必须替换把前一段落与当前段落绑定 + 的格式提示,改为把当前段落与后一段落绑定。doc/src/sgml + 中提供了一个实用程序fixrtf来完成这些修复: + +doc/src/sgml$ ./fixrtf --refentry postgres.rtf + + + + + 该脚本会添加{\s0 Normal;}作为文档的第 0 号样式。 + 按照Applixware的说法,RTF 标准不允许添加 + 隐式的第 0 号样式,不过 Microsoft Word 恰好能处理这种情况。对于修复 + refentry小节,脚本会把\keepn + 标记替换为\keep。 + + + + + + 在Applixware Words中打开一个新文档, + 然后导入RTF文件。 + + + + + + 使用Applixware生成新目录(ToC)。 + + + + + + 从第一行第一个字符开始到最后一行最后一个字符为止,选中现有的 ToC 行。 + + + + + + 使用ToolsBook + BuildingCreate Table of + Contents构建新的 ToC。选择让 ToC 包含 + 前三级标题。这会用Applixware原生的 ToC + 替换从 RTF 导入的现有行。 + + + + + + 使用FormatStyle + 调整 ToC 格式,依次选择三种 ToC 样式,并调整First + 和Left的缩进。使用以下数值: + + + + + + 样式 + 首行缩进(英寸) + 左缩进(英寸) + + + + + + TOC-Heading 1 + 0.4 + 0.4 + + + + TOC-Heading 2 + 0.8 + 0.8 + + + + TOC-Heading 3 + 1.2 + 1.2 + + + + + + + + + + + + 在整个文档中完成以下工作: + + + + + 调整分页。 + + + + + + 调整表格列宽。 + + + + + + + + + 用正确的值替换 ToC 中 Examples 和 Figures 部分右对齐的页码。这只需要 + 几分钟。 + + + + + + 如果索引节为空,从文档中删除它。 + + + + + + 重新生成并调整目录。 + + + + + + 选中 ToC 域。 + + + + + + 选择ToolsBook + BuildingCreate Table of + Contents。 + + + + + + 通过选择ToolsField + EditingUnprotect + 解除 ToC 的保护。 + + + + + + 删除 ToC 中的第一行,那是 ToC 自身的条目。 + + + + + + + + 将文档保存为Applixware + Words原生格式,以便日后更容易地进行最后时刻的编辑。 + + + + + + 把文档打印到 PostScript 格式的文件。 + + + + + + + 纯文本文件 + + + 安装说明也以纯文本形式分发,以便在没有更好的阅读工具时使用。 + INSTALL文件对应,并针对不同语境作了少量调整。 + 要重新生成该文件,请切换到doc/src/sgml目录,然后输入make INSTALL。 + + + + 过去,发行说明和回归测试说明也曾以纯文本形式分发,但现已停止这种做法。 + + + + + 语法检查 + + + 构建文档可能非常耗时。但有一种方法可以只检查文档文件的语法是否正确, + 这只需要几秒钟: + +doc/src/sgml$ make check + + + + + + + + 文档编写 + + SGMLDocBook的开源编写工具并不多。最常用的工具组合是Emacs/XEmacs编辑器加上适当的编辑模式。在某些系统上,典型的完整安装会包含这些工具。 + + + Emacs/PSGML + + PSGML是编辑SGML文档最常用、也最强大的模式。正确配置后,可以使用Emacs插入标签并检查标记一致性。它也可以用于HTML。下载、安装说明和详细文档请参阅PSGML 网站 + + 使用PSGML时有一点需要注意:它的作者假定主SGML DTD目录是/usr/local/lib/sgml。如果像本章示例一样使用/usr/local/share/sgml,就必须作相应调整,可以设置SGML_CATALOG_FILES环境变量,也可以定制PSGML的安装配置(其手册介绍了具体方法)。 + + 将以下内容放入~/.emacs环境文件中(将路径名调整为适合你系统的值): +; ********** for SGML mode (psgml) + +(setq sgml-omittag t) +(setq sgml-shorttag t) +(setq sgml-minimize-attributes nil) +(setq sgml-always-quote-attributes t) +(setq sgml-indent-step 1) +(setq sgml-indent-data t) +(setq sgml-parent-document nil) +(setq sgml-exposed-tags nil) +(setq sgml-catalog-files '("/usr/local/share/sgml/catalog")) + +(autoload 'sgml-mode "psgml" "Major mode to edit SGML files." t ) +并在同一文件中为SGML加入一项,添加到现有的变量定义中。该变量是auto-mode-alist: + +(setq + auto-mode-alist + '(("\\.sgml$" . sgml-mode) + )) + + + + 使用PSGML时,你可能会发现,处理这些分别保存书籍各部分的文件,有一种方便的方式:在编辑时插入适当的DOCTYPE声明。例如,当前这个源码文件是一章附录,因此可以将它指定为 DocBook 文档的appendix实例,把第一行写成这样: +<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook V4.2//EN"> +这样,所有读取SGML的工具都能正确处理该文档,并且可以用以下命令验证文档:nsgmls -s docguide.sgml。(但在构建整套文档之前,需要移除这一行。) + + + + 其他 Emacs 模式 + + GNU Emacs自带另一种SGML模式,功能不如PSGML强大,但更容易理解,也更轻量。它还提供语法高亮(font lock),这可能很有帮助。src/tools/editors/emacs.samples包含此模式的示例设置。 + + Norm Walsh 提供了一种专用于 DocBook 的主模式,也带有 font-lock 和若干减少输入量的功能。 + + + + + + + 风格指南 + + + 参考页 + + + 参考页应遵循标准布局。这能让用户更快找到所需信息,也能促使作者记录命令的所有相关方面。 + 不仅希望PostgreSQL参考页彼此保持一致, + 也希望它们与操作系统及其他软件包提供的参考页保持一致。因此制定了下列准则。 + 它们在很大程度上与多个操作系统所建立的类似准则保持一致。 + + + 描述可执行命令的参考页应按以下顺序包含这些小节。不适用的小节可以省略。只有在特殊情况下才应增加顶层小节;这些信息通常应放在用法小节中。 + + 名称 + + + 本节自动生成。其中包含命令名以及一句简短的功能摘要。 + + + + + + 概要 + + + 本节包含命令的语法图。概要通常不应列出每一个命令行选项;这些内容会在下文说明。 + 相反,它应列出命令行的主要组成部分,例如输入文件和输出文件的位置。 + + + + + + 描述 + + + 用若干段解释该命令的作用。 + + + + + + 选项 + + + 用列表描述每个命令行选项。如果选项很多,可以使用小节。 + + + + + + 退出状态 + + + 如果程序以 0 表示成功、以非零表示失败,那么你通常不需要记录这一点。 + 如果不同的非零退出代码有各自的含义,就在这里列出它们。 + + + + + + 用法 + + + 描述程序的任何子语言或运行时接口。如果程序不是交互式的,通常可以省略本节。 + 否则,本节就是用于描述运行时特性的总括性小节。适当时可以使用小节。 + + + + + + 环境 + + + 列出程序可能使用的所有环境变量。尽量完整;即便像SHELL这样看似微不足道的变量, + 也可能让用户感兴趣。 + + + + + + 文件 + + + 列出程序可能会隐式访问的任何文件。也就是说,不要列出在命令行上指定的输入和输出文件, + 而应列出配置文件等内容。 + + + + + + 诊断 + + + 解释程序可能产生的任何不寻常输出。不要去列出所有可能的错误消息。 + 这项工作量很大,而在实践中用处不大。不过,例如如果错误消息有一种用户可以解析的标准格式, + 那这里就是解释它的地方。 + + + + + + 注解 + + + 放不进其他地方的任何内容,尤其包括错误、实现缺陷、安全性考虑和兼容性问题。 + + + + + + 示例 + + + 示例 + + + + + + 历史 + + + 如果该程序的历史上有一些重要里程碑,可以在这里列出。通常本节可以省略。 + + + + + + 作者 + + + 作者(仅用于 contrib 部分) + + + + + + 另见 + + + 交叉引用按以下顺序列出:其他PostgreSQL命令参考页、 + PostgreSQL SQL 命令参考页、 + 对PostgreSQL手册的引用、 + 其他参考页(例如操作系统或其他软件包)、其他文档。 + 同一组中的条目按字母顺序排列。 + + + + + + + + + 描述 SQL 命令的参考页应包含下列小节:名称、概要、描述、参数、输出、注解、示例、 + 兼容性、历史、另见。参数小节类似于选项小节,但在决定列出命令的哪些子句时有更大的自由度。 + 只有当命令返回的内容不是默认的命令完成标签时,才需要输出小节。兼容性小节应解释该命令在多大程度上符合 + SQL 标准,或者它与哪种其他数据库系统兼容。SQL 命令的另见小节应先列出 SQL 命令, + 再列出对程序的交叉引用。 + + + + + diff --git a/zh/9.6/earthdistance.sgml b/zh/9.6/earthdistance.sgml new file mode 100644 index 00000000..76f069b2 --- /dev/null +++ b/zh/9.6/earthdistance.sgml @@ -0,0 +1,165 @@ + + + + earthdistance — 计算大圆距离 + + + earthdistance + + + + earthdistance 模块提供了两种不同的方法,用于计算地球表面上的大圆距离。首先介绍的一种依赖于 cube 模块。第二种方法基于内置的 point 数据类型,使用经度和纬度作为坐标。 + + + + 在这个模块中,地球被假定为完全球形的。(如果这种假设对你而言精度不足,可以参见 PostGIS 项目。) + + + + 必须先安装 cube 模块,才能安装 earthdistance(不过,你也可以使用 CREATE EXTENSIONCASCADE 选项,用一条命令同时安装这两个扩展)。 + + + + 强烈建议将 earthdistancecube 安装到同一个模式中,并且该模式过去没有、将来也不会向任何不受信任的用户授予 CREATE 权限。否则,如果 earthdistance 所在的模式包含由恶意用户定义的对象,就会在安装时产生安全隐患。此外,在安装后使用 earthdistance 的函数时,整个搜索路径都应当只包含受信任的模式。 + + + + 基于立方体的地球距离 + + + 数据以立方体形式存储,而这些立方体实际上是点(两个角相同),使用 3 个坐标分别表示距地心的 x、y、z 距离。这里还提供了一个基于 cube 的域 earth,其中包含约束检查,用以确认该值满足这些限制,并且与地球实际表面足够接近。 + + + + 地球半径取自 earth() 函数,其单位为米。不过,只要修改这一个函数,你就可以让该模块改用其他单位,或者使用你认为更合适的半径值。 + + + + 这个模块在天文数据库中也有用途。天文学家大概会希望修改 earth(),使其返回半径 180/pi(),这样距离单位就是度。 + + + + 这里提供了一些函数,用于按经纬度(以度为单位)输入、输出经纬度、计算两点之间的大圆距离,以及方便地指定一个可用于索引搜索的边界框。 + + + + 所提供的函数见 。 + + + + 基于立方体的地球距离函数 + + + + 函数 + 返回值 + + 描述 + + + + + + earth()earth + float8 + + 返回假定的地球半径。 + + + + sec_to_gc(float8)sec_to_gc + float8 + + 将地球表面两点间的普通直线(割线)距离转换为它们之间的大圆距离。 + + + + gc_to_sec(float8)gc_to_sec + float8 + + 将地球表面两点间的大圆距离转换为它们之间的普通直线(割线)距离。 + + + + ll_to_earth(float8, float8)ll_to_earth + earth + + 给定某点以度为单位的纬度(参数 1)和经度(参数 2),返回该点在地球表面上的位置。 + + + + latitude(earth)latitude + float8 + + 返回地球表面上一个点的纬度(以度数形式)。 + + + + longitude(earth)longitude + float8 + + 返回地球表面上一个点的经度(以度数形式)。 + + + + earth_distance(earth, earth)earth_distance + float8 + + 返回地球表面上两个点之间的大圆距离。 + + + + earth_box(earth, float8)earth_box + cube + 返回一个适合使用 cube @> 操作符进行索引搜索的框,用于查找距离某个位置在给定大圆距离以内的点。框中的某些点到该位置的距离会超过指定的大圆距离,因此查询中应包含使用earth_distance 的二次检查。 + + + +
+ +
+ + + 基于点的地球距离 + + + 该模块的第二部分依赖于将地球上的位置表示为 point 类型的值,其中第一个分量被视为以度为单位的经度,第二个分量被视为以度为单位的纬度。之所以采用 (longitude, latitude) 而不是相反的顺序,是因为经度更接近 x 轴的直观概念,而纬度更接近 y 轴。 + + + + 这一部分只提供了一个操作符,见 。 + + + + 基于点的地球距离操作符 + + + + 操作符 + 返回值 + + 描述 + + + + + + point <@> point + float8 + 给出地球表面两点之间的法定英里距离。 + + + +
+ + + 注意,与该模块基于 cube 的部分不同,这里的单位是固定的:修改 earth() 函数不会影响该操作符的结果。 + + + + 经度/纬度表示的一个缺点是,你需要小心靠近两极以及接近经度 +/- 180 度时的边界情况。基于 cube 的表示可以避免这些不连续性。 + + +
+ +
diff --git a/zh/9.6/ecpg.sgml b/zh/9.6/ecpg.sgml new file mode 100644 index 00000000..740e1706 --- /dev/null +++ b/zh/9.6/ecpg.sgml @@ -0,0 +1,8010 @@ + + + + <application>ECPG</application> - C 中的嵌入式 <acronym>SQL</acronym> + + embedded SQLin C + C + ECPG + + + 这一章描述了用于PostgreSQL的嵌入式SQL包。它由 Linus Tolke(linus@epact.se)和 Michael Meskes(meskes@postgresql.org)编写。最初它是为了与C一起工作而编写的。它也能与C++配合,但是它还不识别所有的C++结构。 + + + + 这份文档还很不完整。不过,由于这一接口是标准化的,因此可以在许多关于 SQL 的资料中找到补充信息。 + + + + 概念 + + 嵌入式 SQL 程序由普通编程语言编写的代码组成(这里使用 C),其中在特殊标记的部分混入 SQL 命令。构建程序时,先将源代码(*.pgc)交给嵌入式 SQL 预处理器,将其转换为普通 C 程序(*.c),然后再用 C 编译器处理。(有关编译和链接的详细信息,参见 。)转换后的 ECPG 应用通过嵌入式 SQL 库(ecpglib)调用 libpq 库中的函数,并使用普通的前端/后端协议与 PostgreSQL 服务器通信。 + + + 与其他在 C 代码中处理SQL命令的方法相比,嵌入式SQL有其优势。首先,它能处理与你的C程序变量之间来回传递信息的繁琐工作。其次,程序中的 SQL 代码会在构建时接受语法正确性检查。第三,C 中的嵌入式SQLSQL标准规定,并且得到许多其他SQL数据库系统的支持。PostgreSQL 的实现被设计为尽可能贴合该标准,因此通常可以比较容易地把为其他 SQL 数据库编写的嵌入式SQL程序移植到PostgreSQL。 + + + 如前所述,为嵌入式 SQL 接口编写的程序,就是插入了特殊代码的普通 C 程序,这些特殊代码用于执行数据库相关操作,其形式始终如下: +EXEC SQL ...; +这些语句在语法上占据一条 C 语句的位置。根据具体语句的不同,它们可以出现在全局层级,也可以出现在函数内部。嵌入式 SQL 语句遵循普通 SQL 代码的大小写规则,而不是 C 的规则。此外,它们还允许 SQL 标准中规定的嵌套 C 风格注释。但程序中的 C 部分仍遵循 C 标准,不接受嵌套注释。 + + + 下列小节解释了所有嵌入式 SQL 语句。 + + + + + 管理数据库连接 + + + 这一节描述如何打开、关闭以及切换数据库连接。 + + + + 连接到数据库服务器 + + 使用以下语句连接数据库: +EXEC SQL CONNECT TO target AS connection-name USER user-name; +其中,target 可以按以下方式指定: + + + dbname@hostname:port + + + + + + tcp:postgresql://hostname:port/dbname?options + + + + + unix:postgresql://hostname:port/dbname?options + + + + + 包含上述某种形式的 SQL 字符串常量 + + + + + + 对包含上述某种形式的字符变量的引用(见示例) + + + + + + DEFAULT + + + 如果直接写出连接目标(即不通过变量引用),且未用引号括起该值,就会应用普通 SQL 的大小写不敏感规则。此时,也可以按需分别用双引号括起各个参数。实际使用中,采用单引号括起的字符串字面值或变量引用,可能更不容易出错。连接目标 DEFAULT 会以默认用户名发起到默认数据库的连接,此时不能单独指定用户名或连接名。 + + + 也有不同的方法来指定用户名: + + + + + username + + + + + + username/password + + + + + + username IDENTIFIED BY password + + + + + + username USING password + + + + + 如上所述,参数username以及password可以是一个 SQL 标识符、一个 SQL 字符串或者一个对字符变量的引用。 + + + + 如果连接目标包含options, + 那么它们由若干个keyword=value项组成,彼此之间用和号(&)分隔。 + 允许的关键字与libpq识别的关键字相同(见)。 + 在任何keywordvalue之前的空格将被忽略,但其中或之后则不会。 + 请注意,无法将&写入value。 + + + + connection-name被用来在一个程序中处理多个连接。如果一个程序只使用一个连接,则可以省略它。最近被打开的连接将成为当前连接,当一个 SQL 语句要被执行时,将默认使用它(见这一章稍后的部分)。 + + + + 如果不受信任的用户可以访问某个尚未采用模式的安全使用方式的数据库,那么每个会话开始时都应先从search_path中移除公共可写模式。 + 例如,可以把options=-c search_path=加到options中,或在连接后执行EXEC SQL SELECT pg_catalog.set_config('search_path', '', false);。 + 这一注意事项并非 ECPG 独有;它适用于所有能执行任意 SQL 命令的接口。 + + + 以下是一些 CONNECT 语句的示例: +EXEC SQL CONNECT TO mydb@sql.mydomain.com; + +EXEC SQL CONNECT TO unix:postgresql://sql.mydomain.com/mydb AS myconnection USER john; + +EXEC SQL BEGIN DECLARE SECTION; +const char *target = "mydb@sql.mydomain.com"; +const char *user = "john"; +const char *passwd = "secret"; +EXEC SQL END DECLARE SECTION; + ... +EXEC SQL CONNECT TO :target USER :user USING :passwd; +/* or EXEC SQL CONNECT TO :target USER :user/:passwd; */ +最后一种形式使用了上面提到的字符变量引用。后续章节会说明,如何在 C 变量前加冒号,以便在 SQL 语句中使用它们。 + + + 请注意,连接目标的格式并未在 SQL 标准中规定。因此,如果你想开发可移植的应用,最好采用类似上面最后一个示例的方法,把连接目标字符串封装在某个地方。 + + + + + 选择一个连接 + + 嵌入式 SQL 程序中的 SQL 语句默认在当前连接上执行,即最近打开的连接。如果应用需要管理多个连接,可以采用两种方式。 + + + 第一个选项是显式地为每一个 SQL 语句选择一个连接,例如: + +EXEC SQL AT connection-name SELECT ...; + + 如果应用需要以混合的顺序使用多个连接,这个选项特别合适。 + + + + 如果你的应用使用多个线程执行,它们不能并发地共享一个连接。你必须显式地控制对连接的访问(使用互斥量)或者为每个线程使用一个连接。 + + + + 第二个选项是执行一个语句来切换当前的连接。该语句是: + +EXEC SQL SET CONNECTION connection-name; + + 如果有很多语句要在同一个连接上执行,这个选项尤其方便。 + + + + 这里有一个管理多个数据库连接的例子程序: + + +EXEC SQL BEGIN DECLARE SECTION; + char dbname[1024]; +EXEC SQL END DECLARE SECTION; + +int +main() +{ + EXEC SQL CONNECT TO testdb1 AS con1 USER testuser; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL CONNECT TO testdb2 AS con2 USER testuser; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL CONNECT TO testdb3 AS con3 USER testuser; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + /* 这个查询将在最近打开的数据库 "testdb3" 中执行 */ + EXEC SQL SELECT current_database() INTO :dbname; + printf("current=%s (should be testdb3)\n", dbname); + + /* 使用 "AT" 在 "testdb2" 中运行一个查询 */ + EXEC SQL AT con2 SELECT current_database() INTO :dbname; + printf("current=%s (should be testdb2)\n", dbname); + + /* 切换当前连接到 "testdb1" */ + EXEC SQL SET CONNECTION con1; + + EXEC SQL SELECT current_database() INTO :dbname; + printf("current=%s (should be testdb1)\n", dbname); + + EXEC SQL DISCONNECT ALL; + return 0; +} +]]> + + 这个例子将产生这样的输出: + +current=testdb3 (should be testdb3) +current=testdb2 (should be testdb2) +current=testdb1 (should be testdb1) + + + + + + 关闭一个连接 + + 要关闭连接,请使用以下语句: +EXEC SQL DISCONNECT connection; +其中,connection 可以按以下方式指定: + + + connection-name + + + + + + DEFAULT + + + + + + CURRENT + + + + + + ALL + + + 如果未指定连接名,则关闭当前连接。 + + + 在一个应用中总是显式地从它打开的每一个连接断开是一种好的风格。 + + + + + + + 运行 SQL 命令 + + + 任何 SQL 命令都可以在一个嵌入式 SQL 应用中被运行。下面是一些在嵌入式 SQL 应用中运行 SQL 命令的例子。 + + + +执行 SQL 语句 + + + 创建一个表: + +EXEC SQL CREATE TABLE foo (number integer, ascii char(16)); +EXEC SQL CREATE UNIQUE INDEX num1 ON foo(number); +EXEC SQL COMMIT; + + + + + 插入行: + +EXEC SQL INSERT INTO foo (number, ascii) VALUES (9999, 'doodad'); +EXEC SQL COMMIT; + + + + + 删除行: + +EXEC SQL DELETE FROM foo WHERE number = 9999; +EXEC SQL COMMIT; + + + + + 更新: + +EXEC SQL UPDATE foo + SET ascii = 'foobar' + WHERE number = 9999; +EXEC SQL COMMIT; + + + + + 返回一个单一结果行的SELECT语句也可以直接使用EXEC SQL执行。要处理有多行的结果集,一个应用必须使用一个游标,可参考下面的(作为一种特殊情况,一个应用可以一次取出多行到一个数组主变量中,参考)。 + + + + 单行选择: + +EXEC SQL SELECT foo INTO :FooBar FROM table1 WHERE ascii = 'doodad'; + + + + + 还有,一个配置参数可以用SHOW命令检索: + +EXEC SQL SHOW search_path INTO :var; + + + + + :something形式的词元是主变量,即它们指向 C 程序中的变量。其定义见。 + + + + + 使用游标 + + + 要检索一个保持多行的结果集,一个应用必须声明一个游标并且从该游标中取得每一行。使用一个游标的步骤如下:声明一个游标、打开它、从该游标取得一行、重复并且最终关闭它。 + + + + 使用游标选择: + +EXEC SQL DECLARE foo_bar CURSOR FOR + SELECT number, ascii FROM foo + ORDER BY ascii; +EXEC SQL OPEN foo_bar; +EXEC SQL FETCH foo_bar INTO :FooBar, DooDad; +... +EXEC SQL CLOSE foo_bar; +EXEC SQL COMMIT; + + + + 有关游标声明的更多细节,参见 ;有关 FETCH 命令的详细信息,参见 + + + + ECPG DECLARE命令实际上不会向 PostgreSQL 后端发送语句。在执行OPEN命令时,游标才会在后端被打开(使用后端的DECLARE命令)。 + + + + + + 管理事务 + + + 在默认模式下,只有发出EXEC SQL COMMIT时命令才会提交。嵌入式 SQL 接口也支持事务自动提交(类似于psql的默认行为),可以通过ecpg命令行选项(见)或者EXEC SQL SET AUTOCOMMIT TO ON语句启用。在自动提交模式中,除非位于显式事务块内,每条命令都会自动提交。这种模式也可以通过EXEC SQL SET AUTOCOMMIT TO OFF显式关闭。 + + + 可以使用以下事务管理命令: + + EXEC SQL COMMIT + + + 提交一个进行中的事务。 + + + + + + EXEC SQL ROLLBACK + + + 回滚一个进行中的事务。 + + + + + + + + + EXEC SQL SET AUTOCOMMIT TO ON + + + 启用自动提交模式。 + + + + + + SET AUTOCOMMIT TO OFF + + + 禁用自动提交模式。这是默认值。 + + + + + + + + +预备语句 + + + 当传递给 SQL 语句的值在编译时未知或者同一个语句要被使用多次时,那么预备语句就有用武之地了。 + + + + 语句使用命令PREPARE进行预备。对于还未知的值,使用占位符?: + +EXEC SQL PREPARE stmt1 FROM "SELECT oid, datname FROM pg_database WHERE oid = ?"; + + + + + 如果一个语句返回一个单一行,应用可以在PREPARE之后调用EXECUTE来执行该语句,同时要用一个USING子句为占位符提供真实的值: + +EXEC SQL EXECUTE stmt1 INTO :dboid, :dbname USING 1; + + + + + 如果一个语句返回多行,应用可以使用一个基于该预备语句声明的游标。要绑定输入参数,该游标必须用一个USING子句打开: + +EXEC SQL PREPARE stmt1 FROM "SELECT oid,datname FROM pg_database WHERE oid > ?"; +EXEC SQL DECLARE foo_bar CURSOR FOR stmt1; + +/* 当到达结果集末尾时,跳出 while 循环 */ +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +EXEC SQL OPEN foo_bar USING 100; +... +while (1) +{ + EXEC SQL FETCH NEXT FROM foo_bar INTO :dboid, :dbname; + ... +} +EXEC SQL CLOSE foo_bar; + + + + + 当你不再需要该预备语句时,你应该释放它: + +EXEC SQL DEALLOCATE PREPARE name; + + + + + 更多有关PREPARE的细节,可参考。关于使用占位符和输入参数的细节,可参考。 + + + + + + 使用主变量 + + + 在中,你已经看到如何从嵌入式 SQL 程序中执行 SQL 语句。其中一些语句只使用固定值,既无法把用户提供的值插入语句中,也无法让程序处理查询返回的值。这类语句在实际应用中并不真正有用。本节将详细解释如何使用一种称为主变量的简单机制,在 C 程序与嵌入式 SQL 语句之间传递数据。在嵌入式 SQL 程序中,SQL 语句被视为主语言(即宿主语言)C 程序代码中的客体,因此 C 程序中的变量被称为主变量。 + + + + 另一种在 PostgreSQL 后端和 ECPG 应用之间交换值的方式是使用 SQL 描述符,它在中介绍。 + + + +概述 + + + 在嵌入式 SQL 中,在 C 程序和 SQL 语句之间传递数据特别简单。我们不必让程序把数据拼接进语句中去,那样会带来诸如正确引用值等各种复杂问题;只需在 SQL 语句中写出 C 变量的名称,并在前面加上一个冒号即可。例如: + +EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2); + + 该语句引用了两个名为v1v2的 C 变量,同时还使用了一个普通的 SQL 字符串常量,以说明你并不局限于只使用某一种数据。 + + + + 这种在 SQL 语句中插入 C 变量的风格可以用在 SQL 语句中每一个应该出现值表达式的地方。 + + + + +声明节 + + + 要把数据从程序传给数据库(例如作为查询参数),或者把数据从数据库传回程序,用来承载这些数据的 C 变量必须声明在经过特殊标记的区段中,这样嵌入式 SQL 预处理器才能识别它们。 + + + + 这个区段始于: + +EXEC SQL BEGIN DECLARE SECTION; + + 并终于: + +EXEC SQL END DECLARE SECTION; + + 在这两行之间,必须是正常的 C 变量声明,例如: + +int x = 4; +char foo[16], bar[16]; + + 如你所见,可以选择为变量指定初始值。变量的作用域由其声明节在程序中的位置决定。你也可以使用下面的语法声明变量,这种写法会隐式创建一个声明节: + +EXEC SQL int i = 4; + + 一个程序中可以根据需要放置任意多个声明节。 + + + + 这些声明也会作为 C 变量被重复在输出文件中,因此无需再次声明它们。不准备在 SQL 命令中使用的变量可以正常地在这些特殊节之外声明。 + + + + 结构体或联合的定义也必须写在DECLARE声明节中。否则预处理器无法处理这些类型,因为它不知道它们的定义。 + + + + +检索查询结果 + + + 现在你应该能够把程序产生的数据传递到一个 SQL 命令中了。但是怎么检索一个查询的结果呢?为此,嵌入式 SQL 提供了常规命令SELECTFETCH的特殊变体。这些命令有一个特殊的INTO子句,它指定被检索到的值要被存储在哪些主变量中。SELECT被用于只返回单一行的查询,而FETCH被用于使用一个游标返回多行的查询。 + + + + 这里是一个例子: + +/* + * 假定有这个表: + * CREATE TABLE test1 (a int, b varchar(50)); + */ + +EXEC SQL BEGIN DECLARE SECTION; +int v1; +VARCHAR v2; +EXEC SQL END DECLARE SECTION; + + ... + +EXEC SQL SELECT a, b INTO :v1, :v2 FROM test; + + 那么INTO子句出现在选择列表和FROM子句之间。选择列表中的元素数量必须和INTO后面列表(也被称为目标列表)的元素数量相等。 + + + + 这里有一个使用命令FETCH的例子: + +EXEC SQL BEGIN DECLARE SECTION; +int v1; +VARCHAR v2; +EXEC SQL END DECLARE SECTION; + + ... + +EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test; + + ... + +do +{ + ... + EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2; + ... +} while (...); + + 这里INTO子句出现在所有正常子句的后面。 + + + + + + 类型映射 + + + 当 ECPG 应用在 PostgreSQL 服务器和 C 应用之间交换值时(例如从服务器检索查询结果时或者用输入参数执行 SQL 语句时),值需要在 PostgreSQL 数据类型和主语言变量类型(具体来说是 C 语言数据类型)之间转换。ECPG 的要点之一就是它会在大多数情况下自动搞定这种转换。 + + + + 在这方面有两类数据类型:一些简单 PostgreSQL 数据类型(例如integertext)可以被应用直接读取和写入。其他 PostgreSQL 数据类型(例如timestampnumeric)只能通过特殊库函数访问,见。 + + + + 展示了哪种 PostgreSQL 数据类型对应于哪一种 C 数据类型。当你希望发送或接收一种给定 PostgreSQL 数据类型的值时,你应该在声明节中声明一个具有相应 C 数据类型的 C 变量。 + + + + 在 PostgreSQL 数据类型和 C 变量类型之间映射 + + + + PostgreSQL 数据类型 + 主变量类型 + + + + + + smallint + short + + + + integer + int + + + + bigint + long long int + + + + decimal + decimal这种类型只能通过特殊的库函数访问,见 + + + + numeric + numeric + + + + real + float + + + + double precision + double + + + + smallserial + short + + + + serial + int + + + + bigserial + long long int + + + + oid + unsigned int + + + + character(n), varchar(n), text + char[n+1], VARCHAR[n+1]ecpglib.h 中声明 + + + + name + char[NAMEDATALEN] + + + + timestamp + timestamp + + + + interval + interval + + + + date + date + + + + boolean + bool如果不是原生类型,则在ecpglib.h中声明 + + + + +
+ + +处理字符串 + + + 要处理 SQL 字符串数据类型(例如varchar以及text),有两种可能的方式来声明主变量。 + + + + 一种方式是使用char[](一个char字符串),这是在 C 中处理字符数据最常见的方式。 + +EXEC SQL BEGIN DECLARE SECTION; + char str[50]; +EXEC SQL END DECLARE SECTION; + + 注意你必须自己照看长度。如果你把这个主变量用作一个查询的目标变量并且该查询返回超过 49 个字符的字符串,那么将会发生缓冲区溢出。 + + + + 另一种方式是使用VARCHAR类型,它是 ECPG 提供的一种特殊类型。在一个VARCHAR类型数组上的定义会被转变成一个命名的struct。这样一个声明: + +VARCHAR var[180]; + + 会被转变成: + +struct varchar_var { int len; char arr[180]; } var; + + 成员arr容纳包含一个终止零字节的字符串。因此,要在一个VARCHAR主变量中存储一个字符串,该主变量必须被声明为具有包括零字节终止符的长度。成员len保存存储在arr中的字符串的长度,不包括终止零字节。当一个主变量被用做一个查询的输入时,如果strlen(arr)len不同,将使用短的那一个。 + + + + VARCHAR可以被写成大写或小写形式,但是不能大小写混合。 + + + + charVARCHAR主变量也可以保存其他 SQL 类型的值,它们将被存储为字符串形式。 + + + + + 访问特殊数据类型 + + + ECPG 包含一些特殊类型帮助你容易地与来自 PostgreSQL 服务器的一些特殊数据类型交互。特别地,它已经实现了对于numericdecimaldatetimestamp以及interval类型的支持。这些数据类型无法有效地被映射到原始的主变量类型(例如intlong long int或者char[]),因为它们有一种复杂的内部结构。应用通过声明特殊类型的主变量以及使用 pgtypes 库中的函数来处理这些类型。pgtypes 库(在中详细描述)包含了处理这些类型的基本函数,这样你不需要仅仅为了给一个时间戳增加一个时段而发送一个查询给 SQL 服务器。 + + + + 下面的小节描述了这些特殊数据类型。关于 pgtypes 库函数的更多细节,请参考。 + + + + timestamp, date + + + 这里有一种在 ECPG 主应用中处理timestamp变量的模式。 + + + + 首先,程序必须包括用于timestamp类型的头文件: + +#include <pgtypes_timestamp.h> + + + + + 接着,在声明节中声明一个主变量为类型timestamp: + +EXEC SQL BEGIN DECLARE SECTION; +timestamp ts; +EXEC SQL END DECLARE SECTION; + + + + + 并且在读入一个值到该主变量中之后,使用 pgtypes 库函数处理它。在下面的例子中,timestamp值被PGTYPEStimestamp_to_asc()函数转变成文本(ASCII)形式: + +EXEC SQL SELECT now()::timestamp INTO :ts; + +printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts)); + + 这个例子将展示像下面形式的一些结果: + +ts = 2010-06-27 18:03:56.949343 + + + + + 另外,DATE 类型可以用相同的方式处理。程序必须包括pgtypes_date.h,声明一个主变量为日期类型并且将一个 DATE 值使用PGTYPESdate_to_asc()函数转变成一种文本形式。关于 pgtypes 库函数的更多细节,请参考。 + + + + +interval + + + 对interval类型的处理也类似于timestampdate类型。不过,必须显式为一个interval类型分配内存。换句话说,该变量的内存空间必须在堆内存中分配,而不是在栈内存中分配。 + + + + 这里是一个例子程序: + +#include <stdio.h> +#include <stdlib.h> +#include <pgtypes_interval.h> + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + interval *in; +EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO testdb; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + in = PGTYPESinterval_new(); + EXEC SQL SELECT '1 min'::interval INTO :in; + printf("interval = %s\n", PGTYPESinterval_to_asc(in)); + PGTYPESinterval_free(in); + + EXEC SQL COMMIT; + EXEC SQL DISCONNECT ALL; + return 0; +} + + + + + +numeric, decimal + + + numericdecimal类型的处理类似于interval类型:需要定义一个指针、在堆上分配一些内存空间并且使用 pgtypes 库函数访问该变量。关于 pgtypes 库函数的更多细节,请参考。 + + + + pgtypes 库没有特别为decimal类型提供函数。一个应用必须使用一个 pgtypes 库函数把它转变成一个numeric变量以便进一步处理。 + + + + 这里是一个处理numericdecimal类型变量的例子程序。 + +#include <stdio.h> +#include <stdlib.h> +#include <pgtypes_numeric.h> + +EXEC SQL WHENEVER SQLERROR STOP; + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + numeric *num; + numeric *num2; + decimal *dec; +EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO testdb; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + num = PGTYPESnumeric_new(); + dec = PGTYPESdecimal_new(); + + EXEC SQL SELECT 12.345::numeric(4,2), 23.456::decimal(4,2) INTO :num, :dec; + + printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 0)); + printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 1)); + printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 2)); + + /* 将一个decimal转变成numeric以显示一个decimal值。 */ + num2 = PGTYPESnumeric_new(); + PGTYPESnumeric_from_decimal(dec, num2); + + printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 0)); + printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 1)); + printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 2)); + + PGTYPESnumeric_free(num2); + PGTYPESdecimal_free(dec); + PGTYPESnumeric_free(num); + + EXEC SQL COMMIT; + EXEC SQL DISCONNECT ALL; + return 0; +} + + + + + + + 非简单类型的主变量 + + + 你也可以把数组、typedefs、结构体和指针用作主变量。 + + + +数组 + + + 将数组用作主变量有两种情况。第一种如所述,是一种将一些文本字符串存储在char[]VARCHAR[]中的方法。第二种是不用一个游标从一个查询结果中检索多行。如果没有一个数组,要处理由多个行组成的查询结果,我们需要使用一个游标以及FETCH命令。但是使用数组主变量,多个行可以被一次收取。该数组的长度必须被定义成足以容纳所有的行,否则很可能会发生一次缓冲区溢出。 + + + + 下面的例子扫描pg_database系统表并且显示所有可用数据库的 OID 和名称: + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + int dbid[8]; + char dbname[8][16]; + int i; +EXEC SQL END DECLARE SECTION; + + memset(dbname, 0, sizeof(char)* 16 * 8); + memset(dbid, 0, sizeof(int) * 8); + + EXEC SQL CONNECT TO testdb; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + /* 一次检索多行到数组中。 */ + EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database; + + for (i = 0; i < 8; i++) + printf("oid=%d, dbname=%s\n", dbid[i], dbname[i]); + + EXEC SQL COMMIT; + EXEC SQL DISCONNECT ALL; + return 0; +} + + + 这个例子显示下面的结果(确切的值取决于本地环境)。 + +oid=1, dbname=template1 +oid=11510, dbname=template0 +oid=11511, dbname=postgres +oid=313780, dbname=testdb +oid=0, dbname= +oid=0, dbname= +oid=0, dbname= + + + + + +结构体 + + + 一个成员名称匹配查询结果列名的结构体可以被用来一次检索多列。该结构体使得我们能够在一个单一主变量中处理多列值。 + + + + 下面的例子从pg_database系统表以及使用pg_database_size()函数检索可用数据库的 OID、名称和尺寸。在这个例子中,一个成员名匹配SELECT结果的每一列的结构体变量dbinfo_t被用来检索结果行,而不需要把多个主变量放在FETCH语句中。 + +EXEC SQL BEGIN DECLARE SECTION; + typedef struct + { + int oid; + char datname[65]; + long long int size; + } dbinfo_t; + + dbinfo_t dbval; +EXEC SQL END DECLARE SECTION; + + memset(&dbval, 0, sizeof(dbinfo_t)); + + EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database; + EXEC SQL OPEN cur1; + + /* 在达到结果集末尾时,跳出 while 循环 */ + EXEC SQL WHENEVER NOT FOUND DO BREAK; + + while (1) + { + /* 将多列取到一个结构中。 */ + EXEC SQL FETCH FROM cur1 INTO :dbval; + + /* 打印该结构的成员。 */ + printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, dbval.size); + } + + EXEC SQL CLOSE cur1; + + + + + 这个例子会显示下列结果(确切的值取决于本地环境)。 + +oid=1, datname=template1, size=4324580 +oid=11510, datname=template0, size=4243460 +oid=11511, datname=postgres, size=4324580 +oid=313780, datname=testdb, size=8183012 + + + + + 结构体主变量吸收的列数与结构体的字段数相同。额外的列可以被分配给其他主变量。例如,上面的程序也可以使用结构体外部的size变量改写: + +EXEC SQL BEGIN DECLARE SECTION; + typedef struct + { + int oid; + char datname[65]; + } dbinfo_t; + + dbinfo_t dbval; + long long int size; +EXEC SQL END DECLARE SECTION; + + memset(&dbval, 0, sizeof(dbinfo_t)); + + EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database; + EXEC SQL OPEN cur1; + + /* 在达到结果集末尾时,跳出 while 循环 */ + EXEC SQL WHENEVER NOT FOUND DO BREAK; + + while (1) + { + /* 将多列取到一个结构中。 */ + EXEC SQL FETCH FROM cur1 INTO :dbval, :size; + + /* 打印该结构的成员。 */ + printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size); + } + + EXEC SQL CLOSE cur1; + + + + + + typedef + + 使用 typedef 关键字将新类型映射到已有类型。 +EXEC SQL BEGIN DECLARE SECTION; + typedef char mychartype[40]; + typedef long serial_t; +EXEC SQL END DECLARE SECTION; +注意,也可以使用: +EXEC SQL TYPE serial_t IS long; +此声明不必放在声明区段内。 + + + + 指针 + + + 你可以声明最常见类型的指针。不过注意,你不能使用指针作为不带自动分配内存的查询的目标变量。关于自动分配内存的详情请参考。 + + + + +EXEC SQL BEGIN DECLARE SECTION; + int *intp; + char **charp; +EXEC SQL END DECLARE SECTION; + + + + +
+ + + 处理非原始 SQL 数据类型 + + + 本节介绍如何在 ECPG 应用中处理非标量以及用户定义的 SQL 层数据类型。注意,这与上一节介绍的非原始类型主变量的处理方式是不同的。 + + + + 数组 + + + ECPG 不直接支持 SQL 层的多维数组。一维 SQL 数组可以映射为 C 数组主变量,反之亦然。不过,在创建语句时,ecpg 并不知道列的类型,因此无法检查某个 C 数组是否会作为对应 SQL 数组的输入。在处理 SQL 语句输出时,ecpg 则掌握所需信息,因此会检查双方是否都是数组。 + + + + 如果查询是分别访问数组的元素,那就可以避开在 ECPG 中直接使用数组。此时应使用一种能映射到该元素类型的主变量。例如,如果某列的类型是integer数组,就可以使用int类型的主变量;如果元素类型是varchartext,则可以使用char[]VARCHAR[]类型的主变量。 + + + + 下面是一个示例。假定有下列表: + +CREATE TABLE t3 ( + ii integer[] +); + +testdb=> SELECT * FROM t3; + ii +------------- + {1,2,3,4,5} +(1 row) + + + 下面的示例程序检索该数组的第 4 个元素,并将其存入一个类型为int的主变量中: + +EXEC SQL BEGIN DECLARE SECTION; +int ii; +EXEC SQL END DECLARE SECTION; + +EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[4] FROM t3; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + EXEC SQL FETCH FROM cur1 INTO :ii ; + printf("ii=%d\n", ii); +} + +EXEC SQL CLOSE cur1; + + + 该示例会显示下列结果: + +ii=4 + + + + + 要把多个数组元素映射到一个数组类型主变量中的多个元素,数组列的每一个元素以及主变量数组的每一个元素都必须被单独管理,例如: + +EXEC SQL BEGIN DECLARE SECTION; +int ii_a[8]; +EXEC SQL END DECLARE SECTION; + +EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[1], ii[2], ii[3], ii[4] FROM t3; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + EXEC SQL FETCH FROM cur1 INTO :ii_a[0], :ii_a[1], :ii_a[2], :ii_a[3]; + ... +} + + + + + 注意 + +EXEC SQL BEGIN DECLARE SECTION; +int ii_a[8]; +EXEC SQL END DECLARE SECTION; + +EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii FROM t3; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + /* 错误 */ + EXEC SQL FETCH FROM cur1 INTO :ii_a; + ... +} + + 在这种情况中不会正确工作,因为你无法把一个数组类型列直接映射到一个数组主变量。 + + + + 另一种变通办法是把数组的外部字符串表示存入char[]VARCHAR[]类型的主变量中。关于这种表示的更多细节见。注意,这意味着在宿主程序中无法自然地把它当作数组访问,除非再做一步解析文本表示的处理。 + + + + + 复合类型 + + + ECPG 不直接支持复合类型,但有一种简单的变通办法。可用方案与上文处理数组时类似:要么分别访问每个属性,要么使用外部字符串表示。 + + + + 对于下列例子,假定有下面的类型和表: + +CREATE TYPE comp_t AS (intval integer, textval varchar(32)); +CREATE TABLE t4 (compval comp_t); +INSERT INTO t4 VALUES ( (256, 'PostgreSQL') ); + + + 最显而易见的解决方案是单独访问每一个属性。下面的程序通过单独选择类型comp_t的每一个属性从例子表中检索数据: + +EXEC SQL BEGIN DECLARE SECTION; +int intval; +varchar textval[33]; +EXEC SQL END DECLARE SECTION; + +/* 将复合类型列的每一个元素放在 SELECT 列表中。 */ +EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + /* 将复合类型列的每一个元素取到主变量中。 */ + EXEC SQL FETCH FROM cur1 INTO :intval, :textval; + + printf("intval=%d, textval=%s\n", intval, textval.arr); +} + +EXEC SQL CLOSE cur1; + + + + 为了改进这个示例,可以将 FETCH 命令中用于保存值的主变量集中到一个结构中。有关结构形式的主变量,参见 。要改用结构,可以将示例修改如下。两个主变量 intvaltextval 变为 comp_t 结构的成员,并在 FETCH 命令中指定该结构。 +EXEC SQL BEGIN DECLARE SECTION; +typedef struct +{ + int intval; + varchar textval[33]; +} comp_t; + +comp_t compval; +EXEC SQL END DECLARE SECTION; + +/* 将复合类型列的每一个元素放在 SELECT 列表中。 */ +EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + /* 将 SELECT 列表中的所有值放入一个结构。 */ + EXEC SQL FETCH FROM cur1 INTO :compval; + + printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); +} + +EXEC SQL CLOSE cur1; +虽然 FETCH 命令已经使用了结构,SELECT 子句中的属性名仍是逐一指定的。可以使用 * 请求复合类型值的所有属性,进一步简化。 +... +EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4; +EXEC SQL OPEN cur1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + /* 将 SELECT 列表中的所有值放入一个结构。 */ + EXEC SQL FETCH FROM cur1 INTO :compval; + + printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); +} +... +这样,即使 ECPG 不理解复合类型本身,也能将复合类型几乎无缝地映射到结构。 + + + 最后,也可以把复合类型值的外部字符串表示存入char[]VARCHAR[]类型的主变量中。不过,如果采用这种方法,就很难在宿主程序中访问该值的各个字段。 + + + + + 用户定义的基础类型 + + + ECPG 并不直接支持新的用户定义的基础类型。你可以使用外部字符串表示以及类型char[]VARCHAR[]的主变量,并且这种方案事实上对很多类型都是合适和足够的。 + + + 下面的示例使用数据类型 complex,它来自 中的示例。该类型的外部字符串表示为 (%f,%f),由函数 complex_in()complex_out() 定义,见 。以下示例将复数类型的值 (1,1)(3,3) 分别插入列 ab,然后从表中查询它们。 +EXEC SQL BEGIN DECLARE SECTION; + varchar a[64]; + varchar b[64]; +EXEC SQL END DECLARE SECTION; + + EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)'); + + EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex; + EXEC SQL OPEN cur1; + + EXEC SQL WHENEVER NOT FOUND DO BREAK; + + while (1) + { + EXEC SQL FETCH FROM cur1 INTO :a, :b; + printf("a=%s, b=%s\n", a.arr, b.arr); + } + + EXEC SQL CLOSE cur1; +该示例的结果如下: +a=(1,1), b=(3,3) + + + + + 另一种变通方案是避免在 ECPG 中直接使用用户定义的类型,而是创建一个在用户定义的类型和 ECPG 能处理的简单类型之间转换的函数或者类型转换。不过要注意,在类型系统中引入类型转换(特别是隐式类型转换)要非常小心。 + + + + 例如, + +CREATE FUNCTION create_complex(r double, i double) RETURNS complex +LANGUAGE SQL +IMMUTABLE +AS $$ SELECT $1 * complex '(1,0')' + $2 * complex '(0,1)' $$; + + 在这个定义之后 ,下面的语句 + +EXEC SQL BEGIN DECLARE SECTION; +double a, b, c, d; +EXEC SQL END DECLARE SECTION; + +a = 1; +b = 2; +c = 3; +d = 4; + +EXEC SQL INSERT INTO test_complex VALUES (create_complex(:a, :b), create_complex(:c, :d)); + + 具有和 + +EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,4)'); + + 相同的效果。 + + + + + + 指示符 + + 上面的示例没有处理空值。实际上,如果读取示例从数据库取得空值,就会抛出错误。要向数据库传入空值或从数据库取得空值,需要在每个包含数据的主变量后,再指定第二个主变量。第二个主变量称为指示符,其中包含一个标志,表示该数据是否为空值;如果为空值,就会忽略实际主变量的值。下面的示例能正确处理空值的读取: +EXEC SQL BEGIN DECLARE SECTION; +VARCHAR val; +int val_ind; +EXEC SQL END DECLARE SECTION: + + ... + +EXEC SQL SELECT b INTO :val :val_ind FROM test1; +指示符变量 val_ind 在数据非空时为零,在数据为空值时为负数。 + + + 指示符有另一种功能:如果指示符值为正,它表示值不为空,但是当它被存储在主变量中时已被截断。 + + + + 如果向预处理器ecpg传入参数-r no_indicator,它就会工作在无指示符模式下。在这种模式中,如果没有指定指示符变量,那么对于字符串类型,空值会在输入和输出时表现为空串;对于整数类型,空值会表现为该类型可能的最小值(例如对于int就是INT_MIN)。 + + +
+ + +动态 SQL + + + 在很多情况中,一个应用必须要执行的特定 SQL 语句在编写该应用时就已知。不过在某些情况中,SQL 语句在运行时构造或者由一个外部来源提供。这样你就不能直接把 SQL 语句嵌入到 C 源代码,不过有一种功能允许你调用在一个字符串变量中提供的任意 SQL 语句。 + + + +执行没有结果集的语句 + + + 执行一个任意 SQL 语句的最简单方法是使用命令EXECUTE IMMEDIATE。例如: + +EXEC SQL BEGIN DECLARE SECTION; +const char *stmt = "CREATE TABLE test1 (...);"; +EXEC SQL END DECLARE SECTION; + +EXEC SQL EXECUTE IMMEDIATE :stmt; + + EXECUTE IMMEDIATE可以被用于不返回结果集的 SQL 语句(例如 DDL、INSERTUPDATEDELETE)。你不能用这种方法执行检索数据的语句(例如SELECT)。下一节将描述如何执行这一种语句。 + + + + +执行一个有输入参数的语句 + + + 执行任意 SQL 语句的一种更强大的方法是准备它们一次并且在每次需要时执行该预备语句。也可以准备一个一般化的语句,然后通过替换参数执行它的特定版本。在准备语句时,在你想要稍后替换参数的地方写上问号。例如: + +EXEC SQL BEGIN DECLARE SECTION; +const char *stmt = "INSERT INTO test1 VALUES(?, ?);"; +EXEC SQL END DECLARE SECTION; + +EXEC SQL PREPARE mystmt FROM :stmt; + ... +EXEC SQL EXECUTE mystmt USING 42, 'foobar'; + + + + + 当你不再需要该预备语句时,你应该释放它: + +EXEC SQL DEALLOCATE PREPARE name; + + + + + +执行一个有结果集的语句 + + + 要执行一个只有单一结果行的 SQL 语句,可以使用EXECUTE。要保存结果,在其中增加一个INTO子句。 + ?"; +int v1, v2; +VARCHAR v3[50]; +EXEC SQL END DECLARE SECTION; + +EXEC SQL PREPARE mystmt FROM :stmt; + ... +EXEC SQL EXECUTE mystmt INTO :v1, :v2, :v3 USING 37; +]]> + + 一个EXECUTE命令可以有一个INTO子句、一个USING子句,可以同时有这两个子句,也可以不带这两个子句。 + + + + 如果一个查询被期望返回多于一个结果行,应该如下列例子所示使用一个游标(关于游标详见)。 + +EXEC SQL BEGIN DECLARE SECTION; +char dbaname[128]; +char datname[128]; +char *stmt = "SELECT u.usename as dbaname, d.datname " + " FROM pg_database d, pg_user u " + " WHERE d.datdba = u.usesysid"; +EXEC SQL END DECLARE SECTION; + +EXEC SQL CONNECT TO testdb AS con1 USER testuser; +EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + +EXEC SQL PREPARE stmt1 FROM :stmt; + +EXEC SQL DECLARE cursor1 CURSOR FOR stmt1; +EXEC SQL OPEN cursor1; + +EXEC SQL WHENEVER NOT FOUND DO BREAK; + +while (1) +{ + EXEC SQL FETCH cursor1 INTO :dbaname,:datname; + printf("dbaname=%s, datname=%s\n", dbaname, datname); +} + +EXEC SQL CLOSE cursor1; + +EXEC SQL COMMIT; +EXEC SQL DISCONNECT ALL; + + + + + + + pgtypes 库 + + + pgtypes 库将PostgreSQL数据库类型映射到 C 中等价的类型以便在 C 程序中使用。它还提供在 C 中对这些类型进行基本计算的函数,即不依赖PostgreSQL服务器进行计算。请看下面的例子: + + + + + +字符串 + + PGTYPESnumeric_to_asc之类的一些函数返回一个新分配的字符串的指针。这些结果应该用PGTYPESchar_free而不是free释放(这只在Windows上很重要,因为Windows上的内存分配和释放有时候需要由同一个库完成)。 + + + + + numeric类型 + + numeric类型用来完成对任意精度的计算。PostgreSQL服务器中等效的类型请见。因为要用于任意精度,这种变量需要能够动态地扩展和收缩。这也是为什么你只能用PGTYPESnumeric_newPGTYPESnumeric_free函数在堆上创建numeric变量。decimal类型与numeric类型相似但是在精度上有限制,decimal类型可以在堆上创建也可以在栈上创建。 + + 可以使用以下函数处理 numeric 类型: + + PGTYPESnumeric_new + + + 请求一个指向新分配的numeric变量的指针。 + +numeric *PGTYPESnumeric_new(void); + + + + + + + PGTYPESnumeric_free + + + 释放一个numeric类型,释放它所有的内存。 + +void PGTYPESnumeric_free(numeric *var); + + + + + + + PGTYPESnumeric_from_asc + + + 从字符串词元中解析一个numeric类型。 + +numeric *PGTYPESnumeric_from_asc(char *str, char **endptr); + + 例如,可用的格式是: + -2、 + .794、 + +3.44、 + 592.49E07或者 + -32.84e-4。 + 如果值能被成功地解析,将返回一个有效的指针,否则返回 NULL 指针。目前 ECPG 总是解析整个字符串并且因此当前不支持把第一个非法字符的地址存储在*endptr中。你可以安全地把endptr设置为 NULL。 + + + + + + PGTYPESnumeric_to_asc + + + 返回由malloc分配的字符串的指针,它包含numeric类型num的字符串表示。 + +char *PGTYPESnumeric_to_asc(numeric *num, int dscale); + + numeric值将被使用dscale小数位打印,必要时会圆整。结果必须用PGTYPESchar_free()释放。 + + + + + + PGTYPESnumeric_add + + + 把两个numeric变量相加放到第三个numeric变量中。 + +int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result); + + 该函数把变量var1var2相加放到结果变量result中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_sub + + + 把两个numeric变量相减并且把结果返回到第三个numeric变量。 + +int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result); + + 该函数把变量var2从变量var1中减除。该操作的结果被存储在变量result中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_mul + + + 把两个numeric变量相乘并且把结果返回到第三个numeric变量。 + +int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result); + + 该函数把变量var1var2相乘。该操作的结果被存储在变量result中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_div + + + 把两个numeric变量相除并且把结果返回到第三个numeric变量。 + +int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result); + + 该函数用变量var2除变量var1。该操作的结果被存储在变量result中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_cmp + + + 比较两个numeric变量。 + +int PGTYPESnumeric_cmp(numeric *var1, numeric *var2) + + 这个函数比较两个numeric变量。错误时会返回INT_MAX。成功时,该函数返回三种可能结果之一: + + + + var1大于var2则返回 1 + + + + + 如果var1小于var2则返回 -1 + + + + + 如果var1var2相等则返回 0 + + + + + + + + + PGTYPESnumeric_from_int + + + 把一个整数变量转换成一个numeric变量。 + +int PGTYPESnumeric_from_int(signed int int_val, numeric *var); + + 这个函数接受一个有符号整型变量并且把它存储在numeric变量var中。成功时返回 0,失败时返回 -1。 + + + + + + PGTYPESnumeric_from_long + + + 把一个长整型变量转换成一个numeric变量。 + +int PGTYPESnumeric_from_long(signed long int long_val, numeric *var); + + 这个函数接受一个有符号长整型变量并且把它存储在numeric变量var中。成功时返回 0,失败时返回 -1。 + + + + + + PGTYPESnumeric_copy + + + 把一个numeric变量复制到另一个中。 + +int PGTYPESnumeric_copy(numeric *src, numeric *dst); + + 这个函数把src指向的变量的值复制到dst指向的变量中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_from_double + + + 把一个双精度类型的变量转换成一个numeric变量。 + +int PGTYPESnumeric_from_double(double d, numeric *dst); + + 这个函数接受一个双精度类型的变量并且把结果存储在dst指向的变量中。成功时该函数返回 0,出错时返回 -1。 + + + + + + PGTYPESnumeric_to_double + + 将 numeric 类型的变量转换为 double。 +int PGTYPESnumeric_to_double(numeric *nv, double *dp) +该函数将 nv 所指变量中的 numeric 值转换为 double,并存入 dp 所指的变量。成功时返回 0,发生错误(包括溢出)时返回 -1。发生溢出时,还会将全局变量 errno 设为 PGTYPES_NUM_OVERFLOW + + + + + PGTYPESnumeric_to_int + + 将 numeric 类型的变量转换为 int。 +int PGTYPESnumeric_to_int(numeric *nv, int *ip); +该函数将 nv 所指变量中的 numeric 值转换为整数,并存入 ip 所指的变量。成功时返回 0,发生错误(包括溢出)时返回 -1。发生溢出时,还会将全局变量 errno 设为 PGTYPES_NUM_OVERFLOW + + + + + PGTYPESnumeric_to_long + + 将 numeric 类型的变量转换为 long。 +int PGTYPESnumeric_to_long(numeric *nv, long *lp); +该函数将 nv 所指变量中的 numeric 值转换为 long,并存入 lp 所指的变量。成功时返回 0,发生错误(包括溢出)时返回 -1。发生溢出时,还会将全局变量 errno 设为 PGTYPES_NUM_OVERFLOW + + + + + PGTYPESnumeric_to_decimal + + 将 numeric 类型的变量转换为 decimal。 +int PGTYPESnumeric_to_decimal(numeric *src, decimal *dst); +该函数将 src 所指变量中的 numeric 值转换为 decimal,并存入 dst 所指的变量。成功时返回 0,发生错误(包括溢出)时返回 -1。发生溢出时,还会将全局变量 errno 设为 PGTYPES_NUM_OVERFLOW + + + + + PGTYPESnumeric_from_decimal + + + 将一个decimal类型的变量转换成numeric。 + +int PGTYPESnumeric_from_decimal(decimal *src, numeric *dst); + + 该函数将src指向的变量中的 decimal 值转换成dst指向的 numeric 变量。成功时该函数返回 0,出错时返回 -1。由于 decimal 类型被实现为 numeric 类型的一个受限版本,这种转换不会发生溢出。 + + + + + + + + + 日期类型 + + C 中的日期类型允许你的程序处理 SQL 日期类型的数据。PostgreSQL服务器的等效类型可见。 + + 可以使用以下函数处理 date 类型: + + PGTYPESdate_from_timestamp + + + 从一个时间戳中抽取日期部分。 + +date PGTYPESdate_from_timestamp(timestamp dt); + + 该函数接收一个时间戳作为它的唯一参数并且从这个时间戳返回抽取的日期部分。 + + + + + + PGTYPESdate_from_asc + + + 从日期的文本表示解析一个日期。 + +date PGTYPESdate_from_asc(char *str, char **endptr); + + 该函数接收一个 C 的字符串str以及一个指向 C 字符串的指针endptr。当前 ECPG 总是解析完整的字符串并且因此当前不支持将第一个非法字符的地址存储在*endptr中。你可以安全地把endptr设置为 NULL。 + + + 注意该函数总是假定格式按照 MDY 格式化并且当前在 ECPG 中没有变体可以改变这种格式。 + + + 展示了所有允许的输入格式。 + + + <function>PGTYPESdate_from_asc</function>的合法输入格式 + + + + 输入 + 结果 + + + + + January 8, 1999 + January 8, 1999 + + + 1999-01-08 + January 8, 1999 + + + 1/8/1999 + January 8, 1999 + + + 1/18/1999 + January 18, 1999 + + + 01/02/03 + February 1, 2003 + + + 1999-Jan-08 + January 8, 1999 + + + Jan-08-1999 + January 8, 1999 + + + 08-Jan-1999 + January 8, 1999 + + + 99-Jan-08 + January 8, 1999 + + + 08-Jan-99 + January 8, 1999 + + + 08-Jan-06 + January 8, 2006 + + + Jan-08-99 + January 8, 1999 + + + 19990108 + ISO 8601; January 8, 1999 + + + 990108 + ISO 8601; January 8, 1999 + + + 1999.008 + year and day of year + + + J2451187 + Julian day + + + January 8, 99 BC + year 99 before the Common Era + + + +
+
+
+ + + PGTYPESdate_to_asc + + + 返回一个日期变量的文本表示。 + +char *PGTYPESdate_to_asc(date dDate); + + 该函数接收日期dDate作为它的唯一参数。它将以形式1999-01-18输出该日期,即以YYYY-MM-DD格式输出。结果必须用PGTYPESchar_free()释放。 + + + + + + PGTYPESdate_julmdy + + + 从一个日期类型变量中抽取日、月和年的值。 + +void PGTYPESdate_julmdy(date d, int *mdy); + + + 该函数接收日期d以及一个指向有 3 个整数值的数组mdy的指针。变量名就表明了顺序:mdy[0]将被设置为包含月份,mdy[1]将被设置为日的值,而mdy[2]将包含年。 + + + + + + PGTYPESdate_mdyjul + + + 从一个由 3 个整数构成的数组创建一个日期值,3 个整数分别指定日、月和年。 + +void PGTYPESdate_mdyjul(int *mdy, date *jdate); + + 这个函数接收 3 个整数(mdy)组成的数组作为其第一个参数,其第二个参数是一个指向日期类型变量的指针,它被用来保存操作的结果。 + + + + + + PGTYPESdate_dayofweek + + + 为一个日期值返回表示它是星期几的数字。 + +int PGTYPESdate_dayofweek(date d); + + 这个函数接收日期变量d作为它唯一的参数并且返回一个整数说明这个日期是星期几。 + + + + 0 - 星期日 + + + + + 1 - 星期一 + + + + + 2 - 星期二 + + + + + 3 - 星期三 + + + + + 4 - 星期四 + + + + + 5 - 星期五 + + + + + 6 - 星期六 + + + + + + + + + PGTYPESdate_today + + + 得到当前日期。 + +void PGTYPESdate_today(date *d); + + 该函数接收一个指向一个日期变量(d)的指针并且把该参数设置为当前日期。 + + + + + + PGTYPESdate_fmt_asc + + + 使用一个格式掩码将一个日期类型的变量转换成它的文本表示。 + +int PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf); + + 该函数接收要转换的日期(dDate)、格式掩码(fmtstring)以及将要保存日期的文本表示的字符串(outbuf)。 + + + 成功时,返回 0;如果发生错误,则返回一个负值。 + + + 下面是你可以使用的域指示符: + + + + dd - 一个月中的第几天。 + + + + + mm - 一年中的第几个月。 + + + + + yy - 两位数的年份。 + + + + + yyyy - 四位数的年份。 + + + + + ddd - 星期几的名称(简写)。 + + + + + mmm - 月份的名称(简写)。 + + + + 所有其他字符会被原封不动地复制到输出字符串中。 + + + 指出了一些可能的格式。这将给你一些线索如何使用这个函数。所有输出都是基于同一个日期:1959年11月23日。 + + + <function>PGTYPESdate_fmt_asc</function>的合法输入格式 + + + + 格式 + 结果 + + + + + mmddyy + 112359 + + + ddmmyy + 231159 + + + yymmdd + 591123 + + + yy/mm/dd + 59/11/23 + + + yy mm dd + 59 11 23 + + + yy.mm.dd + 59.11.23 + + + .mm.yyyy.dd. + .11.1959.23. + + + mmm. dd, yyyy + Nov. 23, 1959 + + + mmm dd yyyy + Nov 23 1959 + + + yyyy dd mm + 1959 23 11 + + + ddd, mmm. dd, yyyy + Mon, Nov. 23, 1959 + + + (ddd) mmm. dd, yyyy + (Mon) Nov. 23, 1959 + + + +
+
+
+ + + PGTYPESdate_defmt_asc + + 使用格式掩码,将 C char* 字符串转换为 date 类型的值。 +int PGTYPESdate_defmt_asc(date *d, char *fmt, char *str); +该函数接受以下参数:指向用于保存操作结果的 date 值的指针(d)、解析日期所用的格式掩码(fmt),以及包含日期文本表示的 C char* 字符串(str)。文本表示应与格式掩码匹配,但字符串与格式掩码不必一一对应(1:1)。该函数只分析先后顺序,并查找表示年份位置的字面值 yyyyyy、表示月份位置的 mm,以及表示日位置的 dd + + 给出了一些可能的格式。这将给你一些线索如何使用这个函数。 + + + <function>rdefmtdate</function>的合法输入格式 + + + + 格式 + 字符串 + 结果 + + + + + ddmmyy + 21-2-54 + 1954-02-21 + + + ddmmyy + 2-12-54 + 1954-12-02 + + + ddmmyy + 20111954 + 1954-11-20 + + + ddmmyy + 130464 + 1964-04-13 + + + mmm.dd.yyyy + MAR-12-1967 + 1967-03-12 + + + yy/mm/dd + 1954, February 3rd + 1954-02-03 + + + mmm.dd.yyyy + 041269 + 1969-04-12 + + + yy/mm/dd + In the year 2525, in the month of July, mankind will be alive on the 28th day + 2525-07-28 + + + dd-mm-yy + I said on the 28th of July in the year 2525 + 2525-07-28 + + + mmm.dd.yyyy + 9/14/58 + 1958-09-14 + + + yy/mm/dd + 47/03/29 + 1947-03-29 + + + mmm.dd.yyyy + oct 28 1975 + 1975-10-28 + + + mmddyy + Nov 14th, 1985 + 1985-11-14 + + + +
+
+
+
+
+
+ + + 时间戳类型 + + C 中的时间戳类型允许你的程序处理 SQL 时间戳类型的数据。PostgreSQL服务器的等效类型可见。 + + 可以使用以下函数处理 timestamp 类型: + + PGTYPEStimestamp_from_asc + + 将时间戳的文本表示解析为 timestamp 变量。 +timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr); +该函数接受要解析的字符串(str)和一个指向 C char* 的指针(endptr)。目前 ECPG 始终解析整个字符串,因此尚不支持将第一个无效字符的地址存入 *endptr。可以放心地将 endptr 设为 NULL。 + + 该函数在成功时返回解析后的时间戳。出错时返回PGTYPESInvalidTimestamp, + 并将errno设置为PGTYPES_TS_BAD_TIMESTAMP。 + 关于该值的重要说明,见。 + + + 通常,输入字符串可以包含允许的日期规范、空白字符和允许的时间规范的任意组合。请注意,ECPG 不支持时区。它可以解析时区,但不像PostgreSQL服务器那样应用任何计算。时区标识符会被静默丢弃。 + + + 包含了一些输入字符串的示例。 + + +<function>PGTYPEStimestamp_from_asc</function>的合法输入格式 + + + + 输入 + 结果 + + + + + 1999-01-08 04:05:06 + 1999-01-08 04:05:06 + + + January 8 04:05:06 1999 PST + 1999-01-08 04:05:06 + + + 1999-Jan-08 04:05:06.789-8 + 1999-01-08 04:05:06.789 (time zone specifier ignored) + + + J2451187 04:05-08:00 + 1999-01-08 04:05:00 (time zone specifier ignored) + + + +
+
+
+ + + PGTYPEStimestamp_to_asc + + 将日期转换为 C char* 字符串。 +char *PGTYPEStimestamp_to_asc(timestamp tstamp); +该函数接受时间戳 tstamp 作为唯一参数,并返回一个已分配内存的字符串,其中包含时间戳的文本表示。要释放结果,必须调用 PGTYPESchar_free()。 + + + + + + PGTYPEStimestamp_current + + + 检索当前时间戳。 + +void PGTYPEStimestamp_current(timestamp *ts); + + 该函数检索当前时间戳,并将其保存到ts指向的时间戳变量中。 + + + + + + PGTYPEStimestamp_fmt_asc + + + 将时间戳变量转换为C char*,使用格式掩码。 + +int PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr); + + 该函数接收一个指向要转换的时间戳的指针作为第一个参数(ts), + 一个指向输出缓冲区的指针(output),已为输出缓冲区分配的最大长度 + (str_len),以及用于转换的格式掩码(fmtstr)。 + + + 成功时返回 0,发生错误时返回负值。 + + 格式掩码可以使用以下格式说明符。它们与函数 strftime 所用的格式说明符相同,该函数属于 libc。不是格式说明符的内容都会被复制到输出缓冲区。 + + + %A - 替换为完整星期名称的本地化表示。 + + + + + %a - 替换为缩写星期名称的本地化表示。 + + + + + %B - 替换为完整月份名称的本地化表示。 + + + + + %b - 替换为缩写月份名称的本地化表示。 + + + + + %C - 被(年份 / 100)替换为十进制数;单个数字前面加零。 + + + + + %c - 替换为时间和日期的本地化表示。 + + + + + %D - 等同于 + %m/%d/%y。 + + + + %d - 替换为一个月中的日,以十进制数表示(01-31)。 + + + + %E* %O* - POSIX 本地化扩展。序列 + %Ec + %EC + %Ex + %EX + %Ey + %EY + %Od + %Oe + %OH + %OI + %Om + %OM + %OS + %Ou + %OU + %OV + %Ow + %OW + %Oy + 应该提供替代表示。 + + + 另外%OB用于表示替代的月份名称(单独使用,不包括日期)。 + + + + %e - 替换为一个月中的日,以十进制数表示(1-31);一位数前补空格。 + + + + %F - 等同于%Y-%m-%d。 + + + + + %G - 被一个包含世纪的十进制数字年份替换。这一年是包含大部分周的那一年(以周一作为一周的第一天)。 + + + + %g - 替换为与 %G 相同的年份,但以不带世纪的十进制数表示(00-99)。 + + + %H - 替换为小时(24 小时制),以十进制数表示(00-23)。 + + + + %h - 与%b相同。 + + + + %I - 替换为小时(12 小时制),以十进制数表示(01-12)。 + + + %j - 替换为一年中的第几天,以十进制数表示(001-366)。 + + + %k - 替换为小时(24 小时制),以十进制数表示(0-23);一位数前补空格。 + + + %l - 替换为小时(12 小时制),以十进制数表示(1-12);一位数前补空格。 + + + %M - 替换为分钟,以十进制数表示(00-59)。 + + + %m - 替换为月份,以十进制数表示(01-12)。 + + + + %n - 被换行符替换。 + + + + + %O* - 与%E*相同。 + + + + + %p - 按需要替换为上午下午的本地化表示。 + + + + + %R - 等同于%H:%M。 + + + + + %r - 等同于%I:%M:%S %p。 + + + + %S - 替换为秒,以十进制数表示(00-60)。 + + + + %s - 被替换为自纪元时以来的秒数,协调世界时。 + + + + + %T - 等同于%H:%M:%S + + + + + %t - 被替换为一个制表符。 + + + + %U - 替换为一年中的周数,以星期日为每周第一天,以十进制数表示(00-53)。 + + + %u - 替换为星期几,以星期一为每周第一天,以十进制数表示(1-7)。 + + + %V - 替换为一年中的周数,以星期一为每周第一天,以十进制数表示(01-53)。如果包含 1 月 1 日的那一周有四天或更多天属于新年,该周就是第 1 周;否则,该周是上一年的最后一周,下一周才是第 1 周。 + + + + %v - 等同于 + %e-%b-%Y。 + + + + %W - 替换为一年中的周数,以星期一为每周第一天,以十进制数表示(00-53)。 + + + %w - 替换为星期几,以星期日为每周第一天,以十进制数表示(0-6)。 + + + + %X - 被时间的本地化表示替换。 + + + + + %x - 替换为日期的本地化表示。 + + + + + %Y - 被年份替换,包括世纪,以十进制数表示。 + + + + %y - 替换为不带世纪的年份,以十进制数表示(00-99)。 + + + + %Z - 被时区名称替换。 + + + + + %z - 被UTC时间偏移替换;前导加号表示UTC东部,减号表示UTC西部,后跟两位数字的小时和分钟,它们之间没有分隔符(RFC 822日期头的常见形式)。 + + + + + %+ - 被日期和时间的本地化表示所替换。 + + + + + %-* - GNU libc扩展。在执行数字输出时不进行任何填充。 + + + + + $_* - GNU libc扩展。明确指定填充空间。 + + + + + %0* - GNU libc扩展。显式地指定零来填充。 + + + + + %% - 被%替换。 + + + + + + + + + PGTYPEStimestamp_sub + + + 从一个时间戳减去另一个时间戳,并将结果保存在 interval 类型变量中。 + +int PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv); + + 该函数从ts1指向的时间戳变量中减去ts2指向的时间戳变量,并将结果存入iv指向的 interval 变量中。 + + + 成功时返回 0,发生错误时返回负值。 + + + + + + PGTYPEStimestamp_defmt_asc + + + 使用格式掩码从文本表示中解析时间戳值。 + +int PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d); + + 该函数接收时间戳的文本表示,存储在变量str中,以及要在变量fmt中使用的格式掩码。 + 结果将存储在d指向的变量中。 + + + 如果格式掩码fmt为 NULL,函数将退回到默认格式掩码,即%Y-%m-%d %H:%M:%S。 + + + 这是与相反的函数。请参阅那里的文档,以了解可能的格式掩码条目。 + + + + + + PGTYPEStimestamp_add_interval + + + 向时间戳变量加上一个 interval 变量。 + +int PGTYPEStimestamp_add_interval(timestamp *tin, interval *span, timestamp *tout); + + 该函数接收一个指向时间戳变量tin的指针和一个指向间隔变量span的指针。 + 它将间隔添加到时间戳中,并将结果时间戳保存在tout指向的变量中。 + + + 成功时返回 0,发生错误时返回负值。 + + + + + + PGTYPEStimestamp_sub_interval + + + 从时间戳变量中减去一个 interval 变量。 + +int PGTYPEStimestamp_sub_interval(timestamp *tin, interval *span, timestamp *tout); + + 该函数从span指向的时间间隔变量中减去tin指向的时间戳变量,并将结果保存到tout指向的变量中。 + + + 成功时返回 0,发生错误时返回负值。 + + + +
+
+
+ + + interval 类型 + + C 中的 interval 类型允许程序处理 SQL interval 类型的数据。PostgreSQL服务器中的等效类型见。 + + 可以使用以下函数处理 interval 类型: + + + PGTYPESinterval_new + + + 返回一个指向新分配 interval 变量的指针。 + +interval *PGTYPESinterval_new(void); + + + + + + + PGTYPESinterval_free + + + 释放先前分配的 interval 变量所占用的内存。 + +void PGTYPESinterval_free(interval *intvl); + + + + + + + PGTYPESinterval_from_asc + + + 从文本表示解析一个区间。 + +interval *PGTYPESinterval_from_asc(char *str, char **endptr); + + 该函数解析输入字符串str,并返回一个已分配 interval 变量的指针。目前 ECPG 总是解析整个字符串,因此暂不支持把第一个非法字符的地址存储在*endptr中。可以安全地把endptr设为 NULL。 + + + + + + PGTYPESinterval_to_asc + + 将 interval 类型的变量转换为文本表示。 +char *PGTYPESinterval_to_asc(interval *span); +该函数将 span 所指的 interval 变量转换为 C char*。输出类似于以下示例:@ 1 day 12 hours 59 mins 10 secs。要释放结果,必须调用 PGTYPESchar_free()。 + + + + + + PGTYPESinterval_copy + + + 复制一个 interval 类型变量。 + +int PGTYPESinterval_copy(interval *intvlsrc, interval *intvldest); + + 该函数把intvlsrc指向的 interval 变量复制到intvldest指向的 interval 变量中。注意,需要事先为目标变量分配好内存。 + + + + + + + + + decimal类型 + + decimal类型和numeric类型相似。不过,它被限制为最大精度是 30 个有效位。与numeric类型只能在堆上创建相反,decimal类型既可以在栈上也可以在堆上创建(使用函数PGTYPESdecimal_newPGTYPESdecimal_free)。在中描述的Informix兼容模式中有很多其它函数可以处理decimal类型。 + + 以下函数可用于处理 decimal 类型,它们并非只包含在 libcompat 库中。 + + PGTYPESdecimal_new + + + 要求一个指向新分配的decimal变量的指针。 + +decimal *PGTYPESdecimal_new(void); + + + + + + + PGTYPESdecimal_free + + + 释放一个decimal类型,释放它的所有内存。 + +void PGTYPESdecimal_free(decimal *var); + + + + + + + + + + pgtypeslib 的 errno 值 + + + + PGTYPES_NUM_BAD_NUMERIC + + + 一个参数应该包含一个numeric变量(或者指向一个numeric变量),但是实际上它的内存表示非法。 + + + + + + PGTYPES_NUM_OVERFLOW + + + 发生一次溢出。由于numeric类型可以处理几乎任何精度,将一个numeric变量转换成其他类型可能导致溢出。 + + + + + + PGTYPES_NUM_UNDERFLOW + + + 发生一次下溢。由于numeric类型可以处理几乎任何精度,将一个numeric变量转换成其他类型可能导致下溢。 + + + + + + PGTYPES_NUM_DIVIDE_ZERO + + + 尝试了一次除零。 + + + + + + PGTYPES_DATE_BAD_DATE + + + 一个非法的日期字符串被传给了PGTYPESdate_from_asc函数。 + + + + + + PGTYPES_DATE_ERR_EARGS + + + 非法参数被传给了PGTYPESdate_defmt_asc函数。 + + + + + + PGTYPES_DATE_ERR_ENOSHORTDATE + + + PGTYPESdate_defmt_asc函数在输入字符串中发现了一个非法词元。 + + + + + + PGTYPES_INTVL_BAD_INTERVAL + + + 一个非法的区间字符串被传给了PGTYPESinterval_from_asc函数,或者一个非法的区间值被传给了PGTYPESinterval_to_asc函数。 + + + + + + PGTYPES_DATE_ERR_ENOTDMY + + + 在PGTYPESdate_defmt_asc函数中有日/月/年不匹配的赋值。 + + + + + + PGTYPES_DATE_BAD_DAY + + + PGTYPESdate_defmt_asc函数发现了月中的一个非法日值。 + + + + + + PGTYPES_DATE_BAD_MONTH + + + PGTYPESdate_defmt_asc函数发现了一个非法的月值。 + + + + + + PGTYPES_TS_BAD_TIMESTAMP + + + 一个非法的时间戳字符串被传给了PGTYPEStimestamp_from_asc函数,或者一个非法的时间戳值被传给了PGTYPEStimestamp_to_asc函数。 + + + + + + PGTYPES_TS_ERR_EINFTIME + + + 在一个无法处理无限时间戳值的环境中遇到了这样一个值。 + + + + + + + + + pgtypeslib 的特殊常量 + + + + PGTYPESInvalidTimestamp + + + 表示一个非法时间戳的时间戳类型值。在解析错误时,函数PGTYPEStimestamp_from_asc会返回这个值。注意由于timestamp数据类型的内部表达,PGTYPESInvalidTimestamp在同时也是一个合法的时间戳。它被设置为1899-12-31 23:59:59。为了检测到错误,确认你的应用在每次调用PGTYPEStimestamp_from_asc后不仅仅测试PGTYPESInvalidTimestamp,还应该测试errno != 0。 + + + + + + +
+ + + 使用描述符区域 + + + 一个 SQL 描述符区域是一种处理SELECTFETCH或者DESCRIBE语句结果的高级方法。一个 SQL 描述符区域把数据中一行的数据及元数据项组合到一个数据结构中。在执行动态 SQL 语句时(结果行的性质无法提前预知),元数据特别有用。PostgreSQL 提供两种方法来使用描述符区域:命名 SQL 描述符区域和 C 结构体 SQLDA。 + + + + 命名 SQL 描述符区域 + + + 一个命名 SQL 描述符区域由一个头部以及一个或多个条目描述符区域构成,头部包含与整个描述符相关的信息,而条目描述符区域则描述结果行中的每一列。 + + + + 在使用 SQL 描述符区域之前,需要先分配一个: + +EXEC SQL ALLOCATE DESCRIPTOR identifier; + + identifier 会作为该描述符区域的变量名分配的描述符的作用域是什么?当不再需要该描述符时,应当释放它: + +EXEC SQL DEALLOCATE DESCRIPTOR identifier; + + + + + 要使用一个描述符区域,把它指定为INTO子句的存储目标(而不是列出主变量): + +EXEC SQL FETCH NEXT FROM mycursor INTO SQL DESCRIPTOR mydesc; + + 如果结果集为空,该描述符区域仍然会包含查询的元数据,即域的名称。 + + + + 对于还没有执行的预备查询,DESCRIBE可以被用来得到其结果集的元数据: + +EXEC SQL BEGIN DECLARE SECTION; +char *sql_stmt = "SELECT * FROM table1"; +EXEC SQL END DECLARE SECTION; + +EXEC SQL PREPARE stmt1 FROM :sql_stmt; +EXEC SQL DESCRIBE stmt1 INTO SQL DESCRIPTOR mydesc; + + + + + 在 PostgreSQL 9.0 之前,SQL关键词是可选的,因此使用DESCRIPTORSQL DESCRIPTOR都会产生命名 SQL 描述符区域。现在该关键词是强制性的,省略SQL关键词会产生 SQLDA 描述符区域(见)。 + + + + 在DESCRIBEFETCH语句中,INTOUSING关键词的使用相似:它们产生结果集以及一个描述符区域中的元数据。 + + + 要从描述符区取得数据,可以将它视为一个包含命名字段的结构。使用以下命令,从头部取得某个字段的值,并存入主变量: +EXEC SQL GET DESCRIPTOR name :hostvar = field; +目前只定义了一个头部字段:COUNT,它表示有多少个项目描述符区(即结果包含多少列)。主变量必须是整数类型。要从项目描述符区取得字段,请使用以下命令: +EXEC SQL GET DESCRIPTOR name VALUE num :hostvar = field; + + num 可以是整数字面值,也可以是包含整数的主变量。可用字段如下: + + CARDINALITY (整数) + + + 结果集中的行数 + + + + + + DATA + + + 实际的数据项(因此,这个域的数据类型取决于查询) + + + + + + DATETIME_INTERVAL_CODE (整数) + + + 当TYPE9时, + DATETIME_INTERVAL_CODE将具有以下值之一: + 1 表示 DATE, + 2 表示 TIME, + 3 表示 TIMESTAMP, + 4 表示 TIME WITH TIME ZONE, + 5 表示 TIMESTAMP WITH TIME ZONE。 + + + + + + DATETIME_INTERVAL_PRECISION (整数) + + + 没有实现 + + + + + + INDICATOR (整数) + + + 指示符(表示一个空值或者一个值截断) + + + + + + KEY_MEMBER (整数) + + + 没有实现 + + + + + + LENGTH (整数) + + + 以字符计的数据长度 + + + + + + NAME (字符串) + + + 列名 + + + + + + NULLABLE (整数) + + + 没有实现 + + + + + + OCTET_LENGTH (整数) + + + 以字节计的数据字符表示的长度 + + + + + + PRECISION (整数) + + + 精度(用于类型numeric) + + + + + + RETURNED_LENGTH (整数) + + + 以字符计的数据长度 + + + + + + RETURNED_OCTET_LENGTH (整数) + + + 以字节计的数据字符表示的长度 + + + + + + SCALE (整数) + + 小数位数(用于类型 numeric + + + + + TYPE (整数) + + + 列的数据类型的数字编码 + + + + + + + + 在EXECUTEDECLARE以及OPEN语句中,INTOUSING关键词的效果不同。也可以手工建立一个描述符区域来为一个查询或者游标提供输入参数,并且USING SQL DESCRIPTOR name是用来传递输入参数给参数化查询的方法。建立一个命名 SQL 描述符区域的语句如下: + +EXEC SQL SET DESCRIPTOR name VALUE num field = :hostvar; + + + + + PostgreSQL 支持在一个FETCH语句中检索多于一个记录并且在这种情况下把主变量假定为一个数组来存储数据。例如: + +EXEC SQL BEGIN DECLARE SECTION; +int id[5]; +EXEC SQL END DECLARE SECTION; + +EXEC SQL FETCH 5 FROM mycursor INTO SQL DESCRIPTOR mydesc; + +EXEC SQL GET DESCRIPTOR mydesc VALUE 1 :id = DATA; + + + + + + + + SQLDA 描述符区域 + + + SQLDA 描述符区域是一个 C 语言结构体,它也能被用来得到一个查询的结果集和元数据。一个结构体存储一个来自结果集的记录。 + +EXEC SQL include sqlda.h; +sqlda_t *mysqlda; + +EXEC SQL FETCH 3 FROM mycursor INTO DESCRIPTOR mysqlda; + + 注意SQL关键词被省略了。中关于INTOUSING关键词用例的段落在一定条件下也适用于这里。在一个DESCRIBE语句中,如果使用了INTO关键词,则DESCRIPTOR关键词可以完全被省略: + +EXEC SQL DESCRIBE prepared_statement INTO mysqlda; + + + + + + 使用 SQLDA 的程序的一般流程是: + + 准备一个查询,并且为它声明一个游标。 + 为结果行声明一个 SQLDA 。 + 为输入参数声明一个 SQLDA,并且初始化它们(内存分配、参数设置)。 + 用输入 SQLDA 打开一个游标。 + 从游标中取得行,并且把它们存储到一个输出 SQLDA。 + 从输出 SQLDA 读取值到主变量中(必要时使用转换)。 + 关闭游标。 + 关闭为输入 SQLDA 分配的内存区域。 + + + + SQLDA 数据结构 + + + SQLDA 使用三种数据结构类型:sqlda_tsqlvar_t以及struct sqlname。 + + + + + PostgreSQL 的 SQLDA 与 IBM DB2 Universal 数据库中的相似数据结构很接近,因此一些关于 DB2 SQLDA 的技术信息有助于更好地理解 PostgreSQL 的 SQLDA。 + + + + + sqlda_t 结构体 + + + 结构体类型sqlda_t是实际 SQLDA 的类型。它保存一个记录。并且两个或者更多个sqlda_t结构体能够以desc_next域中的指针连接成一个链表,这样可以表示一个有序的行集合。因此,当两个或多个行被取得时,应用可以通过沿着每一个sqlda_t节点中的desc_next指针读取它们。 + + + 类型 sqlda_t 的定义如下: +struct sqlda_struct +{ + char sqldaid[8]; + long sqldabc; + short sqln; + short sqld; + struct sqlda_struct *desc_next; + struct sqlvar_struct sqlvar[1]; +}; + +typedef struct sqlda_struct sqlda_t; +各字段的含义如下: + + sqldaid + + + 它包含一个字符串"SQLDA "。 + + + + + + sqldabc + + + 它包含已分配空间的尺寸(以字节计)。 + + + + + + sqln + + + 当它通过USING关键字传递给OPENDECLAREEXECUTE语句时,它包含一个参数化查询实例的输入参数个数。如果它被用作SELECTEXECUTEFETCH语句的输出,其值与sqld相同。 + + + + + + sqld + + + 它包含一个结果集中的域的数量。 + + + + + + desc_next + + + 如果查询返回不止一个记录,会返回多个链接在一起的 SQLDA 结构体,并且desc_next保存一个指向下一个项的指针。 + + + + + sqlvar + + + 这是结果集中列的数组。 + + + + + + + + + sqlvar_t 结构体 + + 结构类型 sqlvar_t 保存列值,以及类型和长度等元数据。该类型的定义如下: +struct sqlvar_struct +{ + short sqltype; + short sqllen; + char *sqldata; + short *sqlind; + struct sqlname sqlname; +}; + +typedef struct sqlvar_struct sqlvar_t; +各字段的含义如下: + + sqltype + + + 包含该域的类型标识符。值可以参考ecpgtype.h中的enum ECPGttype。 + + + + + + sqllen + + + 包含域的二进制长度,例如ECPGt_int是 4 字节。 + + + + + + sqldata + + + 指向数据。数据的格式在中描述。 + + + + + + sqlind + + + 指向空值指示符。0 表示非空,-1 表示空。 + + + + + + sqlname + + + 域的名称。 + + + + + + + + + struct sqlname 结构体 + + 一个 struct sqlname 结构保存一个列名。它用作 sqlvar_t 结构的成员。该结构的定义如下: +#define NAMEDATALEN 64 + +struct sqlname +{ + short length; + char data[NAMEDATALEN]; +}; +各字段的含义如下: + + length + + + 包含域名称的长度。 + + + + + data + + + 包含实际的域名称。 + + + + + + + + + +使用一个 SQLDA 检索一个结果集 + + + + 通过一个 SQLDA 检索一个查询结果集的一般步骤是: + + 声明一个sqlda_t结构体来接收结果集。 + 执行 FETCH/EXECUTE/DESCRIBE 命令来处理一个指定已声明 SQLDA 的查询。 + 通过查看sqlda_t结构体的成员sqln来检查结果集中记录的数量。 + sqlda_t结构体的成员sqlvar[0]sqlvar[1]等中得到每一列的值。 + 沿着sqlda_t结构体的成员desc_next指针到达下一行(sqlda_t)。 + 根据你的需要重复上述步骤。 + + + + 这里是一个通过 SQLDA 检索结果集的例子。 + + + + 首先,声明一个sqlda_t结构体来接收结果集。 + +sqlda_t *sqlda1; + + + + + 接下来,指定一个命令中的 SQLDA。这是一个FETCH命令的例子。 + +EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1; + + + + + 运行一个循环顺着链表来检索行。 + +sqlda_t *cur_sqlda; + +for (cur_sqlda = sqlda1; + cur_sqlda != NULL; + cur_sqlda = cur_sqlda->desc_next) +{ + ... +} + + + + + 在循环内部,运行另一个循环来检索行中每一列的数据(sqlvar_t结构体)。 + +for (i = 0; i < cur_sqlda->sqld; i++) +{ + sqlvar_t v = cur_sqlda->sqlvar[i]; + char *sqldata = v.sqldata; + short sqllen = v.sqllen; + ... +} + + + + + 要得到一列的值,应检查sqlvar_t结构体的成员sqltype的值。然后,根据列类型切换到一种合适的方法从sqlvar域中复制数据到一个主变量。 + +char var_buf[1024]; + +switch (v.sqltype) +{ + case ECPGt_char: + memset(&var_buf, 0, sizeof(var_buf)); + memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen)); + break; + + case ECPGt_int: /* integer */ + memcpy(&intval, sqldata, sqllen); + snprintf(var_buf, sizeof(var_buf), "%d", intval); + break; + + ... +} + + + + + + 使用一个 SQLDA 传递查询参数 + + + + 使用一个 SQLDA 传递输入参数给一个预备查询的一般步骤是: + + 创建一个预备查询(预备语句)。 + 声明一个 sqlda_t 结构,作为输入 SQLDA。 + 为输入 SQLDA 分配内存区域(作为 sqlda_t 结构体)。 + 在分配好的内存中设置(复制)输入值。 + 打开一个说明了输入 SQLDA 的游标。 + + + + 这里是一个例子。 + + + + 首先,创建一个预备语句。 + +EXEC SQL BEGIN DECLARE SECTION; +char query[1024] = "SELECT d.oid, * FROM pg_database d, pg_stat_database s WHERE d.oid = s.datid AND (d.datname = ? OR d.oid = ?)"; +EXEC SQL END DECLARE SECTION; + +EXEC SQL PREPARE stmt1 FROM :query; + + + + + 接下来为一个 SQLDA 分配内存,并且在sqlda_t结构体的sqln成员变量中设置输入参数的数量。当预备查询要求两个或多个输入参数时,应用必须分配额外的内存空间,空间的大小为 (参数数目 - 1) * sizeof(sqlvar_t)。这里的例子展示了为两个输入参数分配内存空间。 + +sqlda_t *sqlda2; + +sqlda2 = (sqlda_t *) malloc(sizeof(sqlda_t) + sizeof(sqlvar_t)); +memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t)); + +sqlda2->sqln = 2; /* 输入变量的数目 */ + + + + + 内存分配之后,把参数值存储到sqlvar[]数组(当 SQLDA 在接收结果集时,这也是用来检索列值的数组)。在这个例子中,输入参数是"postgres"(字符串类型)和1(整数类型)。 + +sqlda2->sqlvar[0].sqltype = ECPGt_char; +sqlda2->sqlvar[0].sqldata = "postgres"; +sqlda2->sqlvar[0].sqllen = 8; + +int intval = 1; +sqlda2->sqlvar[1].sqltype = ECPGt_int; +sqlda2->sqlvar[1].sqldata = (char *) &intval; +sqlda2->sqlvar[1].sqllen = sizeof(intval); + + + + + 通过打开一个游标并且说明之前已经建立好的 SQLDA,输入参数被传递给预备语句。 + +EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2; + + + + + 最后,用完输入 SQLDA 后必须显式地释放已分配的内存空间,这与用于接收查询结果的 SQLDA 不同。 + +free(sqlda2); + + + + + + 一个使用 SQLDA 的应用例子 + + + 这里是一个例子程序,它描述了如何按照输入参数的指定从系统目录中取得数据库的访问统计。 + + + + 这个应用在数据库 OID 上连接两个系统表(pg_database 和 pg_stat_database),并且还取得和显示通过两个输入参数(一个数据库postgres和 OID 1)检索到的数据库统计。 + + + + 首先,为输入和输出分别声明一个 SQLDA。 + +EXEC SQL include sqlda.h; + +sqlda_t *sqlda1; /* 一个输出描述符 */ +sqlda_t *sqlda2; /* 一个输入描述符 */ + + + + + 接下来,连接到数据库,准备一个语句并且为预备语句声明一个游标。 + +int +main(void) +{ + EXEC SQL BEGIN DECLARE SECTION; + char query[1024] = "SELECT d.oid,* FROM pg_database d, pg_stat_database s WHERE d.oid=s.datid AND ( d.datname=? OR d.oid=? )"; + EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO testdb AS con1 USER testuser; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + EXEC SQL PREPARE stmt1 FROM :query; + EXEC SQL DECLARE cur1 CURSOR FOR stmt1; + + + + + 然后,为输入参数在输入 SQLDA 中放入一些值。为输入 SQLDA 分配内存,并且在sqln中设置输入参数的数目。在sqlvar结构体的sqltypesqldatasqllen中存入类型、值和值长度。 + + + /* 为输入参数创建 SQLDA 结构。 */ + sqlda2 = (sqlda_t *) malloc(sizeof(sqlda_t) + sizeof(sqlvar_t)); + memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t)); + sqlda2->sqln = 2; /* 输入变量的数量 */ + + sqlda2->sqlvar[0].sqltype = ECPGt_char; + sqlda2->sqlvar[0].sqldata = "postgres"; + sqlda2->sqlvar[0].sqllen = 8; + + intval = 1; + sqlda2->sqlvar[1].sqltype = ECPGt_int; + sqlda2->sqlvar[1].sqldata = (char *)&intval; + sqlda2->sqlvar[1].sqllen = sizeof(intval); + + + + + 设置完输入 SQLDA 之后,用输入 SQLDA 打开一个游标。 + + + /* 用输入参数打开一个游标。 */ + EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2; + + + + + 从打开的游标中取行到输出 SQLDA 中(通常,你不得不在循环中反复调用FETCH来取出结果集中的所有行)。 + + while (1) + { + sqlda_t *cur_sqlda; + + /* 分配描述符给游标 */ + EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1; + + + + + 再后,沿着sqlda_t结构体的链表从 SQLDA 中检索取得的记录。 + + for (cur_sqlda = sqlda1 ; + cur_sqlda != NULL ; + cur_sqlda = cur_sqlda->desc_next) + { + ... + + + + + 读取第一个记录中的每一列。列的数量被存储在sqld中,第一列的实际数据被存储在sqlvar[0]中,两者都是sqlda_t结构体的成员。 + + + /* 打印一行中的每一列。 */ + for (i = 0; i < sqlda1->sqld; i++) + { + sqlvar_t v = sqlda1->sqlvar[i]; + char *sqldata = v.sqldata; + short sqllen = v.sqllen; + + strncpy(name_buf, v.sqlname.data, v.sqlname.length); + name_buf[v.sqlname.length] = '\0'; + + + + 现在,列数据已存储在变量 v 中。将每个数据项复制到主变量,并查看 v.sqltype 来确定列的类型。 + switch (v.sqltype) { + int intval; + double doubleval; + unsigned long long int longlongval; + + case ECPGt_char: + memset(&var_buf, 0, sizeof(var_buf)); + memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf)-1 : sqllen)); + break; + + case ECPGt_int: /* 整数 */ + memcpy(&intval, sqldata, sqllen); + snprintf(var_buf, sizeof(var_buf), "%d", intval); + break; + + ... + + default: + ... + } + + printf("%s = %s (type: %d)\n", name_buf, var_buf, v.sqltype); + } + + + + + 处理所有记录后关闭游标,并且从数据库断开连接。 + + EXEC SQL CLOSE cur1; + EXEC SQL COMMIT; + + EXEC SQL DISCONNECT ALL; + + + + + 整个程序显示在中。 + + + + 示例 SQLDA 程序 + +#include <stdlib.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +EXEC SQL include sqlda.h; + +sqlda_t *sqlda1; /* 用于输出的描述符 */ +sqlda_t *sqlda2; /* 用于输入的描述符 */ + +EXEC SQL WHENEVER NOT FOUND DO BREAK; +EXEC SQL WHENEVER SQLERROR STOP; + +int +main(void) +{ + EXEC SQL BEGIN DECLARE SECTION; + char query[1024] = "SELECT d.oid,* FROM pg_database d, pg_stat_database s WHERE d.oid=s.datid AND ( d.datname=? OR d.oid=? )"; + + int intval; + unsigned long long int longlongval; + EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO uptimedb AS con1 USER uptime; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + EXEC SQL PREPARE stmt1 FROM :query; + EXEC SQL DECLARE cur1 CURSOR FOR stmt1; + + /* 为一个输入参数创建一个 SQLDA 结构 */ + sqlda2 = (sqlda_t *)malloc(sizeof(sqlda_t) + sizeof(sqlvar_t)); + memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t)); + sqlda2->sqln = 2; /* 输入变量的数量 */ + + sqlda2->sqlvar[0].sqltype = ECPGt_char; + sqlda2->sqlvar[0].sqldata = "postgres"; + sqlda2->sqlvar[0].sqllen = 8; + + intval = 1; + sqlda2->sqlvar[1].sqltype = ECPGt_int; + sqlda2->sqlvar[1].sqldata = (char *) &intval; + sqlda2->sqlvar[1].sqllen = sizeof(intval); + + /* 用输入参数打开一个游标。 */ + EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2; + + while (1) + { + sqlda_t *cur_sqlda; + + /* 给游标分配描述符 */ + EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1; + + for (cur_sqlda = sqlda1 ; + cur_sqlda != NULL ; + cur_sqlda = cur_sqlda->desc_next) + { + int i; + char name_buf[1024]; + char var_buf[1024]; + + /* 打印一行中的每一列。 */ + for (i=0 ; i<cur_sqlda->sqld ; i++) + { + sqlvar_t v = cur_sqlda->sqlvar[i]; + char *sqldata = v.sqldata; + short sqllen = v.sqllen; + + strncpy(name_buf, v.sqlname.data, v.sqlname.length); + name_buf[v.sqlname.length] = '\0'; + + switch (v.sqltype) + { + case ECPGt_char: + memset(&var_buf, 0, sizeof(var_buf)); + memcpy(&var_buf, sqldata, (sizeof(var_buf)<=sqllen ? sizeof(var_buf)-1 : sqllen) ); + break; + + case ECPGt_int: /* 整数 */ + memcpy(&intval, sqldata, sqllen); + snprintf(var_buf, sizeof(var_buf), "%d", intval); + break; + + case ECPGt_long_long: /* 大整数 */ + memcpy(&longlongval, sqldata, sqllen); + snprintf(var_buf, sizeof(var_buf), "%lld", longlongval); + break; + + default: + { + int i; + memset(var_buf, 0, sizeof(var_buf)); + for (i = 0; i < sqllen; i++) + { + char tmpbuf[16]; + snprintf(tmpbuf, sizeof(tmpbuf), "%02x ", (unsigned char) sqldata[i]); + strncat(var_buf, tmpbuf, sizeof(var_buf)); + } + } + break; + } + + printf("%s = %s (type: %d)\n", name_buf, var_buf, v.sqltype); + } + + printf("\n"); + } + } + + EXEC SQL CLOSE cur1; + EXEC SQL COMMIT; + + EXEC SQL DISCONNECT ALL; + + return 0; +} + + + + 这个例子的输出应该看起来类似下面的结果(一些数字会变化)。 + + + +oid = 1 (type: 1) +datname = template1 (type: 1) +datdba = 10 (type: 1) +encoding = 0 (type: 5) +datistemplate = t (type: 1) +datallowconn = t (type: 1) +datconnlimit = -1 (type: 5) +datlastsysoid = 11510 (type: 1) +datfrozenxid = 379 (type: 1) +dattablespace = 1663 (type: 1) +datconfig = (type: 1) +datacl = {=c/uptime,uptime=CTc/uptime} (type: 1) +datid = 1 (type: 1) +datname = template1 (type: 1) +numbackends = 0 (type: 5) +xact_commit = 113606 (type: 9) +xact_rollback = 0 (type: 9) +blks_read = 130 (type: 9) +blks_hit = 7341714 (type: 9) +tup_returned = 38262679 (type: 9) +tup_fetched = 1836281 (type: 9) +tup_inserted = 0 (type: 9) +tup_updated = 0 (type: 9) +tup_deleted = 0 (type: 9) + +oid = 11511 (type: 1) +datname = postgres (type: 1) +datdba = 10 (type: 1) +encoding = 0 (type: 5) +datistemplate = f (type: 1) +datallowconn = t (type: 1) +datconnlimit = -1 (type: 5) +datlastsysoid = 11510 (type: 1) +datfrozenxid = 379 (type: 1) +dattablespace = 1663 (type: 1) +datconfig = (type: 1) +datacl = (type: 1) +datid = 11511 (type: 1) +datname = postgres (type: 1) +numbackends = 0 (type: 5) +xact_commit = 221069 (type: 9) +xact_rollback = 18 (type: 9) +blks_read = 1176 (type: 9) +blks_hit = 13943750 (type: 9) +tup_returned = 77410091 (type: 9) +tup_fetched = 3253694 (type: 9) +tup_inserted = 0 (type: 9) +tup_updated = 0 (type: 9) +tup_deleted = 0 (type: 9) + + + + + + + + 错误处理 + + + 这一节描述在一个嵌入式 SQL 程序中如何处理异常情况和警告。有两种非互斥的工具可以用于这个目的。 + + + + + 可以使用WHENEVER命令配置回调来处理警告和错误情况。 + + + + + + 可以从sqlca变量中获得错误或警告的详细信息。 + + + + + + + 设置回调 + + + 一种捕捉错误和警告的简单方法是设置一个特殊的动作,只要一个特定情况发生就执行该动作。通常是这样: + +EXEC SQL WHENEVER condition action; + + + + + condition 可以是以下值之一: + + SQLERROR + + + 只要在 SQL 语句执行期间发生一个错误就调用指定的动作。 + + + + + + SQLWARNING + + + 只要在 SQL 语句执行期间发生一个警告就调用指定的动作。 + + + + + + NOT FOUND + + + 只要一个 SQL 语句检索或者影响零行就调用指定的动作(这种情况不是一个错误,但是你可能需要特别地处理它)。 + + + + + + + + action 可以是以下值之一: + + CONTINUE + + + 这实际上表示该情况被忽略。这是默认值。 + + + + + + GOTO label + GO TO label + + + 跳到指定的标签(使用一个 C goto语句)。 + + + + + + SQLPRINT + + + 把一个消息打印到标准错误。对于简单程序或原型开发中这很有用。消息的细节无法配置。 + + + + + + STOP + + + 调用exit(1)终止程序。 + + + + + + DO BREAK + + + 执行 C 语句break。只应被用在循环或switch语句中。 + + + + + + CALL name (args) + DO name (args) + + 使用指定参数调用指定的 C 函数。 + + + SQL 标准只规定了以下动作:CONTINUEGOTO(以及 GO TO)。 + + + + 这里有一个可能会用在简单程序中的例子。当一个警告发生时它打印一个简单消息,而发生一个错误时它会中止程序: + +EXEC SQL WHENEVER SQLWARNING SQLPRINT; +EXEC SQL WHENEVER SQLERROR STOP; + + + + + 语句EXEC SQL WHENEVER是 SQL 预处理器的一个指令,而不是一个 C 语句。不管 C 程序的控制流程如何,该语句设置的错误或警告动作适用于所有位于处理程序设置点之后的嵌入式 SQL 语句,除非在第一个EXEC SQL WHENEVER和导致情况的 SQL 语句之间为同一个情况设置了不同的动作。因此下面的两个 C 程序都不会得到预期的效果: + +/* + * 错误 + */ +int main(int argc, char *argv[]) +{ + ... + if (verbose) { + EXEC SQL WHENEVER SQLWARNING SQLPRINT; + } + ... + EXEC SQL SELECT ...; + ... +} + + + +/* + * 错误 + */ +int main(int argc, char *argv[]) +{ + ... + set_error_handler(); + ... + EXEC SQL SELECT ...; + ... +} + +static void set_error_handler(void) +{ + EXEC SQL WHENEVER SQLERROR STOP; +} + + + + + +sqlca + + + 为了更强大的错误处理,嵌入式 SQL 接口提供了一个名为sqlca(SQL 通讯区域)的全局变量,其结构体定义如下: + +struct +{ + char sqlcaid[8]; + long sqlabc; + long sqlcode; + struct + { + int sqlerrml; + char sqlerrmc[SQLERRMC_LEN]; + } sqlerrm; + char sqlerrp[8]; + long sqlerrd[6]; + char sqlwarn[8]; + char sqlstate[5]; +} sqlca; + + (在一个多线程程序中,每一个线程会自动得到它自己的sqlca副本。这和对于标准 C 全局变量errno的处理相似。) + + + + sqlca覆盖了警告和错误。如果执行一个语句时发生了多个警告或错误,那么sqlca将只包含关于最后一个的信息。 + + + + 如果在上一个SQL语句中没有产生错误,sqlca.sqlcode将为 0 并且sqlca.sqlstate将为"00000"。如果发生一个警告或错误,则sqlca.sqlcode将为负并且sqlca.sqlstate将不为"00000"。一个正的sqlca.sqlcode表示一种无害的情况,例如上一个查询返回零行。sqlcodesqlstate是两种不同的错误代码模式,详见下文。 + + + + 如果上一个 SQL 语句成功,那么sqlca.sqlerrd[1]包含被处理行的 OID (如果可用),并且sqlca.sqlerrd[2]包含被处理或被返回的行数(如果适用于该命令)。 + + + + 在发生一个错误或警告的情况下,sqlca.sqlerrm.sqlerrmc将包含一个描述该错误的字符串。域sqlca.sqlerrm.sqlerrml包含存储在sqlca.sqlerrm.sqlerrmc中错误消息的长度(strlen()的结果,对于 C 程序员来说通常不太有用)。注意一些消息可能太长不能适应定长的sqlerrmc数组,它们将被截断。 + + + + 在发生一个警告的情况下,sqlca.sqlwarn[2]被设置为W(在所有其他情况中,它被设置为不同于W的东西)。如果sqlca.sqlwarn[1]被设置为W,那么一个值被存储在一个主变量中时会被截断。如果任意其他元素被设置为指示一个警告,sqlca.sqlwarn[0]会被设置为W。 + + + + 域sqlcaid、 + sqlabc, + sqlerrp以及 + sqlerrd的剩余元素还有 + sqlwarn当前不包含有用的信息。 + + + + SQL 标准中没有定义sqlca结构体,但是在一些其他的 SQL 数据库系统中都有实现。在核心上这些定义都相似,但是如果你想要编写可移植的应用,那么你应该仔细研究不同的实现。 + + + + 这里有一个整合使用WHENEVERsqlca的例子,当一个错误发生时打印出sqlca的内容。在安装一个更用户友好的错误处理器之前,这可能对调试或开发原型应用有用。 + + +EXEC SQL WHENEVER SQLERROR CALL print_sqlca(); + +void +print_sqlca() +{ + fprintf(stderr, "==== sqlca ====\n"); + fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode); + fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml); + fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc); + fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2], + sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]); + fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2], + sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5], + sqlca.sqlwarn[6], sqlca.sqlwarn[7]); + fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate); + fprintf(stderr, "===============\n"); +} + + + 结果看起来像(这里的错误是一个拼写错误的表名): + + +==== sqlca ==== +sqlcode: -400 +sqlerrm.sqlerrml: 49 +sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38 +sqlerrd: 0 0 0 0 0 0 +sqlwarn: 0 0 0 0 0 0 0 0 +sqlstate: 42P01 +=============== + + + + + + <literal>SQLSTATE</literal> 与 <literal>SQLCODE</literal> + + + 域sqlca.sqlstate以及sqlca.sqlcode是提供错误代码的两种不同模式。两种都源自于 SQL 标准,但是在标准的 SQL-92 版本中SQLCODE已经被标记为弃用并且在后面的版本中被删除。因此,强烈建议新应用使用SQLSTATE。 + + + + SQLSTATE是一个五字符数组。这五个字符包含数字或大写字母,它表示多种错误或警告情况的代码。SQLSTATE具有一种层次模式:前两个字符表示情况的总体分类,后三个字符表示总体情况的子类。代码00000表示一种成功的状态。SQL 标准中的大部分都有对应的SQLSTATE代码。PostgreSQL服务器本地支持SQLSTATE错误代码,因此通过在所有应用中自始至终使用这种错误代码模式可以实现高度的一致性。进一步的信息请见。 + + + + 被弃用的错误代码模式SQLCODE是一个简单的整数。值为 0 表示成功,一个正值表示带附加信息的成功,一个负值表示一个错误。SQL 标准只定义了正值 +100,它表示上一个命令返回或者影响了零行,并且没有特定的负值。因此,这种模式只能实现很可怜的可移植性并且不具有层次性的代码分配。历史上,PostgreSQL的嵌入式 SQL 处理器已经分配了一些特定的SQLCODE值供它使用,它们的数字值和符号名称被列在下文。记住这些对其他 SQL 实现不是可移植的。为了简化移植应用到SQLSTATE模式,对应的SQLSTATE也被列出。不过,在两种模式之间没有一对一或者一对多的映射(事实上是多对多),因此在每一种情况下你都应该参考中列出的全局SQLSTATE。 + + + 以下是已分配的 SQLCODE 值: + + 0 (ECPG_NO_ERROR) + + + 表示没有错误(SQLSTATE 00000)。 + + + + + + 100 (ECPG_NOT_FOUND) + + + 这是一种无害情况,它表示上一个命令检索或者处理了零行,或者你到达了游标的末尾(SQLSTATE 02000)。 + + + + 在一个循环中处理一个游标时,你可以使用这个代码作为一种方法来检测何时中止该循环,像这样: + +while (1) +{ + EXEC SQL FETCH ... ; + if (sqlca.sqlcode == ECPG_NOT_FOUND) + break; +} + + 但是WHENEVER NOT FOUND DO BREAK实际上会在内部这样做,因此显式地把它写出来通常没有什么好处。 + + + + + + -12 (ECPG_OUT_OF_MEMORY) + + + 表示你的虚拟内存已被耗尽。数字值被定义为-ENOMEM(SQLSTATE YE001)。 + + + + + + -200 (ECPG_UNSUPPORTED) + + + 表示预处理器已经产生了一些该库不知道的东西。也许你正在运行一个不兼容版本的预处理和库(SQLSTATE YE002)。 + + + + + + -201 (ECPG_TOO_MANY_ARGUMENTS) + + + 这表示命令指定了超过该命令预期数量的主变量(SQLSTATE 07001 或 07002)。 + + + + + + -202 (ECPG_TOO_FEW_ARGUMENTS) + + + 这表示命令指定的主变量数量低于该命令的预期(SQLSTATE 07001 或 07002) + + + + + + -203 (ECPG_TOO_MANY_MATCHES) + + + 这意味着一个查询已经返回了多个行,但是该语句只准备存储一个结果行(例如,因为指定的变量不是数组)(SQLSTATE 21000)。 + + + + + + -204 (ECPG_INT_FORMAT) + + + 主变量是类型int而数据库中的数据是一种不同的类型并且含有一个不能被解释为int的值。该库使用strtol()进行这种转换(SQLSTATE 42804)。 + + + + + + -205 (ECPG_UINT_FORMAT) + + + 主变量是类型unsigned int而数据库中的数据是一种不同的类型并且含有一个不能被解释为unsigned int的值。该库使用strtoul()进行这种转换(SQLSTATE 42804)。 + + + + + + -206 (ECPG_FLOAT_FORMAT) + + + 主变量是类型float而数据库中的数据是另一种类型并且含有一个不能被解释为float的值。该库使用strtod()进行这种转换(SQLSTATE 42804)。 + + + + + + -207 (ECPG_NUMERIC_FORMAT) + + + 主变量是类型numeric而数据库中的数据是另一种类型并且含有一个不能被解释为numeric的值(SQLSTATE 42804)。 + + + + + + -208 (ECPG_INTERVAL_FORMAT) + + + 主变量是类型interval而数据库中的数据是另一种类型并且含有一个不能被解释为interval的值(SQLSTATE 42804)。 + + + + + + -209 (ECPG_DATE_FORMAT) + + + 主变量是类型date而数据库中的数据是另一种类型并且含有一个不能被解释为date的值(SQLSTATE 42804)。 + + + + + + -210 (ECPG_TIMESTAMP_FORMAT) + + + 主变量是类型timestamp而数据库中的数据是另一种类型并且含有一个不能被解释为timestamp的值(SQLSTATE 42804)。 + + + + + + -211 (ECPG_CONVERT_BOOL) + + + 这表示主变量是类型bool而数据库中的数据既不是't'也不是'f'(SQLSTATE 42804)。 + + + + + + -212 (ECPG_EMPTY) + + + 发送给PostgreSQL服务器的语句是空的(通常在一个嵌入式 SQL 程序中不会发生,因此它可能指向一个内部错误)(SQLSTATE YE002)。 + + + + + + -213 (ECPG_MISSING_INDICATOR) + + + 返回了一个空值并且没有提供空值指示符(SQLSTATE 22002)。 + + + + + + -214 (ECPG_NO_ARRAY) + + + 在要求一个数组的地方使用了一个普通变量(SQLSTATE 42804)。 + + + + + + -215 (ECPG_DATA_NOT_ARRAY) + + + 在一个要求数组值的地方数据库返回了一个普通变量(SQLSTATE 42804)。 + + + + + + + -216 (ECPG_ARRAY_INSERT) + + + 该值不能被插入到数组(SQLSTATE 42804)。 + + + +]]> + + + -220 (ECPG_NO_CONN) + + + 程序尝试访问一个不存在的连接(SQLSTATE 08003)。 + + + + + + -221 (ECPG_NOT_CONN) + + + 程序尝试访问一个存在的连接但是它没有打开(这是一个内部错误)(SQLSTATE YE002)。 + + + + + + -230 (ECPG_INVALID_STMT) + + + 你尝试使用的语句还没有被准备好(SQLSTATE 26000)。 + + + + + + -239 (ECPG_INFORMIX_DUPLICATE_KEY) + + + 重复键错误,违背唯一约束(Informix 兼容模式)(SQLSTATE 23505)。 + + + + + + -240 (ECPG_UNKNOWN_DESCRIPTOR) + + + 没有找到指定的描述符。你尝试使用的语句还没有被准备好(SQLSTATE 33000)。 + + + + + + -241 (ECPG_INVALID_DESCRIPTOR_INDEX) + + + 指定的描述符超出范围(SQLSTATE 07009)。 + + + + + + -242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM) + + + 请求了一个非法的描述符(这是一个内部错误)(SQLSTATE YE002)。 + + + + + + -243 (ECPG_VAR_NOT_NUMERIC) + + + 在执行一个动态语句期间,数据库返回了一个numeric值而主变量不是numeric的(SQLSTATE 07006)。 + + + + + + -244 (ECPG_VAR_NOT_CHAR) + + + 在执行一个动态语句期间,数据库返回了一个非numeric值而主变量是numeric的(SQLSTATE 07006)。 + + + + + + -284 (ECPG_INFORMIX_SUBSELECT_NOT_ONE) + + + 子查询的结果不是单一行(Informix 兼容模式)(SQLSTATE 21000)。 + + + + + + -400 (ECPG_PGSQL) + + + PostgreSQL服务器导致了某个错误。该消息包含来自PostgreSQL服务器的错误消息。 + + + + + + -401 (ECPG_TRANS) + + + PostgreSQL服务器通知我们不能启动、提交或回滚事务(SQLSTATE 08007)。 + + + + + + -402 (ECPG_CONNECT) + + + 到数据库的连接尝试没有成功(SQLSTATE 08001)。 + + + + + + -403 (ECPG_DUPLICATE_KEY) + + + 重复键错误,违背唯一约束(SQLSTATE 23505)。 + + + + + + -404 (ECPG_SUBSELECT_NOT_ONE) + + + 子查询的结果不是单一行(SQLSTATE 21000)。 + + + + + + + -600 (ECPG_WARNING_UNRECOGNIZED) + + 从服务器收到了无法识别的警告。 + + + + + -601 (ECPG_WARNING_QUERY_IGNORED) + + 当前事务已中止。在事务块结束之前,查询都会被忽略。 + + +]]> + + + -602 (ECPG_WARNING_UNKNOWN_PORTAL) + + + 指定了一个非法的游标名(SQLSTATE 34000)。 + + + + + + -603 (ECPG_WARNING_IN_TRANSACTION) + + + 事务正在进行(SQLSTATE 25001)。 + + + + + + -604 (ECPG_WARNING_NO_TRANSACTION) + + + 没有活动(正在进行)的事务(SQLSTATE 25P01)。 + + + + + + -605 (ECPG_WARNING_PORTAL_EXISTS) + + + 指定了一个现有的游标名(SQLSTATE 42P03)。 + + + + + + + + + + + 预处理器指令 + + + 一些预处理器指令可以用来改变ecpg预处理器解析和处理一个文件的方式。 + + + +包括文件 + + + 要包括一个外部文件到你的嵌入式 SQL 程序中,可以用: + +EXEC SQL INCLUDE filename; +EXEC SQL INCLUDE <filename>; +EXEC SQL INCLUDE "filename"; + + 嵌入式 SQL 预处理器将查找一个名为filename.h的文件,处理它并且把它包括在结果 C 输出中。这样,被包括文件中的嵌入式 SQL 语句会被正确地处理。 + + + + ecpg预处理器将以下列顺序在几个目录中搜索一个文件: + + + 当前目录 + /usr/local/include + PostgreSQL 的头文件目录,在编译时定义(例如/usr/local/pgsql/include + /usr/include + + + 但是当使用EXEC SQL INCLUDE "filename"时,只有当前目录会被搜索。 + + + + 在每一个目录中,预处理器将首先按给定的文件名搜索,如果没有找到将会追加.h到文件名并且重试(除非指定的文件名已经具有该后缀)。 + + + + 注意EXEC SQL INCLUDE同于: + +#include <filename.h> + + 因为这个文件不服从 SQL 命令预处理。自然地,你可以继续使用 C 的#include指令来包括其他头文件。 + + + + + 包括文件名是大小写敏感的,即使EXEC SQL INCLUDE命令的剩余部分遵守通常的 SQL 大小写敏感规则。 + + + + + + define 和 undef 指令 + + 与 C 中我们熟知的指令#define相似,嵌入式 SQL 具有类似的概念: + +EXEC SQL DEFINE name; +EXEC SQL DEFINE name value; + + 因此你可以定义一个名称: + +EXEC SQL DEFINE HAVE_FEATURE; + + 并且你也可以定义常量: + +EXEC SQL DEFINE MYNUMBER 12; +EXEC SQL DEFINE MYSTRING 'abc'; + + 使用undef来移除一个之前的定义: + +EXEC SQL UNDEF MYNUMBER; + + + + + 当然在你的嵌入式 SQL 程序中你可以继续使用 C 版本的#define#undef。区别在于你定义的值会在哪里被计算。如果你使用EXEC SQL DEFINE,那么ecpg预处理器会计算这些定义并且替换值。例如,如果你写: + +EXEC SQL DEFINE MYNUMBER 12; +... +EXEC SQL UPDATE Tbl SET col = MYNUMBER; + + 那么ecpg将已经做过替换并且你的 C 编译器将永远不会看见名为MYNUMBER的任何名称或标识符。注意你不能把#define用于一个将要在一个嵌入式 SQL 查询中使用的常量,因为在这种情况下嵌入式 SQL 预处理器不能看到这个声明。 + + + + + ifdef、ifndef、else、elif 和 endif 指令 + 可以使用以下指令对代码区段进行条件编译: + + EXEC SQL ifdef name; + + 检查 name,如果已通过 EXEC SQL define name 创建了 name,就处理后续各行。 + + + + + EXEC SQL ifndef name; + + 检查 name,如果尚未通过 EXEC SQL define name 创建 name,就处理后续各行。 + + + + + EXEC SQL else; + + 开始处理一个备选区段,对应由 EXEC SQL ifdef nameEXEC SQL ifndef name 引入的区段。 + + + + + EXEC SQL elif name; + + 检查 name,如果已通过 EXEC SQL define name 创建了 name,就开始一个备选区段。 + + + + + EXEC SQL endif; + + 结束备选区段。 + + + + + + 示例: +EXEC SQL ifndef TZVAR; +EXEC SQL SET TIMEZONE TO 'GMT'; +EXEC SQL elif TZNAME; +EXEC SQL SET TIMEZONE TO TZNAME; +EXEC SQL else; +EXEC SQL SET TIMEZONE TO TZVAR; +EXEC SQL endif; + + + + + + + +处理嵌入式 SQL 程序 + + + 现在你已经对如何构造嵌入式 SQL C 程序有所了解了,你可能希望知道如何编译它们。在编译之前,你需要让该文件通过嵌入式SQL C预处理器,它会把你用到的SQL转换成特殊的函数调用。在编译之后,你必须链接一个包含所需函数的特殊库。这些函数从参数中取得信息、使用libpq执行SQL命令并且把结果放在指定的参数中用来输出。 + + + + 该预处理器程序被称作ecpg并且被包括在一个正常的PostgreSQL安装中。嵌入式 SQL 程序通常带有扩展名.pgc。如果你有一个程序文件prog1.pgc,你可以调用下面的命令对它进行预处理: + +ecpg prog1.pgc + + 这将创建一个文件prog1.c。如果你的输入文件不遵循建议的命名模式,你可以用选项显式地指定输出文件。 + + + + 预处理过的文件可以被正常地编译,例如: + +cc -c prog1.c + + 产生的 C 源文件从PostgreSQL安装中包括头文件,因此如果你把PostgreSQL安装在一个不被默认搜索的位置,你必须在编译命令行中增加一个选项(例如-I/usr/local/pgsql/include)。 + + + + 要链接一个嵌入式 SQL 程序,你需要包括libecpg库,像这样: + +cc -o myprog prog1.o prog2.o ... -lecpg + + 再次,你可能不得不在命令行中增加类似-L/usr/local/pgsql/lib的选项。 + + + + 你可以使用pg_configpg_configwith + ecpg + 或者pkg-configpkg-configwith + ecpg 加上包名libecpg来得到你的安装路径。 + + + + 如果你使用make来管理一个大工程的构建过程,把下面的隐式规则包括在你的 makefile 中将会很方便: + +ECPG = ecpg + +%.c: %.pgc + $(ECPG) $< + + + + + ecpg命令的完整语法可见。 + + + + ecpg库默认是线程安全的。不过,你可能需要使用一些线程命令行选项来编译你的客户端代码。 + + + + + 库函数 + + + libecpg库主要包含用于实现嵌入式 SQL 命令所表达功能的隐藏函数。但是也有一些可以被直接调用的函数。但是注意这会让你的代码不可移植。 + + + + + + 如果调用时第一个参数非零,ECPGdebug(int on, FILE *stream)会打开调试日志。调试日志在上完成。该日志包含所有插入了输入变量的SQL语句,以及来自于PostgreSQL服务器的结果。在你的SQL语句中查找错误时这会非常有用。 + + + + 在 Windows 上,如果ecpg库和应用使用不同标志编译的,这个函数调用将会使应用崩溃,因为FILE指针的内部表示不同。特别地,库和使用库的应用应该使用相同的多线程/单线程、发行/调试以及静态/动态标志。 + + + + + + + ECPGget_PGconn(const char *connection_name) + 返回由给定名称标识的库数据库连接句柄。如果connection_name被设置为NULL,当前连接句柄将被返回。如果无法定位到连接句柄,该函数返回NULL。如果需要,返回的连接句柄可以被用来调用任何其他来自于libpq的函数。 + + + + 直接使用libpq例程来操纵ecpg中建立的数据库连接句柄是一种糟糕的做法。 + + + + + + ECPGtransactionStatus(const char *connection_name) 返回由 connection_name 标识的连接的当前事务状态。返回状态码的详细信息,参见 和 libpq 的 PQtransactionStatus() + + + + + 如果你连接到了一个数据库,ECPGstatus(int lineno, + const char* connection_name)会返回真;否则返回假。 + 如果使用的是一个单一连接,connection_name可以为NULL。 + + + + + + +大对象 + + + ECPG 并不直接支持大对象,在调用ECPGget_PGconn()函数获得所需的PGconn对象后,ECPG 应用能通过 libpq 大对象函数操纵大对象(不过,对ECPGget_PGconn()函数的使用以及直接接触PGconn对象都必须非常小心,并且最好不要与其他 ECPG 数据库访问调用混合在一起)。 + + + + 更多关于ECPGget_PGconn()的细节可见。大对象函数接口的相关信息可见。 + + + + 大对象函数必须在一个事务块中被调用,因此当自动提交关闭时,必须显式地发出BEGIN命令。 + + + + 给出了一个例子程序,它展示了在一个 ECPG 应用中如何创建、写入和读取一个大对象。 + + + +访问大对象的 ECPG 程序 + +#include +#include +#include + +EXEC SQL WHENEVER SQLERROR STOP; + +int +main(void) +{ + PGconn *conn; + Oid loid; + int fd; + char buf[256]; + int buflen = 256; + char buf2[256]; + int rc; + + memset(buf, 1, buflen); + + EXEC SQL CONNECT TO testdb AS con1; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + conn = ECPGget_PGconn("con1"); + printf("conn = %p\n", conn); + + /* create */ + loid = lo_create(conn, 0); + if (loid < 0) + printf("lo_create() failed: %s", PQerrorMessage(conn)); + + printf("loid = %d\n", loid); + + /* write test */ + fd = lo_open(conn, loid, INV_READ|INV_WRITE); + if (fd < 0) + printf("lo_open() failed: %s", PQerrorMessage(conn)); + + printf("fd = %d\n", fd); + + rc = lo_write(conn, fd, buf, buflen); + if (rc < 0) + printf("lo_write() failed\n"); + + rc = lo_close(conn, fd); + if (rc < 0) + printf("lo_close() failed: %s", PQerrorMessage(conn)); + + /* read test */ + fd = lo_open(conn, loid, INV_READ); + if (fd < 0) + printf("lo_open() failed: %s", PQerrorMessage(conn)); + + printf("fd = %d\n", fd); + + rc = lo_read(conn, fd, buf2, buflen); + if (rc < 0) + printf("lo_read() failed\n"); + + rc = lo_close(conn, fd); + if (rc < 0) + printf("lo_close() failed: %s", PQerrorMessage(conn)); + + /* check */ + rc = memcmp(buf, buf2, buflen); + printf("memcmp() = %d\n", rc); + + /* cleanup */ + rc = lo_unlink(conn, loid); + if (rc < 0) + printf("lo_unlink() failed: %s", PQerrorMessage(conn)); + + EXEC SQL COMMIT; + EXEC SQL DISCONNECT ALL; + return 0; +} +]]> + + + + + <acronym>C++</acronym> 应用 + + + ECPG 对于 C++ 应用提供了有限的支持。这一节描述了一些忠告。 + + + ecpg 预处理器接受一个用 C(或类似 C 的语言)和嵌入式 SQL 命令编写的输入文件,将嵌入式 SQL 命令转换为 C 代码片段,最终生成 .c 文件。在 C++ 中使用时,ecpg 生成的 C 代码片段所调用的库函数,其头文件声明会包在 extern "C" { ... } 块中,因此应该能在 C++ 中无缝工作。 + + + 不过,通常ecpg预处理器只理解 C,它无法处理 C++ 语言的特殊语法和保留词。因此,一些写在 C++ 应用代码中的使用了 C++ 特定复杂特性的嵌入式 SQL 代码可能无法被正确地预处理或者无法按预期工作。 + + + + 使用 C++ 应用中嵌入式 SQL 代码的安全方法是把 ECPG 调用隐藏在一个 C 模块中,C++ 应用代码会调用它来访问数据库,还要把它和剩余的 C++ 代码链接起来。详见。 + + + +主变量的可见范围 + + + ecpg预处理器能理解 C 中变量的可见范围。在 C 语言中,这是相当简单的,因为变量的可见范围是基于它们的代码块的。不过在 C++ 中,引用类成员变量的代码块是不同于定义它的代码块的,因此ecpg预处理器将无法理解类成员变量的可见范围。 + + + + 例如,在下面的情况中,ecpg预处理器无法为test方法中的变量dbname找到任何声明,因此将发生一个错误。 + + +class TestCpp +{ + EXEC SQL BEGIN DECLARE SECTION; + char dbname[1024]; + EXEC SQL END DECLARE SECTION; + + public: + TestCpp(); + void test(); + ~TestCpp(); +}; + +TestCpp::TestCpp() +{ + EXEC SQL CONNECT TO testdb1; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; +} + +void Test::test() +{ + EXEC SQL SELECT current_database() INTO :dbname; + printf("current_database = %s\n", dbname); +} + +TestCpp::~TestCpp() +{ + EXEC SQL DISCONNECT ALL; +} + + + 这段代码将导致一个这样的错误: + +ecpg test_cpp.pgc +test_cpp.pgc:28: ERROR: variable "dbname" is not declared + + + + + 为了避免这种可见性问题,可以修改test方法来把一个本地变量用作中间存储。但是这种方法只是一种比较差的变通方案,因为它让代码变得丑陋并且降低了性能。 + + +void TestCpp::test() +{ + EXEC SQL BEGIN DECLARE SECTION; + char tmp[1024]; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT current_database() INTO :tmp; + strlcpy(dbname, tmp, sizeof(tmp)); + + printf("current_database = %s\n", dbname); +} + + + + + + 使用外部 C 模块的 C++ 应用开发 + + + 如果你理解了 C++ 中ecpg预处理器的这些技术限制,你可能已经知道在链接阶段把 C 对象和 C++ 对象链接起来让 C++ 应用能使用 ECPG 特性比直接在 C++ 代码中写一些嵌入式 SQL 命令要更好。这一节用一个简单的例子描述了一种将嵌入式 SQL 命令从 C++ 应用代码中独立出去的方法。在这个例子中,应用由 C++ 实现,而 C 和 ECPG 被用来连接到 PostgreSQL 服务器。 + + + 需要创建三种文件:C 文件(*.pgc)、头文件和 C++ 文件: + + test_mod.pgc + + + 一个执行嵌入在 C 中的 SQL 命令的子例程模块。它将被预处理器转换成test_mod.c。 + + +#include "test_mod.h" +#include <stdio.h> + +void +db_connect() +{ + EXEC SQL CONNECT TO testdb1; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; +} + +void +db_test() +{ + EXEC SQL BEGIN DECLARE SECTION; + char dbname[1024]; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT current_database() INTO :dbname; + printf("current_database = %s\n", dbname); +} + +void +db_disconnect() +{ + EXEC SQL DISCONNECT ALL; +} + + + + + + + test_mod.h + + + 包含 C 模块(test_mod.pgc)中函数定义的头文件。它会被test_cpp.cpp包括。这个文件必须在声明周围有一个extern "C"块,因为它将被链接到 C++ 模块。 + + +#ifdef __cplusplus +extern "C" { +#endif + +void db_connect(); +void db_test(); +void db_disconnect(); + +#ifdef __cplusplus +} +#endif + + + + + + + test_cpp.cpp + + 应用的主要代码,包括 main 函数,在本示例中还包括一个 C++ 类。 +#include "test_mod.h" + +class TestCpp +{ + public: + TestCpp(); + void test(); + ~TestCpp(); +}; + +TestCpp::TestCpp() +{ + db_connect(); +} + +void +TestCpp::test() +{ + db_test(); +} + +TestCpp::~TestCpp() +{ + db_disconnect(); +} + +int +main(void) +{ + TestCpp *t = new TestCpp(); + + t->test(); + return 0; +} + + + + + + + + + 要构建该应用,按以下步骤处理。通过运行ecpgtest_mod.pgc转换为test_mod.c,并且用 C 编译器将test_mod.c编译成test_mod.o: + +ecpg -o test_mod.c test_mod.pgc +cc -c test_mod.c -o test_mod.o + + + + + 接着,用 C++ 编译器把test_cpp.cpp编译成test_cpp.o: + +c++ -c test_cpp.cpp -o test_cpp.o + + + + + 最后,使用 C++ 编译器链接这些对象文件(test_cpp.otest_mod.o)成为一个可执行文件: + +c++ test_cpp.o test_mod.o -lecpg -o test_cpp + + + + + + + 嵌入式 SQL 命令 + + + 这一节描述嵌入式 SQL 所有特定的 SQL 命令。中的 SQL 命令也能被用于嵌入式 SQL,如果有例外会特别说明。 + + + + + ALLOCATE DESCRIPTOR + 分配一个 SQL 描述符区域 + + + + +ALLOCATE DESCRIPTOR name + + + + +描述 + + + ALLOCATE DESCRIPTOR分配一个新的命名 SQL 描述符区域,它可用于在 PostgreSQL 服务器与程序之间交换数据。 + + + + 以后可以使用DEALLOCATE DESCRIPTOR命令释放描述符区域。 + + + + + 参数 + + + + name + + + SQL 描述符的名称,大小写敏感。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + + +例子 + + +EXEC SQL ALLOCATE DESCRIPTOR mydesc; + + + + +兼容性 + + + SQL 标准中说明了ALLOCATE DESCRIPTOR。 + + + + +参见 + + + + + + + + + + + + CONNECT + 建立一个数据库连接 + + + + +CONNECT TO connection_target [ AS connection_name ] [ USER connection_user ] +CONNECT TO DEFAULT +CONNECT connection_user +DATABASE connection_target + + + + +描述 + + + CONNECT命令在客户端和 PostgreSQL 服务器之间建立一个连接。 + + + + + 参数 + + + + connection_target + + + connection_target 以若干种形式之一指定连接的目标服务器。 + + [ database_name ] [ @host ] [ :port ] + + + 通过 TCP/IP 连接 + + + + + + unix:postgresql://host [ :port ] / [ database_name ] [ ?connection_option ] + + + 通过 Unix 域套接字 + + + + + + tcp:postgresql://host [ :port ] / [ database_name ] [ ?connection_option ] + + + 通过 TCP/IP 连接 + + + + + + SQL 字符串常量 + + + 包含上述形式之一的一个值 + + + + + + 主变量 + + + 类型char[]VARCHAR[]的主变量,它包含上述形式之一的一个值 + + + + + + + + + + connection_name + + + 用于该连接的一个可选标识符,这样可以在其他命令中引用它。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + connection_user + + + 用于数据库连接的用户名。 + + + + 使用 + user_name/password、 + user_name IDENTIFIED BY password或者 + user_name USING password之一,这个参数也能指定用户名和密码。 + + + + 用户名和密码可以是 SQL 标识符、字符串常量或者主变量。 + + + + + + DEFAULT + + + 按 libpq 的定义使用所有默认连接参数。 + + + + + + + +例子 + + + 这里是一些指定连接参数的变体: + +EXEC SQL CONNECT TO "connectdb" AS main; +EXEC SQL CONNECT TO "connectdb" AS second; +EXEC SQL CONNECT TO "unix:postgresql://200.46.204.71/connectdb" AS main USER connectuser; +EXEC SQL CONNECT TO "unix:postgresql://localhost/connectdb" AS main USER connectuser; +EXEC SQL CONNECT TO 'connectdb' AS main; +EXEC SQL CONNECT TO 'unix:postgresql://localhost/connectdb' AS main USER :user; +EXEC SQL CONNECT TO :db AS :id; +EXEC SQL CONNECT TO :db USER connectuser USING :pw; +EXEC SQL CONNECT TO @localhost AS main USER connectdb; +EXEC SQL CONNECT TO REGRESSDB1 as main; +EXEC SQL CONNECT TO AS main USER connectdb; +EXEC SQL CONNECT TO connectdb AS :id; +EXEC SQL CONNECT TO connectdb AS main USER connectuser/connectdb; +EXEC SQL CONNECT TO connectdb AS main; +EXEC SQL CONNECT TO connectdb@localhost AS main; +EXEC SQL CONNECT TO tcp:postgresql://localhost/ USER connectdb; +EXEC SQL CONNECT TO tcp:postgresql://localhost/connectdb USER connectuser IDENTIFIED BY connectpw; +EXEC SQL CONNECT TO tcp:postgresql://localhost:20/connectdb USER connectuser IDENTIFIED BY connectpw; +EXEC SQL CONNECT TO unix:postgresql://localhost/ AS main USER connectdb; +EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb AS main USER connectuser; +EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb USER connectuser IDENTIFIED BY "connectpw"; +EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb USER connectuser USING "connectpw"; +EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb?connect_timeout=14 USER connectuser; + + + + + 这里是一个展示使用主变量指定连接参数的例子程序: + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + char *dbname = "testdb"; /* 数据库名 */ + char *user = "testuser"; /* 连接用户名 */ + char *connection = "tcp:postgresql://localhost:5432/testdb"; + /* 连接字符串 */ + char ver[256]; /* 存储版本字符串的缓冲区 */ +EXEC SQL END DECLARE SECTION; + + ECPGdebug(1, stderr); + + EXEC SQL CONNECT TO :dbname USER :user; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL SELECT version() INTO :ver; + EXEC SQL DISCONNECT; + + printf("version: %s\n", ver); + + EXEC SQL CONNECT TO :connection USER :user; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL SELECT version() INTO :ver; + EXEC SQL DISCONNECT; + + printf("version: %s\n", ver); + + return 0; +} + + + + + +兼容性 + + + SQL 标准中说明了CONNECT,但是连接参数的格式是与实现相关的。 + + + + +参见 + + + + + + + + + + + DEALLOCATE DESCRIPTOR + 释放一个 SQL 描述符区域 + + + + +DEALLOCATE DESCRIPTOR name + + + + +描述 + + + DEALLOCATE DESCRIPTOR释放一个命名的 SQL 描述符区域。 + + + + + 参数 + + + + name + + + 要被释放的描述符的名称。它是大小写敏感的。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + + +例子 + + +EXEC SQL DEALLOCATE DESCRIPTOR mydesc; + + + + +兼容性 + + + SQL 标准说明了DEALLOCATE DESCRIPTOR。 + + + + +参见 + + + + + + + + + + + + DECLARE + 定义一个游标 + + + + +DECLARE cursor_name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR prepared_name +DECLARE cursor_name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR query + + + + +描述 + + + DECLARE声明一个游标用来在一个预备语句的结果集上迭代。这个命令与直接的 SQL 命令DECLARE在语义上有一点点区别:后者会执行一个查询并且准备结果集用于检索,而这个嵌入式 SQL 命令仅仅声明一个名称作为循环变量用于在一个查询的结果集上迭代,实际的执行在游标被OPEN命令打开时才发生。 + + + + + 参数 + + + + cursor_name + + + 一个游标名称,大小写敏感。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + prepared_name + + + 一个预备查询的名称,可以是一个 SQL 标识符或者一个主变量。 + + + + + + query + + + 一个提供游标要返回的行的或者命令。 + + + + + + + 游标选项的含义请见。 + + + + +例子 + + + 为一个查询声明一个游标的例子: + +EXEC SQL DECLARE C CURSOR FOR SELECT * FROM My_Table; +EXEC SQL DECLARE C CURSOR FOR SELECT Item1 FROM T; +EXEC SQL DECLARE cur1 CURSOR FOR SELECT version(); + + + + + 为一个预备语句声明一个游标的例子: + +EXEC SQL PREPARE stmt1 AS SELECT version(); +EXEC SQL DECLARE cur1 CURSOR FOR stmt1; + + + + + +兼容性 + + + SQL 标准中说明了DECLARE。 + + + + +参见 + + + + + + + + + + + + DESCRIBE + 得到有关一个预备语句或结果集的信息 + + + + +DESCRIBE [ OUTPUT ] prepared_name USING [ SQL ] DESCRIPTOR descriptor_name +DESCRIBE [ OUTPUT ] prepared_name INTO [ SQL ] DESCRIPTOR descriptor_name +DESCRIBE [ OUTPUT ] prepared_name INTO sqlda_name + + + + +描述 + + + DESCRIBE检索被一个预备语句所含的结果列的元信息,而不会实际取得一行。 + + + + + 参数 + + + + prepared_name + + + 一个预备语句的名称。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + descriptor_name + + + 一个描述符名称。它是大小写敏感的。它可以是一个 SQL 标识符或者一个主变量。 + + + + + + sqlda_name + + + 一个 SQLDA 变量的名称。 + + + + + + + +例子 + + +EXEC SQL ALLOCATE DESCRIPTOR mydesc; +EXEC SQL PREPARE stmt1 FROM :sql_stmt; +EXEC SQL DESCRIBE stmt1 INTO SQL DESCRIPTOR mydesc; +EXEC SQL GET DESCRIPTOR mydesc VALUE 1 :charvar = NAME; +EXEC SQL DEALLOCATE DESCRIPTOR mydesc; + + + + +兼容性 + + + SQL 标准中说明了DESCRIBE。 + + + + +参见 + + + + + + + + + + + DISCONNECT + 终止一个数据库连接 + + + + +DISCONNECT connection_name +DISCONNECT [ CURRENT ] +DISCONNECT DEFAULT +DISCONNECT ALL + + + + +描述 + + + DISCONNECT关闭一个(或者所有)到数据库的连接。 + + + + + 参数 + + + + connection_name + + + 一个由CONNECT命令建立的数据库连接名称。 + + + + + + CURRENT + + + 关闭当前的连接,它可以是最近打开的连接或者是由SET CONNECTION命令设置的连接。如果没有参数被传给DISCONNECT命令,这将是默认值。 + + + + + + DEFAULT + + 关闭默认连接。 + + + + + ALL + + + 关闭所有打开的连接。 + + + + + + + + 例子 + + +int +main(void) +{ + EXEC SQL CONNECT TO testdb AS DEFAULT USER testuser; + EXEC SQL CONNECT TO testdb AS con1 USER testuser; + EXEC SQL CONNECT TO testdb AS con2 USER testuser; + EXEC SQL CONNECT TO testdb AS con3 USER testuser; + + EXEC SQL DISCONNECT CURRENT; /* close con3 */ + EXEC SQL DISCONNECT DEFAULT; /* close DEFAULT */ + EXEC SQL DISCONNECT ALL; /* close con2 and con1 */ + + return 0; +} + + + + +兼容性 + + + SQL 标准中说明了DISCONNECT。 + + + + +参见 + + + + + + + + + + + EXECUTE IMMEDIATE + 动态地准备和执行一个语句 + + + + +EXECUTE IMMEDIATE string + + + + +描述 + + + EXECUTE IMMEDIATE立刻预备并且执行一个动态指定的 SQL 语句,不检索结果行。 + + + + + 参数 + + + + string + + C 字符串字面值,或包含待执行 SQL 语句的主变量。 + + + + + + +例子 + + + 这里是一个用EXECUTE IMMEDIATE和一个名为command的主变量执行INSERT语句的例子: + +sprintf(command, "INSERT INTO test (name, amount, letter) VALUES ('db: ''r1''', 1, 'f')"); +EXEC SQL EXECUTE IMMEDIATE :command; + + + + + +兼容性 + + + SQL 标准中说明了EXECUTE IMMEDIATE。 + + + + + + + GET DESCRIPTOR + 从一个 SQL 描述符区域得到信息 + + + + +GET DESCRIPTOR descriptor_name :cvariable = descriptor_header_item [, ... ] +GET DESCRIPTOR descriptor_name VALUE column_number :cvariable = descriptor_item [, ... ] + + + + + 描述 + + + GET DESCRIPTOR从一个 SQL 描述符区域检索关于一个查询结果集的信息并且把它存储在主变量中。在使用这个命令把信息传输到主语言变量之前,一个描述符区域通常是用FETCHSELECT填充的。 + + + 此命令有两种形式。第一种取得描述符的头部项目,它们适用于整个结果集,例如行数。第二种需要额外提供列号参数,取得特定列的信息,例如列名和实际列值。 + + + + 参数 + + + + descriptor_name + + + 一个描述符名称。 + + + + + + descriptor_header_item + + + 一个标识要检索哪一个头部信息项的词元。当前只支持用于得到结果集中列数的COUNT。 + + + + + + column_number + + + 要检索其信息的列号。计数从 1 开始。 + + + + + + descriptor_item + + + 一个标识要检索哪一个有关一列信息的项的词元。被支持的项可见。 + + + + + + cvariable + + + 接收从描述符区域检索到的数据的主变量。 + + + + + + + +例子 + + + 检索一个结果集中列数的例子: + +EXEC SQL GET DESCRIPTOR d :d_count = COUNT; + + + + + 检索第一列中数据长度的例子: + +EXEC SQL GET DESCRIPTOR d VALUE 1 :d_returned_octet_length = RETURNED_OCTET_LENGTH; + + + + + 把第二列的数据体检索成一个字符串的例子: + +EXEC SQL GET DESCRIPTOR d VALUE 2 :d_data = DATA; + + + + + 这里是执行SELECT current_database();并且显示列数、列数据长度和列数据的完整过程的例子: + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + int d_count; + char d_data[1024]; + int d_returned_octet_length; +EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO testdb AS con1 USER testuser; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL ALLOCATE DESCRIPTOR d; + + /* 描述、打开一个游标,并且分配一个描述符给该游标 */ + EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(); + EXEC SQL OPEN cur; + EXEC SQL FETCH NEXT FROM cur INTO SQL DESCRIPTOR d; + + /* 得到全部列的数量 */ + EXEC SQL GET DESCRIPTOR d :d_count = COUNT; + printf("d_count = %d\n", d_count); + + /* 得到一个返回列的长度 */ + EXEC SQL GET DESCRIPTOR d VALUE 1 :d_returned_octet_length = RETURNED_OCTET_LENGTH; + printf("d_returned_octet_length = %d\n", d_returned_octet_length); + + /* 将返回的列取出成一个字符串 */ + EXEC SQL GET DESCRIPTOR d VALUE 1 :d_data = DATA; + printf("d_data = %s\n", d_data); + + /* 关闭 */ + EXEC SQL CLOSE cur; + EXEC SQL COMMIT; + + EXEC SQL DEALLOCATE DESCRIPTOR d; + EXEC SQL DISCONNECT ALL; + + return 0; +} + + 当该例子被执行时,结果看起来是: + +d_count = 1 +d_returned_octet_length = 6 +d_data = testdb + + + + + +兼容性 + + + SQL 标准中说明了GET DESCRIPTOR。 + + + + +参见 + + + + + + + + + + + OPEN + 打开一个动态游标 + + + + +OPEN cursor_name +OPEN cursor_name USING value [, ... ] +OPEN cursor_name USING SQL DESCRIPTOR descriptor_name + + + + +描述 + + + OPEN打开一个游标并且可选地绑定实际值到游标声明中的占位符。该游标必须之前用DECLARE命令声明。OPEN的执行会导致查询开始在服务器上执行。 + + + + + 参数 + + + + cursor_name + + + 要被打开的游标的名称。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + value + + + 要被绑定到游标中一个占位符的值。这可以是一个 SQL 常量、一个主变量或者一个带有指示符的主变量。 + + + + + + descriptor_name + + + 包含要绑定到游标中占位符的值的描述符的名称。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + + +例子 + + +EXEC SQL OPEN a; +EXEC SQL OPEN d USING 1, 'test'; +EXEC SQL OPEN c1 USING SQL DESCRIPTOR mydesc; +EXEC SQL OPEN :curname1; + + + + +兼容性 + + + SQL 标准中说明了OPEN。 + + + + +参见 + + + + + + + + + + + PREPARE + 准备一个语句用于执行 + + + + +PREPARE name FROM string + + + + +描述 + + + PREPARE将一个作为字符串动态指定的语句准备好执行。这不同于直接的 SQL 语句(也可以用于嵌入式程序)。命令被用来执行两种类型的预备语句。 + + + + + 参数 + + + + prepared_name + + + 预备查询的一个标识符。 + + + + + + string + + C 字符串字面值,或包含可预备语句的主变量,语句可以是 SELECT、INSERT、UPDATE 或 DELETE。 + + + + + + +例子 + +char *stmt = "SELECT * FROM test1 WHERE a = ? AND b = ?"; + +EXEC SQL ALLOCATE DESCRIPTOR outdesc; +EXEC SQL PREPARE foo FROM :stmt; + +EXEC SQL EXECUTE foo USING SQL DESCRIPTOR indesc INTO SQL DESCRIPTOR outdesc; + + + + +兼容性 + + + SQL 标准中说明了PREPARE。 + + + + +参见 + + + + + + + + + + SET AUTOCOMMIT + 设置当前会话的自动提交行为 + + + + +SET AUTOCOMMIT { = | TO } { ON | OFF } + + + + +描述 + + + SET AUTOCOMMIT设置当前数据库会话的自动提交行为。默认情况下,嵌入式 SQL 程序在自动提交模式中,因此需要显式地发出COMMIT。这个命令可以把会话改成自动提交模式,这样每一个单独的语句都会被隐式提交。 + + + + +兼容性 + + + SET AUTOCOMMIT是 PostgreSQL ECPG 的扩展。 + + + + + + + SET CONNECTION + 选择一个数据库连接 + + + + +SET CONNECTION [ TO | = ] connection_name + + + + +描述 + + + SET CONNECTION设置当前的数据库连接,除非被覆盖,所有命令都会使用这个连接。 + + + + + 参数 + + + + connection_name + + + 一个由CONNECT命令建立的数据库连接名称。 + + + + + + DEFAULT + + 将连接设为默认连接。 + + + + + + +例子 + + +EXEC SQL SET CONNECTION TO con2; +EXEC SQL SET CONNECTION = con1; + + + + +兼容性 + + + SQL 标准中说明了SET CONNECTION。 + + + + +参见 + + + + + + + + + + + SET DESCRIPTOR + 在一个 SQL 描述符区域中设置信息 + + + + +SET DESCRIPTOR descriptor_name descriptor_header_item = value [, ... ] +SET DESCRIPTOR descriptor_name VALUE number descriptor_item = value [, ...] + + + + +描述 + + + SET DESCRIPTOR用值填充一个 SQL 描述符区域。然后该描述符区域通常会被用来在一个预备查询执行中绑定参数。 + + + + 这个命令由两种形式:第一种形式适用于描述符头部,它独立于特定的数据。第二种形式为由数字标识的特定数据赋值。 + + + + + 参数 + + + + descriptor_name + + + 一个描述符名称。 + + + + + + descriptor_header_item + + + 一个标识要设置哪个头部信息项的词元。当前只有设置描述符项数量的COUNT被支持。 + + + + + + number + + + 要设置的描述符项的编号。计数从 1 开始。 + + + + + + descriptor_item + + + 一个标识在描述符中要设置哪个信息项的词元。受支持的项的列表可见。 + + + + + + value + + + 一个要存储在描述符项中的值。这可以是一个 SQL 标识符或者一个主变量。 + + + + + + + +例子 + +EXEC SQL SET DESCRIPTOR indesc COUNT = 1; +EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = 2; +EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = :val1; +EXEC SQL SET DESCRIPTOR indesc VALUE 2 INDICATOR = :val1, DATA = 'some string'; +EXEC SQL SET DESCRIPTOR indesc VALUE 2 INDICATOR = :val2null, DATA = :val2; + + + + +兼容性 + + + SQL 标准中说明了SET DESCRIPTOR。 + + + + +参见 + + + + + + + + + + + TYPE + 定义一种新数据类型 + + + + +TYPE type_name IS ctype + + + + +描述 + + + TYPE命令定义一个新的 C 类型。它等效于把一个typedef放在声明节中。 + + + + 只有使用选项运行ecpg时才能识别这个命令。 + + + + + 参数 + + + + type_name + + + 新类型的名称。这必须是一个合法的 C 类型名。 + + + + + + ctype + + + 一个 C 类型说明。 + + + + + + + +例子 + + +EXEC SQL TYPE customer IS + struct + { + varchar name[50]; + int phone; + }; + +EXEC SQL TYPE cust_ind IS + struct ind + { + short name_ind; + short phone_ind; + }; + +EXEC SQL TYPE c IS char reference; +EXEC SQL TYPE ind IS union { int integer; short smallint; }; +EXEC SQL TYPE intarray IS int[AMOUNT]; +EXEC SQL TYPE str IS varchar[BUFFERSIZ]; +EXEC SQL TYPE string IS char[11]; + + + + 这里是一个使用EXEC SQL TYPE的例子程序: + +EXEC SQL WHENEVER SQLERROR SQLPRINT; + +EXEC SQL TYPE tt IS + struct + { + varchar v[256]; + int i; + }; + +EXEC SQL TYPE tt_ind IS + struct ind { + short v_ind; + short i_ind; + }; + +int +main(void) +{ +EXEC SQL BEGIN DECLARE SECTION; + tt t; + tt_ind t_ind; +EXEC SQL END DECLARE SECTION; + + EXEC SQL CONNECT TO testdb AS con1; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + + EXEC SQL SELECT current_database(), 256 INTO :t:t_ind LIMIT 1; + + printf("t.v = %s\n", t.v.arr); + printf("t.i = %d\n", t.i); + + printf("t_ind.v_ind = %d\n", t_ind.v_ind); + printf("t_ind.i_ind = %d\n", t_ind.i_ind); + + EXEC SQL DISCONNECT con1; + + return 0; +} + + + 这个程序的输出看起来像: + +t.v = testdb +t.i = 256 +t_ind.v_ind = 0 +t_ind.i_ind = 0 + + + + + +兼容性 + + + TYPE命令是一种 PostgreSQL 扩展。 + + + + + + + VAR + 定义一个变量 + + + + +VAR varname IS ctype + + + + +描述 + + + VAR命令分配一个新的 C 数据类型给一个主变量。主变量必须之前在一个声明节中声明过。 + + + + + 参数 + + + + varname + + + 一个 C 变量名。 + + + + + + ctype + + + 一个 C 类型说明。 + + + + + + + +例子 + + +Exec sql begin declare section; +short a; +exec sql end declare section; +EXEC SQL VAR a IS int; + + + + +兼容性 + + + VAR命令是一个 PostgreSQL 扩展。 + + + + + + + WHENEVER + 指定一个要在一个 SQL 语句导致发生一个特定类别的情况时要采取的动作 + + + + +WHENEVER { NOT FOUND | SQLERROR | SQLWARNING } action + + + + +描述 + + + 定义一个行为,它会在 SQL 执行结果的特殊情况(行未找到、SQL 警告或错误)中被调用。 + + + + +参数 + + + 参数描述见。 + + + + + 例子 + + +EXEC SQL WHENEVER NOT FOUND CONTINUE; +EXEC SQL WHENEVER NOT FOUND DO BREAK; +EXEC SQL WHENEVER SQLWARNING SQLPRINT; +EXEC SQL WHENEVER SQLWARNING DO warn(); +EXEC SQL WHENEVER SQLERROR sqlprint; +EXEC SQL WHENEVER SQLERROR CALL print2(); +EXEC SQL WHENEVER SQLERROR DO handle_error("select"); +EXEC SQL WHENEVER SQLERROR DO sqlnotice(NULL, NONO); +EXEC SQL WHENEVER SQLERROR DO sqlprint(); +EXEC SQL WHENEVER SQLERROR GOTO error_label; +EXEC SQL WHENEVER SQLERROR STOP; + + + + 一个典型的应用是使用WHENEVER NOT FOUND BREAK来处理通过结果集的循环: + +int +main(void) +{ + EXEC SQL CONNECT TO testdb AS con1; + EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; + EXEC SQL ALLOCATE DESCRIPTOR d; + EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256; + EXEC SQL OPEN cur; + + /* 当到达结果集末尾时,跳出循环 */ + EXEC SQL WHENEVER NOT FOUND DO BREAK; + + while (1) + { + EXEC SQL FETCH NEXT FROM cur INTO SQL DESCRIPTOR d; + ... + } + + EXEC SQL CLOSE cur; + EXEC SQL COMMIT; + + EXEC SQL DEALLOCATE DESCRIPTOR d; + EXEC SQL DISCONNECT ALL; + + return 0; +} + + + + + +兼容性 + + + SQL 标准中说明了WHENEVER,但是大部分动作是 PostgreSQL 扩展。 + + + + + + + <productname>Informix</productname>兼容模式 + + ecpg可以运行在一种所谓的Informix 兼容模式中。如果这种模式被激活,它的行为就好像它是一个用于Informix E/SQL 的Informix预处理器。一般而言,这将允许你使用美元符号替代EXEC SQL来引入嵌入式 SQL 命令: + +$int j = 3; +$CONNECT TO :dbname; +$CREATE TABLE test(i INT PRIMARY KEY, j INT); +$INSERT INTO test(i, j) VALUES (7, :j); +$COMMIT; + + + + + + 在$之间不能有任何空白以及下列之一的预处理器指令:includedefineifdef等。否则,预处理器将把词元解析成一个主变量。 + + + + + 有两种兼容性模式:INFORMIXINFORMIX_SE + + + 在链接使用这种兼容性模式的程序时,要记得链接上和 ECPG 一起发布的libcompat。 + + + 除了之前解释过的语法糖,Informix兼容性模式从 E/SQL 中移植了一些用于输入、输出和数据转换的函数以及嵌入式 SQL 语句到 ECPG 中。 + + + Informix兼容模式与 ECPG 的 pgtypeslib 库密切相关。pgtypeslib 将 SQL 数据类型映射到 C 宿主程序中的数据类型, + Informix兼容模式的大多数附加功能允许你对这些 C 宿主程序类型进行操作。但请注意,兼容性的范围是有限的。 + 它不会尝试复制Informix的行为;它允许你执行大致相同的操作,并提供具有相同名称和相同基本行为的函数, + 但如果你目前正在使用Informix,它并不是一个完全可替代的替代品。此外,一些数据类型是不同的。例如, + PostgreSQL的日期时间和间隔类型不了解像YEAR TO MINUTE这样的范围,因此在ECPG中也找不到对其的支持。 + + + +附加类型 + + 用于存储右切边字符串数据的 Informix-特殊的 "string" 伪类型现在在 Informix 模式中不用typedef就能支持。事实上,在 Informix 模式中,ECPG 拒绝处理包含typedef sometype string;的源文件。 + +EXEC SQL BEGIN DECLARE SECTION; +string userid; /* 这个变量将包含切边过的数据 */ +EXEC SQL END DECLARE SECTION; + +EXEC SQL FETCH MYCUR INTO :userid; + + + + + + 附加的/缺少的 嵌入式 SQL 语句 + + + + CLOSE DATABASE + + + 这个语句关闭当前连接。实际上,这是ECPG的DISCONNECT CURRENT的同义词: + +$CLOSE DATABASE; /* close the current connection */ +EXEC SQL CLOSE DATABASE; + + + + + + FREE cursor_name + + + 由于ECPG的工作方式与Informix的ESQL/C有所不同(即,哪些步骤纯粹是语法转换,哪些步骤依赖于底层运行时库), + 在ECPG中没有FREE cursor_name语句。这是因为在ECPG中, + DECLARE CURSOR不会转换为使用游标名称的运行时库的函数调用。 + 这意味着在ECPG运行时库中没有SQL游标的运行时记录,只在PostgreSQL服务器中有。 + + + + + FREE statement_name + + + FREE statement_nameDEALLOCATE PREPARE statement_name的同义词。 + + + + + + + + + Informix-兼容的 SQLDA 描述符区域 + + Informix-兼容模式支持一种与中所述不同的结构体。如下: + +struct sqlvar_compat +{ + short sqltype; + int sqllen; + char *sqldata; + short *sqlind; + char *sqlname; + char *sqlformat; + short sqlitype; + short sqlilen; + char *sqlidata; + int sqlxid; + char *sqltypename; + short sqltypelen; + short sqlownerlen; + short sqlsourcetype; + char *sqlownername; + int sqlsourceid; + char *sqlilongdata; + int sqlflags; + void *sqlreserved; +}; + +struct sqlda_compat +{ + short sqld; + struct sqlvar_compat *sqlvar; + char desc_name[19]; + short desc_occ; + struct sqlda_compat *desc_next; + void *reserved; +}; + +typedef struct sqlvar_compat sqlvar_t; +typedef struct sqlda_compat sqlda_t; + + + + 全局属性如下: + + + sqld + + + SQLDA描述符中域的数量。 + + + + + + sqlvar + + + 每一个域属性的指针。 + + + + + + desc_name + + + 未使用,用零字节填充。 + + + + + + desc_occ + + + 已分配结构体的尺寸。 + + + + + + desc_next + + + 如果结果集包含多于一个记录,这个域是下一个 SQLDA 结构体的指针。 + + + + + + reserved + + + 未使用的指针,包含 NULL。为 Informix-兼容性而保留。 + + + + + 每个字段的属性如下,它们存储在 sqlvar 数组中: + + + sqltype + + + 域的类型。可以使用的常量定义在sqltypes.h中。 + + + + + + sqllen + + + 域数据的长度。 + + + + + + sqldata + + 指向字段数据的指针,类型为 char *,所指的数据采用二进制格式。例如: +int intval; + +switch (sqldata->sqlvar[i].sqltype) +{ + case SQLINTEGER: + intval = *(int *)sqldata->sqlvar[i].sqldata; + break; + ... +} + + + + + + + sqlind + + 指向 NULL 指示符的指针。如果由 DESCRIBE 或 FETCH 返回,它始终是有效指针。如果作为 EXECUTE ... USING sqlda; 的输入,则 NULL 指针表示此字段的值不是 NULL。否则,必须正确设置有效指针和 sqlitype。例如: +if (*(int2 *)sqldata->sqlvar[i].sqlind != 0) + printf("value is NULL\n"); + + + + + + + sqlname + + + 域的名称。以 0 终止的字符串。 + + + + + + sqlformat + + 在 Informix 中保留;这里是对该字段调用 PQfformat() 所得的值。 + + + + + sqlitype + + + NULL 指示符数据的类型。当从服务器返回数据时,它总是 SQLSMINT。当SQLDA被用于一个参数化查询时,数据要根据设置的类型对待。 + + + + + + sqlilen + + + NULL 指示符数据的长度。 + + + + + + sqlxid + + 字段的扩展类型,即 PQftype() 的结果。 + + + + + sqltypename + sqltypelen + sqlownerlen + sqlsourcetype + sqlownername + sqlsourceid + sqlflags + sqlreserved + + + 未使用。 + + + + + + sqlilongdata + + + 如果sqllen大于 32kB,它等于sqldata。 + + + + + 示例: +EXEC SQL INCLUDE sqlda.h; + + sqlda_t *sqlda; /* 这不需要在嵌入式 DECLARE SECTION 下 */ + + EXEC SQL BEGIN DECLARE SECTION; + char *prep_stmt = "select * from table1"; + int i; + EXEC SQL END DECLARE SECTION; + + ... + + EXEC SQL PREPARE mystmt FROM :prep_stmt; + + EXEC SQL DESCRIBE mystmt INTO sqlda; + + printf("# of fields: %d\n", sqlda->sqld); + for (i = 0; i < sqlda->sqld; i++) + printf("field %d: \"%s\"\n", sqlda->sqlvar[i]->sqlname); + + EXEC SQL DECLARE mycursor CURSOR FOR mystmt; + EXEC SQL OPEN mycursor; + EXEC SQL WHENEVER NOT FOUND GOTO out; + + while (1) + { + EXEC SQL FETCH mycursor USING sqlda; + } + + EXEC SQL CLOSE mycursor; + + free(sqlda); /* 主结构完全被 free(),sqlda 和 sqlda->sqlvar 在一个已分配区域中 */ +更多信息参见 sqlda.h 头文件和 src/interfaces/ecpg/test/compat_informix/sqlda.pgc 回归测试。 + + + + 附加函数 + + + + decadd + + + 添加两个十进制类型的值。 + +int decadd(decimal *arg1, decimal *arg2, decimal *sum); + + 该函数接收一个指向十进制类型第一个操作数的指针 + (arg1),一个指向十进制类型第二个操作数的指针 + (arg2),以及一个指向将包含和的十进制类型值的指针 + (sum)。成功时,函数返回0。 + 溢出时返回ECPG_INFORMIX_NUM_OVERFLOW, + 下溢时返回ECPG_INFORMIX_NUM_UNDERFLOW。 + 其他故障返回-1,并将errno设置为相应的pgtypeslib的errno编号。 + + + + + + deccmp + + + 比较两个decimal类型的变量。 + +int deccmp(decimal *arg1, decimal *arg2); + + 该函数接收第一个decimal值的指针 + (arg1),第二个decimal值的指针 + (arg2),并返回一个整数值,指示哪个值更大。 + + + + 1,如果arg1指向的值大于var2指向的值 + + + + + -1,如果arg1指向的值小于arg2指向的值 + + + + + 0,如果arg1指向的值和arg2指向的值相等 + + + + + + + + + deccopy + + + 复制一个十进制值。 + +void deccopy(decimal *src, decimal *target); + + 该函数接收应该被复制的十进制值的指针作为第一个参数(src), + 并将目标类型为十进制的结构体的指针作为第二个参数(target)。 + + + + + + deccvasc + + + 将一个值从其ASCII表示转换为十进制类型。 + +int deccvasc(char *cp, int len, decimal *np); + + 该函数接收一个指向包含要转换的数字的字符串表示的指针(cp), + 以及它的长度lennp是一个指向保存操作结果的十进制值的指针。 + + + 例如,有效的格式包括: + -2, + .794, + +3.44, + 592.49E07或 + -32.84e-4。 + + + 该函数成功返回0。如果发生溢出或下溢,则返回ECPG_INFORMIX_NUM_OVERFLOW或 + ECPG_INFORMIX_NUM_UNDERFLOW。如果ASCII表示无法解析, + 则返回ECPG_INFORMIX_BAD_NUMERIC,或者如果在解析指数时出现问题,则返回 + ECPG_INFORMIX_BAD_EXPONENT。 + + + + + + deccvdbl + + + 将double类型的值转换为decimal类型的值。 + +int deccvdbl(double dbl, decimal *np); + + 该函数接收应该被转换的double类型变量作为其第一个参数(dbl)。 + 作为第二个参数(np),该函数接收一个指向应该保存操作结果的decimal变量的指针。 + + + 该函数在成功时返回0,在转换失败时返回负值。 + + + + + + deccvint + + + 将int类型的值转换为decimal类型的值。 + +int deccvint(int in, decimal *np); + + 该函数接收应该被转换的int类型变量作为其第一个参数(in)。 + 作为第二个参数(np),该函数接收一个指向应该保存操作结果的decimal变量的指针。 + + + 该函数在成功时返回0,在转换失败时返回负值。 + + + + + + deccvlong + + + 将类型为long的值转换为类型为decimal的值。 + +int deccvlong(long lng, decimal *np); + + 该函数接收应该被转换的类型为long的变量作为其第一个参数(lng)。 + 作为第二个参数(np),该函数接收一个指向应该保存操作结果的decimal变量的指针。 + + + 该函数在成功时返回0,在转换失败时返回负值。 + + + + + + decdiv + + + 将两个decimal类型的变量相除。 + +int decdiv(decimal *n1, decimal *n2, decimal *result); + + 该函数接收指向第一个(n1)和第二个(n2)操作数的变量的指针, + 并计算n1/n2result是应该保存操作结果的变量的指针。 + + + 在成功时返回0,如果除法失败则返回负值。 + 如果发生溢出或下溢,则函数分别返回 + ECPG_INFORMIX_NUM_OVERFLOW或 + ECPG_INFORMIX_NUM_UNDERFLOW。如果尝试 + 除以零,则函数返回ECPG_INFORMIX_DIVIDE_ZERO。 + + + + + + decmul + + + 两个十进制值相乘。 + +int decmul(decimal *n1, decimal *n2, decimal *result); + + 该函数接收指向第一个(n1)和第二个(n2)操作数的变量的指针, + 并计算n1*n2result是一个指向应该保存操作结果的变量的指针。 + + + 在成功时返回0,如果乘法失败则返回负值。如果发生溢出或下溢,函数分别返回 + ECPG_INFORMIX_NUM_OVERFLOW或 + ECPG_INFORMIX_NUM_UNDERFLOW。 + + + + + + decsub + + + 从另一个十进制值中减去一个十进制值。 + +int decsub(decimal *n1, decimal *n2, decimal *result); + + 该函数接收指向第一个(n1)和第二个(n2)操作数的变量的指针, + 并计算n1-n2result是应该保存操作结果的变量的指针。 + + + 在成功时返回0,如果减法失败则返回负值。如果发生溢出或下溢,函数分别返回 + ECPG_INFORMIX_NUM_OVERFLOW或 + ECPG_INFORMIX_NUM_UNDERFLOW。 + + + + + + dectoasc + + + 将decimal类型的变量转换为C char*字符串中的ASCII表示。 + +int dectoasc(decimal *np, char *cp, int len, int right) + + 该函数接收一个指向decimal类型变量的指针(np),将其转换为文本表示。 + cp是应该保存操作结果的缓冲区。参数right指定输出中小数点右侧应包含的位数。 + 结果将四舍五入到这个小数位数。将right设置为-1表示应在输出中包含所有可用的小数位数。 + 如果输出缓冲区的长度,由len指示,不足以容纳包括尾随零字节在内的文本表示, + 则结果中仅存储一个*字符,并返回-1。 + + + 该函数返回-1,如果缓冲区cp太小,或者返回ECPG_INFORMIX_OUT_OF_MEMORY, + 如果内存耗尽。 + + + + + + dectodbl + + 将 decimal 类型的变量转换为 double。 +int dectodbl(decimal *np, double *dblp); +该函数接受指向待转换 decimal 值的指针(np),以及指向用于保存操作结果的 double 变量的指针(dblp)。 + + + 当成功时,返回0;如果转换失败,则返回负值。 + + + + + + dectoint + + 将 decimal 类型的变量转换为整数。 +int dectoint(decimal *np, int *ip); +该函数接受指向待转换 decimal 值的指针(np),以及指向用于保存操作结果的整数变量的指针(ip)。 + + + 当成功时,返回0;如果转换失败,则返回负值。如果发生溢出,将返回ECPG_INFORMIX_NUM_OVERFLOW。 + + + 请注意,ECPG实现与Informix实现不同。 + Informix将整数限制在-32767到32767的范围内, + 而ECPG实现中的限制取决于架构(INT_MIN .. INT_MAX)。 + + + + + + dectolong + + 将 decimal 类型的变量转换为长整数。 +int dectolong(decimal *np, long *lngp); +该函数接受指向待转换 decimal 值的指针(np),以及指向用于保存操作结果的 long 变量的指针(lngp)。 + + + 当成功时,返回0;如果转换失败,则返回负值。如果发生溢出,将返回ECPG_INFORMIX_NUM_OVERFLOW。 + + + 请注意,ECPG实现与Informix实现不同。 + Informix将长整型限制在-2,147,483,647到2,147,483,647的范围内, + 而ECPG实现中的限制取决于架构(-LONG_MAX .. LONG_MAX)。 + + + + + + rdatestr + + 将日期转换为 C char* 字符串。 +int rdatestr(date d, char *str); +该函数接受两个参数,第一个是待转换的日期(d),第二个是指向目标字符串的指针。输出格式始终为 yyyy-mm-dd,因此必须为字符串分配至少 11 字节(包括值为零的终止字节)。 + + 该函数在成功时返回0,在错误时返回负值。 + + + 请注意,ECPG的实现与Informix的实现不同。在Informix中, + 格式可以通过设置环境变量来影响。然而,在ECPG中,你无法更改输出格式。 + + + + + + rstrdate + + + 解析日期的文本表示。 + +int rstrdate(char *str, date *d); + + 该函数接收要转换的日期的文本表示(str)和指向类型为date的变量的指针 + (d)。此函数不允许你指定格式掩码。它使用Informix的默认格式掩码, + 即mm/dd/yyyy。在内部,此函数通过rdefmtdate实现。 + 因此,rstrdate不会更快,如果可以选择,应选择允许你显式指定格式掩码的 + rdefmtdate。 + + + 这个函数返回与rdefmtdate相同的值。 + + + + + + rtoday + + + 获取当前日期。 + +void rtoday(date *d); + + 该函数接收一个指向日期变量(d)的指针,将其设置为当前日期。 + + + 在内部,此函数使用函数。 + + + + + + rjulmdy + + + 从一个类型为date的变量中提取日、月和年的值。 + +int rjulmdy(date d, short mdy[3]); + + 该函数接收日期d和一个指向包含3个short整数值的数组mdy的指针。 + 变量名指示了顺序:mdy[0]将被设置为包含月份的数字, + mdy[1]将被设置为日期的值,mdy[2]将包含年份。 + + + 这个函数目前总是返回0。 + + + 在内部,该函数使用函数。 + + + + + + rdefmtdate + + + 使用格式掩码将字符串转换为日期类型的值。 + +int rdefmtdate(date *d, char *fmt, char *str); + + 该函数接收一个指向应该保存操作结果的日期值的指针(d), + 用于解析日期的格式掩码(fmt)和包含日期文本表示的C char*字符串 + (str)。文本表示应与格式掩码匹配。但是,你不需要将字符串 + 与格式掩码进行一一映射。该函数只分析先后顺序,并查找表示年份位置的字面文本 + yyyyyy,表示月份位置的mm + 和表示日期位置的dd。 + + + 该函数返回以下值: + + + + 0 - 函数成功终止。 + + + + + ECPG_INFORMIX_ENOSHORTDATE - 日期不包含 + 日、月和年之间的分隔符。在这种情况下,输入 + 字符串必须恰好为6或8个字节长,但实际不是。 + + + + + ECPG_INFORMIX_ENOTDMY - 格式字符串未正确指示 + 年、月和日的顺序。 + + + + + ECPG_INFORMIX_BAD_DAY - 输入字符串不包含 + 有效的日。 + + + + + ECPG_INFORMIX_BAD_MONTH - 输入字符串不包含 + 有效的月。 + + + + + ECPG_INFORMIX_BAD_YEAR - 输入字符串不包含 + 有效的年。 + + + + + + 在内部,此函数实现为使用函数。请参阅那里的参考资料,了解示例输入表。 + + + + + + rfmtdate + + + 将日期类型的变量使用格式掩码转换为其文本表示形式。 + +int rfmtdate(date d, char *fmt, char *str); + + 该函数接收要转换的日期(d)、格式掩码(fmt)和将保存日期文本表示的字符串(str)。 + + + 成功时返回 0,发生错误时返回负值。 + + + 在内部,此函数使用函数,有关示例,请参阅那里的参考资料。 + + + + + + rmdyjul + + + 从指定日期的一组3个短整数创建一个日期值,这些整数指定了日期的日、月和年。 + +int rmdyjul(short mdy[3], date *d); + + 该函数接收一个包含3个短整数的数组(mdy)和一个指向应该保存操作结果的date类型变量的指针。 + + + 目前该函数始终返回0。 + + + 在内部,该函数实现为使用函数。 + + + + + + rdayofweek + + + 返回表示日期值的星期几的数字。 + +int rdayofweek(date d); + + 该函数接收日期变量d作为其唯一参数,并返回一个整数,表示该日期的星期几。 + + + + 0 - 星期日 + + + + + 1 - 星期一 + + + + + 2 - 星期二 + + + + + 3 - 星期三 + + + + + 4 - 星期四 + + + + + 5 - 星期五 + + + + + 6 - 星期六 + + + + + + 在内部,该函数被实现为使用函数。 + + + + + + dtcurrent + + + 检索当前时间戳。 + +void dtcurrent(timestamp *ts); + + 该函数检索当前时间戳,并将其保存到ts指向的时间戳变量中。 + + + + + + dtcvasc + + + 将时间戳从其文本表示解析为时间戳变量。 + +int dtcvasc(char *str, timestamp *ts); + + 该函数接收要解析的字符串(str)和指向应该保存操作结果的时间戳变量的指针(ts)。 + + + 该函数在成功时返回0,在错误时返回负值。 + + + 在内部,此函数使用函数。请参阅那里的参考资料,了解包含示例输入的表格。 + + + + + + dtcvfmtasc + + + 从文本表示中使用格式掩码解析时间戳为时间戳变量。 + +dtcvfmtasc(char *inbuf, char *fmtstr, timestamp *dtvalue) + + 该函数接收要解析的字符串(inbuf)、要使用的格式掩码 + (fmtstr)以及应该保存操作结果的时间戳变量的指针 + (dtvalue)。 + + + 这个函数是通过函数实现的。请参阅那里的文档,了解可用的格式说明符列表。 + + + 该函数在成功时返回0,在错误时返回负值。 + + + + + + dtsub + + + 从一个时间戳减去另一个时间戳,并返回一个间隔类型的变量。 + +int dtsub(timestamp *ts1, timestamp *ts2, interval *iv); + + 该函数将从ts1指向的时间戳变量中减去ts2指向的时间戳变量, + 并将结果存储在iv指向的间隔变量中。 + + + 成功时返回 0,发生错误时返回负值。 + + + + + + dttoasc + + 将 timestamp 变量转换为 C char* 字符串。 +int dttoasc(timestamp *ts, char *output); +该函数接受指向待转换 timestamp 变量的指针(ts),以及用于保存操作结果的字符串(output)。它将 ts 转换为符合 SQL 标准的文本表示,格式为 YYYY-MM-DD HH:MM:SS。 + + + 成功时返回 0,发生错误时返回负值。 + + + + + + dttofmtasc + + 使用格式掩码,将 timestamp 变量转换为 C char*。 +int dttofmtasc(timestamp *ts, char *output, int str_len, char *fmtstr); +该函数的第一个参数是指向待转换时间戳的指针(ts),随后是指向输出缓冲区的指针(output)、为输出缓冲区分配的最大长度(str_len),以及转换所用的格式掩码(fmtstr)。 + + + 成功时返回 0,发生错误时返回负值。 + + + 在内部,此函数使用函数。请参阅那里的参考资料,了解可以使用哪些格式掩码说明符。 + + + + + + intoasc + + 将 interval 变量转换为 C char* 字符串。 +int intoasc(interval *i, char *str); +该函数接受指向待转换 interval 变量的指针(i),以及用于保存操作结果的字符串(str)。它将 i 转换为符合 SQL 标准的文本表示,格式为 YYYY-MM-DD HH:MM:SS。 + + + 成功时返回 0,发生错误时返回负值。 + + + + + + rfmtlong + + + 将长整型值使用格式掩码转换为其文本表示形式。 + +int rfmtlong(long lng_val, char *fmt, char *outbuf); + + 该函数接收长整型值lng_val,格式掩码fmt和指向输出缓冲区outbuf的指针。 + 它根据格式掩码将长整型值转换为其文本表示形式。 + + + 格式掩码可以由以下格式指定字符组成: + + + + *(星号)- 如果此位置为空白,用星号填充。 + + + + + &(和号)- 如果此位置为空白,用零填充。 + + + + + # - 将前导零转换为空格。 + + + + + < - 将数字左对齐在字符串中。 + + + + + ,(逗号)- 将四位或更多位数的数字分组为以逗号分隔的三位数组。 + + + + + .(句点)- 此字符将整数部分与小数部分分隔开。 + + + + + -(减号)- 如果数字是负值,则显示减号。 + + + + + +(加号)- 如果数字是正值,则显示加号。 + + + + + ( - 这个字符替换负数前面的减号。减号不会显示。 + + + + + ) - 这个字符替换减号,并打印在负值后面。 + + + + + $ - 货币符号。 + + + + + + + + + rupshift + + + 将字符串转换为大写。 + +void rupshift(char *str); + + 该函数接收一个指向字符串的指针,并将每个小写字符转换为大写。 + + + + + + byleng + + + 返回字符串中字符的数量,不包括末尾的空格。 + +int byleng(char *str, int len); + + 该函数期望一个固定长度的字符串作为第一个参数 + (str),并将其长度作为第二个参数 + (len)。它返回有效字符的数量,即不包括末尾空格的字符串长度。 + + + + + + ldchar + + + 将固定长度的字符串复制到以空字符结尾的字符串中。 + +void ldchar(char *src, int len, char *dest); + + 该函数接收要复制的固定长度字符串(src)、其长度(len)和指向目标内存的指针(dest)。 + 请注意,你需要为dest指向的字符串保留至少len+1字节。 + 该函数最多复制len字节到新位置(如果源字符串具有尾随空格,则会少一些),并添加空字符终止符。 + + + + + + rgetmsg + + + +int rgetmsg(int msgnum, char *s, int maxsize); + + 这个函数存在,但目前尚未实现! + + + + + + rtypalign + + + +int rtypalign(int offset, int type); + + 这个函数存在,但目前尚未实现! + + + + + + rtypmsize + + + +int rtypmsize(int type, int len); + + 这个函数存在,但目前尚未实现! + + + + + + rtypwidth + + + +int rtypwidth(int sqltype, int sqllen); + + 这个函数存在,但目前尚未实现! + + + + + + rsetnull + + + 将一个变量设置为NULL。 + +int rsetnull(int t, char *ptr); + + 该函数接收一个整数,表示变量的类型,以及一个指向变量本身的指针,该指针被转换为C中的char*指针。 + + + 下列类型存在: + + + + CCHARTYPE - 用于charchar*类型的变量 + + + + + CSHORTTYPE - 用于short int类型的变量 + + + + + CINTTYPE - 用于int类型的变量 + + + + + CBOOLTYPE - 用于boolean类型的变量 + + + + + CFLOATTYPE - 用于float类型的变量 + + + + + CLONGTYPE - 用于long类型的变量 + + + + + CDOUBLETYPE - 用于double类型的变量 + + + + + CDECIMALTYPE - 用于decimal类型的变量 + + + + + CDATETYPE - 用于date类型的变量 + + + + + CDTIMETYPE - 用于timestamp类型的变量 + + + + + + + 这是调用此函数的示例: + + + + + + + + risnull + + + 测试变量是否为NULL。 + +int risnull(int t, char *ptr); + + 该函数接收要测试的变量类型(t)以及指向该变量的指针(ptr)。 + 请注意,后者需要转换为char*类型。查看函数以获取可能的变量类型列表。 + + + 这是如何使用这个函数的示例: + + + + + + + + + + + 额外的常量 + 注意,这里的所有常量都表示错误,且都定义为负值。各常量的说明中还列出了它们在当前实现中的具体值,但不应依赖这些具体数值。可以依赖的事实是:它们都定义为负值。 + + ECPG_INFORMIX_NUM_OVERFLOW + + + 如果在一次计算中发生了溢出,函数会返回这个值。在内部它被定义为 -1200(Informix定义)。 + + + + + + ECPG_INFORMIX_NUM_UNDERFLOW + + + 如果在一次计算中发生了下溢,函数会返回这个值。在内部它被定义为 -1201(Informix定义)。 + + + + + + ECPG_INFORMIX_DIVIDE_ZERO + + + 如果发现尝试除零,函数会返回这个值。在内部它被定义为 -1202(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_YEAR + + + 如果在解析一个日期时为年找到了一个坏的值,函数会返回这个值。在内部它被定义为 -1204(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_MONTH + + + 如果在解析一个日期时为月找到了一个坏的值,函数会返回这个值。在内部它被定义为 -1205(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_DAY + + + 如果在解析一个日期时为日找到了一个坏的值,函数会返回这个值。在内部它被定义为 -1206(Informix定义)。 + + + + + + ECPG_INFORMIX_ENOSHORTDATE + + + 如果一个解析例程需要一个短日期表示但是却没有得到正确长度的日期自如穿,函数会返回这个值。在内部它被定义为 -1209(Informix定义)。 + + + + + + ECPG_INFORMIX_DATE_CONVERT + + + 如果在日期格式化时产生了一个错误,函数会返回这个值。在内部它被定义为 -1210(Informix定义)。 + + + + + + ECPG_INFORMIX_OUT_OF_MEMORY + + + 如果在操作时内存被耗尽,函数会返回这个值。在内部它被定义为 -1211(Informix定义)。 + + + + + + ECPG_INFORMIX_ENOTDMY + + + 如果一个解析例程被假定为得到一个格式掩码(如mmddyy)但是列出的域并不是全部正确,函数会返回这个值。在内部它被定义为 -1212(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_NUMERIC + + + 如果一个解析例程因为一个numeric值的文本表示包含错误而不能解析它或者一个例程因为至少一个numeric变量非法而无法完成一次涉及numeric变量的计算,函数会返回这个值。在内部它被定义为 -1213(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_EXPONENT + + + 如果一个解析例程不能解析一个指数,函数会返回这个值。在内部它被定义为 -1216(Informix定义)。 + + + + + + ECPG_INFORMIX_BAD_DATE + + + 如果一个解析例程不能解析一个日期,函数会返回这个值。在内部它被定义为 -1218(Informix定义)。 + + + + + + ECPG_INFORMIX_EXTRA_CHARS + + + 如果一个解析例程被传递了它不能解析的额外字符,函数会返回这个值。在内部它被定义为 -1264(Informix定义)。 + + + + + + + + + + 内部 + + + 这一节解释ECPG在内部如何工作。这些信息有时有助于用户理解如何使用ECPG。 + + + + ecpg写入输出的前四行是固定的:其中两行是注释,另外两行是与库接口所必需的包含行。随后预处理器会通读整个文件并写出输出,通常它只是把所有内容原样回显到输出中。 + + + + 当它看到EXEC SQL语句时,就会介入并对其进行改写。命令以EXEC SQL开始,以;结束。其间的所有内容都会被视为一条SQL语句,并进行变量替换解析。 + + + + 当某个符号以冒号(:)开头时,就会发生变量替换。系统会在此前于EXEC SQL DECLARE声明节中声明的变量里查找同名变量。 + + + + 该库中最重要的函数是ECPGdo,它负责执行大部分命令。它采用可变数量的参数。可以很容易地增加到最多 50 个左右的参数,并且我们希望在任何平台上这都不会成为问题。 + + + 参数如下: + + 一个行号 + + + 这是原始行的行号,只用于错误消息。 + + + + + + 一个字符串 + + + 这是将要发出的SQL命令。输入变量会对它进行修改,也就是那些在编译时尚未知晓、但要填入命令中的变量。字符串中的?表示变量应当放置的位置。 + + + + + + 输入变量 + + + 每个输入变量都会生成十个参数(见下文)。 + + + + + + ECPGt_EOIT + + + 一个表明后面没有更多输入变量的enum值。 + + + + + + 输出变量 + + + 每个输出变量都会生成十个参数(见下文)。这些变量将由该函数填充。 + + + + + + ECPGt_EORT + + + 一个表明后面没有更多变量的enum值。 + + + + + + + + 对于每一个作为SQL命令一部分的变量,该函数得到十个参数: + + + + + 作为一个特殊符号的类型。 + + + + + + 一个值的指针或者一个指针的指针。 + + + + + + 如果变量是一个char或者varchar,这是它的尺寸。 + + + + + + 数组中元素的数量(用于数组获取)。 + + + + + + 数组中下一个元素的偏移量(用于数组获取)。 + + + + + + 作为一个特殊符号的指示符变量的类型。 + + + + + + 一个指示符变量的指针。 + + + + + + 0 + + + + + + 指示符数组中的元素数量(用于数组获取)。 + + + + + + 到指示符数组中下一个元素的偏移量(用于数组获取)。 + + + + + + + 注意并非所有 SQL 命令都被以这种方式对待。例如,一个打开游标语句: + +EXEC SQL OPEN cursor; + + 它不会被复制到输出中。相反,游标的DECLARE命令会被放在OPEN命令的位置上,因为真正打开游标的正是它。 + + + + 这里有一个完整的例子,它描述了一个文件foo.pgc的预处理器输出(对预处理器的每一个特定版本细节可能不同): + +EXEC SQL BEGIN DECLARE SECTION; +int index; +int result; +EXEC SQL END DECLARE SECTION; +... +EXEC SQL SELECT res INTO :result FROM mytable WHERE index = :index; + + 会被转换成: +; +#include ; + +/* 声明节开始 */ + +#line 1 "foo.pgc" + + int index; + int result; +/* 声明节结束 */ +... +ECPGdo(__LINE__, NULL, "SELECT res FROM mytable WHERE index = ? ", + ECPGt_int,&(index),1L,1L,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(result),1L,1L,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT); +#line 147 "foo.pgc" +]]> + + (这里的缩进是为了可读性而添加的,并非是预处理器做的处理)。 + + +
diff --git a/zh/9.6/errcodes.sgml b/zh/9.6/errcodes.sgml new file mode 100644 index 00000000..62c29b56 --- /dev/null +++ b/zh/9.6/errcodes.sgml @@ -0,0 +1,56 @@ + + + + <productname>PostgreSQL</productname>错误代码 + + + error codes + list of + + + + PostgreSQL 服务器发出的所有消息都被分配了五字符错误代码,遵循 SQL 标准对 SQLSTATE 代码的约定。需要知道发生了哪一种错误条件的应用,通常应测试错误代码,而不是查看错误消息的文本内容。这些错误代码在不同 PostgreSQL 版本之间更不容易发生变化,也不会因错误消息的本地化而改变。请注意,PostgreSQL 产生的错误代码中,只有一部分由 SQL 标准定义;还有一些用于标准未定义情况的附加错误代码,是新设的或从其他数据库借用而来的。 + + + + 根据标准,错误代码的前两个字符表示错误类别,后三个字符表示该类别中的特定条件。因此,即使应用无法识别某个具体错误代码,仍可能从错误类别推断出应采取的操作。 + + + + 列出了 PostgreSQL &version; 中定义的所有错误代码。(其中有些当前实际上并未使用,不过它们由 SQL 标准定义。)其中也显示了错误类别。对于每个错误类别,都有一个后三个字符为 000标准错误代码。该代码只用于那些属于该类别但没有分配更具体代码的错误情况。 + + + + 条件名称列中显示的符号是在 PL/pgSQL 中使用的条件名称。条件名称既可以写成大写,也可以写成小写。(注意,PL/pgSQL 不识别警告条件名称,也就是说不识别 00、01 和 02 这几个类别。) + + + + 对于某些类型的错误,服务器会报告与该错误关联的数据库对象名称(例如表、表列、数据类型或约束);例如,会报告导致 unique_violation 错误的唯一约束名称。这些名称会在错误报告消息的独立字段中提供,因此应用不必尝试从可能已经本地化的人类可读消息文本中提取它们。截至 PostgreSQL 9.3,只有 SQLSTATE 类 23(完整性约束违反)中的错误对此特性实现了完整覆盖,但未来很可能会扩展到更多类别。 + + + + + <productname>PostgreSQL</productname>错误代码 + + + + + + + + + 错误代码 + 条件名称 + + + + + + &errcodes-table; + + + +
+ + +
diff --git a/zh/9.6/event-trigger.sgml b/zh/9.6/event-trigger.sgml new file mode 100644 index 00000000..49e63d99 --- /dev/null +++ b/zh/9.6/event-trigger.sgml @@ -0,0 +1,1149 @@ + + + + 事件触发器 + + + 事件触发器 + + + + 为了补充 中讨论的触发器机制, + PostgreSQL 还提供了事件触发器。与仅附着于单个表并且 + 只捕获 DML 事件的常规触发器不同,事件触发器在某个特定数据库中是全局的, + 并且能够捕获 DDL 事件。 + + + + 与常规触发器类似,事件触发器可以用任何支持事件触发器的过程语言编写,也可以 + 用 C 编写,但不能用纯 SQL 编写。 + + + + 事件触发器行为概述 + + + 只要与事件触发器关联的事件在其定义所在数据库中发生,事件触发器就会被触发。 + 目前仅支持以下事件: + ddl_command_start、 + ddl_command_end、 + table_rewrite + 和 sql_drop。 + 未来版本可能会增加对更多事件的支持。 + + + + ddl_command_start 事件发生在 + CREATEALTERDROP、 + SECURITY LABEL、 + COMMENTGRANTREVOKE + 命令即将执行之前。在事件触发器触发之前,不会检查受影响对象究竟存在还是不存在。 + 但有一个例外:对于以共享对象 — 数据库、角色和表空间 — + 为目标的 DDL 命令或者针对事件触发器本身的命令,不会发生该事件。事件触发器机制 + 不支持这些对象类型。 + ddl_command_start 也会在 SELECT INTO + 命令即将执行之前发生,因为它等价于 CREATE TABLE AS。 + + + + ddl_command_end 事件发生在上述同一组命令执行之后。 + 要获取这些 DDL 操作的更多细节,可在 + ddl_command_end 事件触发器代码中使用集合返回函数 + pg_event_trigger_ddl_commands()(见 + )。注意,触发器是在这些动作已发生 + 之后(但在事务提交之前)触发的,因此读取系统目录时,看到的已是变更后的状态。 + + + + 对于任何删除数据库对象的操作,sql_drop 事件都发生在 + ddl_command_end 事件触发器之前。要列出已删除的对象,可在 + sql_drop 事件触发器代码中使用 + 集合返回函数 pg_event_trigger_dropped_objects()(见 + )。注意,触发器是在这些对象已经从 + 系统目录中删除之后执行的,因此已无法再查找它们。 + + + + table_rewrite 事件发生在表即将因 + ALTER TABLEALTER TYPE 命令中的某些 + 操作而被重写之前。虽然还有其他控制语句也可以重写表,例如 + CLUSTERVACUUM,但它们不会触发 + table_rewrite 事件。 + + + 事件触发器(与其他函数一样)不能在已中止的事务中执行。因此,如果 DDL 命令因 + 错误而失败,任何关联的 ddl_command_end 触发器都不会执行。 + 反之,如果 ddl_command_start 触发器因错误而失败,后续事 + 件触发器都不会触发,也不会尝试执行该命令本身。类似地,如果 + ddl_command_end 触发器因错误而失败,DDL 语句的效果将被回 + 滚,就像包含该语句的事务在任何其他情况下中止时那样。 + + + + 有关事件触发器机制所支持命令的完整列表,参见 + 。 + + + + 事件触发器通过命令 创建。 + 为了创建事件触发器,必须先创建一个返回类型为 + event_trigger 的特殊函数。 + 该函数不需要(也不能)返回值;这个返回类型仅用于表明该函数要作为事件触发器 + 被调用。 + + + + 如果为某个特定事件定义了多个事件触发器,它们将按触发器名称的字母顺序触发。 + + + + 触发器定义也可以指定一个 WHEN 条件,这样,例如, + ddl_command_start 触发器就可以只针对用户希望拦截的特定命 + 令触发。这类触发器的一个常见用途是限制用户可执行的 DDL 操作范围。 + + + + + 事件触发器触发矩阵 + + + 列出了事件触发器所支持的 + 所有命令。 + + + + 按命令标签分类的事件触发器支持 + + + + 命令标签 + ddl_command_start + ddl_command_end + sql_drop + table_rewrite + 备注 + + + + + ALTER AGGREGATE + X + X + - + - + + + + ALTER COLLATION + X + X + - + - + + + + ALTER CONVERSION + X + X + - + - + + + + ALTER DOMAIN + X + X + - + - + + + + ALTER DEFAULT PRIVILEGES + X + X + - + - + + + + ALTER EXTENSION + X + X + - + - + + + + ALTER FOREIGN DATA WRAPPER + X + X + - + - + + + + ALTER FOREIGN TABLE + X + X + X + - + + + + ALTER FUNCTION + X + X + - + - + + + + ALTER LANGUAGE + X + X + - + - + + + + ALTER LARGE OBJECT + X + X + - + - + + + + ALTER MATERIALIZED VIEW + X + X + - + - + + + + ALTER OPERATOR + X + X + - + - + + + + ALTER OPERATOR CLASS + X + X + - + - + + + + ALTER OPERATOR FAMILY + X + X + - + - + + + + ALTER POLICY + X + X + - + - + + + + + ALTER SCHEMA + X + X + - + - + + + + ALTER SEQUENCE + X + X + - + - + + + + ALTER SERVER + X + X + - + - + + + + + + ALTER TABLE + X + X + X + X + + + + ALTER TEXT SEARCH CONFIGURATION + X + X + - + - + + + + ALTER TEXT SEARCH DICTIONARY + X + X + - + - + + + + ALTER TEXT SEARCH PARSER + X + X + - + - + + + + ALTER TEXT SEARCH TEMPLATE + X + X + - + - + + + + ALTER TRIGGER + X + X + - + - + + + + ALTER TYPE + X + X + - + X + + + + ALTER USER MAPPING + X + X + - + - + + + + ALTER VIEW + X + X + - + - + + + + COMMENT + X + X + - + - + 仅限本地对象 + + + CREATE ACCESS METHOD + X + X + - + - + + + + CREATE AGGREGATE + X + X + - + - + + + + CREATE CAST + X + X + - + - + + + + CREATE COLLATION + X + X + - + - + + + + CREATE CONVERSION + X + X + - + - + + + + CREATE DOMAIN + X + X + - + - + + + + CREATE EXTENSION + X + X + - + - + + + + CREATE FOREIGN DATA WRAPPER + X + X + - + - + + + + CREATE FOREIGN TABLE + X + X + - + - + + + + CREATE FUNCTION + X + X + - + - + + + + CREATE INDEX + X + X + - + - + + + + CREATE LANGUAGE + X + X + - + - + + + + CREATE MATERIALIZED VIEW + X + X + - + - + + + + CREATE OPERATOR + X + X + - + - + + + + CREATE OPERATOR CLASS + X + X + - + - + + + + CREATE OPERATOR FAMILY + X + X + - + - + + + + CREATE POLICY + X + X + - + - + + + + + CREATE RULE + X + X + - + - + + + + CREATE SCHEMA + X + X + - + - + + + + CREATE SEQUENCE + X + X + - + - + + + + CREATE SERVER + X + X + - + - + + + + + + CREATE TABLE + X + X + - + - + + + + CREATE TABLE AS + X + X + - + - + + + + CREATE TEXT SEARCH CONFIGURATION + X + X + - + - + + + + CREATE TEXT SEARCH DICTIONARY + X + X + - + - + + + + CREATE TEXT SEARCH PARSER + X + X + - + - + + + + CREATE TEXT SEARCH TEMPLATE + X + X + - + - + + + + CREATE TRIGGER + X + X + - + - + + + + CREATE TYPE + X + X + - + - + + + + CREATE USER MAPPING + X + X + - + - + + + + CREATE VIEW + X + X + - + - + + + + DROP ACCESS METHOD + X + X + X + - + + + + DROP AGGREGATE + X + X + X + - + + + + DROP CAST + X + X + X + - + + + + DROP COLLATION + X + X + X + - + + + + DROP CONVERSION + X + X + X + - + + + + DROP DOMAIN + X + X + X + - + + + + DROP EXTENSION + X + X + X + - + + + + DROP FOREIGN DATA WRAPPER + X + X + X + - + + + + DROP FOREIGN TABLE + X + X + X + - + + + + DROP FUNCTION + X + X + X + - + + + + DROP INDEX + X + X + X + - + + + + DROP LANGUAGE + X + X + X + - + + + + DROP MATERIALIZED VIEW + X + X + X + - + + + + DROP OPERATOR + X + X + X + - + + + + DROP OPERATOR CLASS + X + X + X + - + + + + DROP OPERATOR FAMILY + X + X + X + - + + + + DROP OWNED + X + X + X + - + + + + DROP POLICY + X + X + X + - + + + + + DROP RULE + X + X + X + - + + + + DROP SCHEMA + X + X + X + - + + + + DROP SEQUENCE + X + X + X + - + + + + DROP SERVER + X + X + X + - + + + + + + DROP TABLE + X + X + X + - + + + + DROP TEXT SEARCH CONFIGURATION + X + X + X + - + + + + DROP TEXT SEARCH DICTIONARY + X + X + X + - + + + + DROP TEXT SEARCH PARSER + X + X + X + - + + + + DROP TEXT SEARCH TEMPLATE + X + X + X + - + + + + DROP TRIGGER + X + X + X + - + + + + DROP TYPE + X + X + X + - + + + + DROP USER MAPPING + X + X + X + - + + + + DROP VIEW + X + X + X + - + + + + GRANT + X + X + - + - + 仅限本地对象 + + + IMPORT FOREIGN SCHEMA + X + X + - + - + + + + REFRESH MATERIALIZED VIEW + X + X + - + - + + + + REVOKE + X + X + - + - + 仅限本地对象 + + + SECURITY LABEL + X + X + - + - + 仅限本地对象 + + + SELECT INTO + X + X + - + - + + + + +
+
+ + + 用 C 编写事件触发器函数 + + + 事件触发器 + 在 C 中 + + + + 本节描述事件触发器函数接口的底层细节。只有在用 C 编写事件触发器函数时才需要 + 这些信息。如果你使用更高级的语言,那么这些细节会由该语言处理。在大多数情况 + 下,你应先考虑使用过程语言,再决定是否用 C 编写事件触发器。每种过程语言的文 + 档都说明了如何在该语言中编写事件触发器。 + + + + 事件触发器函数必须使用 版本 1 函数管理器接口。 + + + + 当事件触发器管理器调用函数时,不会向它传递任何普通参数,而是会传入一个 + 上下文 指针,指向一个 EventTriggerData + 结构体。C 函数可以通 + 过执行下列宏来检查自己是否由事件触发器管理器调用: + +CALLED_AS_EVENT_TRIGGER(fcinfo) + + 该宏会展开为: + +((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData)) + + 如果该宏返回真,那么就可以安全地将 fcinfo->context + 转换为 EventTriggerData * 类型,并使用它所指向的 + EventTriggerData 结构体。该函数 + 不得修改 EventTriggerData + 结构体或它所指向的任何数据。 + + + + struct EventTriggerData定义在commands/event_trigger.h: + + +typedef struct EventTriggerData +{ + NodeTag type; + const char *event; /* event name */ + Node *parsetree; /* parse tree */ + const char *tag; /* command tag */ +} EventTriggerData; +其成员定义如下: + + type + + + 始终为 T_EventTriggerData。 + + + + + + event + + + 描述调用该函数对应的事件,可为 + "ddl_command_start"、 + "ddl_command_end""sql_drop"、 + "table_rewrite" 之一。 + 关于这些事件的含义,参见 。 + + + + + + parsetree + + + 指向该命令语法解析树的指针。详见 PostgreSQL 源代码。 + 语法解析树结构可能在不另行通知的情况下发生变化。 + + + + + + tag + + + 与触发该事件触发器的事件相关联的命令标签,例如 + "CREATE FUNCTION"。 + + + + + + + + 事件触发器函数必须返回一个 NULL 指针 + (不是 SQL 空值,也就是不要将 + isNull 设为真)。 + + + + + 完整的事件触发器示例 + + + 下面是一个用 C 编写的非常简单的事件触发器函数示例。 + (使用过程语言编写的触发器示例可在各过程语言的文档中找到。) + + + + 函数 noddl 每次被调用时都会抛出异常。 + 事件触发器定义将该函数与 ddl_command_start 事件关联起来。 + 其效果是阻止所有 DDL 命令运行(不包括 + 中提到的例外)。 + + + 下面是触发器函数的源代码:context; + + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("command \"%s\" denied", trigdata->tag))); + + PG_RETURN_NULL(); +} +]]> + + + 编译源代码后(见),声明函数和触发器: +CREATE FUNCTION noddl() RETURNS event_trigger + AS 'noddl' LANGUAGE C; + +CREATE EVENT TRIGGER noddl ON ddl_command_start + EXECUTE PROCEDURE noddl(); + + + + 现在可以测试触发器的工作情况: +=# \dy + List of event triggers + Name | Event | Owner | Enabled | Procedure | Tags +-------+-------------------+-------+---------+-----------+------ + noddl | ddl_command_start | dim | enabled | noddl | +(1 row) + +=# CREATE TABLE foo(id serial); +ERROR: command "CREATE TABLE" denied + + + + + 在这种情况下,为了能在需要时执行某些 DDL 命令,你必须删除该事件触发器或将其 + 禁用。只在一个事务期间禁用该触发器通常更方便: + +BEGIN; +ALTER EVENT TRIGGER noddl DISABLE; +CREATE TABLE foo (id serial); +ALTER EVENT TRIGGER noddl ENABLE; +COMMIT; + + (请记住,针对事件触发器本身的 DDL 命令不会受事件触发器影响。) + + + + + 表重写事件触发器示例 + + + 借助 table_rewrite 事件,可以实现一种表重写策略, + 从而只允许在维护窗口内执行重写。 + + + 下面是一个实现这种策略的示例。 +CREATE OR REPLACE FUNCTION no_rewrite() + RETURNS event_trigger + LANGUAGE plpgsql AS +$$ +--- +--- Implement local Table Rewriting policy: +--- public.foo is not allowed rewriting, ever +--- other tables are only allowed rewriting between 1am and 6am +--- unless they have more than 100 blocks +--- +DECLARE + table_oid oid := pg_event_trigger_table_rewrite_oid(); + current_hour integer := extract('hour' from current_time); + pages integer; + max_pages integer := 100; +BEGIN + IF pg_event_trigger_table_rewrite_oid() = 'public.foo'::regclass + THEN + RAISE EXCEPTION 'you''re not allowed to rewrite the table %', + table_oid::regclass; + END IF; + + SELECT INTO pages relpages FROM pg_class WHERE oid = table_oid; + IF pages > max_pages + THEN + RAISE EXCEPTION 'rewrites only allowed for table with less than % pages', + max_pages; + END IF; + + IF current_hour NOT BETWEEN 1 AND 6 + THEN + RAISE EXCEPTION 'rewrites only allowed between 1am and 6am'; + END IF; +END; +$$; + +CREATE EVENT TRIGGER no_rewrite_allowed + ON table_rewrite + EXECUTE PROCEDURE no_rewrite(); + + + +
diff --git a/zh/9.6/extend.sgml b/zh/9.6/extend.sgml new file mode 100644 index 00000000..ce6977c9 --- /dev/null +++ b/zh/9.6/extend.sgml @@ -0,0 +1,1159 @@ + + + + 扩展 <acronym>SQL</acronym> + + + 扩展 SQL + + + + 在接下来的各节中,我们将讨论如何通过增加以下内容来扩展 + PostgreSQL SQL 查询语言: + + + + + 函数(从 开始) + + + + + 聚合(从 开始) + + + + + 数据类型(从 开始) + + + + + 操作符(从 开始) + + + + + 索引的操作符类(从 开始) + + + + + 相关对象的包(从 开始) + + + + + + + 可扩展性如何运作 + + + PostgreSQL 之所以具有可扩展性,是因为其 + 运作由系统目录驱动。如果你熟悉标准的关系数据库系统,就会知道它们把 + 有关数据库、表、列等的信息存储在通常所说的系统目录中(有些系统把这 + 称为数据字典)。这些目录对用户而言看起来就像普通表一样,但 + DBMS 会在其中保存自己的内部管理信息。 + PostgreSQL 与标准关系数据库系统的一个关键差别是, + PostgreSQL 在目录中存储的信息要多得多:不仅有关于表和列的信息,还有关于数据 + 类型、函数、访问方法等的信息。这些表可以由用户修改,而 + PostgreSQL 又是基于这些表来运行的,这意味着 + PostgreSQL 可以由用户扩展。相比之下,传统数据库 + 系统只能通过修改源代码中的硬编码过程,或加载由 + DBMS 供应商专门编写的模块来扩展。 + + + + 此外,PostgreSQL 服务器还能通过动态加载把用户 + 编写的代码纳入自身。也就是说,用户可以指定一个实现了新类型或新函数 + 的目标代码文件(例如共享库),而 PostgreSQL + 会在需要时加载它。把用 SQL 编写的代码加入服务器就更 + 为简单了。这种能够即时修改自身行为的能力,使 + PostgreSQL 特别适合用于新应用和新存储结构的快速 + 原型设计。 + + + + + <productname>PostgreSQL</productname> 类型系统 + + + 基础类型 + + + + 数据类型 + 基础 + + + + 复合类型 + + + + 数据类型 + 复合 + + + PostgreSQL 数据类型可以分为基础类型、复合类型、域和伪类型。 + + + 基础类型 + + 基础类型是在 SQL 语言层之下实现的类型(通常使用 C 等低级语言),例如 int4。它们通常对应于所谓的抽象数据类型。PostgreSQL 只能通过用户提供的函数操作这些类型,对其行为的理解也仅限于用户所描述的内容。基础类型进一步分为标量类型和数组类型。对于每个标量类型,都会自动创建相应的数组类型,以存储由该标量类型组成的可变大小数组。 + + + + 复合类型 + + 用户每次创建表时,都会创建复合类型(也称行类型)。还可以使用 定义不关联任何表的独立复合类型。复合类型就是一个带有对应字段名的类型列表。复合类型的值是由字段值组成的一行或一条记录。用户可以在 SQL 查询中访问各个组成字段。关于复合类型的更多信息,参见 + + + + + + 域基于某一种特定的基础类型,并且在很多场合下都可以与其基础类型互换。不过,域可以带有约束,把其合法值限制在底层基础类型所允许值的一个子集内。 + + 可以使用 SQL 命令 创建域。本章不讨论域的创建和使用。 + + + + 伪类型 + + 有少数几种伪类型用于特殊目的。伪类型不能作为表的列或复合类型的属性出现,但它们可以用来声明函数的参数类型和结果类型。这为在类型系统内部识别特殊类别的函数提供了一种机制。 列出了现有的伪类型。 + + + + 多态类型 + + + 多态类型 + + + + 多态函数 + + + + 类型 + 多态 + + + + 函数 + 多态 + + + 五种特别值得关注的伪类型是 anyelementanyarrayanynonarrayanyenumanyrange,它们统称为多态类型。使用这些类型声明的函数称为多态函数。多态函数可以操作多种不同的数据类型,具体类型由某次调用时实际传入的数据类型决定。 + + 多态参数和结果相互关联,在解析调用多态函数的查询时,会被解析为某种具体的数据类型。每个声明为 anyelement 的位置(参数或返回值)都可以具有任意具体的实际数据类型,但在同一次调用中,它们必须具有相同的实际类型。每个声明为 anyarray 的位置可以具有任意数组数据类型,但同样必须全部具有相同的类型。类似地,声明为 anyrange 的位置必须全部具有相同的范围类型。此外,如果某些位置声明为 anyarray,另一些位置声明为 anyelement,那么 anyarray 位置上的实际数组类型,其元素类型必须与 anyelement 位置上的类型相同。类似地,如果某些位置声明为 anyrange,另一些位置声明为 anyelementanyarray,那么 anyrange 位置上的实际范围类型,其子类型必须与 anyelement 位置上的类型相同,并与 anyarray 位置上的元素类型相同。anynonarray 的处理与 anyelement 完全相同,但增加了实际类型不能为数组类型的约束。anyenum 的处理与 anyelement 完全相同,但增加了实际类型必须为枚举类型的约束。 + + + 因此,当多个参数位置被声明为多态类型时,其总体效果就是只允许某些实际 + 参数类型组合。例如,声明为 + equal(anyelement, anyelement) 的函数可以接受任意两 + 个输入值,只要它们属于同一数据类型。 + + + 如果函数的返回值声明为多态类型,那么至少有一个参数位置也必须是多态的,并且实际传入的参数数据类型决定该次调用的实际结果类型。例如,如果尚不存在数组下标机制,可以将实现下标访问的函数定义为 subscript(anyarray, integer) returns anyelement。这一声明将实际的第一个参数约束为数组类型,并允许解析器根据第一个参数的实际类型推导正确的结果类型。另一个例子是,声明为 f(anyarray) returns anyenum 的函数只接受枚举类型的数组。 + + 在大多数情况下,解析器可以根据属于另一种多态类型的参数,推导出多态结果类型的实际数据类型。例如,可以从 anyelement 推导出 anyarray,反之亦然。例外是:类型为 anyrange 的多态结果要求有一个类型为 anyrange 的参数;无法从 anyarrayanyelement 参数推导出来。这是因为多个范围类型可能具有相同的子类型。 + + + 注意,anynonarrayanyenum 并不表示独立 + 的类型变量;它们与 anyelement 是同一个类型,只是附加了额 + 外约束。例如,把函数声明为 + f(anyelement, anyenum),等价于把它声明为 + f(anyenum, anyenum):两个实际参数都必须是同一种枚 + 举类型。 + + + 可变参数函数(接受可变数量参数的函数,见 )可以是多态的:将其最后一个参数声明为 VARIADIC anyarray 即可。就参数匹配和确定实际结果类型而言,这样的函数与写出相应数量的 anynonarray 参数时行为相同。 + + + + &xfunc; + &xaggr; + &xtypes; + &xoper; + &xindex; + + + + 将相关对象打包成扩展 + + + 扩展 + + + + 一个有用的 PostgreSQL 扩展通常包含多个 + SQL 对象;例如,一种新的数据类型将需要新的函数、新的操作符,并且很可 + 能还需要新的索引操作符类。把所有这些对象收集到一个单独的包中,有助于 + 简化数据库管理。PostgreSQL 将这样的包称为 + 扩展。要定义一个扩展,至少需要一个 + 脚本文件,其中包含用于创建扩展对象的 + SQL 命令,以及一个 控制文件, + 用来指定扩展本身的一些基本属性。如果扩展包含 C 代码,通常还会有一个 + 共享库文件,C 代码已被构建到其中。一旦准备好这些文件,只需执行一个简 + 单的 + 命令,就能把这些对象装入数据库。 + + + + 使用扩展而不是仅仅运行 SQL 脚本把一堆松散 + 对象装入数据库,最主要的优点在于 PostgreSQL + 能够理解这些对象是属于同一个扩展的。你可以用一条 + + 命令删除全部对象(无需维护单独的卸载脚本)。更有用的是, + pg_dump 知道自己不应转储扩展的各个成员对象 + — 它只会在转储中包含一条 CREATE EXTENSION + 命令。这极大地简化了迁移到扩展新版本的过程,即使新版本包含比旧版本更 + 多或不同的对象也是如此。不过要注意,在把这样的转储装载到新数据库时, + 必须能够访问该扩展的控制文件、脚本文件以及其他相关文件。 + + + + PostgreSQL 不允许你删除扩展中包含的单个对象, + 除非删除整个扩展。另外,虽然你可以修改扩展成员对象的定义(例如对函数 + 使用 CREATE OR REPLACE FUNCTION),但要记住,被修 + 改后的定义不会被 pg_dump 转储。这样的修改通 + 常只有在你同时在扩展脚本文件中做出同样修改时才有意义。(不过,对于包 + 含配置数据的表有特殊规定;见 + 。)在生产环境中,通 + 常更好的做法是创建一个扩展更新脚本,用它来完成对扩展成员对象的修改。 + + + + 扩展脚本可以使用 GRANTREVOKE + 语句,为属于扩展的对象设置权限。每个对象的最终权限集合(如果设置了权 + 限)会存储在 + pg_init_privs + 系统目录中。使用 pg_dump 时,转储中会包含 + CREATE EXTENSION 命令,后面再跟上一组必要的 + GRANTREVOKE 语句,以把对象 + 权限恢复到生成该转储时的状态。 + + + + PostgreSQL 目前不支持扩展脚本发出 + CREATE POLICYSECURITY LABEL + 语句。它们应当在扩展创建完成之后再设置。扩展对象上的所有 RLS 策略和 + 安全标签都会包含在 pg_dump 创建的转储中。 + + + + 扩展机制还提供了用于打包修改脚本的支持,以便调整扩展中所含 SQL 对象 + 的定义。例如,如果扩展 1.1 版相比 1.0 版增加了一个函数,并修改了另一 + 个函数的函数体,那么扩展作者可以提供一个 更新脚本 + 来只完成这两项修改。随后就可以使用 + ALTER EXTENSION UPDATE 命令应用这些修改,并跟踪在 + 某个数据库中实际安装的是该扩展的哪个版本。 + + + + 哪些 SQL 对象种类可以成为扩展成员,见 + + 的说明。特别是,数据库集簇范围内的对象,如数据库、角色和表空间,不能 + 成为扩展成员,因为扩展只在单个数据库内可见。(尽管并不禁止扩展脚本创 + 建这类对象,但如果这样做,它们不会作为扩展的一部分受到跟踪。)还要注意, + 虽然表可以成为扩展成员,但其附属对象(如索引)并不直接被视为扩展成员。 + 另一个重要点是,模式可以属于扩展,但反过来不成立:扩展本身只有一个非 + 限定名,并不位于任何模式中。不过,扩展的成员对象会在其 + 对象类型适用的情况下属于某个模式。扩展是否应当拥有其成员对象所在的模 + 式,则要视具体情况而定。 + + + + 扩展文件 + + + 控制文件 + + + 命令依赖于每个扩展都有一个控制文件,其名称必须是扩展名加上 .control 后缀,并放在安装目录的 SHAREDIR/extension 目录中。还必须至少有一个 SQL 脚本文件,其名称遵循 extension--version.sql 的模式(例如,扩展 foo1.0 版本使用 foo--1.0.sql)。默认情况下,脚本文件也放在 SHAREDIR/extension 目录中,但控制文件可以为脚本文件指定其他目录。 + + + 扩展控制文件的格式与 postgresql.conf 文件相同, + 即由一组 parameter_name + = value 赋值组成,每行一 + 条。允许空行和以 # 引入的注释。任何不是单个单词或 + 数字的值都要记得加引号。 + + + + 控制文件可以设置下列参数: + + + + + directory (string) + + + 包含扩展 SQL 脚本文件的目录。除非给出的是绝对路 + 径,否则该名称相对于安装的 SHAREDIR 目录。默认行为 + 等价于指定 directory = 'extension'。 + + + + + + default_version (string) + + + 扩展的默认版本(即在 CREATE EXTENSION 中未指定版 + 本时将安装的版本)。虽然这个参数可以省略,但那样一来,如果没有给出 + VERSION 选项,CREATE EXTENSION + 就会失败,因此一般不希望这样做。 + + + + + + comment (string) + + + 关于扩展的注释(任意字符串)。该注释会在初次创建扩展时应用,但不会 + 在扩展更新时应用(因为那样可能会覆盖用户后来添加的注释)。另外,也 + 可以在脚本文件中写一个 命令来设置扩 + 展注释。 + + + + + + encoding (string) + + + 脚本文件所使用的字符集编码。如果脚本文件包含任何非 ASCII 字符,就 + 应指定这个参数。否则将假定这些文件使用数据库编码。 + + + + + + module_pathname (string) + + + 该参数的值会替换脚本文件中每次出现的 + MODULE_PATHNAME。如果未设置该参数,则不会进行替 + 换。通常会把它设置为 + $libdir/shared_library_name, + 然后在 C 语言函数的 CREATE FUNCTION 命令中使用 + MODULE_PATHNAME,这样脚本文件就无需把共享库的名 + 字硬编码进去。 + + + + + + requires (string) + + + 本扩展所依赖的其他扩展名称列表,例如 + requires = 'foo, bar'。这些被依赖的扩展必须先安 + 装好,本扩展才能安装。 + + + + + + superuser (boolean) + + 如果此参数为 true(默认值),则只有超级用户可以创建扩展或将其更新到新版本。如果设为 false,则只需具有执行安装或更新脚本中命令所需的权限。 + + + + + relocatable (boolean) + + + 如果一个扩展在初次创建之后仍然可以把其包含的对象移动到不同的模式 + 中,那么它就是可重定位的。默认值为 + false,也就是该扩展不可重定位。详见 + 。 + + + + + + schema (string) + + + 该参数只能为不可重定位扩展设置。它强制扩展装载到指定名称的模式中, + 而不能装载到其他模式。schema 参数只在初次创建扩展 + 时起作用,扩展更新时不会使用。详见 + 。 + + + + + + + 除主控制文件 + extension.control 外,扩展还 + 可以有按如下样式命名的次级控制文件: + extension--version.control。 + 如果提供了这些文件,它们必须位于脚本文件目录中。次级控制文件遵循与主 + 控制文件相同的格式。在安装或更新到该扩展的相应版本时,次级控制文件中 + 设置的任何参数都会覆盖主控制文件中的设置。不过, + directorydefault_version + 这两个参数不能在次级控制文件中设置。 + + + + 扩展的 SQL 脚本文件可以包含任何 SQL 命令,但事务控 + 制命令(BEGINCOMMIT 等)和无 + 法在事务块内执行的命令(例如 VACUUM)除外。这是因 + 为脚本文件会被隐式地放在事务块中执行。 + + + + 扩展的 SQL 脚本文件也可以包含以 + \echo 开头的行,扩展机制会忽略这些行(将其视为注 + 释)。这一约定通常用于在脚本文件被直接交给 psql + 而不是通过 CREATE EXTENSION 装载时抛出错误(见 + 中的示例脚本)。如果没有 + 这种机制,用户可能会意外地把扩展内容作为松散对象装载, + 而不是作为一个扩展来装载,这种状态恢复起来会有些麻烦。 + + + + 虽然脚本文件可以包含指定编码允许的任意字符,但控制文件应只包含纯 + ASCII 字符,因为 PostgreSQL 无法知道控制文 + 件采用的是什么编码。在实践中,只有当你想在扩展注释中使用非 ASCII 字 + 符时这才会成为问题。对此推荐的做法是不要使用控制文件中的 + comment 参数,而是在脚本文件中使用 + COMMENT ON EXTENSION 来设置注释。 + + + + + + 扩展的可重定位性 + + + 用户经常希望把扩展中的对象装载到与扩展作者原先设想不同的模式中。对 + 于这种可重定位性,支持三个级别: + + + + + + 完全可重定位的扩展可以在任何时候移动到另一个模式中,即使它已经被装 + 载到数据库之后也是如此。这通过 + ALTER EXTENSION SET SCHEMA 命令完成,该命令会自 + 动把所有成员对象重命名到新模式中。通常,只有当扩展对其任何对象所在 + 模式都没有内部假设时,才有可能做到这一点。此外,扩展的对象一开始必 + 须全部位于同一个模式中(不属于任何模式的对象,如过程语言,不算在 + 内)。要把一个扩展标记为完全可重定位,只需在其控制文件中设置 + relocatable = true。 + + + + + + 扩展可能在安装期间可重定位,但安装之后不可重定位。如果扩展脚本文件 + 需要显式引用目标模式,例如为 SQL 函数设置 + search_path 属性时,通常就是这种情况。对于这样的 + 扩展,应在控制文件中设置 + relocatable = false,并在脚本文件中使用 + @extschema@ 来引用目标模式。在脚本执行前,该字符 + 串的每次出现都会被替换为实际目标模式的名称。用户 + 可以通过 CREATE EXTENSION 的 + SCHEMA 选项设置目标模式。 + + + + + + 如果扩展完全不支持重定位,应在控制文件中设置 + relocatable = false,并把 + schema 设置为预定目标模式的名称。这样将阻止使用 + CREATE EXTENSIONSCHEMA + 选项,除非它指定的正是控制文件中命名的那个模式。如果扩展对模式名 + 称有无法通过 @extschema@ 替换解决的内部假设,通 + 常就需要采用这种方式。在这种情况下, + @extschema@ 替换机制仍然可用,只是由于模式名由控 + 制文件决定,其用途比较有限。 + + + + + + 在所有情况下,脚本文件执行时,其初始 + 都会指向目标模式;也就是说, + CREATE EXTENSION 做的事情等价于: + +SET LOCAL search_path TO @extschema@, pg_temp; + + 这使得脚本文件创建的对象能够进入目标模式。脚本文件当然也可以修改 + search_path,但通常不建议这样做。 + CREATE EXTENSION 完成后, + search_path 会恢复为先前的设置。 + + + + 目标模式由控制文件中的 schema 参数决定(如果给出了 + 该参数);否则由 CREATE EXTENSION 的 + SCHEMA 选项决定(如果给出了该选项);否则由当前默 + 认的对象创建模式决定(也就是调用者的 search_path + 中第一个模式)。当使用控制文件中的 schema 参数时, + 如果目标模式尚不存在,则会自动创建;而在另外两种情况下,目标模式必须 + 已经存在。 + + + + 如果控制文件的 requires 中列出了任何前置扩展,它们 + 的目标模式会追加到 search_path 的初始设置中,排在 + 新扩展的目标模式之后。这样新扩展的脚本文件就可以看到这些扩展的对象。 + + + + 出于安全考虑,pg_temp 在所有情况下都会自动追加到 + search_path 的末尾。 + + + + 虽然不可重定位扩展可以包含分布在多个模式中的对象,但通常仍然希望把所 + 有供外部使用的对象放在单个模式中,这个模式会被视为该扩展的目标模式。 + 这种安排与创建依赖扩展时 search_path 的默认设置配 + 合起来会比较方便。 + + + + + 扩展配置表 + + + 有些扩展包含配置表,其中存放的数据可能会在扩展安装后被用户新增或修改。 + 通常,如果表属于某个扩展,那么 pg_dump 既 + 不会转储该表的定义,也不会转储其内容。但这种行为对于配置表来说并不理 + 想;用户所做的数据更改必须包含在转储中,否则在转储并恢复之后,扩展的 + 行为就会发生变化。 + + + + pg_extension_config_dump + + + + 为了解决这个问题,扩展脚本文件可以把它创建的某个表或序列标记为配置关 + 系,这样 pg_dump 就会把该表或序列的内容 + (而不是定义)包含到转储中。做法是在创建表或序列之后调用函数 + pg_extension_config_dump(regclass, text),例如: + +CREATE TABLE my_config (key text, value text); +CREATE SEQUENCE my_config_seq; + +SELECT pg_catalog.pg_extension_config_dump('my_config', ''); +SELECT pg_catalog.pg_extension_config_dump('my_config_seq', ''); + + 可以用这种方式标记任意数量的表或序列。与 serial 或 + bigserial 列关联的序列也可以这样标记。 + + + + 当 pg_extension_config_dump 的第二个参数是空字 + 符串时,pg_dump 会转储该表的全部内容。通 + 常只有当该表在扩展脚本创建时最初为空时,这样做才是正确的。如果表中混 + 有初始数据和用户提供的数据,那么 + pg_extension_config_dump 的第二个参数就提供了 + 一个用于选择要转储数据的 WHERE 条件。例如,你可以 + 这样做: + +CREATE TABLE my_config (key text, value text, standard_entry boolean); + +SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry'); + + 然后确保只有扩展脚本创建的那些行,其 + standard_entry 才为真。 + + + + 对于序列,pg_extension_config_dump 的第二个参数没 + 有作用。 + + + + 更复杂的情况,例如用户可能会修改最初提供的数据行,可以通过在配置表上 + 创建触发器来处理,以确保被修改的行被正确标记。 + + + + 你可以再次调用 pg_extension_config_dump 来修改与 + 配置表关联的过滤条件。(这通常在扩展更新脚本中很有用。)要把某个表标 + 记为不再是配置表,唯一的方法是通过 + ALTER EXTENSION ... DROP TABLE 将它与扩展解除关 + 联。 + + + + 注意,这些表之间的外键关系会决定 pg_dump 转储它们的顺序。具体来说, + pg_dump 会尝试先转储被引用表,再转储引用表。由于外键关系是在 + CREATE EXTENSION 时建立的(那时数据尚未装入这些表中),因此不支持环 + 状依赖。若存在环状依赖,数据仍会被转储出来,但该转储将无法直接恢复, + 需要用户介入处理。 + + + + 与 serialbigserial 列关联的序列需要被 + 直接标记,才能转储它们的状态。仅仅标记它们的父关系还不足以达到这一目 + 的。 + + + + + 扩展更新 + + + 扩展机制的一个优点是,它为管理定义扩展对象的 SQL 命令的更新提供了便 + 利方式。这是通过为每个已发布版本的扩展安装脚本关联一个版本名或版本号 + 来实现的。此外,如果你希望用户能够把数据库从一个版本动态更新到下一个 + 版本,就应提供 更新脚本,以完成从一个版本切换 + 到下一版本所需的修改。更新脚本的名称遵循如下模式: + extension--old_version--target_version.sql + (例如,foo--1.0--1.1.sql 包含把扩展 + foo1.0 版修改为 + 1.1 版所需的命令)。 + + + + 在有合适更新脚本可用的前提下, + ALTER EXTENSION UPDATE 命令会把已安装的扩展更新 + 到指定的新版本。更新脚本运行在 + CREATE EXTENSION 为安装脚本提供的同一环境中:尤其 + 是,search_path 的设置方式完全相同,而且脚本创建的 + 任何新对象都会自动加入扩展中。 + + + + 如果扩展有次级控制文件,那么用于更新脚本的控制参数就是与该脚本目标 + (新)版本相关联的那些参数。 + + + 更新机制可以用于解决一个重要的特殊情况:将松散对象集合转换为扩展。在 PostgreSQL 于 9.1 加入扩展机制之前,许多人编写的扩展模块只是创建各种未打包的对象。对于包含这些对象的现有数据库,如何将它们转换为正确打包的扩展?删除它们再执行普通的 CREATE EXTENSION 是一种办法,但如果对象具有依赖关系(例如,某些表列使用扩展创建的数据类型),就不适合这样做。解决方法是先创建一个空扩展,再使用 ALTER EXTENSION ADD 将每个现有对象附加到扩展中,最后创建当前扩展版本中存在、而未打包版本中没有的新对象。CREATE EXTENSION 通过 FROM old_version 选项支持这种情况:它不运行目标版本的普通安装脚本,而是运行名为 extension--old_version--target_version.sql 的更新脚本。用作 old_version 的虚拟版本名由扩展作者选择,不过通常约定使用 unpackaged。如果需要将多个先前版本更新为扩展形式,应使用不同的虚拟版本名来标识它们。 + + + ALTER EXTENSION 能够执行一系列更新脚本文件来完成 + 请求的更新。例如,如果只有 foo--1.0--1.1.sql 和 + foo--1.1--2.0.sql 可用,那么当当前安装的是 + 1.0,而请求更新到 2.0 时, + ALTER EXTENSION 就会按顺序应用这两个脚本。 + + + + PostgreSQL 并不对版本名称的属性作任何假设: + 例如,它并不知道 1.1 是否跟在 + 1.0 之后。它只是匹配可用的版本名,并选择需要应用 + 更新脚本最少的那条路径。(实际上,版本名可以是任何不包含 + --,且不以 - 开头或结尾的字 + 符串。) + + + + 有时提供降级脚本也是有用的,例如 + foo--1.1--1.0.sql 可用于回退与 + 1.1 版相关的修改。如果你这样做,要注意某个降级脚 + 本可能会因为产生更短的路径而意外被采用。危险情况是,存在一个跨越多个 + 版本的快速路径更新脚本,同时又有一个可降级到该快速路 + 径起点的脚本。这时,先降级再走快速路径,可能比按版本逐步前进所需的步 + 数更少。如果降级脚本删除了任何不可替代的对象,就会产生不希望看到的结 + 果。 + + + + 要检查是否存在意外的更新路径,可使用以下命令: + +SELECT * FROM pg_extension_update_paths('extension_name'); + + 它会显示指定扩展的每一对不同已知版本名,以及从源版本到目标版本将采 + 用的更新路径序列;如果没有可用的更新路径,则显示 NULL。 + 路径会以文本形式显示,并使用 -- 作为分隔符。如果你 + 更喜欢数组形式,可以使用 + regexp_split_to_array(path,'--')。 + + + + + 扩展的安全注意事项 + + + 广泛分发的扩展应尽量少假定其所处数据库的环境。因此,以一种不会被基于 + 搜索路径的攻击破坏的安全风格来编写扩展所提供的函数,是合适的做法。 + + + superuser 属性设为真的扩展,还必须考虑其安装和更新脚本所执行操作的安全风险。恶意用户可以创建木马对象,破坏以后对编写不慎的扩展脚本的执行,从而获得超级用户权限;这并非特别困难。 + + + 关于如何安全地编写函数,建议见下面的 + ;关于如何安全地编 + 写安装脚本,建议见 + 。 + + + + 扩展函数的安全注意事项 + + + 扩展提供的 SQL 语言函数和 PL 语言函数在执行时会面临基于搜索路径的攻 + 击风险,因为这些函数是在执行时而不是创建时解析的。 + + + + CREATE + FUNCTION 参考页中包含了关于如何安全编写 + SECURITY DEFINER 函数的建议。对于扩展提供的任意函 + 数,都最好应用这些技术,因为该函数可能会被高权限用户调用。 + + + + + 如果你无法把 search_path 设置为只包含安全模式,那就 + 应假定每个非限定名都可能被解析为恶意用户定义的对象。要警惕那些会隐 + 式依赖 search_path 的构造;例如, + IN 和 + CASE expression WHEN + 总是通过搜索路径来选择操作符。应改用 + OPERATOR(schema.=) ANY + 和 CASE WHEN expression。 + + + + 通用扩展通常不应假定它被安装到了安全模式中,这意味着即使对其自身对象 + 使用了模式限定引用,也并非完全没有风险。例如,如果扩展定义了函数 + myschema.myfunc(bigint),那么像 + myschema.myfunc(42) 这样的调用,就可能被恶意函数 + myschema.myfunc(integer) 截获。要注意函数和操作符 + 所传参数的数据类型必须与声明的参数类型精确匹配,必要时请使用显式类型转 + 换。 + + + + + 扩展脚本的安全注意事项 + + + 编写扩展安装脚本或更新脚本时,应防范脚本执行过程中发生基于搜索路径的 + 攻击。如果脚本中的对象引用可能被解析为脚本作者原本无意引用的其他对 + 象,那么破坏既可能立刻发生,也可能在稍后使用这个定义错误的扩展对象时 + 才发生。 + + + + 像 CREATE FUNCTION 和 + CREATE OPERATOR CLASS 这样的 DDL 命令通常是安全 + 的,但要警惕任何把通用表达式作为组成部分的命令。例如, + CREATE VIEW 就需要仔细审查, + CREATE FUNCTION 中的 + DEFAULT 表达式也是如此。 + + + + 有时扩展脚本可能需要执行通用 SQL,例如为完成 DDL 无法做到的系统目录 + 调整。执行这类命令时,要确保使用安全的 + search_path不要相信 + CREATE/ALTER EXTENSION 提供的路径一定是安全的。 + 最佳做法是临时把 search_path 设为 + 'pg_catalog, + pg_temp',并在需要时显式插入对扩展安装 + 模式的引用。(这种做法对创建视图也可能有帮助。)在 + PostgreSQL 源代码发行包的 + contrib 模块中可以找到示例。 + + + 跨扩展引用极难做到完全安全,部分原因是无法确定另一个扩展位于哪个模式中。如果两个扩展安装在同一模式,风险会降低,因为此时恶意对象无法在安装时的 search_path 中排在被引用扩展之前。不过,目前没有机制强制这一点。 + + + 不要使用CREATE OR REPLACE + FUNCTION,除非是在某个更新脚本中必须修改已知为扩展成员的函数定义时。(其他带有OR REPLACE选项的命令同理。)不必要地使用OR REPLACE不仅有意外覆盖他人函数的风险,还会带来安全隐患:被覆盖的函数仍归其原来的所有者所有,而该所有者可以修改它。 + + + + + + 扩展示例 + + + 下面给出一个纯 SQL 扩展的完整示例:一个双元素复合 + 类型,它可以在两个槽位中存储任意类型的值,这两个槽位名为 + kv。非文本值会自动强制转换为文本后 + 再存储。 + + + 脚本文件pair--1.0.sql如下所示: (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair); + +-- "SET search_path" is easy to get right, but qualified names perform better. +CREATE FUNCTION lower(pair) +RETURNS pair LANGUAGE SQL +AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;' +SET search_path = pg_temp; + +CREATE FUNCTION pair_concat(pair, pair) +RETURNS pair LANGUAGE SQL +AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k, + $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;'; +]]> + + + + 控制文件pair.control如下所示: +# pair extension +comment = 'A key/value pair data type' +default_version = '1.0' +# cannot be relocatable because of use of @extschema@ +relocatable = false + + + + + 虽然你几乎不需要专门写一个 makefile 来把这两个文件安装到正确的目录 + 中,但你也可以使用如下内容的 Makefile: + + +EXTENSION = pair +DATA = pair--1.0.sql + +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + + + 这个 makefile 依赖于 PGXS,其说明见 + 。执行 make install + 命令会把控制文件和脚本文件安装到 pg_config + 报告的正确目录中。 + + + 安装文件后,使用 命令将这些对象装入某个具体数据库。 + + + + + 扩展构建基础设施 + + + pgxs + + + + 如果你打算分发自己的 PostgreSQL 扩展模块, + 那么为它们搭建一个可移植的构建系统可能相当困难。因此, + PostgreSQL 安装提供了一套称为 + PGXS 的扩展构建基础设施,使简单的扩展模块可以针对 + 已安装好的服务器直接构建。PGXS 主要面向包含 C 代码 + 的扩展,不过它也能用于纯 SQL 扩展。注意, + PGXS 并不打算成为一个可以用来构建任意与 + PostgreSQL 交互软件的通用构建系统框架;它 + 只是把简单服务器扩展模块的常见构建规则自动化。对于更复杂的软件包,你 + 可能还是需要自己编写构建系统。 + + + 要使用PGXS基础设施构建扩展,必须编写一个简单的 makefile。在其中需要设置一些变量,并包含全局PGXSmakefile。下面的示例构建一个扩展模块,名为isbn_issn,由包含一些 C 代码的共享库、扩展控制文件、SQL 脚本以及文档文本文件组成: +MODULES = isbn_issn +EXTENSION = isbn_issn +DATA = isbn_issn--1.0.sql +DOCS = README.isbn_issn + +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +最后三行应始终相同。在文件的前面部分,可以给变量赋值或添加自定义的make规则。 + + + 设定下列三个变量中的一个,以指定要构建的内容: + + + + MODULES + + + 要从同名源文件构建的共享库对象列表(列表中不要包含库后缀) + + + + + + MODULE_big + + + 要从多个源文件构建的共享库(在 OBJS 中列出目标 + 文件) + + + + + + PROGRAM + + + 要构建的可执行程序(在 OBJS 中列出目标文件) + + + + + + 还可以设置以下变量: + + + + EXTENSION + + + 扩展名称;对于每个名称,你都必须提供一个 + extension.control 文 + 件,它将安装到 + prefix/share/extension + + + + + + MODULEDIR + + + prefix/share 下的子目录, + 用于安装 DATA 和 DOCS 文件(若未设置,则在设置了 + EXTENSION 时默认为 + extension,否则默认为 + contrib) + + + + + + DATA + + + 要安装到 prefix/share/$MODULEDIR + 的任意文件 + + + + + + DATA_built + + + 要安装到 prefix/share/$MODULEDIR + 的任意文件,但它们需要先被构建 + + + + + + DATA_TSEARCH + + + 要安装到 + prefix/share/tsearch_data + 下的任意文件 + + + + + + DOCS + + + 要安装到 + prefix/doc/$MODULEDIR + 下的任意文件 + + + + + + SCRIPTS + + + 要安装到 prefix/bin + 的脚本文件(不是二进制文件) + + + + + + SCRIPTS_built + + + 要安装到 prefix/bin + 的脚本文件(不是二进制文件),但它们需要先被构建 + + + + + + REGRESS + + + 回归测试用例列表(不带后缀),详见下文 + + + + + + REGRESS_OPTS + + + 传递给 pg_regress 的额外开关 + + + + + + + EXTRA_CLEAN + + + 在 make clean 中要额外删除的文件 + + + + + + PG_CPPFLAGS + + + 将被添加到 CPPFLAGS 前面 + + + + + + PG_CFLAGS + + + 将被添加到 CFLAGS 后面 + + + + + + PG_CXXFLAGS + + + 将被添加到 CXXFLAGS 后面 + + + + + + PG_LDFLAGS + + + 将被添加到 LDFLAGS 前面 + + + + + + PG_LIBS + + + 将被加入 PROGRAM 的链接命令行 + + + + + + SHLIB_LINK + + + 将被加入 MODULE_big 的链接命令行 + + + + + + PG_CONFIG + + + 要针对其进行构建的 PostgreSQL 安装所对应 + 的 pg_config 程序路径(通常只写 + pg_config,表示使用你 PATH + 中找到的第一个) + + + + + + + + 把这个 makefile 命名为 Makefile,并放在保存扩展的目录中。 + 然后你就可以执行 make 进行编译,再执行 + make install 安装你的模块。默认情况下,该扩展会针 + 对你 PATH 中找到的第一个 + pg_config 所对应的 + PostgreSQL 安装进行编译和安装。你也可以使 + 用不同的安装,只需让 PG_CONFIG 指向它的 + pg_config 程序,无论是在 makefile 中设置,还是在 + make 命令行上设置都可以。 + + + + 如果你想保持构建目录与源代码目录分离,也可以在扩展源代码树之外的目录 + 中运行 make。这一过程也称为 + VPATHVPATH + 构建。做法如下: + +mkdir build_dir +cd build_dir +make -f /path/to/extension/source/tree/Makefile +make -f /path/to/extension/source/tree/Makefile install + + + + + 另外,你也可以像核心代码那样为 VPATH 构建准备一个目录。其中一种方法是 + 使用核心脚本 config/prep_buildtree。准备好之后, + 就可以像下面这样通过设置 make 变量 + VPATH 来构建: + +make VPATH=/path/to/extension/source/tree +make VPATH=/path/to/extension/source/tree install + + 这种方式适用于更多种目录布局。 + + + + 在 REGRESS 变量中列出的脚本用于对模块做回归测试,可 + 在执行完 make install 之后,通过 + make installcheck 来运行。要让它工作,你必须有一个 + 正在运行的 PostgreSQL 服务器。列在 + REGRESS 中的脚本文件必须位于扩展目录下名为 + sql/ 的子目录中。这些文件必须具有 + .sql 扩展名,而该扩展名不能出现在 makefile 的 + REGRESS 列表中。对于每个测试,还应在名为 + expected/ 的子目录中有一个包含期望输出的文件,其主 + 干名相同,扩展名为 .out。 + make installcheck 会用 psql + 执行每个测试脚本,并把得到的输出与对应的期望文件比较。任何差异都会以 + diff -c 格式写入 + regression.diffs 文件。注意,如果尝试运行一个缺少 + 期望文件的测试,将被报告为 trouble,所以请确保所有期望 + 文件都已准备好。 + + + + 创建期望文件最简单的方法是先建立空文件,然后运行一次测试(当然这会报告差异)。检查 results/ 目录中的实际结果文件;如果它们与你对测试的预期一致,就把它们复制到 expected/ 中。 + + + + + diff --git a/zh/9.6/external-projects.sgml b/zh/9.6/external-projects.sgml new file mode 100644 index 00000000..d2f2c648 --- /dev/null +++ b/zh/9.6/external-projects.sgml @@ -0,0 +1,228 @@ + + + + 外部项目 + + + PostgreSQL 是一个复杂的软件项目,对其进行管理并非易事。我们发现,许多对 PostgreSQL 的增强若脱离核心项目单独开发,往往能更高效地完成。 + + + + 客户端接口 + + + 接口 + 外部维护 + + + 基础PostgreSQL发行版中只包含两个客户端接口: + + + 之所以包含 libpq,是因为它是主要的 C 语言接口,而且许多其他客户端接口都构建在它之上。 + + + + + + 之所以包含 ECPG,是因为它依赖服务器端 SQL 语法,因此对 PostgreSQL 自身的变更非常敏感。 + + + 其他所有语言接口都属于外部项目,并单独发布。列出了其中一些项目。请注意,其中有些软件包可能并非按与PostgreSQL相同的许可发布。有关各语言接口的更多信息,包括许可条款,请参阅其网站和文档。 + + + 由外部维护的客户端接口 + + + + + 名称 + 语言 + 注释 + 网站 + + + + + + DBD::Pg + Perl + Perl DBI 驱动 + + + + + JDBC + Java + 第 4 类 JDBC 驱动 + + + + + libpqxx + C++ + C++ 接口 + + + + + node-postgres + JavaScript + Node.js 驱动 + + + + + Npgsql + .NET + .NET 数据提供程序 + + + + + pgtcl + Tcl + + + + + + pgtclng + Tcl + + + + + + pq + Go + 用于 Go 的 database/sql 的纯 Go 驱动 + + + + + psqlODBC + ODBC + ODBC 驱动 + + + + + psycopg + Python + 符合 DB API 2.0 + + + + +
+
+ + + 管理工具 + + + 管理工具 + 外部维护 + + + + 有若干管理工具可用于 PostgreSQL。其中最流行的是 pgAdmin,另外也有若干商业可用的工具。 + + + + + 过程语言 + + + 过程语言 + 外部维护 + + + + 基础 PostgreSQL 发行版中包含若干过程语言:PL/pgSQL、PL/Tcl、 + PL/Perl 以及 PL/Python。 + + + 此外,还有一些过程语言是在核心PostgreSQL发行版之外开发和维护的。列出了其中一些软件包。请注意,其中有些项目可能并非按与PostgreSQL相同的许可发布。有关各过程语言的更多信息,包括许可信息,请参阅其网站和文档。 + + + 由外部维护的过程语言 + + + + + 名称 + 语言 + 网站 + + + + + + PL/Java + Java + + + + + PL/PHP + PHP + + + + + PL/Py + Python + + + + + PL/R + R + + + + + PL/Ruby + Ruby + + + + + PL/Scheme + Scheme + + + + + PL/sh + Unix shell + + + + +
+
+ + + 扩展 + + + 扩展 + 外部维护 + + + + PostgreSQL 的设计目标之一,就是使其易于扩展。 + 因此,载入数据库的扩展可以像内置特性一样工作。随源代码一同提供的 + contrib/ 目录中包含若干扩展,它们在 + 中有所描述。其他扩展则是独立开发的,例如 + PostGIS。 + 甚至 PostgreSQL 的复制解决方案也可以在外部开发。 + 例如,Slony-I 就是一个流行的主库/备库复制解决方案,它是独立于核心项目开发的。 + + +
diff --git a/zh/9.6/fdwhandler.sgml b/zh/9.6/fdwhandler.sgml new file mode 100644 index 00000000..41c36126 --- /dev/null +++ b/zh/9.6/fdwhandler.sgml @@ -0,0 +1,812 @@ + + + + 编写外部数据包装器 + + + foreign data wrapper + handler for + + + + 所有在一个外部表上的操作都通过它的外部数据包装器来处理,外部数据包装器由一组被核心服务器调用的函数组成。外部数据包装器负责从远程数据源取得数据并把它返回给PostgreSQL执行器。如果要支持更新外部表,包装器也需要处理更新。本章将介绍如何编写一个新的外部数据包装器。 + + + + 标准发行版中包含的外部数据包装器,是编写你自己的外部数据包装器时很好的参考。请查看源码树中的contrib子目录。参考页中也有一些有用的细节。 + + + + + SQL 标准规定了一个用于编写外部数据包装器的接口。但是,PostgreSQL 没有实现该 API,因为要让 PostgreSQL 适配它需要大量工作,而且该标准 API 也没有得到广泛采用。 + + + + + 外部数据包装器函数 + + + FDW 作者需要实现一个处理器函数,并且可以选择实现一个验证器函数。这两个函数都必须使用 C 之类的编译型语言编写,并采用版本 1 接口。关于 C 语言调用约定和动态加载的细节,请参阅。 + + + + 处理器函数只是返回一个结构体,其中包含供规划器、执行器和各种维护命令调用的回调函数指针。编写 FDW 的大部分工作都在于实现这些回调函数。处理器函数必须在PostgreSQL中注册为不带参数,并返回特殊的伪类型fdw_handler。这些回调函数都是普通的 C 函数,在 SQL 层既不可见也不可调用。回调函数见。 + + + 验证器函数负责验证 CREATEALTER 命令中指定的选项,这些命令可以针对其外部数据包装器,也可以针对使用该包装器的外部服务器、用户映射和外部表。注册验证器函数时,必须声明它接受两个参数:一个 text 数组,包含待验证的选项;以及一个 OID,表示选项所属对象的类型(使用存放该对象的系统目录的 OID,即 ForeignDataWrapperRelationIdForeignServerRelationIdUserMappingRelationIdForeignTableRelationId)。如果未提供验证器函数,创建或修改对象时就不会检查选项。 + + + + + 外部数据包装器回调例程 + + + FDW 处理器函数返回一个通过 palloc 分配的FdwRoutine结构体,其中包含下文描述的回调函数指针。与扫描相关的函数是必需的,其余则是可选的。 + + + + FdwRoutine结构体类型声明在src/include/foreign/fdwapi.h中,其中还有更多细节。 + + + + 扫描外部表的 FDW 例程 + + + +void +GetForeignRelSize (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid); +获取外部表的关系大小估计值。在开始规划扫描外部表的查询时,会调用此函数。root 是规划器中关于查询的全局信息;baserel 是规划器中关于此表的信息;foreigntableid 是该外部表的 pg_class OID。(foreigntableid 可以从规划器的数据结构中取得,但为了方便,这里显式传入。) + + 此函数应更新 baserel->rows,使其表示考虑限制条件过滤后,表扫描预计返回的行数。baserel->rows 的初始值只是一个固定的默认估计,应尽可能替换。如果能更准确地估计结果行的平均宽度,该函数也可以更新 baserel->width。(初始值基于列的数据类型,以及上次 ANALYZE 测得的列平均宽度。)此外,如果能更准确地估计外部表的总行数,该函数可以更新 baserel->tuples。(初始值来自 pg_class.reltuples,表示上次 ANALYZE 所见的总行数。) + + + 更多信息请见。 + + + + +void +GetForeignPaths (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid); +为外部表扫描创建可能的访问路径。此函数在查询规划期间调用,参数与先前已经调用过的 GetForeignRelSize 相同。 + + + 这个函数必须为外部表上的扫描生成至少一个访问路径(ForeignPath 节点),并且必须调用add_path把每一个这样的路径加入到baserel->pathlist中。推荐使用create_foreignscan_path来构造ForeignPath节点。该函数可以生成多个访问路径,例如一个具有合法pathkeys的路径可以表示预排序的结果。每个访问路径都必须包含代价估计,并且可以包含用于标识预期扫描方法的任意 FDW 私有信息。 + + + + 更多信息请见。 + + + + +ForeignScan * +GetForeignPlan (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *best_path, + List *tlist, + List *scan_clauses, + Plan *outer_plan); +根据选中的外部访问路径创建一个 ForeignScan 计划节点。此函数在查询规划结束时调用。参数除了与 GetForeignRelSize 相同的参数外,还包括选中的 ForeignPath(先前由 GetForeignPathsGetForeignJoinPathsGetForeignUpperPaths 生成)、计划节点应输出的目标列表、计划节点应实施的限制子句,以及外层子计划,其所属节点为 ForeignScan;此外层子计划用于以下回调执行的复查:RecheckForeignScan。(如果路径对应连接而非基础关系,foreigntableidInvalidOid。) + + + + 这个函数必须创建并返回一个ForeignScan计划节点,推荐使用make_foreignscan来构造该ForeignScan节点。 + + + + 更多信息请见。 + + + + +void +BeginForeignScan (ForeignScanState *node, + int eflags); +开始执行外部扫描。执行器启动时会调用此函数。它应完成扫描开始前所需的初始化,但不应开始实际扫描(应等到首次调用 IterateForeignScan 时才开始)。ForeignScanState 节点已经创建,但其 fdw_state 字段仍为 NULL。可以通过 ForeignScanState 节点取得待扫描表的信息(特别是底层的 ForeignScan 计划节点,其中的 FDW 私有信息来自 GetForeignPlan)。 + eflags 包含描述执行器针对该计划节点的运行模式的标志位。 + + + 注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真时,这个函数不应执行任何外部可见的动作;它只应做使节点状态对ExplainForeignScanEndForeignScan有效所需的最少工作。 + + + + +TupleTableSlot * +IterateForeignScan (ForeignScanState *node); +从外部数据源取得一行,并放入元组表槽中返回(应使用节点的 ScanTupleSlot)。如果没有更多行,则返回 NULL。元组表槽机制允许返回物理元组或虚拟元组;从性能角度看,大多数情况下后者更合适。注意,此函数在短期内存上下文中调用,该上下文会在各次调用之间重置。如果需要更长久的存储,请在 BeginForeignScan 中创建内存上下文,或者使用 es_query_cxt,它属于节点的 EState。 + + + + 如果提供了fdw_scan_tlist目标列表,则返回的行必须与之匹配;否则,它们必须匹配被扫描外部表的行类型。如果选择优化掉不需要取回的列,则应当在这些列的位置上填入空值,或者生成一个省略这些列的fdw_scan_tlist列表。 + + + + 注意PostgreSQL的执行器并不在乎被返回的行是否违背了定义在该外部表上的任何约束 — 但是规划器会在乎这一点,并且如果在外部表中有可见行不满足一个约束,规划器可能会错误地优化查询。如果当用户已经声明一个约束应该为真时它却被违背,最合适的处理可能是产生一个错误(就像在数据类型失配的情况下所作的那样)。 + + + + +void +ReScanForeignScan (ForeignScanState *node); +从头重新扫描。注意,扫描所依赖的参数值可能已经改变,因此新扫描不一定返回完全相同的行。 + + + +void +EndForeignScan (ForeignScanState *node); +结束扫描并释放资源。通常无需专门释放 palloc 分配的内存,但应清理打开的文件、到远程服务器的连接等资源。 + + + + + 扫描外部连接的 FDW 例程 + + + 如果一个 FDW 支持远程执行外部表连接(而不是先取回两个表的数据再在本地执行连接),它应当提供这个回调函数: + + + + +void +GetForeignJoinPaths (PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); +为属于同一外部服务器的两个或多个外部表的连接创建可能的访问路径。此可选函数在查询规划期间调用。与 GetForeignPaths 一样,此函数应生成 ForeignPath 路径,针对给定的 joinrel,并调用 add_path,将这些路径加入该连接的候选路径集合。但与 GetForeignPaths 不同,此函数不必保证至少创建一条路径,因为始终可以采用本地连接的路径。 + + + 注意为相同的连接关系将会重复地调用这个函数用来生成内外关系的不同组合。FDW 需要负责最小化其中重复的工作。 + + + + 如果某条 ForeignPath 路径被选中用于该连接,它就表示整个连接过程;为组成该连接的各表及其子连接生成的路径将不会再使用。之后对该连接路径的处理,和处理扫描单个外部表的路径大体相同。一个区别是,所得 ForeignScan 计划节点的 scanrelid 应设为零,因为它不代表某个单一关系;相反,ForeignScan 节点的 fs_relids 字段表示被连接的关系集合。(后者由核心规划器代码自动设置,FDW 无需填充。)另一个区别是,由于远程连接的列列表无法从系统目录中找到,FDW 必须用适当的 TargetEntry 节点列表填充 fdw_scan_tlist,表示它在运行时会在返回的元组中提供哪些列。 + + + + 更多信息请见。 + + + + + 规划扫描或连接之后处理的 FDW 例程 + + + 如果一个 FDW 支持执行远程的扫描/连接后处理,例如远程聚合,那么它应该提供这个回调函数: + + + + +void +GetForeignUpperPaths (PlannerInfo *root, + UpperRelationKind stage, + RelOptInfo *input_rel, + RelOptInfo *output_rel); +创建可能的访问路径,用于上层关系处理;这是规划器对扫描或连接之后的所有查询处理步骤的称呼,包括聚合、窗口函数、排序和表更新。此可选函数在查询规划期间调用。目前,只有查询涉及的所有基础关系都属于同一个 FDW 时,才会调用它。对于 FDW 能在远程执行的扫描或连接之后的处理,此函数应生成 ForeignPath 路径,并调用 add_path,将这些路径加入指定的上层关系。与 GetForeignJoinPaths 一样,此函数不必保证成功创建任何路径,因为始终可以采用本地处理的路径。 + + stage 参数标识当前考虑的是扫描或连接之后的哪个步骤。output_rel 是应接收该步骤计算路径的上层关系,input_rel 是表示该步骤输入的关系。(注意,加入 output_relForeignPath 路径通常不会直接依赖 input_rel 的路径,因为这些处理应在外部完成。不过,检查为前一处理步骤生成的路径,有助于避免重复的规划工作。) + + + 更多信息请见。 + + + + + 更新外部表的 FDW 例程 + + + 如果一个 FDW 支持可写外部表,那么应根据该 FDW 的需要和能力提供以下部分或全部回调函数: + + + + +void +AddForeignUpdateTargets (Query *parsetree, + RangeTblEntry *target_rte, + Relation target_relation); + + + UPDATEDELETE 操作作用于先前由表扫描函数取得的行。FDW 可能需要行 ID 或主键列值等额外信息,才能准确识别要更新或删除的行。为此,该函数可以添加额外的隐藏目标列,也称为 junk 目标列,并加入要从外部表取得的列列表,适用于 UPDATEDELETE。 + + + 为此,应向 parsetree->targetList 添加 TargetEntry 项,其中包含要额外取得的值的表达式。每个这样的项都必须标记为 resjunk = true,且必须有不同的 resname,以便在执行时识别。避免使用与 ctidNwholerowwholerowN 匹配的名称,因为核心系统可能生成这些名称的 junk 列。如果额外表达式比简单的 Var 更复杂,在加入目标列表之前,必须先用 eval_const_expressions 处理。 + + 虽然此函数在规划期间调用,但提供的信息与其他规划例程得到的信息略有不同。parsetreeUPDATEDELETE 命令的语法解析树,而 target_rtetarget_relation 描述目标外部表。 + + + 如果AddForeignUpdateTargets指针被设置为NULL,则不会添加额外的目标表达式。(这会使DELETE操作无法实现,不过如果 FDW 依赖一个不会变化的主键来标识行,UPDATE仍可能可行。) + + + + +List * +PlanForeignModify (PlannerInfo *root, + ModifyTable *plan, + Index resultRelation, + int subplan_index); +执行外部表插入、更新或删除所需的额外规划操作。此函数生成 FDW 私有信息,这些信息会附加到负责更新操作的 ModifyTable 计划节点上。私有信息必须采用 List 的形式,并传递给 BeginForeignModify,供执行阶段使用。 + + root 是规划器中关于查询的全局信息。planModifyTable 计划节点,除 fdwPrivLists 字段外已经完整。resultRelation 用范围表索引标识目标外部表。subplan_index 标识这是 ModifyTable 计划节点的哪个目标,从零开始计数;如果需要索引 plan->plansplan 节点的其他子结构,可使用此值。 + + + 更多信息请见。 + + + + 如果PlanForeignModify指针被设置为NULL,就不会执行额外的规划期动作,传递给BeginForeignModifyfdw_private 列表将为 NIL。 + + + + +void +BeginForeignModify (ModifyTableState *mtstate, + ResultRelInfo *rinfo, + List *fdw_private, + int subplan_index, + int eflags); +开始执行外部表修改操作。执行器启动时会调用此例程,它应完成实际修改表之前所需的初始化。随后,会针对每个待插入、更新或删除的元组,分别调用 ExecForeignInsertExecForeignUpdateExecForeignDelete + + + mtstate 是正在执行的 ModifyTable 计划节点的整体状态;可通过该结构体访问计划和执行状态的全局数据。rinfo 是描述目标外部表的 ResultRelInfo 结构体。(ResultRelInfori_FdwState 字段可供 FDW 存储本次操作所需的任意私有状态。)fdw_private 包含由PlanForeignModify生成的私有数据(如果有的话)。subplan_index 标识这是 ModifyTable 计划节点的哪个目标。eflags 包含描述执行器对该计划节点操作模式的标志位。 + + + + 注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真时,这个函数不应执行任何外部可见的动作;它只需做最少的工作,使节点状态对ExplainForeignModifyEndForeignModify有效。 + + + + 如果BeginForeignModify指针被设置为NULL,在执行器启动期间将不会采取任何动作。 + + + + +TupleTableSlot * +ExecForeignInsert (EState *estate, + ResultRelInfo *rinfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); +向外部表插入一个元组。estate 是查询的全局执行状态。rinfo 是描述目标外部表的 ResultRelInfo 结构。slot 包含待插入的元组,与外部表的行类型定义一致。planSlot 包含由 ModifyTable 计划节点的子计划生成的元组;它与 slot 的不同之处在于可能包含额外的 junk 列。(planSlot 对于 INSERT 通常没有太大用处,但为了完整性仍会提供。) + + + 返回值可以是一个包含实际被插入数据的槽(例如,触发器动作可能使其不同于所提供的数据),或者为 NULL,表示实际上没有插入任何行(通常也是触发器导致的)。传入的slot可重用于这一目的。 + + + 只有 INSERT 查询包含 RETURNING 子句,或外部表具有 AFTER ROW 触发器时,才会使用返回槽中的数据。触发器需要所有列,但 FDW 可以根据 RETURNING 子句的内容,选择省略部分或全部列的返回,以进行优化。无论如何,都必须返回某个槽来表示成功,否则查询报告的行数会不正确。 + + + 如果ExecForeignInsert指针被设置为NULL,尝试向外部表插入将会失败并报告一个错误消息。 + + + + +TupleTableSlot * +ExecForeignUpdate (EState *estate, + ResultRelInfo *rinfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); +更新外部表中的一个元组。estate 是查询的全局执行状态。rinfo 是描述目标外部表的 ResultRelInfo 结构。slot 包含元组的新数据,与外部表的行类型定义一致。planSlot 包含由 ModifyTable 计划节点的子计划生成的元组;它与 slot 的不同之处在于可能包含额外的 junk 列。特别是,可以从此槽中取得由 AddForeignUpdateTargets 请求的所有 junk 列。 + + + 返回值可以是一个包含实际被更新数据的槽(例如,触发器动作可能导致它与提供的数据不同),或者为 NULL,表示实际上没有更新任何行(通常也是触发器导致的)。传入的slot可重用于这一目的。 + + + 只有 UPDATE 查询包含 RETURNING 子句,或外部表具有 AFTER ROW 触发器时,才会使用返回槽中的数据。触发器需要所有列,但 FDW 可以根据 RETURNING 子句的内容,选择省略部分或全部列的返回,以进行优化。无论如何,都必须返回某个槽来表示成功,否则查询报告的行数会不正确。 + + + 如果ExecForeignUpdate指针被设置为NULL,尝试更新外部表将会失败并报告一个错误消息。 + + + + +TupleTableSlot * +ExecForeignDelete (EState *estate, + ResultRelInfo *rinfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); +从外部表删除一个元组。estate 是查询的全局执行状态。rinfo 是描述目标外部表的 ResultRelInfo 结构。slot 在调用时不包含有用内容,但可以用来存放返回的元组。planSlot 包含由 ModifyTable 计划节点的子计划生成的元组;特别是,它包含所有由 AddForeignUpdateTargets 请求的 junk 列。必须使用这些 junk 列来识别待删除的元组。 + + + 返回值可以是一个包含实际被删除行的槽,也可以是 NULL,表示实际上没有删除任何行(通常是触发器导致的)。传入的slot可被用来保存待返回的元组。 + + + + 返回槽中的数据只会在以下情况下使用:DELETE 查询带有 RETURNING 子句,或者外部表具有 AFTER ROW 触发器。 + 触发器需要所有列,但 FDW 可以根据 RETURNING 子句的内容,选择优化掉部分或全部返回列。 + 不管怎样,某些槽必须被返回来指示成功,或者查询报告的行计数将会是错误的。 + + + + 如果ExecForeignDelete指针被设置为NULL,尝试从外部表中删除将会失败并报告一个错误消息。 + + + + +void +EndForeignModify (EState *estate, + ResultRelInfo *rinfo); +结束表更新并释放资源。通常无需专门释放 palloc 分配的内存,但应清理打开的文件、到远程服务器的连接等资源。 + + + 如果EndForeignModify指针被设置为NULL,在执行器关闭期间不会采取任何动作。 + + + + +int +IsForeignRelUpdatable (Relation rel); +报告指定外部表支持哪些更新操作。返回值应为规则事件编号的位掩码,用于表示外部表支持的操作,采用 CmdType 枚举;即 (1 << CMD_UPDATE) = 4 对应 UPDATE, + (1 << CMD_INSERT) = 8 对应 INSERT,以及 (1 << CMD_DELETE) = 16 对应 DELETE。 + + + + 如果IsForeignRelUpdatable指针被设置为NULL,而FDW提供了ExecForeignInsertExecForeignUpdateExecForeignDelete,则外部表分别被假定为可插入、可更新或可删除。只有在FDW支持某些表是可更新的而某些不是可更新的时候,才需要这个函数(即便如此,也允许在执行例程中抛出一个错误而不是在这个函数中检查。但是,这个函数被用来决定显示在information_schema视图中的可更新性)。 + + + 可以通过实现另一组接口,优化外部表上的某些插入、更新和删除操作。普通的插入、更新和删除接口会从远程服务器取得行,再逐行修改。在某些情况下,逐行处理是必需的,但效率可能不高。如果外部服务器无需实际取回行就能确定要修改哪些行,而且没有会影响该操作的本地结构(本地行级触发器或来自父视图的 WITH CHECK OPTION 约束),就可以安排整个操作在远程服务器上执行。下面介绍的接口可以实现这一点。 + + + +bool +PlanDirectModify (PlannerInfo *root, + ModifyTable *plan, + Index resultRelation, + int subplan_index); +判断能否安全地在远程服务器上执行直接修改。如果可以,完成所需的规划操作后返回 true。否则,返回 false。此可选函数在查询规划期间调用。如果成功,执行阶段就会改为调用 BeginDirectModify, + IterateDirectModifyEndDirectModify。否则,会使用上文介绍的表更新函数执行表修改。参数与以下函数相同:PlanForeignModify。 + + + 要在远程服务器上直接执行修改,此函数必须将目标子计划重写为一个 ForeignScan 计划节点,由它在远程服务器上执行直接修改。ForeignScanoperation 字段必须设为相应的 CmdType 枚举值:UPDATE 对应 CMD_UPDATEINSERT 对应 CMD_INSERTDELETE 对应 CMD_DELETE + + + 更多信息请见。 + + + + 如果PlanDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。 + + + + +void +BeginDirectModify (ForeignScanState *node, + int eflags); +准备在远程服务器上执行直接修改。执行器启动时会调用此函数。它应完成直接修改之前所需的初始化(实际修改应等到首次调用 IterateDirectModify 时才执行)。ForeignScanState 节点已经创建,但其 fdw_state 字段仍为 NULL。可以通过 ForeignScanState 节点取得待修改表的信息(特别是底层的 ForeignScan 计划节点,其中的 FDW 私有信息来自 PlanDirectModify)。 + eflags 包含描述执行器针对该计划节点的运行模式的标志位。 + + + 注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真时,这个函数不应执行任何外部可见的动作。它只应做使该节点状态对ExplainDirectModifyEndDirectModify有效所需的最少工作。 + + + + 如果BeginDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。 + + + + +TupleTableSlot * +IterateDirectModify (ForeignScanState *node); +INSERTUPDATEDELETE 查询没有 RETURNING 子句时,在远程服务器上直接执行修改后,返回 NULL 即可。如果查询包含该子句,则应取得一条包含 RETURNING 计算所需数据的结果,并将其放入元组表槽中返回(应使用节点的 ScanTupleSlot)。实际插入、更新或删除的数据,必须存入 es_result_relation_info->ri_projectReturning->pi_exprContext->ecxt_scantuple,该字段属于节点的 EState。如果没有更多行,返回 NULL。注意,此函数在短期内存上下文中调用,该上下文会在各次调用之间重置。如果需要更长久的存储,请在 BeginDirectModify 中创建内存上下文,或者使用 es_query_cxt,该字段属于节点的 EState。 + + + + 如果提供了fdw_scan_tlist目标列表,则被返回的行必须匹配它。否则,被返回的行必须匹配被更新的外部表的行类型。如果选择优化掉RETURNING计算不需要的列,应该在这些列的位置上插入空值,或者生成一个忽略这些列的fdw_scan_tlist列表。 + + + + 无论查询是否带有该子句,查询报告的行数都必须由 FDW 自行递增。当查询不带该子句时,在 EXPLAIN ANALYZE 情况下,FDW 还必须递增 ForeignScanState 节点上的行计数。 + + + + 如果IterateDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。 + + + + +void +EndDirectModify (ForeignScanState *node); +在远程服务器上直接修改后进行清理。通常无需专门释放 palloc 分配的内存,但应清理打开的文件、到远程服务器的连接等资源。 + + + 如果EndDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。 + + + + + + 行锁的 FDW 例程 + + + 如果一个 FDW 希望支持晚期行锁定(如中所述),它必须提供下列回调函数: + + + + +RowMarkType +GetForeignRowMarkType (RangeTblEntry *rte, + LockClauseStrength strength); +报告外部表应使用哪种行标记选项。rteRangeTblEntry 节点,对应该表;strength 描述相关 FOR UPDATE/SHARE 子句所请求的锁强度(如有)。结果必须是 RowMarkType 枚举类型的成员。 + + + 这个函数在查询规划期间会为每一个出现在UPDATEDELETE或者SELECT FOR UPDATE/SHARE查询中的外部表调用,并且该外部表不是UPDATEDELETE的目标。 + + + + 如果GetForeignRowMarkType指针被设置为NULL,将总是使用ROW_MARK_COPY选项(这意味着将不会调用RefetchForeignRow,因此也不必提供它)。 + + + + 更多信息请见。 + + + + +HeapTuple +RefetchForeignRow (EState *estate, + ExecRowMark *erm, + Datum rowid, + bool *updated); +从外部表重新取得一个元组,必要时先将其锁定。estate 是查询的全局执行状态。ermExecRowMark 结构,描述目标外部表及需要取得的行锁类型(如有)。rowid 标识要取得的元组。updated 是输出参数。 + + 此函数应返回用 palloc 分配的所取元组的副本;如果无法取得行锁,则返回 NULL。需要取得的行锁类型由 erm->markType 定义,它是先前 GetForeignRowMarkType 返回的值。(ROW_MARK_REFERENCE 表示只重新取得元组,不取得任何锁;此例程不会收到 ROW_MARK_COPY。) + + + 此外,如果取得的是一个更新过的版本而不是之前获得的同一版本,*updated应被设置为true(如果 FDW 无法确定这一点,推荐总是返回true)。 + + + 注意,默认情况下,无法取得行锁应抛出错误;只有 erm->waitPolicy 指定了 SKIP LOCKED 选项时,才适合返回 NULL + + + rowid是要被重新取得的行之前读到的ctid值。尽管rowid值被作为Datum传递,但是目前它只能被读作tid。选择该函数 API 是希望未来能允许其他的行 ID 数据类型。 + + + + 如果RefetchForeignRow指针被设置为NULL,重新取得行的尝试将会失败并伴随有一个错误消息。 + + + + 更多信息请见。 + + + + +bool +RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot); +重新检查先前返回的元组是否仍满足相关扫描和连接条件,并可能提供该元组的修改版本。对于不执行连接下推的外部数据包装器,通常将此项设为 NULL,并适当设置 fdw_recheck_quals 会更方便。不过,如果下推了外连接,仅对结果元组重新检查所有基础表的相关条件还不够,即使所需属性都在也是如此,因为不满足某个条件时,可能应将某些属性设为 NULL,而不是不返回元组。RecheckForeignScan 可以重新检查条件,仍满足时返回 true,否则返回 false;它也可以将替代元组存入所提供的槽。 + + + 要实现连接下推,外部数据包装器通常会构造一个替代性的本地连接计划,它只用于重新检查。这将成为 ForeignScan 的外层子计划。在需要执行重检查时,可以执行这个子计划,并把结果元组存入槽中。该计划不必很高效,因为不会有任何基表返回超过一行。例如,它可以把所有连接都实现为嵌套循环。函数 GetExistingLocalJoinPath 可用于在已有路径中搜索合适的本地连接路径,并将其用作替代性的本地连接计划。GetExistingLocalJoinPath 会在指定连接关系的路径列表中搜索一个非参数化路径(如果找不到这样的路径,它将返回 NULL;在这种情况下,外部数据包装器可以自行构造本地路径,或者选择不为该连接创建访问路径)。 + + + + + <command>EXPLAIN</command> 的 FDW 例程 + + + +void +ExplainForeignScan (ForeignScanState *node, + ExplainState *es); +打印额外的 EXPLAIN 输出,用于说明外部表扫描。此函数可以调用 ExplainPropertyText 及相关函数,向 EXPLAIN 输出添加字段。可以利用 es 中的标志字段确定打印内容;也可以检查 ForeignScanState 节点的状态,以提供运行时统计信息,这适用于 EXPLAIN ANALYZE 的情况。 + + + 如果ExplainForeignScan指针被设置为NULL,在EXPLAIN期间不会打印任何额外的信息。 + + + + +void +ExplainForeignModify (ModifyTableState *mtstate, + ResultRelInfo *rinfo, + List *fdw_private, + int subplan_index, + struct ExplainState *es); +打印额外的 EXPLAIN 输出,用于说明外部表更新。此函数可以调用 ExplainPropertyText 及相关函数,向 EXPLAIN 输出添加字段。可以利用 es 中的标志字段确定打印内容;也可以检查 ModifyTableState 节点的状态,以提供运行时统计信息,这适用于 EXPLAIN ANALYZE 的情况。前四个参数与以下函数相同:BeginForeignModify。 + + + + 如果ExplainForeignModify指针被设置为NULL,在EXPLAIN期间不会打印任何额外的信息。 + + + + +void +ExplainDirectModify (ForeignScanState *node, + ExplainState *es); +打印额外的 EXPLAIN 输出,用于说明远程服务器上的直接修改。此函数可以调用 ExplainPropertyText 及相关函数,向 EXPLAIN 输出添加字段。可以利用 es 中的标志字段确定打印内容;也可以检查 ForeignScanState 节点的状态,以提供运行时统计信息,这适用于 EXPLAIN ANALYZE 的情况。 + + + 如果ExplainDirectModify指针被设置为NULLEXPLAIN期间不会打印出额外的信息。 + + + + + + <command>ANALYZE</command> 的 FDW 例程 + + + +bool +AnalyzeForeignTable (Relation relation, + AcquireSampleRowsFunc *func, + BlockNumber *totalpages); +当在外部表上执行 时,会调用此函数。如果 FDW 能收集该外部表的统计信息,应返回 true,并将从表中收集样本行的函数指针放入 func 中,将按页面数估计的表大小放入 totalpages 中。否则,返回 false。 + + + + 如果FDW不支持为任何表收集统计信息,AnalyzeForeignTable指针可以被设置为NULL。 + + + 如果提供样本收集函数,其签名必须为 +int +AcquireSampleRowsFunc (Relation relation, int elevel, + HeapTuple *rows, int targrows, + double *totalrows, + double *totaldeadrows); +应从表中随机收集最多 targrows 行,并存入调用者提供的 rows 数组。必须返回实际收集的行数。此外,还应将表中存活行和死行总数的估计值,分别存入输出参数 totalrowstotaldeadrows。(如果 FDW 没有死行的概念,则将 totaldeadrows 设为零。) + + + + + <command>IMPORT FOREIGN SCHEMA</command> 的 FDW 例程 + + + +List * +ImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid); +取得外部表创建命令的列表。执行 时会调用此函数,传入该语句的语法解析树和要使用的外部服务器的 OID。它应返回一个 C 字符串列表,每个字符串必须包含一条 命令。这些字符串将由核心服务器解析并执行。 + + + 在ImportForeignSchemaStmt结构体中,remote_schema是要从其中导入这些表的远程模式的名称。list_type标识如何过滤表名:FDW_IMPORT_SCHEMA_ALL表示该远程模式中的所有表都应该被导入(这种情况下table_list为空),FDW_IMPORT_SCHEMA_LIMIT_TO表示只包括table_list中列出的表,而FDW_IMPORT_SCHEMA_EXCEPT则表示排除table_list中列出的表。options是一个用于该导入处理的选项列表。选项的含义由 FDW 决定。例如,一个 FDW 可以用一个选项来定义是否应该导入列的NOT NULL属性。这些选项不需要与那些 FDW 支持的数据库对象选项有什么关系。 + + + + FDW 可以忽略 ImportForeignSchemaStmtlocal_schema 字段,因为核心服务器会自动把该名称插入解析后的 CREATE FOREIGN TABLE 命令中。 + + + + FDW 也不必担心实现list_type以及table_list所指定的过滤,因为核心服务器将自动根据那些选项跳过为被排除的表所返回的命令。不过,起初就避免为被排除的表创建命令当然更好。函数IsImportableForeignTable()可以用来测试一个给定的外部表名是否能通过该过滤器。 + + + + 如果 FDW 不支持导入表定义,ImportForeignSchema指针可以被设置为NULL。 + + + + + + 并行执行的 FDW 例程 + + ForeignScan节点可以选择支持并行执行。一个并行的ForeignScan会在多个进程中执行,并且应当在这些协作进程中每个元组只返回一次。为做到这一点,各进程可以通过固定大小的动态共享内存块协作。并不保证这部分共享内存在每个进程中都映射到相同地址,因此不能使用指针。下面的回调通常都是可选的,但若要支持并行执行,就必须提供。 + + + + +bool +IsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte); + + 测试某个扫描是否可以在并行工作进程中执行。只有当规划器认为可以使用并行计划时才会调用这个函数;如果该扫描在并行工作进程中安全,此函数应返回真。如果远程数据源具有事务语义,通常这并不成立,除非工作进程到该数据源的连接能够以某种方式共享与领导者相同的事务环境。 + + + + 如果没有定义这个回调,则假定该扫描必须放在并行领导者中。注意,返回真并不意味着该扫描本身可以并行完成,只是说明它可以在并行工作进程中执行。因此,即便不支持并行执行,定义这个方法也可能有用。 + + + + +Size +EstimateDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt); + + 估算并行操作所需的动态共享内存的数量。这可能比实际要用的数量更大,但是绝不能更小。返回值的单位是字节。 + + + + +void +InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt, + void *coordinate); + + 初始化并行操作所需的动态共享内存;coordinate指向一块已分配的空间,其大小等于EstimateDSMForeignScan的返回值。 + + + + +void +InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc, + void *coordinate); + + 基于领导者通过InitializeDSMForeignScan建立的共享状态初始化并行工作者的本地状态。 + + + + + + + 外部数据包装器助手函数 + + + 核心服务器导出了若干辅助函数,使外部数据包装器作者能够方便地访问 FDW 相关对象的属性,例如 FDW 选项。要使用这些函数中的任意一个,需要在源码文件中包含头文件foreign/foreign.h。该头文件也定义了这些函数返回的结构体类型。 + + + + +ForeignDataWrapper * +GetForeignDataWrapper(Oid fdwid); + + + 这个函数为具有给定 OID 的外部数据包装器返回一个ForeignDataWrapper对象。该ForeignDataWrapper对象包含该 FDW 的属性(详见foreign/foreign.h)。 + + + + +ForeignServer * +GetForeignServer(Oid serverid); + + + 这个函数为一个具有给定 OID 的外部服务器返回ForeignServer对象。该ForeignServer对象包含该服务器的属性(详见foreign/foreign.h)。 + + + + +UserMapping * +GetUserMapping(Oid userid, Oid serverid); + + + 这个函数为给定角色在给定服务器上的用户映射返回UserMapping对象(如果指定用户没有映射,则返回PUBLIC的映射;如果也没有,则抛出错误)。该UserMapping对象包含该用户映射的属性(详见foreign/foreign.h)。 + + + + +ForeignTable * +GetForeignTable(Oid relid); + + + 该函数为一个具有给定 OID 的外部表返回ForeignTable对象。该ForeignTable对象包含该外部表的属性(详见foreign/foreign.h)。 + + + + +List * +GetForeignColumnOptions(Oid relid, AttrNumber attnum); + + + 这个函数为具有给定外部表 OID 和属性号的列返回 FDW 选项,形式为一个DefElem列表。如果该列没有选项则返回 NIL。 + + + + 某些对象类型除了基于OID的查找函数之外,还具有基于名称的查找函数: + + + + +ForeignDataWrapper * +GetForeignDataWrapperByName(const char *name, bool missing_ok); + + + 这个函数为一个具有给定名称的外部数据包装器返回ForeignDataWrapper对象。如果找不到该包装器,则在 missing_ok 为真时返回 NULL,否则抛出错误。 + + + + +ForeignServer * +GetForeignServerByName(const char *name, bool missing_ok); + + + 这个函数为一个具有给定名称的外部服务器返回ForeignServer对象。如果找不到该服务器,则在 missing_ok 为真时返回 NULL,否则抛出错误。 + + + + + + 外部数据包装器查询规划 + + + FDW 回调函数GetForeignRelSizeGetForeignPathsGetForeignPlanPlanForeignModifyGetForeignJoinPathsGetForeignUpperPaths以及PlanDirectModify必须适应PostgreSQL规划器的工作方式。下面给出一些关于它们必须做什么的说明。 + + + + rootbaserel 中的信息可用于减少必须从外部表获取的信息量(从而降低代价)。baserel->baserestrictinfo 特别重要,因为它包含限制条件(WHERE 子句),这些条件应当用来过滤待获取的行。(FDW 本身并不一定非要强制这些条件,因为核心执行器也可以检查它们。)baserel->reltarget->exprs 可用于确定需要取回哪些列;但要注意,它只列出必须由 ForeignScan 计划节点输出的列,而不包括在条件求值中使用但不在查询结果中输出的列。 + + + + FDW 规划函数可以利用多个私有字段来保存信息。通常,存储在 FDW 私有字段中的任何内容都应通过 palloc 分配,这样它们会在规划结束时被回收。 + + + + baserel->fdw_private 是一个 void 指针,FDW 规划函数可以用它来存储与特定外部表相关的信息。核心规划器除了在创建 RelOptInfo 节点时把它初始化为 NULL 之外,不会碰它。它非常适合把信息从GetForeignRelSize传给GetForeignPaths,以及/或者从GetForeignPaths传给GetForeignPlan,从而避免重复计算。 + + + + GetForeignPaths 可以通过把私有信息存储在 ForeignPath 节点的 fdw_private 字段中,来标识不同访问路径的含义。fdw_private 被声明为 List 指针,但实际上可以包含任何内容,因为核心规划器不会碰它。不过,最佳实践是使用一种能够被 nodeToString 导出的表示形式,以便利用后端提供的调试支持。 + + + + GetForeignPlan 可以检查所选中 ForeignPath 节点的 fdw_private 字段,并生成 fdw_exprsfdw_private 列表,放入 ForeignScan 计划节点中,以供执行期使用。这两个列表都必须采用 copyObject 能够复制的表示形式。fdw_private 列表没有其他限制,核心后端也不会以任何方式解释它。如果 fdw_exprs 列表不是 NIL,则它应包含打算在运行时执行的表达式树。这些树会经过规划器的后处理,变成完全可执行的形式。 + + + + 在GetForeignPlan中,通常可以把传入的目标列表原样复制到计划节点中。传入的 scan_clauses 列表与 baserel->baserestrictinfo 包含相同的子句,但可能会为了更高的执行效率而重新排序。在简单情况下,FDW 可以仅从 scan_clauses 列表中剥离 RestrictInfo 节点(使用extract_actual_clauses),并把所有子句都放到计划节点的条件列表中,这意味着所有子句都将在运行时由执行器检查。更复杂的 FDW 可能能够在内部检查某些子句,这种情况下可将这些子句从计划节点的条件列表中移除,从而避免执行器重复检查。 + + + + 举例来说,FDW 可能会识别出某些形如 foreign_variable = sub_expression 的限制子句,并判断它们可以利用本地计算出的 sub_expression 值在远程服务器上执行。这类子句的识别应当在GetForeignPaths期间完成,因为它会影响该路径的代价估计。路径的 fdw_private 字段很可能会包含指向已识别子句的 RestrictInfo 节点的指针。然后,GetForeignPlan 会把该子句从 scan_clauses 中移除,但会把 sub_expression 加入 fdw_exprs,以确保它被整理成可执行形式。它还很可能会把控制信息放入计划节点的 fdw_private 字段中,用以告诉执行函数在运行时该做什么。传给远程服务器的查询将会包含类似 WHERE foreign_variable = $1 这样的条件,其中参数值在运行时通过求值 fdw_exprs 表达式树获得。 + + + + 任何从计划节点条件列表中移除的子句,都必须改为加入 fdw_recheck_quals,或者由 RecheckForeignScan 重新检查,以确保在 READ COMMITTED 隔离级别下行为正确。当查询涉及的其他某个表发生并发更新时,执行器可能需要验证原先的全部条件对该元组是否仍然成立,甚至可能是在另一组参数值下验证。使用 fdw_recheck_quals 往往比在 RecheckForeignScan 中自行实现检查更容易,但当外连接已经被下推时,这种方法就不够用了,因为在那种情况下,连接元组的某些字段可能会变成 NULL,而不是让整个元组被拒绝。 + + + + 另一个可由 FDW 填充的 ForeignScan 字段是 fdw_scan_tlist,它描述 FDW 为该计划节点返回的元组。对于简单的外部表扫描,它可以设置为 NIL,表示返回的元组具有该外部表声明的行类型。非 NIL 的值必须是一个目标列表(即 TargetEntry 列表),其中包含表示返回列的 Var 和/或表达式。例如,这可用于表明 FDW 省略了某些它发现对查询并不需要的列。再比如,如果 FDW 能以低于本地计算的代价完成查询所用的表达式,也可以把这些表达式加入 fdw_scan_tlist。注意,连接计划(由GetForeignJoinPaths生成的路径创建而来)始终必须提供 fdw_scan_tlist,以描述它们将返回的列集合。 + + + + FDW 应当始终至少构造一条仅依赖于表限制子句的路径。在连接查询中,它还可能选择构造依赖连接子句的路径,例如 foreign_variable = local_variable。这类子句不会出现在 baserel->baserestrictinfo 中,而必须在关系的连接列表中查找。使用这类子句的路径被称为参数化路径。它必须使用适当的 param_info 值来标识被选中连接子句中使用到的其他关系;可使用get_baserel_parampathinfo 来计算该值。在GetForeignPlan中,连接子句中的 local_variable 部分会被加入 fdw_exprs,之后在运行时的处理方式就与普通限制子句相同。 + + + + 如果某个 FDW 支持远程连接,GetForeignJoinPaths 就应当像GetForeignPaths为基本表生成路径那样,为潜在的远程连接生成 ForeignPath。有关预期连接的信息可以用前面所述的相同方式传递给GetForeignPlan。不过,baserestrictinfo 对连接关系并不适用;相反,特定连接相关的连接子句会作为单独参数(extra->restrictlist)传给GetForeignJoinPaths。 + + + + FDW 还可能进一步支持直接执行一些高于扫描和连接层次的计划动作,例如分组或聚合。为了提供这类能力,FDW 应当生成路径并将其插入适当的上层关系中。例如,表示远程聚合的路径应当使用add_path插入到 UPPERREL_GROUP_AGG 关系中。该路径会在代价层面与本地聚合进行比较,而本地聚合通过读取外部关系的简单扫描路径来完成(注意,这样的路径也必须提供,否则在规划时会出现错误)。如果远程聚合路径胜出,而这通常会发生,它就会像往常一样通过调用GetForeignPlan被转换成计划。推荐在GetForeignUpperPaths回调函数中生成这类路径;当查询的所有基本关系都来自同一个 FDW 时,这个回调会为每一个上层关系(也就是每个扫描后/连接后处理步骤)调用一次。 + + + + PlanForeignModify 以及中描述的其他回调,都是围绕这样一个假设设计的:外部关系会以常规方式被扫描,然后单行更新将由本地的 ModifyTable 计划节点驱动。这种方法对于更新既需要读取本地表又需要读取外部表的一般情况是必需的。不过,如果某个操作可以完全由外部服务器执行,那么 FDW 就可以生成一个表示该操作的路径,并将其插入到 UPPERREL_FINAL 上层关系中,与 ModifyTable 方案竞争。这种方式也可用于实现远程 SELECT FOR UPDATE,而不是使用中描述的行锁定回调。要记住,插入到 UPPERREL_FINAL 中的路径必须负责实现该查询的全部行为。 + + + + 在规划 UPDATEDELETE 时,PlanForeignModifyPlanDirectModify 可以查找外部表的 RelOptInfo 结构体,并利用先前由扫描规划函数创建的 baserel->fdw_private 数据。不过在 INSERT 中,目标表不会被扫描,因此不存在对应的 RelOptInfoPlanForeignModify 返回的 ListForeignScan 计划节点的 fdw_private 列表有相同的限制,也就是说,它只能包含 copyObject 知道如何复制的结构体。 + + + + 带有 ON CONFLICT 子句的 INSERT 不支持显式指定冲突目标,因为远程表上的唯一约束或排他约束在本地是不可见的。这又意味着 ON CONFLICT DO UPDATE 也不受支持,因为在那里冲突目标是必需的。 + + + + + + 外部数据包装器中的行锁定 + + + 如果某个 FDW 的底层存储机制具有锁定单行以防止并发更新这些行的概念,那么通常值得让该 FDW 执行行级锁定,以尽可能接近普通 PostgreSQL 表所采用的语义。这里面需要考虑多个方面。 + + + + 一个关键决策是执行早期锁定还是晚期锁定。在早期锁定中,一行在第一次从底层存储中取回时就会被锁定;而在晚期锁定中,只有在确认它确实需要被锁定时才加锁。(之所以会有这种差异,是因为有些行可能会被本地检查的限制条件或连接条件丢弃。)早期锁定要简单得多,并且避免了与远程存储之间的额外往返,但它可能导致某些本来无需锁定的行也被锁定,从而降低并发性,甚至引发意外的死锁。此外,只有在后续还能唯一重新识别出需要加锁的那一行时,晚期锁定才是可行的。理想情况下,行标识符应能标识该行的某个特定版本,就像 PostgreSQL 的 TID 那样。 + + + + 默认情况下,PostgreSQL 在与 FDW 交互时不会考虑锁定问题,但 FDW 可以在没有核心代码显式支持的情况下执行早期锁定。中描述的 API 函数是在 PostgreSQL 9.5 中加入的,它们允许 FDW 在需要时使用晚期锁定。 + + + + 另一个需要考虑的问题是,在 READ COMMITTED 隔离级别下,PostgreSQL 可能需要针对某个目标元组的更新版本重新检查限制条件和连接条件。重新检查连接条件意味着要重新取得之前与目标元组连接过的非目标行副本。对于标准 PostgreSQL 表,这是通过在连接投影出的列列表中包含非目标表的 TID,并在需要时重新取回这些非目标行来完成的。这种方式能让连接数据集保持紧凑,但它要求重新取回元组的代价较低,而且 TID 必须能唯一标识待重新取回的行版本。因此,对于外部表,默认做法是在连接投影出的列列表中包含从外部表取回的整行副本。这不会对 FDW 提出特殊要求,但会降低归并连接和哈希连接的性能。能够满足重新取回要求的 FDW 可以选择采用前一种方式。 + + + + 对于外部表上的 UPDATEDELETE,建议目标表上的 ForeignScan 操作对其取回的行执行早期锁定,例如通过等价于 SELECT FOR UPDATE 的方式。FDW 可以在规划时通过将表的 relid 与 root->parse->resultRelation 比较,或者在执行时通过使用 ExecRelationIsTargetRelation(),来检测某张表是否是 UPDATE/DELETE 的目标。另一种可能性是在 ExecForeignUpdateExecForeignDelete 回调中执行晚期锁定,但系统并未为此提供专门支持。 + + + + 对于被 SELECT FOR UPDATE/SHARE 命令指定为需要锁定的外部表,ForeignScan 操作同样可以通过以等价于 SELECT FOR UPDATE/SHARE 的方式取回元组来执行早期锁定。若要改为执行晚期锁定,请提供中定义的回调函数。在GetForeignRowMarkType中,应根据所请求的锁强度选择 ROW_MARK_EXCLUSIVEROW_MARK_NOKEYEXCLUSIVEROW_MARK_SHAREROW_MARK_KEYSHARE 行标记选项。(无论选择这四者中的哪一个,核心代码的行为都是相同的。)在其他地方,可以在规划时使用get_plan_rowmark,或者在执行时使用ExecFindRowMark,来检测某张外部表是否被此类命令指定为需要加锁;你不仅要检查是否返回了非空的行标记结构体,还必须检查其 strength 字段不为 LCS_NONE。 + + + + 最后,对于那些出现在 UPDATEDELETESELECT FOR UPDATE/SHARE 命令中、但并未被指定为行锁定的外部表,你可以覆盖复制整行的默认做法:让 GetForeignRowMarkType 在看到锁强度 LCS_NONE 时选择 ROW_MARK_REFERENCE 选项。这样会导致 RefetchForeignRow 以该值作为 markType 被调用;它随后应在不获取任何新锁的情况下重新取回该行。(如果你实现了 GetForeignRowMarkType,但不希望重新取回未加锁的行,那么在 LCS_NONE 情况下请选择 ROW_MARK_COPY。) + + + + 更多信息请参阅 src/include/nodes/lockoptions.h,以及 src/include/nodes/plannodes.h 中关于 RowMarkTypePlanRowMark 的注释,还有 src/include/nodes/execnodes.h 中关于 ExecRowMark 的注释。 + + + + + diff --git a/zh/9.6/features.sgml b/zh/9.6/features.sgml new file mode 100644 index 00000000..157c071c --- /dev/null +++ b/zh/9.6/features.sgml @@ -0,0 +1,122 @@ + + + + SQL 符合性 + + + 本节尝试概述PostgreSQL在多大程度上符合当前的 SQL + 标准。下文并不是一份完整的符合性声明,但会在合理且对用户有用的范围内, + 尽可能详细地介绍主要主题。 + + + + SQL 标准的正式名称是 ISO/IEC 9075 Database + Language SQL。该标准会不时发布修订版,最近一次更新出现在 2011 年。 + 2011 年版称为 ISO/IEC 9075:2011,也简称 SQL:2011。 + 此前的版本依次为 SQL:2008、SQL:2003、 + SQL:1999 和 SQL-92。每个版本都会取代前一版本,因此宣称符合更早版本并无官方意义。 + PostgreSQL 的开发目标是在不违背传统特性或常识的前提下, + 尽可能符合标准的最新正式版本。SQL 标准要求的许多特性都得到了支持, + 虽然有时语法或功能会略有不同。随着时间推移,仍可期待它在符合性方面继续推进。 + + + + SQL-92为符合性定义了三个特性集:入门(Entry)、中级(Intermediate) + 和完整(Full)。多数宣称符合SQL标准的数据库管理系统, + 实际上只符合入门(Entry)级别,因为中级(Intermediate)和完整(Full)级别中的全部特性 + 要么过于庞杂,要么与既有行为相冲突。 + + + + 从SQL:1999开始,SQL 标准不再沿用SQL-92中 + 那种效果不佳且过于宽泛的三级分类,而是定义了大量独立特性。其中很大一个子集构成了 + 核心(Core)特性,也就是每个符合 SQL 标准的实现都必须提供的特性。 + 其余特性则完全是可选的。 + + 某些可选特性被组合在一起,形成,SQL 实现可以声明符合这些包,从而表明符合特定的特性组。 + + SQL:2003开始的各版标准还被拆分为若干部分,每一部分都有一个简称。请注意,这些部分的编号并不是连续的。 + ISO/IEC 9075-1 框架(SQL/Framework)SQL/Framework + ISO/IEC 9075-2 基础(SQL/Foundation)SQL/Foundation + ISO/IEC 9075-3 调用级接口(SQL/CLI)SQL/CLI + ISO/IEC 9075-4 持久化存储模块(SQL/PSM)SQL/PSM + ISO/IEC 9075-9 外部数据管理(SQL/MED)SQL/MED + ISO/IEC 9075-10 对象语言绑定(SQL/OLB)SQL/OLB + ISO/IEC 9075-11 信息与定义模式(SQL/Schemata)SQL/Schemata + ISO/IEC 9075-13 使用 Java 语言的例程和类型(SQL/JRT)SQL/JRT + ISO/IEC 9075-14 XML 相关规范(SQL/XML)SQL/XML + + + + + PostgreSQL核心部分涵盖第 1、2、9、11 和 14 部分。 + 第 3 部分由 ODBC 驱动实现,第 13 部分由 PL/Java 插件实现,但目前尚未核验这些组件的确切符合性。 + PostgreSQL目前还没有第 4 和 10 部分的实现。 + + + + PostgreSQL 支持 SQL:2011 的大多数主要特性。在达到完整 Core 符合性所要求的 179 个强制特性中, + PostgreSQL 至少符合其中 160 个。此外,还有一长串已支持的可选特性。 + 值得一提的是,在写作本文时,还没有任何数据库管理系统的现行版本宣称完全符合 SQL:2011 的核心要求。 + + + + 接下来的两节先列出PostgreSQL已支持的特性, + 再列出SQL:2011中定义但PostgreSQL尚未支持的特性。 + 这两个列表都只是近似描述:某个被列为已支持的特性,可能仍有少量细节并不完全符合标准; + 反之,一个被列为不支持的特性,其很大一部分实际上也可能已经实现。 + 关于哪些内容可用、哪些内容不可用,文档正文始终是最准确的信息来源。 + + + + + 带连字符的特性代码表示子特性。因此,如果某个特定子特性不受支持, + 则即使该主特性的其他子特性受支持,该主特性也会被列为不受支持。 + + + + + 支持的特性 + + + + + + + 标识符 + + 描述 + 注释 + + + + &features-supported; + + + + + + + + 不支持的特性 + + 下列在SQL:2011中定义的特性,在本版本的PostgreSQL中尚未实现。在少数情况下,可用等效功能替代。 + + + + + 标识符 + + 描述 + 注释 + + + + &features-unsupported; + + + + + + + diff --git a/zh/9.6/file-fdw.sgml b/zh/9.6/file-fdw.sgml new file mode 100644 index 00000000..c362a1ee --- /dev/null +++ b/zh/9.6/file-fdw.sgml @@ -0,0 +1,211 @@ + + + + file_fdw + + + file_fdw + + + + file_fdw 模块提供外部数据包装器 + file_fdw,可用于访问服务器文件系统中的数据文件。数据文件必须采用 + COPY FROM 可读取的格式;详见 。 + 当前对这类数据文件的访问为只读。 + + + + 使用该包装器创建的外部表可以具有以下选项: + + + + + + filename + + + + 指定要读取的文件。必需。相对路径以数据目录为基准。 + + + + + + format + + + + 指定文件的格式,与COPYFORMAT选项相同。 + + + + + + header + + + + 指定文件是否包含标题行,与COPYHEADER选项相同。 + + + + + + delimiter + + + + 指定文件的定界符字符,与COPYDELIMITER选项相同。 + + + + + + quote + + + + 指定文件的引用字符,与COPYQUOTE选项相同。 + + + + + + escape + + + + 指定文件的转义字符,与COPYESCAPE选项相同。 + + + + + + null + + + + 指定文件的空值串,与COPYNULL选项相同。 + + + + + + encoding + + + 指定文件的编码,与COPYENCODING选项相同。 + + + + + + 请注意,虽然COPY允许诸如 OIDS 和 HEADER 这样的选项在没有对应值的情况下指定,但外部数据包装器语法要求在所有情况下都必须提供一个值。要启用通常不带值使用的COPY选项,可以改传值 TRUE。 + + + 使用此包装器创建的外部表的列可以具有下列选项: + + + + + + force_not_null + + + + 这是一个布尔选项。如果为真,它指定该列的值不应与空值串(即文件级的 + null选项)匹配。其效果与在COPY的 + FORCE_NOT_NULL选项中列出该列相同。 + + + + + + force_null + + + + 这是一个布尔选项。如果为真,它指定该列中与空值串匹配的值即使带有引号, + 也会作为NULL返回。没有此选项时,只有未加引号且与空值串匹配的值 + 才会作为NULL返回。其效果与在COPY的 + FORCE_NULL选项中列出该列相同。 + + + + + + + file_fdw当前尚不支持COPYOIDSFORCE_QUOTE选项。 + + + 这些选项只能在外部表或其列上指定,不能在file_fdw外部数据包装器的选项中指定, + 也不能在使用该包装器的服务器或用户映射的选项中指定。 + + + 出于安全原因,更改表级选项需要超级用户权限:只有超级用户才能决定读取哪个文件。原则上可以允许非超级用户更改其他选项,但目前尚不支持。 + + + + 对于使用file_fdw的外部表,EXPLAIN会显示待读取的文件名。除非指定COSTS OFF,否则还会显示文件大小(以字节为单位)。 + + + + 为 PostgreSQL CSV 日志创建外部表 + + + file_fdw的一个明显用途,是将 PostgreSQL 活动日志作为表提供以供查询。 + 要做到这一点,首先必须将日志记录到 CSV 文件中, + 这里将该文件称为pglog.csv。首先,将file_fdw + 安装为扩展: + + + +CREATE EXTENSION file_fdw; + + + + 然后创建一个外部服务器: + + +CREATE SERVER pglog FOREIGN DATA WRAPPER file_fdw; + + + + 现在可以创建外部数据表了。使用CREATE FOREIGN TABLE命令时,需要定义表的列、CSV 文件名及其格式: +CREATE FOREIGN TABLE pglog ( + log_time timestamp(3) with time zone, + user_name text, + database_name text, + process_id integer, + connection_from text, + session_id text, + session_line_num bigint, + command_tag text, + session_start_time timestamp with time zone, + virtual_transaction_id text, + transaction_id bigint, + error_severity text, + sql_state_code text, + message text, + detail text, + hint text, + internal_query text, + internal_query_pos integer, + context text, + query text, + query_pos integer, + location text, + application_name text +) SERVER pglog +OPTIONS ( filename '/home/josh/9.1/data/pg_log/pglog.csv', format 'csv' ); + + + + + 到这里就完成了 — 现在可以直接查询日志了。当然,在生产环境中, + 还需要定义某种方法来处理日志轮转。 + + + + diff --git a/zh/9.6/filelist.sgml b/zh/9.6/filelist.sgml new file mode 100644 index 00000000..699166f8 --- /dev/null +++ b/zh/9.6/filelist.sgml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%allfiles; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/func.sgml b/zh/9.6/func.sgml new file mode 100644 index 00000000..3be85122 --- /dev/null +++ b/zh/9.6/func.sgml @@ -0,0 +1,14252 @@ + + + + 函数和操作符 + + + function + + + + operator + + + + PostgreSQL为内置数据类型提供了大量函数和操作符。 + 用户也可以按中的说明定义自己的函数和操作符。 + psql命令\df\do可分别用于列出所有可用的函数和操作符。 + + + + 如果你关心可移植性,请注意,本章描述的大多数函数和操作符,除了最简单的算术和比较操作符以及少数明确标记的函数之外,都不是SQL标准规定的。不过,其中一些扩展功能也存在于其他SQL数据库管理系统中,而且在很多情况下,各实现之间的这类功能是兼容且一致的。本章也并非详尽无遗;手册的相关章节中还有其他函数。 + + + + 逻辑操作符 + + + 操作符 + 逻辑 + + + + 布尔 + 操作符 + 操作符,逻辑 + + + + 常用的逻辑操作符有: + + + AND(操作符) + + + + OR(操作符) + + + + NOT(操作符) + + + + 合取 + + + + 析取 + + + + 否定 + + + + AND + OR + NOT + + + SQL使用三值的逻辑系统,包括真、假和null,它表示未知。观察下面的真值表: + + + + + + a + b + a AND b + a OR b + + + + + + TRUE + TRUE + TRUE + TRUE + + + + TRUE + FALSE + FALSE + TRUE + + + + TRUE + NULL + NULL + TRUE + + + + FALSE + FALSE + FALSE + FALSE + + + + FALSE + NULL + FALSE + NULL + + + + NULL + NULL + NULL + NULL + + + + + + + + + + a + NOT a + + + + + + TRUE + FALSE + + + + FALSE + TRUE + + + + NULL + NULL + + + + + + + + 操作符ANDOR是可交换的,也就是说,你可以交换左右操作数而不影响结果。 + 但请参见获取有关子表达式计算顺序的更多信息。 + + + + + 比较函数和操作符 + + + 比较 + 操作符 + + + + 常见的比较操作符都可用,如所示。 + + + + 比较操作符 + + + + + 操作符 + 描述 + + + + + + < + 小于 + + + + > + 大于 + + + + <= + 小于或等于 + + + + >= + 大于或等于 + + + + = + 等于 + + + + <>!= + 不等于 + + + +
+ + + != 操作符在解析器阶段会转换为 <>。不可能实现行为不同的 !=<> 操作符。 + + + 比较操作符适用于所有相关的数据类型。所有比较操作符都是返回 boolean 类型值的二元操作符;类似 1 < 2 < 3 的表达式是无效的(因为没有用于比较布尔值和 3< 操作符)。 + + + 如所示,也有一些比较谓词。它们的行为和操作符很像,但是具有 SQL 标准所要求的特殊语法。 + + + + 比较谓词 + + + + + 谓词 + + 描述 + + + + + + a BETWEEN x AND y + 位于两者之间 + + + + a NOT BETWEEN x AND y + 不位于两者之间 + + + + a BETWEEN SYMMETRIC x AND y + 对比较值排序后,位于两者之间 + + + + a NOT BETWEEN SYMMETRIC x AND y + 对比较值排序后,不位于两者之间 + + + + a IS DISTINCT FROM b + 不等于,将 null 视为普通值 + + + + a IS NOT DISTINCT FROM b + 等于,将 null 视为普通值 + + + + expression IS NULL + 为 null + + + + expression IS NOT NULL + 不为 null + + + + expression ISNULL + 为 null(非标准语法) + + + + expression NOTNULL + 不为 null(非标准语法) + + + + boolean_expression IS TRUE + 为真 + + + + boolean_expression IS NOT TRUE + 为假或未知 + + + + boolean_expression IS FALSE + 为假 + + + + boolean_expression IS NOT FALSE + 为真或未知 + + + + boolean_expression IS UNKNOWN + 为未知 + + + + boolean_expression IS NOT UNKNOWN + 为真或假 + + + +
+ + + + BETWEEN + BETWEEN谓词可以简化范围测试: +a BETWEEN x AND y +等价于 +a >= x AND a <= y +注意,BETWEEN将两个端点值都视为包含在范围内。NOT BETWEEN执行相反的比较: +a NOT BETWEEN x AND y +等价于 +a < x OR a > y + + + BETWEEN SYMMETRIC + + BETWEEN SYMMETRIC类似于BETWEEN,但不要求AND左侧的参数小于或等于右侧的参数。如果不是这样,这两个参数会自动交换,以确保表示的范围始终非空。 + + + + IS DISTINCT FROM + + + IS NOT DISTINCT FROM + + 当任一输入为 null 时,普通的比较操作符会得到 null(表示未知),而不是真或假。例如,7 = NULL得到 null,7 <> NULL也一样。如果这种行为不合适,可以使用IS NOT DISTINCT FROM谓词: + +a IS DISTINCT FROM b +a IS NOT DISTINCT FROM b + + 对于非 null 输入,IS DISTINCT FROM<>操作符一样。不过,如果两个输入都为 null,它会返回假。而如果只有一个输入为 null,它会返回真。类似地,IS NOT DISTINCT FROM对于非 null 输入的行为与=相同,但是当两个输入都为 null 时它返回真,并且当只有一个输入为 null 时返回假。因此,这些谓词实际上把 null 当作一种普通数据值,而不是未知。 + + + + + IS NULL + + + IS NOT NULL + + + ISNULL + + + NOTNULL + + 要检查一个值是否为 null,使用下面的谓词: + +expression IS NULL +expression IS NOT NULL + + 或者等效但非标准的谓词: + +expression ISNULL +expression NOTNULL + + 空值比较 + + + + 不要expression = NULL,因为NULL并不等于NULL。(null 值表示未知值,而我们并不知道两个未知值是否相等。) + + + + + + 有些应用可能期望表达式expression = NULLexpression求值为 null 时返回真。我们强烈建议此类应用修改为遵循 SQL 标准。但是,如果无法这样修改,那么可以使用配置变量。如果将其打开,PostgreSQL会把x = NULL子句转换成x IS NULL。 + + + + + 如果expression是行值,那么当行表达式本身为 null 或其所有字段都为 null 时,IS NULL 为真;而当行表达式本身非 null 且其所有字段都非 null 时,IS NOT NULL 为真。由于这种行为,IS NULLIS NOT NULL并不总是对行值表达式返回相反的结果;特别是,一个同时包含 null 和非 null 字段的行值表达式会对这两种测试都返回假。在某些情况下,写成row IS DISTINCT FROM NULL或者row IS NOT DISTINCT FROM NULL可能更合适,因为它们只会检查整个行值是否为 null,而不会再对行字段做额外测试。 + + + + + IS TRUE + + + IS NOT TRUE + + + IS FALSE + + + IS NOT FALSE + + + IS UNKNOWN + + + IS NOT UNKNOWN + + 布尔值也可以使用下列谓词进行测试: + +boolean_expression IS TRUE +boolean_expression IS NOT TRUE +boolean_expression IS FALSE +boolean_expression IS NOT FALSE +boolean_expression IS UNKNOWN +boolean_expression IS NOT UNKNOWN + + 这些谓词总是返回真或假,从不返回空值,即使操作数为 null 也是如此。null 输入被当作逻辑值未知。请注意,IS UNKNOWNIS NOT UNKNOWN实际上分别等同于IS NULLIS NOT NULL,只是输入表达式必须是布尔类型。 + + + + + + 如中所示,也有一些比较相关的函数可用。 + + + + 比较函数 + + + + 函数 + 描述 + 示例 + 示例结果 + + + + + num_nonnulls num_nonnulls(VARIADIC "any") + 返回非 null 参数的数量 + num_nonnulls(1, NULL, 2) + 2 + + + num_nulls num_nulls(VARIADIC "any") + 返回 null 参数的数量 + num_nulls(1, NULL, 2) + 1 + + + +
+ +
+ + + 数学函数和操作符 + + + PostgreSQL为很多类型提供了数学操作符。对于那些没有标准数学惯例的类型(如日期/时间类型),我们将在后续小节中描述实际的行为。 + + + 列出了可用的数学操作符。 + + + 数学操作符 + + + + + 操作符 + 描述 + 示例 + 结果 + + + + + + + + 加法 + 2 + 3 + 5 + + + + - + 减法 + 2 - 3 + -1 + + + + * + 乘法 + 2 * 3 + 6 + + + + / + 除法(整数除法会截断结果) + 4 / 2 + 2 + + + + % + 取模(余数) + 5 % 4 + 1 + + + + ^ + 求幂(从左向右结合) + 2.0 ^ 3.0 + 8 + + + + |/ + 平方根 + |/ 25.0 + 5 + + + + ||/ + 立方根 + ||/ 27.0 + 3 + + + + ! + 阶乘(已弃用,改用 factorial() + 5 ! + 120 + + + + !! + 以前缀操作符形式求阶乘(已弃用,改用 factorial() + !! 5 + 120 + + + + @ + 绝对值 + @ -5.0 + 5 + + + + & + 按位与 + 91 & 15 + 11 + + + + | + 按位或 + 32 | 3 + 35 + + + + # + 按位异或 + 17 # 5 + 20 + + + + ~ + 按位非 + ~1 + -2 + + + + << + 按位左移 + 1 << 4 + 16 + + + + >> + 按位右移 + 8 >> 2 + 2 + + + + +
+ + 按位操作符仅适用于整数数据类型,也可用于位串类型 bitbit varying,如所示。 + + 列出了可用的数学函数。表中的 dp 表示 double precision。许多函数提供了参数类型不同的多种形式。除非另有说明,函数的每种形式都返回与其参数相同的数据类型。处理 double precision 数据的函数大多基于主机系统的 C 库实现;因此,其精度和边界情况下的行为可能因主机系统而异。 + + + 数学函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + abs abs(x) + (与输入相同) + 绝对值 + abs(-17.4) + 17.4 + + + + cbrt cbrt(dp) + dp + 立方根 + cbrt(27.0) + 3 + + + + ceil ceil(dp or numeric) + (与输入相同) + 大于或等于参数的最小整数 + ceil(-42.8) + -42 + + + + ceiling ceiling(dp or numeric) + (与输入相同) + 大于或等于参数的最小整数(与 ceil 相同) + ceiling(-95.3) + -95 + + + + degrees degrees(dp) + dp + 将弧度转换为角度 + degrees(0.5) + 28.6478897565412 + + + + div div(y numeric, x numeric) + numeric + y/x 的整数商 + div(9,4) + 2 + + + + exp exp(dp or numeric) + (与输入相同) + 指数函数 + exp(1.0) + 2.71828182845905 + + + + factorial factorial(bigint) + numeric + 阶乘 + factorial(5) + 120 + + + + floor floor(dp or numeric) + (与输入相同) + 小于或等于参数的最大整数 + floor(-42.8) + -43 + + + + ln ln(dp or numeric) + (与输入相同) + 自然对数 + ln(2.0) + 0.693147180559945 + + + + log log(dp or numeric) + (与输入相同) + 以 10 为底的对数 + log(100.0) + 2 + + + + log(b numeric, x numeric) + numeric + b 为底的对数 + log(2.0, 64.0) + 6.0000000000 + + + + mod mod(y, x) + (与参数类型相同) + y/x 的余数 + mod(9,4) + 1 + + + + pi pi() + dp + 常数π + pi() + 3.14159265358979 + + + + power power(a dp, b dp) + dp + + ab次幂 + + power(9.0, 3.0) + 729 + + + + power(a numeric, b numeric) + numeric + + ab次幂 + + power(9.0, 3.0) + 729 + + + + radians radians(dp) + dp + 将角度转换为弧度 + radians(45.0) + 0.785398163397448 + + + + round round(dp or numeric) + (与输入相同) + 舍入到最接近的整数 + round(42.4) + 42 + + + + round(v numeric, s int) + numeric + 舍入到 s 位小数 + round(42.4382, 2) + 42.44 + + + + scale scale(numeric) + integer + 参数的小数位数(小数部分的十进制位数) + scale(8.41) + 2 + + + + sign sign(dp or numeric) + (与输入相同) + 参数的符号(-1、0、+1) + sign(-8.4) + -1 + + + + sqrt sqrt(dp or numeric) + (与输入相同) + 平方根 + sqrt(2.0) + 1.4142135623731 + + + + trunc trunc(dp or numeric) + (与输入相同) + 向零截断 + trunc(42.8) + 42 + + + + trunc(v numeric, s int) + numeric + 截断到 s 位小数 + trunc(42.4382, 2) + 42.43 + + + + width_bucket width_bucket(operand dp, b1 dp, b2 dp, count int) + int + 返回 operand 在直方图中所属的桶编号;该直方图将 b1b2 的范围划分为 count 个等宽桶。对于范围之外的输入,返回 0count+1 + width_bucket(5.35, 0.024, 10.06, 5) + 3 + + + + width_bucket(operand numeric, b1 numeric, b2 numeric, count int) + int + 返回 operand 在直方图中所属的桶编号;该直方图将 b1b2 的范围划分为 count 个等宽桶。对于范围之外的输入,返回 0count+1 + width_bucket(5.35, 0.024, 10.06, 5) + 3 + + + + width_bucket(operand anyelement, thresholds anyarray) + int + 根据列出各桶下界的数组,返回 operand 所属的桶编号;对于小于第一个下界的输入,返回 0thresholds 数组必须按升序排序,否则将产生意外结果 + width_bucket(now(), array['yesterday', 'today', 'tomorrow']::timestamptz[]) + 2 + + + +
+ + + 展示了用于产生随机数的函数。 + + + + 随机函数 + + + + + 函数 + 返回类型 + 描述 + + + + + random random() + dp + 范围 0.0 <= x < 1.0 内的随机值 + + + + setseed setseed(dp) + void + 为后续 random() 调用设置种子(值在 -1.0 与 1.0 之间,包含端点) + + + +
+ + random() 返回值的特性取决于系统实现。它不适用于加密应用;替代方案见模块。 + + 最后,列出了可用的三角函数。所有三角函数的参数和返回值都是 double precision 类型。每个三角函数都有两种变体,一种以弧度度量角,另一种以角度度量角。 + + + 三角函数 + + + + + 函数(弧度) + 函数(角度) + 描述 + + + + + + acos acos(x) + acosd acosd(x) + 反余弦 + + + + asin asin(x) + asind asind(x) + 反正弦 + + + + atan atan(x) + atand atand(x) + 反正切 + + + + atan2 atan2(y, x) + atan2d atan2d(y, x) + y/x 的反正切 + + + + cos cos(x) + cosd cosd(x) + 余弦 + + + + cot cot(x) + cotd cotd(x) + 余切 + + + + sin sin(x) + sind sind(x) + 正弦 + + + + tan tan(x) + tand tand(x) + 正切 + + + +
+ + + + + 另一种使用以角度度量的角的方法是使用早前展示的单位转换函数radians()degrees()。不过,使用基于角度的三角函数更好,因为这类方法能避免sind(30)等特殊情况下的舍入误差。 + + + +
+ + + + 字符串函数和操作符 + + 本节描述用于检查和操作字符串值的函数和操作符。这里的字符串包括 charactercharacter varyingtext 类型的值。除非另有说明,下面列出的所有函数都适用于这些类型,但使用 character 类型时,应注意自动填充空格可能带来的影响。一些函数也为位串类型提供了原生实现。 + + + SQL定义了一些字符串函数,它们使用关键字,而不是逗号来分隔参数。详情请见PostgreSQL也提供了这些函数使用正常函数调用语法的版本(见)。 + + + + PostgreSQL 8.3 之前,由于某些非字符串数据类型到 text 的隐式强制转换,这些函数也会悄然接受那些类型的值。由于这些强制转换经常产生出人意料的行为,它们已被移除。不过,字符串串接操作符(||)仍接受非字符串输入,只要至少一个输入属于字符串类型,如所示。对于其他情况,如果需要重现以前的行为,可添加到 text 的显式强制转换。 + + + + <acronym>SQL</acronym>字符串函数和操作符 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + string || + string + text + 字符串串接 字符串 串接 + 'Post' || 'greSQL' + PostgreSQL + + + + string || non-stringnon-string || string + text + 一个输入为非字符串的字符串串接 + 'Value: ' || 42 + Value: 42 + + + + bit_length bit_length(string) + int + 字符串中的位数 + bit_length('jose') + 32 + + + + char_length char_length(string)character_length(string) + int + 字符串中的字符数 字符串 长度 长度 字符串 字符串,长度 + char_length('jose') + 4 + + + + lower lower(string) + text + 将字符串转换为小写 + lower('TOM') + tom + + + + octet_length octet_length(string) + int + 字符串中的字节数 + octet_length('jose') + 4 + + + + overlay overlay(string placing string from int for int) + text + 替换子字符串 + overlay('Txxxxas' placing 'hom' from 2 for 4) + Thomas + + + + position position(substring in string) + int + 指定子字符串的位置 + position('om' in 'Thomas') + 3 + + + + substring substring(string from int for int) + text + 提取子字符串 + substring('Thomas' from 2 for 3) + hom + + + + substring(string from pattern) + text + 提取匹配 POSIX 正则表达式的子字符串。有关模式匹配的更多信息,参见 + substring('Thomas' from '...$') + mas + + + + substring(string from pattern for escape) + text + 提取匹配 SQL 正则表达式的子字符串。有关模式匹配的更多信息,参见 + substring('Thomas' from '%#"o_a#"_' for '#') + oma + + + + trim trim(leading | trailing | both characters from string) + text + string 的开头、结尾或两端(默认为 both)移除仅由 characters 中字符(默认为空格)组成的最长字符串 + trim(both 'xyz' from 'yxTomxx') + Tom + + + + trim(leading | trailing | both from string , characters ) + text + trim() 的非标准语法 + trim(both from 'yxTomxx', 'xyz') + Tom + + + + upper upper(string) + text + 将字符串转换为大写 + upper('tom') + TOM + + + +
+ + 还有其他字符串操作函数,列在 中。其中一些用于内部实现 中列出的 SQL 标准字符串函数。 + + + 其他字符串函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + ascii ascii(string) + int + 参数首字符的 ASCII 码。对于 UTF8,返回该字符的 Unicode 码点。对于其他多字节编码,参数必须是 ASCII 字符。 + ascii('x') + 120 + + + + btrim btrim(string text , characters text) + text + string 的开头和结尾移除仅由 characters 中字符(默认为空格)组成的最长字符串 + btrim('xyxtrimyyx', 'xyz') + trim + + + + chr chr(int) + text + 指定编码值对应的字符。对于 UTF8,参数被视为 Unicode 码点。对于其他多字节编码,参数必须指定一个 ASCII 字符。不允许 NULL(0)字符,因为文本数据类型无法存储这样的字节。 + chr(65) + A + + + + concat concat(str "any" [, str "any" [, ...] ]) + text + 串接所有参数的文本表示。忽略 NULL 参数。 + concat('abcde', 2, NULL, 22) + abcde222 + + + + concat_ws concat_ws(sep text, str "any" [, str "any" [, ...] ]) + text + 使用分隔符串接除第一个参数以外的所有参数。第一个参数用作分隔字符串。忽略 NULL 参数。 + concat_ws(',', 'abcde', 2, NULL, 22) + abcde,2,22 + + + + convert convert(string bytea, src_encoding name, dest_encoding name) + bytea + 将字符串转换为 dest_encoding。原编码由 src_encoding 指定。string 必须是该编码下的有效字符串。可以用 CREATE CONVERSION 定义转换。此外,也有一些预定义的转换。可用的转换见 + convert('text_in_utf8', 'UTF8', 'LATIN1') + 以 Latin-1 编码(ISO 8859-1)表示的 text_in_utf8 + + + + convert_from convert_from(string bytea, src_encoding name) + text + 将字符串转换为数据库编码。原编码由 src_encoding 指定。string 必须是该编码下的有效字符串。 + convert_from('text_in_utf8', 'UTF8') + 以当前数据库编码表示的 text_in_utf8 + + + + convert_to convert_to(string text, dest_encoding name) + bytea + 将字符串转换为 dest_encoding + convert_to('some text', 'UTF8') + 以 UTF8 编码表示的 some text + + + + decode decode(string text, format text) + bytea + string 中的文本表示解码二进制数据。format 的选项与 encode 相同。 + decode('MTIzAAE=', 'base64') + \x3132330001 + + + + encode encode(data bytea, format text) + text + 将二进制数据编码为文本表示。支持的格式为:base64hexescapeescape 将零字节和最高位为 1 的字节转换为八进制序列(\nnn),并将反斜杠双写。 + encode('123\000\001', 'base64') + MTIzAAE= + + + + format format(formatstr text [, formatarg "any" [, ...] ]) + text + 根据格式字符串格式化参数。此函数类似于 C 函数 sprintf。参见 + format('Hello %s, %1$s', 'World') + Hello World, World + + + + initcap initcap(string) + text + 将每个单词的首字母转换为大写,其余字母转换为小写。单词是由非字母数字字符分隔的字母数字字符序列。 + initcap('hi THOMAS') + Hi Thomas + + + + left left(str text, n int) + text + 返回字符串中的前 n 个字符。当 n 为负数时,返回除最后 |n| 个字符以外的所有字符。 + left('abcde', 2) + ab + + + + length length(string) + int + string 中的字符数 + length('jose') + 4 + + + + length(string bytea, encoding name ) + int + 采用给定 encodingstring 中的字符数。string 必须是该编码下的有效字符串。 + length('jose', 'UTF8') + 4 + + + + lpad lpad(string text, length int , fill text) + text + string 前面添加字符 fill(默认为空格),将其填充到长度 length。如果 string 已经长于 length,则从右侧截断。 + lpad('hi', 5, 'xy') + xyxhi + + + + ltrim ltrim(string text , characters text) + text + string 的开头移除仅由 characters 中字符(默认为空格)组成的最长字符串 + ltrim('zzzytest', 'xyz') + test + + + + md5 md5(string) + text + 计算 string 的 MD5 hash,并以十六进制返回结果 + md5('abc') + 900150983cd24fb0 d6963f7d28e17f72 + + + + parse_ident parse_ident(qualified_identifier text [, strictmode boolean DEFAULT true ] ) + text[] + qualified_identifier 拆分为标识符数组,移除各标识符的引号。默认情况下,最后一个标识符后的多余字符会被视为错误;但如果第二个参数为 false,则忽略这些多余字符。(此行为适合解析函数等对象的名称。)注意,此函数不会截断过长的标识符。如需截断,可以将结果强制转换为 name[] + parse_ident('"SomeSchema".someTable') + {SomeSchema,sometable} + + + + pg_client_encoding pg_client_encoding() + name + 当前客户端编码名称 + pg_client_encoding() + SQL_ASCII + + + + quote_ident quote_ident(string text) + text + 为给定字符串添加适当的引号并返回,使其可用作 SQL 语句字符串中的标识符。仅在必要时添加引号(即字符串包含不能用于标识符的字符,或会被大小写折叠时)。内嵌引号会适当地双写。另见 + quote_ident('Foo bar') + "Foo bar" + + + + quote_literal quote_literal(string text) + text + 为给定字符串添加适当的引号并返回,使其可用作 SQL 语句字符串中的字符串字面量。内嵌单引号和反斜杠会被适当地双写。注意,quote_literal 在输入为 null 时返回 null;如果参数可能为 null,quote_nullable 通常更合适。另见 + quote_literal(E'O\'Reilly') + 'O''Reilly' + + + + quote_literal(value anyelement) + text + 将给定值强制转换为文本,然后作为字面量加引号。内嵌单引号和反斜杠会被适当地双写。 + quote_literal(42.5) + '42.5' + + + + quote_nullable quote_nullable(string text) + text + 为给定字符串添加适当的引号并返回,使其可用作 SQL 语句字符串中的字符串字面量;如果参数为 null,则返回 NULL。内嵌单引号和反斜杠会被适当地双写。另见 + quote_nullable(NULL) + NULL + + + + quote_nullable(value anyelement) + text + 将给定值强制转换为文本,然后作为字面量加引号;如果参数为 null,则返回 NULL。内嵌单引号和反斜杠会被适当地双写。 + quote_nullable(42.5) + '42.5' + + + + regexp_matches regexp_matches(string text, pattern text [, flags text]) + setof text[] + 返回 POSIX 正则表达式与 string 匹配所得到的所有捕获子串。更多信息参见 + regexp_matches('foobarbequebaz', '(bar)(beque)') + {bar,beque} + + + + regexp_replace regexp_replace(string text, pattern text, replacement text [, flags text]) + text + 替换匹配 POSIX 正则表达式的子字符串。更多信息参见 + regexp_replace('Thomas', '.[mN]a.', 'M') + ThM + + + + regexp_split_to_array regexp_split_to_array(string text, pattern text [, flags text ]) + text[] + 使用 POSIX 正则表达式作为分隔符拆分 string。更多信息参见 + regexp_split_to_array('hello world', '\s+') + {hello,world} + + + + regexp_split_to_table regexp_split_to_table(string text, pattern text [, flags text]) + setof text + 使用 POSIX 正则表达式作为分隔符拆分 string。更多信息参见 + regexp_split_to_table('hello world', '\s+') + helloworld(2 行) + + + + repeat repeat(string text, number int) + text + string 重复指定的 number + repeat('Pg', 4) + PgPgPgPg + + + + replace replace(string text, from text, to text) + text + string 中每次出现的子字符串 from 替换为子字符串 to + replace('abcdefabcdef', 'cd', 'XX') + abXXefabXXef + + + + reverse reverse(str) + text + 返回反转后的字符串。 + reverse('abcde') + edcba + + + + right right(str text, n int) + text + 返回字符串中的最后 n 个字符。当 n 为负数时,返回除前 |n| 个字符以外的所有字符。 + right('abcde', 2) + de + + + + rpad rpad(string text, length int , fill text) + text + string 后面追加字符 fill(默认为空格),将其填充到长度 length。如果 string 已经长于 length,则截断它。 + rpad('hi', 5, 'xy') + hixyx + + + + rtrim rtrim(string text , characters text) + text + string 的结尾移除仅由 characters 中字符(默认为空格)组成的最长字符串 + rtrim('testxxzx', 'xyz') + test + + + + split_part split_part(string text, delimiter text, field int) + text + delimiter 拆分 string,并返回指定字段(从一开始计数) + split_part('abc~@~def~@~ghi', '~@~', 2) + def + + + + strpos strpos(string, substring) + int + 指定子字符串的位置(与 position(substring in string) 相同,但注意参数顺序相反) + strpos('high', 'ig') + 2 + + + + substr substr(string, from , count) + text + 提取子字符串(与 substring(string from from for count) 相同) + substr('alphabet', 3, 2) + ph + + + + to_ascii to_ascii(string text , encoding text) + text + string 从其他编码转换为 ASCII(只支持从 LATIN1LATIN2LATIN9WIN1250 编码转换) + to_ascii('Karel') + Karel + + + + to_hex to_hex(number int or bigint) + text + number 转换为等价的十六进制表示 + to_hex(2147483647) + 7fffffff + + + + translate translate(string text, from text, to text) + text + string 中与 from 集合中某字符匹配的每个字符替换为 to 集合中的对应字符。如果 fromto 长,则移除输入中出现的 from 中的多余字符。 + translate('12345', '143', 'ax') + a2x5 + + + + +
+ + + concatconcat_wsformat是可变参数函数,因此可以把要串接或格式化的值作为一个标记了VARIADIC关键字的数组进行传递(见)。 + 数组的元素被当作函数的独立普通参数一样处理。如果可变参数数组为 NULL,concatconcat_ws返回 NULL,但format把 NULL 当作一个零元素数组。 + + + 另见中的聚合函数 string_agg + + + 内置转换 + + + + 转换名称 转换名称遵循标准命名规则:将源编码正式名称中的所有非字母数字字符替换为下划线,再接上 _to_,然后接上按相同方式处理的目标编码名称。因此,这些名称可能与惯用的编码名称不同。 + 源编码 + 目标编码 + + + + + + ascii_to_mic + SQL_ASCII + MULE_INTERNAL + + + + ascii_to_utf8 + SQL_ASCII + UTF8 + + + + big5_to_euc_tw + BIG5 + EUC_TW + + + + big5_to_mic + BIG5 + MULE_INTERNAL + + + + big5_to_utf8 + BIG5 + UTF8 + + + + euc_cn_to_mic + EUC_CN + MULE_INTERNAL + + + + euc_cn_to_utf8 + EUC_CN + UTF8 + + + + euc_jp_to_mic + EUC_JP + MULE_INTERNAL + + + + euc_jp_to_sjis + EUC_JP + SJIS + + + + euc_jp_to_utf8 + EUC_JP + UTF8 + + + + euc_kr_to_mic + EUC_KR + MULE_INTERNAL + + + + euc_kr_to_utf8 + EUC_KR + UTF8 + + + + euc_tw_to_big5 + EUC_TW + BIG5 + + + + euc_tw_to_mic + EUC_TW + MULE_INTERNAL + + + + euc_tw_to_utf8 + EUC_TW + UTF8 + + + + gb18030_to_utf8 + GB18030 + UTF8 + + + + gbk_to_utf8 + GBK + UTF8 + + + + iso_8859_10_to_utf8 + LATIN6 + UTF8 + + + + iso_8859_13_to_utf8 + LATIN7 + UTF8 + + + + iso_8859_14_to_utf8 + LATIN8 + UTF8 + + + + iso_8859_15_to_utf8 + LATIN9 + UTF8 + + + + iso_8859_16_to_utf8 + LATIN10 + UTF8 + + + + iso_8859_1_to_mic + LATIN1 + MULE_INTERNAL + + + + iso_8859_1_to_utf8 + LATIN1 + UTF8 + + + + iso_8859_2_to_mic + LATIN2 + MULE_INTERNAL + + + + iso_8859_2_to_utf8 + LATIN2 + UTF8 + + + + iso_8859_2_to_windows_1250 + LATIN2 + WIN1250 + + + + iso_8859_3_to_mic + LATIN3 + MULE_INTERNAL + + + + iso_8859_3_to_utf8 + LATIN3 + UTF8 + + + + iso_8859_4_to_mic + LATIN4 + MULE_INTERNAL + + + + iso_8859_4_to_utf8 + LATIN4 + UTF8 + + + + iso_8859_5_to_koi8_r + ISO_8859_5 + KOI8R + + + + iso_8859_5_to_mic + ISO_8859_5 + MULE_INTERNAL + + + + iso_8859_5_to_utf8 + ISO_8859_5 + UTF8 + + + + iso_8859_5_to_windows_1251 + ISO_8859_5 + WIN1251 + + + + iso_8859_5_to_windows_866 + ISO_8859_5 + WIN866 + + + + iso_8859_6_to_utf8 + ISO_8859_6 + UTF8 + + + + iso_8859_7_to_utf8 + ISO_8859_7 + UTF8 + + + + iso_8859_8_to_utf8 + ISO_8859_8 + UTF8 + + + + iso_8859_9_to_utf8 + LATIN5 + UTF8 + + + + johab_to_utf8 + JOHAB + UTF8 + + + + koi8_r_to_iso_8859_5 + KOI8R + ISO_8859_5 + + + + koi8_r_to_mic + KOI8R + MULE_INTERNAL + + + + koi8_r_to_utf8 + KOI8R + UTF8 + + + + koi8_r_to_windows_1251 + KOI8R + WIN1251 + + + + koi8_r_to_windows_866 + KOI8R + WIN866 + + + + koi8_u_to_utf8 + KOI8U + UTF8 + + + + mic_to_ascii + MULE_INTERNAL + SQL_ASCII + + + + mic_to_big5 + MULE_INTERNAL + BIG5 + + + + mic_to_euc_cn + MULE_INTERNAL + EUC_CN + + + + mic_to_euc_jp + MULE_INTERNAL + EUC_JP + + + + mic_to_euc_kr + MULE_INTERNAL + EUC_KR + + + + mic_to_euc_tw + MULE_INTERNAL + EUC_TW + + + + mic_to_iso_8859_1 + MULE_INTERNAL + LATIN1 + + + + mic_to_iso_8859_2 + MULE_INTERNAL + LATIN2 + + + + mic_to_iso_8859_3 + MULE_INTERNAL + LATIN3 + + + + mic_to_iso_8859_4 + MULE_INTERNAL + LATIN4 + + + + mic_to_iso_8859_5 + MULE_INTERNAL + ISO_8859_5 + + + + mic_to_koi8_r + MULE_INTERNAL + KOI8R + + + + mic_to_sjis + MULE_INTERNAL + SJIS + + + + mic_to_windows_1250 + MULE_INTERNAL + WIN1250 + + + + mic_to_windows_1251 + MULE_INTERNAL + WIN1251 + + + + mic_to_windows_866 + MULE_INTERNAL + WIN866 + + + + sjis_to_euc_jp + SJIS + EUC_JP + + + + sjis_to_mic + SJIS + MULE_INTERNAL + + + + sjis_to_utf8 + SJIS + UTF8 + + + + windows_1258_to_utf8 + WIN1258 + UTF8 + + + + uhc_to_utf8 + UHC + UTF8 + + + + utf8_to_ascii + UTF8 + SQL_ASCII + + + + utf8_to_big5 + UTF8 + BIG5 + + + + utf8_to_euc_cn + UTF8 + EUC_CN + + + + utf8_to_euc_jp + UTF8 + EUC_JP + + + + utf8_to_euc_kr + UTF8 + EUC_KR + + + + utf8_to_euc_tw + UTF8 + EUC_TW + + + + utf8_to_gb18030 + UTF8 + GB18030 + + + + utf8_to_gbk + UTF8 + GBK + + + + utf8_to_iso_8859_1 + UTF8 + LATIN1 + + + + utf8_to_iso_8859_10 + UTF8 + LATIN6 + + + + utf8_to_iso_8859_13 + UTF8 + LATIN7 + + + + utf8_to_iso_8859_14 + UTF8 + LATIN8 + + + + utf8_to_iso_8859_15 + UTF8 + LATIN9 + + + + utf8_to_iso_8859_16 + UTF8 + LATIN10 + + + + utf8_to_iso_8859_2 + UTF8 + LATIN2 + + + + utf8_to_iso_8859_3 + UTF8 + LATIN3 + + + + utf8_to_iso_8859_4 + UTF8 + LATIN4 + + + + utf8_to_iso_8859_5 + UTF8 + ISO_8859_5 + + + + utf8_to_iso_8859_6 + UTF8 + ISO_8859_6 + + + + utf8_to_iso_8859_7 + UTF8 + ISO_8859_7 + + + + utf8_to_iso_8859_8 + UTF8 + ISO_8859_8 + + + + utf8_to_iso_8859_9 + UTF8 + LATIN5 + + + + utf8_to_johab + UTF8 + JOHAB + + + + utf8_to_koi8_r + UTF8 + KOI8R + + + + utf8_to_koi8_u + UTF8 + KOI8U + + + + utf8_to_sjis + UTF8 + SJIS + + + + utf8_to_windows_1258 + UTF8 + WIN1258 + + + + utf8_to_uhc + UTF8 + UHC + + + + utf8_to_windows_1250 + UTF8 + WIN1250 + + + + utf8_to_windows_1251 + UTF8 + WIN1251 + + + + utf8_to_windows_1252 + UTF8 + WIN1252 + + + + utf8_to_windows_1253 + UTF8 + WIN1253 + + + + utf8_to_windows_1254 + UTF8 + WIN1254 + + + + utf8_to_windows_1255 + UTF8 + WIN1255 + + + + utf8_to_windows_1256 + UTF8 + WIN1256 + + + + utf8_to_windows_1257 + UTF8 + WIN1257 + + + + utf8_to_windows_866 + UTF8 + WIN866 + + + + utf8_to_windows_874 + UTF8 + WIN874 + + + + windows_1250_to_iso_8859_2 + WIN1250 + LATIN2 + + + + windows_1250_to_mic + WIN1250 + MULE_INTERNAL + + + + windows_1250_to_utf8 + WIN1250 + UTF8 + + + + windows_1251_to_iso_8859_5 + WIN1251 + ISO_8859_5 + + + + windows_1251_to_koi8_r + WIN1251 + KOI8R + + + + windows_1251_to_mic + WIN1251 + MULE_INTERNAL + + + + windows_1251_to_utf8 + WIN1251 + UTF8 + + + + windows_1251_to_windows_866 + WIN1251 + WIN866 + + + + windows_1252_to_utf8 + WIN1252 + UTF8 + + + + windows_1256_to_utf8 + WIN1256 + UTF8 + + + + windows_866_to_iso_8859_5 + WIN866 + ISO_8859_5 + + + + windows_866_to_koi8_r + WIN866 + KOI8R + + + + windows_866_to_mic + WIN866 + MULE_INTERNAL + + + + windows_866_to_utf8 + WIN866 + UTF8 + + + + windows_866_to_windows_1251 + WIN866 + WIN + + + + windows_874_to_utf8 + WIN874 + UTF8 + + + + euc_jis_2004_to_utf8 + EUC_JIS_2004 + UTF8 + + + + utf8_to_euc_jis_2004 + UTF8 + EUC_JIS_2004 + + + + shift_jis_2004_to_utf8 + SHIFT_JIS_2004 + UTF8 + + + + utf8_to_shift_jis_2004 + UTF8 + SHIFT_JIS_2004 + + + + euc_jis_2004_to_shift_jis_2004 + EUC_JIS_2004 + SHIFT_JIS_2004 + + + + shift_jis_2004_to_euc_jis_2004 + SHIFT_JIS_2004 + EUC_JIS_2004 + + + + +
+ + + <function>format</function> + + + format + + + + 函数format根据一个格式字符串产生格式化的输出,其形式类似于 C 函数sprintf。 + + + + +format(formatstr text [, formatarg "any" [, ...] ]) + + formatstr是指定结果格式的字符串。格式字符串中的文本会直接复制到结果中,但格式说明符所在的位置除外。格式说明符充当字符串中的占位符,定义如何格式化后续函数参数并将其插入结果。每个formatarg参数都按照其数据类型通常的输出规则转换为文本,再根据格式说明符进行格式化并插入结果字符串。 + + + 格式说明符以%字符开头,格式如下: +%[position][flags][width]type +其中各组成字段为: + + position(可选) + + 一个形如n$的字符串,其中n是要打印的参数的索引。索引 1 表示紧跟在以下参数之后的第一个参数:formatstr。如果position被省略,则默认按顺序使用下一个参数。 + + + + + flags(可选) + + 用于控制格式说明符输出格式的附加选项。目前唯一支持的标志是减号(-),它使格式说明符的输出左对齐。只有同时指定了width字段时,它才有效。 + + + + + width(可选) + + + 指定用于显示格式说明符输出的最小字符数。输出将被在左部或右部(取决于-标志)用空格填充以保证充满该宽度。太小的宽度设置不会导致输出被截断,但是会被简单地忽略。宽度可以使用下列形式之一指定:一个正整数;一个星号(*)表示使用下一个函数参数作为宽度;或者一个形式为*n$的字符串表示使用第n个函数参数作为宽度。 + + + + 如果宽度来自一个函数参数,会先使用该宽度参数,再使用作为格式说明符值的参数。如果宽度参数为负数,结果会在长度为abs(width)的字段中左对齐(如同指定了-标志)。 + + + + + + type(必需) + + + 格式转换的类型,用于产生格式说明符的输出。支持下面的类型: + + + + s将参数值格式化为一个简单字符串。空值会被视为空字符串。 + + + + + I将参数值视作 SQL 标识符,并在必要时用双引号包围它。如果参数为 null,则会报错(等效于quote_ident)。 + + + + + L将参数值作为 SQL 字面量加引号。null 值显示为不带引号的字符串NULL(等效于quote_nullable)。 + + + + + + + + + + + 除了以上所述的格式说明符之外,要输出一个字面形式的%字符,可以使用特殊序列%%。 + + + 下面是一些基本格式转换的示例: +SELECT format('Hello %s', 'World'); +结果:Hello World + +SELECT format('Testing %s, %s, %s, %%', 'one', 'two', 'three'); +结果:Testing one, two, three, % + +SELECT format('INSERT INTO %I VALUES(%L)', 'Foo bar', E'O\'Reilly'); +结果:INSERT INTO "Foo bar" VALUES('O''Reilly') + +SELECT format('INSERT INTO %I VALUES(%L)', 'locations', 'C:\Program Files'); +结果:INSERT INTO locations VALUES('C:\Program Files') + + + + 下面是使用width字段和-标志的示例: +SELECT format('|%10s|', 'foo'); +结果:| foo| + +SELECT format('|%-10s|', 'foo'); +结果:|foo | + +SELECT format('|%*s|', 10, 'foo'); +结果:| foo| + +SELECT format('|%*s|', -10, 'foo'); +结果:|foo | + +SELECT format('|%-*s|', 10, 'foo'); +结果:|foo | + +SELECT format('|%-*s|', -10, 'foo'); +结果:|foo | + + + + 这些示例展示了如何使用position字段: +SELECT format('Testing %3$s, %2$s, %1$s', 'one', 'two', 'three'); +结果:Testing three, two, one + +SELECT format('|%*2$s|', 'foo', 10, 'bar'); +结果:| bar| + +SELECT format('|%1$*2$s|', 'foo', 10, 'bar'); +结果:| foo| + + + + 不同于标准 C 函数sprintf, + PostgreSQLformat函数允许在同一个格式字符串中,混合使用带有或不带有position字段的格式说明符。不带position字段的格式说明符,总是使用最后一个已使用参数之后的下一个参数。此外,format函数不要求格式字符串使用全部函数参数。例如: +SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); +结果:Testing three, two, three + + + + + 对于安全地构造动态 SQL 语句,%I%L格式说明符特别有用。参见。 + + + +
+ + + + 二进制串函数和操作符 + + + 二进制数据 + 函数 + + + 本节描述用于检查和操作 bytea 类型值的函数和操作符。 + + + SQL定义了一些使用关键字而不是逗号来分隔参数的字符串函数。详情请见PostgreSQL也提供了这些函数使用常规函数调用语法的版本(参阅)。 + + + + 本页显示的示例结果假定服务器参数 bytea_output 设为 escape(传统的 PostgreSQL 格式)。 + + + + <acronym>SQL</acronym>二进制串函数和操作符 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + string || + string + bytea + 字符串串接 二进制串 串接 + '\\Post'::bytea || '\047gres\000'::bytea + \\Post'gres\000 + + + + octet_length octet_length(string) + int + 二进制串中的字节数 + octet_length('jo\000se'::bytea) + 5 + + + + overlay overlay(string placing string from int for int) + bytea + 替换子字符串 + overlay('Th\000omas'::bytea placing '\002\003'::bytea from 2 for 3) + T\\002\\003mas + + + + position position(substring in string) + int + 指定子字符串的位置 + position('\000om'::bytea in 'Th\000omas'::bytea) + 3 + + + + substring substring(string from int for int) + bytea + 提取子字符串 + substring('Th\000omas'::bytea from 2 for 3) + h\000o + + + + trim trim(both bytes from string) + bytea + string 的开头和结尾移除仅由 bytes 中字节组成的最长字符串 + trim('\000\001'::bytea from '\000Tom\001'::bytea) + Tom + + + +
+ + + 还有一些二进制串处理函数可以使用,在列出。 其中有一些是在内部使用,用于实现列出的 SQL 标准串函数。 + + + + 其他二进制串函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + btrim btrim(string bytea, bytes bytea) + bytea + string 的开头和结尾移除仅由 bytes 中字节组成的最长字符串 + btrim('\000trim\001'::bytea, '\000\001'::bytea) + trim + + + + decode decode(string text, format text) + bytea + string 中的文本表示解码二进制数据。format 的选项与 encode 相同。 + decode('123\000456', 'escape') + 123\000456 + + + + encode encode(data bytea, format text) + text + 将二进制数据编码为文本表示。支持的格式为:base64hexescapeescape 将零字节和最高位为 1 的字节转换为八进制序列(\nnn),并将反斜杠双写。 + encode('123\000456'::bytea, 'escape') + 123\000456 + + + + get_bit get_bit(string, offset) + int + 从字符串中提取位 + get_bit('Th\000omas'::bytea, 45) + 1 + + + + get_byte get_byte(string, offset) + int + 从字符串中提取字节 + get_byte('Th\000omas'::bytea, 4) + 109 + + + + length length(string) + int + 二进制串的长度 二进制串 长度 长度 二进制串 二进制串,长度 + length('jo\000se'::bytea) + 5 + + + + md5 md5(string) + text + 计算 string 的 MD5 hash,并以十六进制返回结果 + md5('Th\000omas'::bytea) + 8ab2d3c9689aaf18 b4958c334c82d8b1 + + + + set_bit set_bit(string, offset, newvalue) + bytea + 设置字符串中的位 + set_bit('Th\000omas'::bytea, 45, 0) + Th\000omAs + + + + set_byte set_byte(string, offset, newvalue) + bytea + 设置字符串中的字节 + set_byte('Th\000omas'::bytea, 4, 64) + Th\000o@as + + + +
+ + + 函数get_byteset_byte把二进制串中的第一个字节编号为字节 0。 + 函数get_bitset_bit在每一个字节中从右边起计数位; + 例如位 0 是第一个字节的最低有效位,而位 15 是第二个字节的最高有效位。 + + + + 参见中的聚合函数string_agg以及中的大对象函数。 + +
+ + + + 位串函数和操作符 + + + 位串 + 函数 + + + 本节描述用于检查和操作位串的函数和操作符,也就是 bitbit varying 类型的值。除常用比较操作符外,还可使用中的操作符。&|# 的位串操作数长度必须相同。移位时会保留字符串的原始长度,如示例所示。 + + + 位串操作符 + + + + + 操作符 + 描述 + 示例 + 结果 + + + + + + || + 串接 + B'10001' || B'011' + 10001011 + + + + & + 按位与 + B'10001' & B'01101' + 00001 + + + + | + 按位或 + B'10001' | B'01101' + 11101 + + + + # + 按位异或 + B'10001' # B'01101' + 11100 + + + + ~ + 按位非 + ~ B'10001' + 01110 + + + + << + 按位左移 + B'10001' << 3 + 01000 + + + + >> + 按位右移 + B'10001' >> 2 + 00100 + + + +
+ + 以下 SQL 标准函数既适用于位串,也适用于字符串:lengthbit_lengthoctet_lengthpositionsubstringoverlay + + 以下函数既适用于位串,也适用于二进制串:get_bitset_bit。处理位串时,这些函数将字符串的第一位(最左侧的位)编号为位 0。 + + 此外,还可以在整数值与bit类型之间进行类型转换。例如: +44::bit(10) 0000101100 +44::bit(3) 100 +cast(-44 as bit(12)) 111111010100 +'1110'::bit(4)::integer 14 +注意,仅转换为bit意味着转换为bit(1),因此只会得到该整数的最低有效位。 + + + 将整数转换为 bit(n) 会复制最右侧的 n 位。将整数转换为宽度大于该整数自身的位串时,会在左侧进行符号扩展。 + + +
+ + + + 模式匹配 + + + 模式匹配 + + + + PostgreSQL提供了三种独立的模式匹配方法:传统的SQL LIKE操作符、较新的SIMILAR TO操作符(在 SQL:1999 中加入),以及POSIX风格的正则表达式。除了用于判断这个字符串是否匹配这个模式?的基本操作符外,还提供了提取或替换匹配子字符串、在匹配位置分割字符串的函数。 + + + + + + 如果你的模式匹配的要求超出了这些,请考虑用 Perl 或 Tcl 写一个用户定义的函数。 + + + + + + + 虽然大多数正则表达式搜索都能很快完成,但特意构造的正则表达式可能需要任意长的处理时间和任意多的内存。接受来自恶意来源的正则表达式搜索模式时应当谨慎。如果必须这样做,建议设置语句超时。 + + + + 使用SIMILAR TO模式进行搜索具有同样的安全风险,因为SIMILAR TO提供了许多与POSIX风格正则表达式相同的能力。 + + + + LIKE搜索比另外两种方法简单得多,因此,当模式可能来自恶意来源时,使用它更安全。 + + + + + <function>LIKE</function> + + + LIKE + + + +string LIKE pattern ESCAPE escape-character +string NOT LIKE pattern ESCAPE escape-character + + + + 如果该string匹配了提供的pattern,那么LIKE表达式返回真(和预期的一样,如果LIKE返回真,那么NOT LIKE表达式返回假, 反之亦然。一个等效的表达式是NOT (string LIKE pattern))。 + + + + 如果pattern不包含百分号或下划线,那么该模式只表示它 + 本身的字符串;在这种情况下,LIKE的行为就像等号操作符。 + pattern中的下划线(_)代表 + (匹配)任意单个字符;百分号(%)匹配任意由零个或多个 + 字符组成的序列。 + + + + 一些示例: + +'abc' LIKE 'abc' true +'abc' LIKE 'a%' true +'abc' LIKE '_b_' true +'abc' LIKE 'c' false + + + + + LIKE模式匹配总是覆盖整个字符串。因此,如果想要匹配字符串内 + 任意位置上的一个序列,该模式就必须以百分号开头并以百分号结尾。 + + + + 要匹配字面量下划线或百分号而不是把它们当作通配符, + pattern中相应的字符前面必须带有转义字符。 + 默认的转义字符是反斜线,但也可以使用ESCAPE子句选择其他 + 转义字符。要匹配转义字符本身,请写两个转义字符。 + + + + + + 如果你关掉了,你在字符串常量中写的任何反斜线都需要被双写。详见。 + + + + + 也可以通过写 ESCAPE '' 来选择不使用转义字符。这会禁用转义机制,从而无法关闭模式中下划线和百分号的特殊含义。 + + + + 可以用关键字 ILIKE 代替 LIKE,使匹配根据当前区域设置忽略大小写。这不属于 SQL 标准,而是 PostgreSQL 的扩展。 + + + + 操作符~~等效于LIKE, 而~~*对应ILIKE。 + 还有 !~~!~~*操作符分别代表NOT LIKENOT ILIKE。 + 所有这些操作符都是PostgreSQL特有的。 + 你可能会在EXPLAIN输出和类似的地方看到这些操作符名称,因为解析器实际上将LIKE等翻译成这些操作符。 + + + + 在 PostgreSQL 语法中,LIKEILIKENOT LIKENOT ILIKE 通常被当作操作符;例如,它们可以用于 expression operator ANY (subquery) 构造,但其中不能包含 ESCAPE 子句。在某些不常见的情况下,可能需要改用底层操作符名称。 + + + + + + <function>SIMILAR TO</function>正则表达式 + + + 正则表达式 + + + + + SIMILAR TO + + + substring + + + +string SIMILAR TO pattern ESCAPE escape-character +string NOT SIMILAR TO pattern ESCAPE escape-character + + + SIMILAR TO 操作符根据其模式是否匹配给定字符串返回真或假。它与 LIKE 类似,但按照 SQL 标准定义的正则表达式来解释模式。SQL 正则表达式是 LIKE 表示法和常见正则表达式表示法的一种奇特结合。 + + + 与LIKE类似,SIMILAR TO操作符只有在其模式匹配整个字符串时才算成功;这一点不同于普通正则表达式,后者可以匹配字符串的任意部分。与LIKE相同,SIMILAR TO也使用_%作为通配符,分别匹配任意单个字符和任意字符串(分别类似于 POSIX 正则表达式中的..*)。 + + + + 除了这些从LIKE借用的功能之外,SIMILAR TO支持下面这些从 POSIX 正则表达式借用的 模式匹配元字符: + + + + + |表示选择(两个候选之一)。 + + + + + *表示重复前面的项零次或更多次。 + + + + + +表示重复前面的项一次或更多次。 + + + + + ?表示重复前面的项零次或一次。 + + + + + {m}表示重复前面的项刚好m次。 + + + + + {m,}表示重复前面的项m次或更多次。 + + + + + {m,n}表示重复前面的项至少m次并且不超过n次。 + + + + + 可以使用圆括号()把多个项组合成一个逻辑项。 + + + + + 一个方括号表达式[...]声明一个字符类,就像 POSIX 正则表达式一样。 + + + + + 注意点号(.)不是SIMILAR TO的一个元字符。 + + + LIKE 一样,反斜杠会禁用这些元字符的特殊含义;也可以用 ESCAPE 指定其他转义字符。 + + 下面是一些示例: +'abc' SIMILAR TO 'abc' true +'abc' SIMILAR TO 'a' false +'abc' SIMILAR TO '%(b|d)%' true +'abc' SIMILAR TO '(b|c)%' false + + + + 带三个参数的 substring 函数,即 substring(string from pattern for escape-character),用于提取匹配 SQL 正则表达式模式的子串。与 SIMILAR TO 一样,指定模式必须匹配整个数据字符串,否则函数失败并返回空值。要指出成功时应返回的模式部分,模式中必须出现两次后跟双引号(")的转义字符。函数返回匹配这两个标记之间的模式部分的文本。 + + 下面是一些示例,其中使用#"界定返回字符串: +substring('foobar' from '%#"o_b#"%' for '#') oob +substring('foobar' from '#"o_b#"%' for '#') NULL + + + + + + <acronym>POSIX</acronym>正则表达式 + + + 正则表达式 + 模式匹配 + + + substring + + regexp_replace + regexp_matches + regexp_split_to_table + regexp_split_to_array + + + 列出了所有可用于 POSIX 正则表达式模式匹配的操作符。 + + + + 正则表达式匹配操作符 + + + + + 操作符 + 描述 + 示例 + + + + + + ~ + 匹配正则表达式,区分大小写 + 'thomas' ~ '.*thomas.*' + + + + ~* + 匹配正则表达式,不区分大小写 + 'thomas' ~* '.*Thomas.*' + + + + !~ + 不匹配正则表达式,区分大小写 + 'thomas' !~ '.*Thomas.*' + + + + !~* + 不匹配正则表达式,不区分大小写 + 'thomas' !~* '.*vadim.*' + + + +
+ + + POSIX正则表达式提供了比LIKESIMILAR TO操作符更强大的模式匹配方式。许多 Unix 工具,例如egrepsedawk,都使用与这里描述的模式匹配语言相似的语言。 + + + + 正则表达式是一个字符序列,是定义一组字符串(一个正则集)的简写。如果字符串属于正则表达式描述的正则集,就称该字符串匹配此正则表达式。与LIKE一样,模式中的字符精确匹配字符串中的字符,除非该模式字符在正则表达式语言中有特殊含义 — 但正则表达式使用的特殊字符与LIKE不同。与LIKE模式不同,正则表达式可以匹配字符串中的任意位置,除非显式将其锚定到字符串开头或末尾。 + + + 下面是一些示例: +'abc' ~ 'abc' true +'abc' ~ '^a' true +'abc' ~ '(b|d)' true +'abc' ~ '^(b|c)' false + + + + + POSIX模式语言的详细描述见下文。 + + + 带两个参数的 substring 函数,即 substring(string from pattern),用于提取匹配 POSIX 正则表达式模式的子串。如果没有匹配,它返回空值;否则返回匹配模式的那部分文本。但是,如果模式包含圆括号,则返回匹配第一个括号子表达式(左圆括号最先出现的那个)的文本。如果要在表达式内部使用圆括号而不触发这个例外,可以在整个表达式外再加一对圆括号。如果需要在想要提取的子表达式之前使用圆括号,请参见下文介绍的非捕获圆括号。 + + 下面是一些示例: +substring('foobar' from 'o.b') oob +substring('foobar' from 'o(.)b') o + + + + regexp_replace函数用新文本替换匹配 POSIX 正则表达式模式的子字符串。它的语法为 regexp_replace(source, pattern, replacement , flags )。如果没有与 pattern 匹配的内容,则原样返回 source 字符串。如果存在匹配,则返回将匹配子字符串替换为 replacement 字符串后的 source 字符串。replacement 字符串可以包含 \n,其中 n 为 1 至 9,表示应插入与模式中第 n 个圆括号子表达式匹配的源子字符串;它也可以包含 \&,表示应插入与整个模式匹配的子字符串。如果需要在替换文本中放置字面的反斜杠,应写成 \\flags 参数是可选的文本字符串,其中包含零个或多个改变函数行为的单字母标志。标志 i 指定不区分大小写的匹配,而标志 g 指定替换每个匹配的子字符串,而不仅是第一个。支持的标志(不包括 g)在中描述。 + + 下面是一些示例: +regexp_replace('foobarbaz', 'b..', 'X') + fooXbaz +regexp_replace('foobarbaz', 'b..', 'X', 'g') + fooXX +regexp_replace('foobarbaz', 'b(..)', 'X\1Y', 'g') + fooXarYXazY + + + + + regexp_matches函数返回一个文本数组,其中包含与 POSIX 正则表达式模式匹配所得到的所有捕获子串。它的语法为 + regexp_matches(string, pattern + , flags )。 + 该函数可以不返回行、返回一行或多行(见下面的g标志)。如果pattern不匹配,该函数不返回任何行。如果模式不包含圆括号子表达式,则返回的每一行都是包含与整个模式匹配的子字符串的单元素文本数组。如果模式包含圆括号子表达式,该函数返回一个文本数组,其中第n个元素是匹配模式的第n个圆括号子表达式的子字符串(不包括非捕获括号;详情见下文)。 + flags参数是一个可选的文本字符串,其中包含零个或多个单个字母标志,用于更改函数的行为。标志g使该函数查找字符串中的每个匹配项,而不仅仅是第一个,并为每个这样的匹配返回一行。支持的标志(但不包括g)在中描述。 + + + 下面是一些示例: +SELECT regexp_matches('foobarbequebaz', '(bar)(beque)'); + regexp_matches +---------------- + {bar,beque} +(1 row) + +SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g'); + regexp_matches +---------------- + {bar,beque} + {bazil,barf} +(2 rows) + +SELECT regexp_matches('foobarbequebaz', 'barbeque'); + regexp_matches +---------------- + {barbeque} +(1 row) + + + + + 可以通过使用子查询强制regexp_matches()总是返回一行;当希望返回所有行(包括不匹配的行)时,这在SELECT目标列表中特别有用: + +SELECT col1, (SELECT regexp_matches(col2, '(bar)(beque)')) FROM tab; + + + + + regexp_split_to_table把一个 POSIX 正则表达式模式当作一个定界符来分割字符串。它的语法形式是regexp_split_to_table(string, pattern , flags )。如果没有与pattern的匹配,该函数返回string。如果至少有一个匹配,对每一个匹配它都返回从上一个匹配的末尾(或者串的开头)到这次匹配开头之间的文本。当没有更多匹配时,它返回从上一次匹配的末尾到串末尾之间的文本。flags参数是一个可选的文本串,它包含零个或更多单字母标志,这些标志可以改变该函数的行为。regexp_split_to_table能支持的标志在中描述。 + + + + regexp_split_to_array函数的行为和regexp_split_to_table相同,不过regexp_split_to_array会把它的结果以一个text数组的形式返回。它的语法是regexp_split_to_array(string, pattern , flags )。这些参数和regexp_split_to_table的相同。 + + + 下面是一些示例: + +SELECT foo FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', '\s+') AS foo; + foo +------- + the + quick + brown + fox + jumps + over + the + lazy + dog +(9 rows) + +SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', '\s+'); + regexp_split_to_array +----------------------------------------------- + {the,quick,brown,fox,jumps,over,the,lazy,dog} +(1 row) + +SELECT foo FROM regexp_split_to_table('the quick brown fox', '\s*') AS foo; + foo +----- + t + h + e + q + u + i + c + k + b + r + o + w + n + f + o + x +(16 rows) + + + + + 正如最后一个示例所示,正则表达式分割函数会忽略出现在字符串开头或结尾 + 或紧跟在前一个匹配项之后的零长度匹配。这与regexp_matches实现的 + 严格的正则表达式匹配定义相矛盾,但在实践中通常是最方便的行为。 + 其他软件系统如Perl使用类似的定义。 + + + + + + 正则表达式细节 + + + PostgreSQL的正则表达式是使用 Henry Spencer 写的一个包来实现的。下面的正则表达式的大部分描述都是从他的手册页中逐字拷贝过来的。 + + + + 正则表达式(RE),在POSIX 1003.2 中定义, 它有两种形式:扩展RE或者是ERE(大概地说就是那些在egrep里的), 基本RE或者是BRE(大概地说就是那些在ed里的)。PostgreSQL支持两种形式,并且还实现了一些POSIX标准中没有但是在类似 Perl 或者 Tcl 这样的语言中得到广泛应用的一些扩展。使用了那些非POSIX扩展的RE高级RE, 或者本文档里说的ARE。ARE 几乎完全是 ERE 的超集,但是 BRE 有几个符号上的不兼容(以及更多的限制)。我们首先描述 ARE 和 ERE 形式, 描述那些只适用于 ARE 的特性,然后描述 BRE 的区别是什么。 + + + + + + PostgreSQL最初总是假定正则表达式遵循 ARE 规则。不过,也可以像中所述那样,在 RE 模式前加上一个嵌入选项,从而改用限制更多的 ERE 或 BRE 规则。这对于需要精确遵循POSIX 1003.2 规则的应用有助于保持兼容性。 + + + + + 一个正则表达式被定义为一个或更多分支,它们之间被|分隔。只要能匹配其中一个分支的东西都能匹配正则表达式。 + + + + 一个分支是零个或多个量化原子约束,连接在一起。 + 它匹配第一个的匹配项,然后是第二个的匹配项,依此类推;一个空分支匹配空字符串。 + + + + 一个量化原子是一个原子,后面可以跟一个量词。没有量词时,匹配一次原子所匹配的内容;有量词时,按量词指定的次数匹配原子所匹配的内容。原子可以是列出的任何一种形式。可用量词及其含义见。 + + + + 一个约束匹配一个空串,但只是在满足特定条件下才匹配。 约束可以在能够使用原子的地方使用,只是它不能跟着量词。简单的约束在里显示; 更多的约束稍后描述。 + + + + + 正则表达式原子 + + + + + + 原子 + 描述 + + + + + + (re) + (其中re是任意正则表达式)匹配re所匹配的内容,并记录该匹配,以备输出结果 + + + + (?:re) + 同上,但不记录匹配结果(非捕获圆括号;仅适用于 ARE) + + + + . + 匹配任意单个字符 + + + + [chars] + 一个方括号表达式, 匹配chars中的任意一个(详见 + + + + \k + (其中k既不是字母也不是数字)把该字符视为普通字符并匹配它,例如,\\匹配反斜线字符 + + + + \c + 其中c是字母或数字(后面可能还有其他字符),这是一个转义,参见(仅适用于 ARE;在 ERE 和 BRE 中,它匹配c + + + + { + 如果后面跟着非数字字符,则匹配左花括号{;如果后面跟着数字,则是bound的开头(见下文) + + + + x + 其中x是一个没有其它意义的单个字符,则匹配该字符 + + + +
+ + + RE 不能以反斜线(\)结尾。 + + + + + + 如果你关掉了,你在字符串常量中写的任何反斜线都需要被双写。详见。 + + + + + + 正则表达式量词 + + + + + + 量词 + 匹配 + + + + + + + * + 一个由原子的 0 次或更多次匹配组成的序列 + + + + + + 一个由原子的 1 次或更多次匹配组成的序列 + + + + ? + 一个由原子的 0 次或 1 次匹配组成的序列 + + + + {m} + 一个由原子的正好m次匹配组成的序列 + + + + {m,} + 一个由原子的m次或更多次匹配组成的序列 + + + + + {m,n} + 一个由原子的从m次到n次(包括)匹配组成的序列;m不能超过n + + + + *? + *的非贪婪版本 + + + + +? + +的非贪婪版本 + + + + ?? + ?的非贪婪版本 + + + + {m}? + {m}的非贪婪版本 + + + + {m,}? + {m,}的非贪婪版本 + + + + + {m,n}? + {m,n}的非贪婪版本 + + + +
+ + + 使用{...}的形式被称作范围。 一个范围内的数字mn都是无符号十进制整数, 允许的数值从 0 到 255(包含)。 + + + + 非贪婪量词(仅适用于 ARE)与对应的普通(贪婪)量词匹配相同的可能内容,但优先选择最少的匹配次数,而不是最多的匹配次数。详见。 + + + + + + 一个量词不能紧跟在另外一个量词后面,例如**是非法的。量词不能作为表达式或者子表达式的开头,也不能跟在^或者|后面。 + + + + + + 正则表达式约束 + + + + + + 约束 + 描述 + + + + + + + ^ + 在字符串开头匹配 + + + + $ + 在字符串末尾匹配 + + + + (?=re) + 正向先行断言在这样的位置匹配:存在从该位置开始且匹配re的子字符串(仅适用于 ARE) + + + + (?!re) + 负向先行断言在这样的位置匹配:不存在任何从该位置开始且匹配re的子字符串(仅适用于 ARE) + + + + (?<=re) + 正向后行断言在这样的位置匹配:存在以该位置结束且匹配re的子字符串(仅适用于 ARE) + + + + (?<!re) + 负向后行断言在这样的位置匹配:不存在任何以该位置结束且匹配re的子字符串(仅适用于 ARE) + + + +
+ + + 先行和后行约束不能包含反向引用(参见),其中的所有圆括号都视为非捕获圆括号。 + +
+ + + 方括号表达式 + + + 方括号表达式是用[]括起来的字符列表。通常,它匹配列表中的任意单个字符(但请参见下文)。如果列表以^开头,则匹配任意在列表剩余部分中的单个字符。如果列表中的两个字符用-分隔,则表示排序序列中这两个字符之间的完整字符范围(包含两个端点);例如,ASCII中的[0-9]匹配任意十进制数字。两个范围共享一个端点是不合法的,例如a-c-e。范围高度依赖排序序列,因此可移植程序应避免依赖它们。 + + + + 想在列表中包含字面字符],可以让它做列表的首字符(如果使用了^,需要放在其后)。 想在列表中包含字面字符-,可以让它做列表的首字符或者尾字符,或者一个范围的第二个端点。 想在列表中把字面字符-当做范围的起点, 把它用[..]包围起来,这样它就成为一个排序元素(见下文)。 除了这些字符本身、一些用[的组合(见下段)以及转义(只在 ARE 中有效)以外,所有其它特殊字符 在方括号表达式里都失去它们的特殊含义。特别是,在 ERE 和 BRE 规则下\不是特殊的, 但在 ARE 里,它是特殊的(引入一个转义)。 + + + + 在一个方括号表达式里,一个排序元素(一个字符、一个被当做一个单一字符排序的多字符序列或者一个表示上面两种情况的排序序列名称) 包含在[..]里面的时候表示该排序元素的字符序列。该序列被当做该方括号列表 的一个单一元素。这允许一个包含多字符排序元素的方括号表达式去匹配多于一个字符,例如,如果排序序列包含一个ch排序元素, 那么 RE [[.ch.]]*c匹配chchcc的头五个字符。 + + + + + + PostgreSQL当前不支持多字符排序元素。这些信息描述了将来可能有的行为。 + + + + + 在方括号表达式里,包围在[==]里的排序元素是一个等价类, 代表等效于那一个的所有排序元素的字符序列,包括它本身(如果没有其它等效排序元素,那么就好像封装定界符是[..])。例如,如果o^是一个等价类的成员,那么[[=o=]][[=^=]][o^]都是同义的。一个等价类不能是一个范围的端点。 + + + 在方括号表达式中,用 [::] 括起的字符类名表示属于该类的所有字符的列表。标准字符类名包括:alnumalphablankcntrldigitgraphlowerprintpunctspaceupperxdigit。它们表示 ctype3 中定义的字符类。区域设置可以提供其他字符类。字符类不能用作范围的端点。 + + 方括号表达式有两个特例:[[:<:]][[:>:]] 是约束,分别匹配单词开头和结尾的空字符串。单词定义为一个前后都没有单词字符的单词字符序列。单词字符是 alnum 字符(由 ctype3 定义)或下划线。这是与 POSIX 1003.2 兼容但未由其规定的扩展,在打算移植到其他系统的软件中应谨慎使用。通常更适合使用下文介绍的约束转义;它们并不更标准,但更容易键入。 + + + + 正则表达式转义 + + + 转义是以\开头,后面跟着一个字母或数字字符的特殊序列。 转义有好几种变体:字符输入转义、字符类简写转义、约束转义以及反向引用。在 ARE 里, 如果一个\后面跟着一个字母或数字,但是并未组成一个合法的转义, 那么它是非法的。在 ERE 中没有转义:在方括号表达式之外,一个后面跟着字母或数字字符的\只是表示该字符是一个普通的字符,而且在一个方括号表达式里,\是一个普通的字符(后者是 ERE 与 ARE 之间唯一的实际不兼容之处)。 + + + + 字符输入转义便于在 RE 中指定不可打印或其他不便输入的字符,见。 + + + + 字符类简写转义用来提供一些常用的字符类简写。它们显示在中。 + + + + 约束转义是以转义形式书写的约束,在满足特定条件时匹配空字符串,见。 + + + + 反向引用\n)匹配数字n指定的被前面的圆括号子表达式匹配的同一个串 (参阅)。 + 例如, ([bc])\1匹配bb或者cc, 但是不匹配bc或者cb。 + RE 中子表达式必须完全在反向引用前面。子表达式以它们的左圆括号的顺序编号。 + 非捕获圆括号并不定义子表达式。 + + + 正则表达式字符输入转义 + + + + + + 转义 + 描述 + + + + + + + \a + 警告(响铃)字符,和 C 中一样 + + + + \b + 退格,和 C 中一样 + + + + \B + 反斜线(\)的同义词,用来减少双写反斜线 + + + + \cX + (其中X是任意字符)低序5位和X相同的字符,它的其他位都是零 + + + + \e + 排序序列名称为ESC的字符;若不存在这样的字符,则使用八进制值为033的字符 + + + + \f + 换页,和 C 中一样 + + + + \n + 换行符,与 C 中相同 + + + + \r + 回车,和 C 中一样 + + + + \t + 水平制表符,和 C 中一样 + + + + \uwxyz + (其中wxyz正好是四个十六进制位)十六进制值为0xwxyz的字符 + + + + \Ustuvwxyz + (其中stuvwxyz正好是八个十六进制位)十六进制值为0xstuvwxyz的字符 + + + + + \v + 垂直制表符,和 C 中一样 + + + + \xhhh + (其中hhh是十六进制位的任意序列)十六进制值为0xhhh的字符(一个单一字符,不管用了多少个十六进制位) + + + + + \0 + 值为0(空字节)的字符 + + + + \xy + (其中xy正好是两个八进制位,并且不是一个反向引用)八进制值为0xy的字符 + + + + \xyz + (其中xyz正好是三个八进制位,并且不是一个反向引用)八进制值为0xyz的字符 + + + +
+ + + 十六进制位是0-9a-fA-F。八进制位是0-7。 + + + + 指定 ASCII 范围(0–127)之外的值的数字字符输入转义的含义取决于数据库编码。 + 当编码是 UTF-8 时,转义值等价于 Unicode 代码点,例如 + \u1234表示字符U+1234。对于其他多字节编码, + 字符输入转义通常只是指定该字符的字节值的串接。如果该转义值不对应数据库编码 + 中的任何合法字符,将不会发生错误,但是它不会匹配任何数据。 + + + + 字符输入转义总是被当作普通字符。例如,\135是 ASCII 中的], 但\135并不终止一个方括号表达式。 + + + + 正则表达式字符类简写转义 + + + + + + 转义 + 描述 + + + + + + \d + [[:digit:]] + + + + \s + [[:space:]] + + + + \w + [[:alnum:]_](注意包括下划线) + + + + \D + [^[:digit:]] + + + + \S + [^[:space:]] + + + + \W + [^[:alnum:]_](注意包括下划线) + + + +
+ + 在方括号表达式中,\d\s\w 会失去其外层方括号,而 \D\S\W 则是非法的。(因此,例如 [a-c\d] 等同于 [a-c[:digit:]]。此外,等同于 [a-c^[:digit:]][a-c\D] 是非法的。) + + + + 正则表达式约束转义 + + + + + + 转义 + 描述 + + + + + + + \A + 只在串开头匹配(与^的不同请参见 + + + + \m + 只在一个词的开头匹配 + + + + \M + 只在一个词的末尾匹配 + + + + \y + 只在一个词的开头或末尾匹配 + + + + \Y + 只在不属于单词开头或末尾的位置匹配 + + + + \Z + 只在串的末尾匹配(与$的不同请参见 + + + +
+ + + 单词的定义与上文[[:<:]][[:>:]]的说明相同。方括号表达式中不允许使用约束转义。 + + + + + 正则表达式反向引用 + + + + + + 转义 + 描述 + + + + + + + \m + (其中m是一个非零数字)一个到第m个子表达式的反向引用 + + + + \mnn + (其中m是一个非零数字,并且nn是后续的若干数字,并且十进制值mnn不大于此前已出现的捕获右圆括号数)一个到第mnn个子表达式的反向引用 + + + +
+ + + + + 八进制字符输入转义与反向引用之间存在固有歧义,按上文提到的启发式规则解决:前导零始终表示八进制转义。单个非零数字,如果后面没有其他数字,始终视为反向引用。不以零开头的多位数字序列,如果前面已有相应的子表达式(即该数字在反向引用的合法范围内),则视为反向引用,否则视为八进制转义。 + + +
+ + + 正则表达式元语法 + + + 除了上面描述的主要语法之外,还有几种特殊形式和杂项语法。 + + + + RE 可以以两种特殊的引导前缀之一开头。如果 RE 以***:开头,余下部分就被视为 ARE。(这在PostgreSQL中通常没有影响,因为 RE 默认被视为 ARE;但如果通过正则表达式函数的flags参数指定了 ERE 或 BRE 模式,它就会产生影响。)如果 RE 以***=开头,余下部分就作为按字面解释的字符串处理,所有字符都视为普通字符。 + + + + 一个 ARE 可以以嵌入选项开头:一个序列(?xyz)(这里的xyz是一个或多个字母字符)声明影响剩余 RE 的选项。 这些选项覆盖任何先前确定的选项 — 特别地,它们可以覆盖一个正则表达式操作符隐含的大小写敏感的行为,或者覆盖正则表达式函数的flags参数所指定的选项。可用的选项字母在中显示。注意这些同样的选项字母也被用在正则表达式函数的flags参数中。 + + + + ARE 嵌入选项字母 + + + + + + 选项 + 描述 + + + + + + + b + RE的剩余部分是一个BRE + + + + c + 大小写敏感的匹配(覆盖操作符类型) + + + + e + RE的剩余部分是一个ERE + + + + i + 大小写不敏感的匹配(见)(覆盖操作符类型) + + + + m + n的历史原因的同义词 + + + + n + 换行敏感的匹配(见 + + + + p + 部分换行敏感的匹配(见 + + + + q + RE 的剩余部分按字面(加引号)解释,所有字符都视为普通字符 + + + + s + 非换行敏感的匹配(默认) + + + + t + 紧凑语法(默认;见下文) + + + + w + 逆部分换行敏感(怪异)的匹配(见 + + + + x + 扩展语法(见下文) + + + +
+ + + 嵌入选项从结束该序列的)处开始生效。它们只能出现在 ARE 的开头(如果存在***:引导前缀,则位于该前缀之后)。 + + + + 除了通常的紧凑 RE 语法(其中所有字符都有意义)之外,还有一种扩展语法,可以通过指定嵌入的x选项来使用。在扩展语法中,RE 中的空白字符会被忽略,同样被忽略的还有#与其后的换行符(或 RE 末尾)之间的所有字符。这使得复杂的 RE 可以分段并添加注释。此基本规则有三个例外: + + + + 空白字符或 # 的前面若有 \,该字符就会被保留。 + + + + 方括号表达式里的空白或者#将被保留 + + + + + 在多字符符号里面不能出现空白和注释,例如(?: + + + + + 在这里,空白字符包括空格、制表符、换行符,以及属于space字符类的任何字符。 + + + + 最后,在 ARE 里,方括号表达式外面,序列(?#ttt)(其中ttt是任意不包含一个)的文本)是一个注释, 它被完全忽略。同样,这样的东西是不允许出现在多字符符号的字符中间的,例如 (?:。这种注释更像是一种历史产物而不是一种有用的设施,并且它们的使用已经被废弃;请使用扩展语法来替代。 + + + 如果指定了开头的***=引导前缀,那么这些元语法扩展都不能使用,因为这表示把用户输入作为按字面解释的字符串,而非 RE 处理。 + +
+ + + 正则表达式匹配规则 + + + 在 RE 可以在给定串中匹配多于一个子串的情况下, RE 匹配串中最靠前的那个子串。如果 RE 可以匹配在那个位置开始 的多个子串,要么是取最长的子串,要么是最短的,具体哪种, 取决于 RE 是贪婪的还是非贪婪的。 + + + + 一个 RE 是否贪婪取决于下面规则: + + + + 大多数原子以及所有约束,都没有贪婪属性(因为它们毕竟无法匹配长度不定的文本)。 + + + + + 在一个 RE 周围加上圆括号并不会改变其贪婪性。 + + + + + 带一个固定重复次数量词 ({m}或者{m}?) 的量化原子和原子自身具有同样的贪婪性(可能是没有)。 + + + + + 一个带其他普通的量词(包括{m,n}m等于n的情况)的量化原子是贪婪的(首选最长匹配)。 + + + + + 一个带非贪婪量词(包括{m,n}?m等于 n的情况)的量化原子是非贪婪的(首选最短匹配)。 + + + + + 一个分支 — 也就是说,一个没有顶级|操作符的 RE — 和它里面的第一个有贪婪属性的量化原子有着同样的贪婪性。 + + + + + 一个由|操作符连接起来的两个或者更多分支组成的 RE 总是贪婪的。 + + + + + + + 上面的规则所描述的贪婪属性不仅仅适用于独立的量化原子, 而且也适用于包含量化原子的分支和整个 RE。这里的意思是, 匹配是按照分支或者整个 RE 作为一个整体匹配最长或者最短的可能子串。 一旦整个匹配的长度确定,那么匹配任意特定子表达式的部分就基于该子表达式的贪婪属性进行判断,在 RE 里面靠前的子表达式的优先级高于靠后的子表达式。 + + + + 一个相应的示例: + +SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})'); +结果:123 +SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); +结果:1 + + 在第一个示例里,RE 作为整体是贪婪的,因为Y*是贪婪的。它可以匹配从Y开始的东西,并且它匹配从这个位置开始的最长的串, 也就是,Y123。输出是这里的圆括号包围的部分,或者说是123。在第二个示例里, RE 总体上是一个非贪婪的 RE,因为Y*?是非贪婪的。它可以匹配从Y开始的最短的子串,也就是说Y1。子表达式[0-9]{1,3}是贪婪的,但是它不能修改总体匹配长度的决定; 因此它被迫只匹配1。 + + + + 简而言之,如果一个 RE 同时包含贪婪和非贪婪的子表达式,那么总的匹配长度要么是尽可能长,要么是尽可能短,这取决于给整个 RE 赋予的属性。给子表达式赋予的属性只影响在这个匹配里,各个子表达式相对于其他子表达式能吃掉多少内容。 + + + + 量词{1,1}{1,1}?可以分别用于在一个子表达式 + 或者整个 RE 上强制贪婪或者非贪婪。当需要整个 RE 具有不同于从其元素中 + 推导出的贪婪属性时,这很有用。例如,假设我们尝试将一个包含一些数字的 + 字符串分隔成数字以及在它们之前和之后的部分,我们可能会尝试这样做: + +SELECT regexp_matches('abc01234xyz', '(.*)(\d+)(.*)'); +结果:{abc0123,4,xyz} + + 这不会有用:第一个.*是贪婪的,因此它会吃掉 + 尽可能多的字符而留下\d+去匹配在最后一个可能位置上的最 + 后一个数字。我们可能会通过让它变成非贪婪来修复: + +SELECT regexp_matches('abc01234xyz', '(.*?)(\d+)(.*)'); +结果:{abc,0,""} + + 这也不会有用:因为现在 RE 作为整体来说是非贪婪的,因此它会尽快结束 + 全部的匹配。我们可以通过强制 RE 整体是贪婪的来得到我们想要的: + +SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}'); +结果:{abc,01234,xyz} + + 独立于 RE 的组件的贪婪性之外控制 RE 的整体贪婪性为处理变长模式提供了 + 很大的灵活性。 + + + + 在决定更长或者更短的匹配时,匹配长度是以字符衡量的,而不是排序元素。一个空串会被认为比什么都不匹配长。例如:bb*匹配abbbc的中间三个字符;(week|wee)(night|knights)匹配weeknights的所有十个字符; 而(.*).*匹配 abc的时候,圆括号包围的子表达式匹配所有三个字符;当(a*)*被拿来匹配bc时,整个 RE 和圆括号 子表达式都匹配一个空串。 + + + + 如果指定不区分大小写的匹配,其效果近似于字母表中的所有大小写差别都消失了。当存在大小写形式的字母作为普通字符出现在方括号表达式之外时,实际上会转换为包含其大小写形式的方括号表达式,例如x变成[xX]。当它出现在方括号表达式内部时,其所有大小写形式都会加入该表达式,例如[x]变成[xX][^x]变成[^xX]。 + + + + 如果指定了换行敏感的匹配,.和使用^的方括号表达式 将永远不会匹配换行字符(这样,匹配就不会跨越行,除非 RE 显式安排了跨行匹配)并且^$除了分别匹配串开头和结尾之外,还将分别匹配换行后面和前面的空串。 + 但是 ARE 转义\A\Z仍然匹配串的开头和结尾。 + + + 如果指定了部分换行敏感的匹配,那么它影响.和方括号表达式, 这个时候和换行敏感的匹配一样,但是不影响^$。 + + + + 如果指定了逆部分换行敏感匹配,那么它影响^$,其作用和在换行敏感的匹配里一样,但是不影响.和方括号表达式。这个并不是很有用,只是为了满足对称性而提供的。 + + + + + 限制和兼容性 + + + 在这个实现里,对 RE 的长度没有特别的限制。但是,那些希望高移植性的程序应该避免使用长度超过 256 字节的 RE,因为 POSIX 兼容 的实现可以拒绝接受这样的 RE。 + + + + ARE 与 POSIX ERE 实际不兼容的唯一特性是:\在方括号表达式中不会失去特殊含义。其他所有 ARE 特性所使用的语法,在 POSIX ERE 中都是非法的,或其效果未定义或未指定;引导前缀的***语法同样不属于 POSIX 的 BRE 或 ERE 语法。 + + + + 许多 ARE 扩展借鉴自 Perl,但其中一些经过了整理和修改,也未实现少数 Perl 扩展。需要注意的不兼容之处包括\b\B、不对末尾换行符作特殊处理、取反的方括号表达式也受换行敏感匹配影响、先行和后行约束中对圆括号及反向引用的限制,以及采用最长或最短匹配而非首次匹配的语义。 + + + ARE 与旧版 ERE 语法存在两点重要的不兼容,这里比较的是 7.4 之前的PostgreSQL: + + + + 在 ARE 中,\ 后跟字母或数字字符时,要么表示转义,要么是错误;而在以前的版本中,它只是书写该字母或数字字符的另一种方式。这应该不是什么大问题,因为以前的版本没有理由使用这样的序列。 + + + 在 ARE 中,\[] 内仍是特殊字符,因此方括号表达式内的字面 \ 必须写成 \\ + + + + + + + + 基本正则表达式 + + + BRE 在几个方面和 ERE 不太一样。在 BRE 中,|+?都是普通字符并且没有与它们功能等价的东西。范围的定界符是\{\},而{}本身是普通字符。嵌套的子表达式的圆括号是\(\),而()自身是普通字符。除非在 RE 开头或者是圆括号子表达式开头,^都是一个普通字符。 除非在 RE 结尾或者是圆括号子表达式的结尾,$是一个普通字符。如果*出现在 RE 开头或者是圆括号封装的子表达式开头 (前面可能有^),那么它是个普通字符。最后,可以用单数字的反向引用,\<\>分别是[[:<:]][[:>:]]的同义词;在 BRE 中没有其它可用的转义。 + + + + + +
+
+ + + + 数据类型格式化函数 + + + 格式化 + + + + PostgreSQL格式化函数提供一套强大的工具用于把各种数据类型 (日期/时间、整数、浮点数、数值) 转换成格式化的字符串以及反过来从格式化的字符串转换成 指定的数据类型。列出了这些函数。这些函数都遵循一个公共的调用规范: 第一个参数是待格式化的值,而第二个是一个定义输出或输入格式的模板。 + + + + 格式化函数 + + + + 函数 + 返回类型 + 描述 + 示例 + + + + + to_char to_char(timestamp, text) + text + 将时间戳转换为字符串 + to_char(current_timestamp, 'HH12:MI:SS') + + + to_char(interval, text) + text + 将时间间隔转换为字符串 + to_char(interval '15h 2m 12s', 'HH24:MI:SS') + + + to_char(int, text) + text + 将整数转换为字符串 + to_char(125, '999') + + + to_char(double precision, + text) + text + 将 real/double precision 转换为字符串 + to_char(125.8::real, '999D9') + + + to_char(numeric, text) + text + 将 numeric 转换为字符串 + to_char(-125.8, '999D99S') + + + to_date to_date(text, text) + date + 将字符串转换为日期 + to_date('05 Dec 2000', 'DD Mon YYYY') + + + to_number to_number(text, text) + numeric + 将字符串转换为 numeric + to_number('12,454.8-', '99G999D9S') + + + to_timestamp to_timestamp(text, text) + timestamp with time zone + 将字符串转换为时间戳 + to_timestamp('05 Dec 2000', 'DD Mon YYYY') + + + +
+ + + 还有一个接受单个参数的 to_timestamp 函数;参见 + + + + + 在一个to_char输出模板串中,一些特定的模式可以被识别并且被替换成基于给定值的被恰当地格式化的数据。任何不属于模板模式的文本都简单地照字面拷贝。同样,在一个输入 模板串里(对其他函数),模板模式标识由输入数据串提供的值。 + + + + 展示了可以用于格式化日期和时间值的模板模式。 + + + + 用于日期/时间格式化的模板模式 + + + + + 模式 + 描述 + + + + + HH + 一天中的小时(01-12) + + + HH12 + 一天中的小时(01-12) + + + HH24 + 一天中的小时(00-23) + + + MI + 分钟(00-59) + + + SS + 秒(00-59) + + + MS + 毫秒(000-999) + + + US + 微秒(000000-999999) + + + SSSS + 自午夜起的秒数(0-86399) + + + AM, am, + PMpm + 上午/下午标记(不带句点) + + + A.M., a.m., + P.M.p.m. + 上午/下午标记(带句点) + + + Y,YYY + 带逗号的年(4 位或者更多位) + + + YYYY + 年(4 位或者更多位) + + + YYY + 年的最后 3 位数字 + + + YY + 年的最后 2 位数字 + + + Y + 年的最后 1 位数字 + + + IYYY + ISO 8601 周编号方式的年(4 位或更多位) + + + IYY + ISO 8601 周编号方式的年的最后 3 位数字 + + + IY + ISO 8601 周编号方式的年的最后 2 位数字 + + + I + ISO 8601 周编号方式的年的最后 1 位数字 + + + BC, bc, + ADad + 纪元指示器(不带句号) + + + B.C., b.c., + A.D.a.d. + 纪元指示器(带句号) + + + MONTH + 大写的月份全称(空格补齐到 9 字符) + + + Month + 首字母大写的月份全称(空格补齐到 9 字符) + + + month + 小写的月份全称(空格补齐到 9 字符) + + + MON + 简写的大写形式的月名(英文 3 字符,本地化长度可变) + + + Mon + 简写的首字母大写形式的月名(英文 3 字符,本地化长度可变) + + + mon + 简写的小写形式的月名(英文 3 字符,本地化长度可变) + + + MM + 月份编号(01-12) + + + DAY + 大写的星期全称(空格补齐到 9 字符) + + + Day + 首字母大写的星期全称(空格补齐到 9 字符) + + + day + 小写的星期全称(空格补齐到 9 字符) + + + DY + 大写的星期简称(英语 3 字符,本地化长度可变) + + + Dy + 首字母大写的星期简称(英语 3 字符,本地化长度可变) + + + dy + 小写的星期简称(英语 3 字符,本地化长度可变) + + + DDD + 一年中的第几天(001-366) + + + IDDD + ISO 8601 周编号年中的第几天(001-371;一年的第 1 天是第一个 ISO 周的星期一) + + + DD + 月内日序数(01-31) + + + D + 星期几,周日 (1) 到周六 (7) + + + ID + ISO 8601 星期几,周一 (1) 到周日 (7) + + + W + 月内周序数(1-5)(第一周从该月第一天开始) + + + WW + 一年中的第几周(1-53)(第一周从该年第一天开始) + + + IW + ISO 8601 周编号年中的第几周(01-53;该年的第一个星期四位于第 1 周) + + + CC + 世纪(2 位数)(21 世纪开始于 2001-01-01) + + + J + 儒略日期(从本地午夜的公元前 4714 年 11 月 24 日开始的整数日数;参见 + + + Q + 季度(被to_dateto_timestamp忽略) + + + RM + 以大写罗马数字表示的月份(I-XII;I=一月) + + + rm + 以小写罗马数字表示的月份(i-xii;i=一月) + + + TZ + 大写形式的时区缩写(仅在to_char中支持) + + + tz + 小写形式的时区缩写(仅在to_char中支持) + + + OF + 相对于 UTC 的时区偏移(仅在to_char中支持) + + + +
+ + + 修饰符可以被应用于模板模式来修改它们的行为。例如,FMMonth就是带着FM修饰符的Month模式。展示了可用于日期/时间格式化的修饰符模式。 + + + + 用于日期/时间格式化的模板模式修饰符 + + + + + 修饰符 + 描述 + 示例 + + + + + FM 前缀 + 填充模式(抑制前导零和填充的空格) + FMMonth + + + TH 后缀 + 大写形式的序数后缀 + DDTH,例如, 12TH + + + th 后缀 + 小写形式的序数后缀 + DDth,例如, 12th + + + FX 前缀 + 固定格式全局选项(见使用须知) + FX Month DD Day + + + TM 前缀 + 翻译模式(根据 输出本地化的星期名和月份名) + TMMonth + + + SP 后缀 + 拼写模式(未实现) + DDSP + + + +
+ + 日期/时间格式化的使用注意事项: + + + + FM抑制了在模式输出中添加前导零和尾随空格的行为,这些前导零和尾随空格 + 本来会被添加以使输出成为固定宽度。在PostgreSQL中, + FM仅修改下一个格式说明,而在Oracle中FM影响所有后续 + 格式说明,并且重复的FM修饰符切换填充模式的开启和关闭。 + + + + + TM 不包含尾随空格。to_timestampto_date 会忽略 TM 修饰符。 + + + + to_timestampto_date 会跳过输入字符串中的多个空格,除非使用 FX 选项。例如,to_timestamp('2000    JUN', 'YYYY MON') 可以工作,但 to_timestamp('2000    JUN', 'FXYYYY MON') 会报错,因为 to_timestamp 只接受一个空格。FX 必须指定为模板中的第一项。 + + + + + to_timestampto_date存在的目的是处理无法通过简单类型转换完成的输入格式。这些函数宽大地解释输入,只做最少的错误检查。虽然它们能产生有效的输出,但转换可能产生意外的结果。例如,这些函数的输入不受正常范围限制,因此to_date('20096040','YYYYMMDD')会返回2014-01-17而不是报错。类型转换则没有这种行为。 + + + + + to_char 模板中允许普通文本,并会按字面输出。可以用双引号括起子串,使其即使包含模式关键字也强制按字面文本解释。例如,在 '"Hello Year "YYYY' 中,YYYY 会被年份数据替换,但 Year 中单独的 Y 不会被替换。在 to_dateto_numberto_timestamp 中,双引号字符串会跳过与该字符串所含字符数相同数量的输入字符,例如 "XX" 跳过两个输入字符。 + + + + 如果要在输出中包含双引号,必须在它前面加上反斜杠,例如 '\"YYYY Month\"' + + + + + + 如果年份格式规范少于四位数字,例如YYY,并且提供的年份少于四位数字, + 年份将被调整为最接近2020年的年份,例如95变为1995年。 + + + + + + + 在to_timestampto_date中, + 负年份被视为BC纪元。如果同时写入负年份和显式的BC字段, + 则再次得到AD。年份零被视为公元前1年。 + + + + + + 在to_timestampto_date中, + YYYY转换在处理超过4位数字的年份时有限制。您必须在YYYY后使用一些非数字字符或模板, + 否则年份总是被解释为4位数字。例如(使用年份20000): + to_date('200001131', 'YYYYMMDD')将被解释为4位年份;而应该在年份后使用非数字分隔符,如 + to_date('20000-1131', 'YYYY-MMDD')或 + to_date('20000Nov31', 'YYYYMonDD')。 + + + + + + + 在从字符串到timestampdate的转换中, + 如果存在YYYYYYYY,YYY字段, + 则忽略CC(世纪)字段。如果CC与 + YYY一起使用,则年份按指定世纪中的该年计算。 + 如果指定了世纪但未指定年份,则假定为该世纪的第一年。 + + + + + + + 可以向to_timestampto_date以下列两种方式之一指定 ISO 8601 周编号日期(与公历日期不同): + + + + 年份、周编号和星期几:例如to_date('2006-42-4', 'IYYY-IW-ID') + 返回日期2006-10-19。 + 如果省略星期几,则假定为1(星期一)。 + + + + + 年份和年内日序数:例如to_date('2006-291', 'IYYY-IDDD')也返回2006-10-19。 + + + + + + + 尝试使用ISO 8601周编号字段和公历日期字段的混合输入日期是荒谬的,并将导致错误。 + 在ISO 8601周编号年的背景下,月份月内日序数的概念没有意义。 + 在公历年的背景下,ISO周没有意义。 + + + + + 虽然to_date会拒绝混合使用公历和ISO周编号日期字段, + 但to_char不会,因为输出格式规范如YYYY-MM-DD (IYYY-IDDD)可能很有用。 + 但要避免编写类似IYYY-MM-DD的内容;那会在年初附近产生令人惊讶的结果。 + (有关更多信息,请参见。) + + + + + + + + 在从字符串到timestamp的转换中,毫秒(MS)或微秒(US)值被用作小数点后的秒数位。 + 例如to_timestamp('12:3', 'SS:MS')不是3毫秒,而是300,因为转换将其计为12 + 0.3秒。 + 这意味着对于格式SS:MS,输入值12:312:3012:300指定相同数量的毫秒。 + 要获得三毫秒,必须写成12:003,转换将其计为12 + 0.003 = 12.003秒。 + + + + 这是一个更复杂的示例: + to_timestamp('15:12:02.020.001230', 'HH24:MI:SS.MS.US') + 为15小时12分钟,秒数为2秒 + 20毫秒 + 1230微秒 = 2.021230秒。 + + + + + + + to_char(..., 'ID')的星期几编号与extract(isodow from ...)函数匹配, + 但to_char(..., 'D')的不匹配extract(dow from ...)的星期几编号。 + + + + + + + to_char(interval) 按照如12小时制时钟上显示的方式格式化HHHH12, + 即零小时和36小时都输出为12, + 而HH24输出完整的小时值,对于时间间隔它可以超过23。 + + + + + + + + 展示了可以用于格式化数值的模板模式。 + + + + + 用于数值格式化的模板模式 + + + + + 模式 + 描述 + + + + + + 9 + 数位(非有效位可以被省略) + + + + 0 + 数位(即便是非有效位也不会被省略) + + + + .(句点) + 小数点 + + + + ,(逗号) + 分组(千)分隔符 + + + + PR + 尖括号内的负值 + + + + S + 紧贴数值的正负号(使用区域设置) + + + + L + 货币符号(使用区域设置) + + + + D + 小数点(使用区域设置) + + + + G + 分组分隔符(使用区域设置) + + + + MI + 在指定位置的负号(如果数字 < 0) + + + + PL + 在指定位置的正号(如果数字 > 0) + + + + SG + 在指定位置的正/负号 + + + + RN + 罗马数字(输入在 1 和 3999 之间) + + + + THth + 序数后缀 + + + + V + 移动指定位数(参阅注解) + + + + EEEE + 科学记数的指数 + + + +
+ + 数值格式化的使用注意事项: + + + + 0指定一个数字位置,即使它包含前导/尾随零,也将始终打印出来。 + 9也指定一个数字位置,但如果它是一个前导零,则将被替换为一个空格, + 而如果它是一个尾随零并且指定了填充模式,则将被删除。 + (对于to_number(),这两个模式字符是等效的。) + + + + + + + 模式字符SLDG表示当前区域设置定义的正负号、货币符号、小数点和千位分隔符字符 + (参见 + 和)。模式字符句点和逗号表示这些确切字符,具有小数点和千位分隔符的含义,不受区域设置影响。 + + + + + + + 如果to_char()的模式中没有明确指定正负号的位置,就会为正负号保留一列,并使其紧贴数值(紧靠数值左侧)。如果S紧邻若干个9的左侧,它同样会紧贴数值。 + + + + + + + 使用SGPLMI格式化的正负号不紧贴数值; + 例如,to_char(-12, 'MI9999')会产生'-  12', + 但to_char(-12, 'S9999')会产生'  -12'。 + (Oracle实现不允许在9之前使用MI,而是要求9MI之前。) + + + + + + + TH不会转换小于零的值,也不会转换小数。 + + + + + + + PLSG和 + THPostgreSQL + 的扩展。 + + + + + + + Vto_char一起, + 将输入值乘以10^n, + 其中n是跟在V后面的数字位数。 + Vto_number一起以类似的方式进行除法。 + to_charto_number不支持与小数点结合使用的V + (例如,不允许使用99.9V99)。 + + + + + + + EEEE(科学计数法)不能与任何其他格式模式或修饰符结合使用,除了数字和小数点模式之外,必须位于格式字符串的末尾(例如,9.99EEEE是一个有效模式)。 + + + + + + + 某些修饰符可以被应用到任何模板来改变其行为。例如,FM99.99是带有FM修饰符的99.99模式。中展示了用于数值格式化的模式修饰符。 + + + + + 用于数值格式化的模板模式修饰符 + + + + + 修饰符 + 描述 + 示例 + + + + + + FM 前缀 + 填充模式(抑制尾随零和填充的空白) + FM99.99 + + + + TH 后缀 + 大写序数后缀 + 999TH + + + + th 后缀 + 小写序数后缀 + 999th + + + +
+ + + 展示了一些使用to_char函数的示例。 + + + + + <function>to_char</function>示例 + + + + + 表达式 + 结果 + + + + + to_char(current_timestamp, 'Day, DD  HH12:MI:SS') + 'Tuesday  , 06  05:39:18' + + + to_char(current_timestamp, 'FMDay, FMDD  HH12:MI:SS') + 'Tuesday, 6  05:39:18' + + + to_char(-0.1, '99.99') + '  -.10' + + + to_char(-0.1, 'FM9.99') + '-.1' + + + to_char(-0.1, 'FM90.99') + '-0.1' + + + to_char(0.1, '0.9') + ' 0.1' + + + to_char(12, '9990999.9') + '    0012.0' + + + to_char(12, 'FM9990999.9') + '0012.' + + + to_char(485, '999') + ' 485' + + + to_char(-485, '999') + '-485' + + + to_char(485, '9 9 9') + ' 4 8 5' + + + to_char(1485, '9,999') + ' 1,485' + + + to_char(1485, '9G999') + ' 1 485' + + + to_char(148.5, '999.999') + ' 148.500' + + + to_char(148.5, 'FM999.999') + '148.5' + + + to_char(148.5, 'FM999.990') + '148.500' + + + to_char(148.5, '999D999') + ' 148,500' + + + to_char(3148.5, '9G999D999') + ' 3 148,500' + + + to_char(-485, '999S') + '485-' + + + to_char(-485, '999MI') + '485-' + + + to_char(485, '999MI') + '485 ' + + + to_char(485, 'FM999MI') + '485' + + + to_char(485, 'PL999') + '+485' + + + to_char(485, 'SG999') + '+485' + + + to_char(-485, 'SG999') + '-485' + + + to_char(-485, '9SG99') + '4-85' + + + to_char(-485, '999PR') + '<485>' + + + to_char(485, 'L999') + 'DM 485' + + + to_char(485, 'RN') + '        CDLXXXV' + + + to_char(485, 'FMRN') + 'CDLXXXV' + + + to_char(5.2, 'FMRN') + 'V' + + + to_char(482, '999th') + ' 482nd' + + + to_char(485, '"Good number:"999') + 'Good number: 485' + + + to_char(485.8, '"Pre:"999" Post:" .999') + 'Pre: 485 Post: .800' + + + to_char(12, '99V999') + ' 12000' + + + to_char(12.4, '99V999') + ' 12400' + + + to_char(12.45, '99V9') + ' 125' + + + to_char(0.0004859, '9.99EEEE') + ' 4.86e-04' + + + +
+ +
+ + + + 日期/时间函数和操作符 + + + 展示了可用于处理日期/时间值的函数,其细节在随后的小节中描述。演示了基本算术操作符 (+*等)的行为。 而与格式化相关的函数,可以参考。你应当熟悉中的日期/时间数据类型的背景知识。 + + + + 此外, 中显示的常用比较操作符也适用于日期/时间类型。 + 日期和时间戳(带或不带时区)都是可比较的,而时间(带或不带时区)和间隔只能与相同数据类型的其他值进行比较。 + 将不带时区的时间戳与带时区的时间戳进行比较时,前者的值假定是在配置参数指定的时区中给出的,并被转换到UTC,以便与后者的值进行比较(其已经在内部用UTC)。 + 类似地,日期值会被假定表示TimeZone区域中的午夜,当它与时间戳进行比较时。 + + + + 所有下文描述的接受timetimestamp输入的函数和操作符实际上都有两种变体: 一种接收time with time zonetimestamp with time zone, 另外一种接受time without time zone或者 timestamp without time zone。 + 为了简化,这些变种没有被独立地展示。 + 此外,+*操作符都是可交换的操作符对(例如,date + integer 和 integer + date);我们只显示每一对中的一个。 + + + + 日期/时间操作符 + + + + + 操作符 + 示例 + 结果 + + + + + + + + date '2001-09-28' + integer '7' + date '2001-10-05' + + + + + + date '2001-09-28' + interval '1 hour' + timestamp '2001-09-28 01:00:00' + + + + + + date '2001-09-28' + time '03:00' + timestamp '2001-09-28 03:00:00' + + + + + + interval '1 day' + interval '1 hour' + interval '1 day 01:00:00' + + + + + + timestamp '2001-09-28 01:00' + interval '23 hours' + timestamp '2001-09-29 00:00:00' + + + + + + time '01:00' + interval '3 hours' + time '04:00:00' + + + + - + - interval '23 hours' + interval '-23:00:00' + + + + - + date '2001-10-01' - date '2001-09-28' + integer '3'(天) + + + + - + date '2001-10-01' - integer '7' + date '2001-09-24' + + + + - + date '2001-09-28' - interval '1 hour' + timestamp '2001-09-27 23:00:00' + + + + - + time '05:00' - time '03:00' + interval '02:00:00' + + + + - + time '05:00' - interval '2 hours' + time '03:00:00' + + + + - + timestamp '2001-09-28 23:00' - interval '23 hours' + timestamp '2001-09-28 00:00:00' + + + + - + interval '1 day' - interval '1 hour' + interval '1 day -01:00:00' + + + + - + timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00' + interval '1 day 15:00:00' + + + + * + 900 * interval '1 second' + interval '00:15:00' + + + + * + 21 * interval '1 day' + interval '21 days' + + + + * + double precision '3.5' * interval '1 hour' + interval '03:30:00' + + + + / + interval '1 hour' / double precision '1.5' + interval '00:40:00' + + + +
+ + + 日期/时间函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + age age(timestamp, timestamp) + interval + + 将两个参数相减,生成一个使用年和月,而不是只用日的符号化的结果 + + age(timestamp '2001-04-10', timestamp '1957-06-13') + 43 years 9 mons 27 days + + + + age(timestamp) + interval + current_date(午夜)减去 + age(timestamp '1957-06-13') + 43 years 8 mons 3 days + + + + clock_timestamp clock_timestamp() + timestamp with time zone + + 当前日期和时间(在语句执行期间变化);参见 + + + + + + + current_date current_date + date + + 当前日期;参见 + + + + + + + current_time current_time + time with time zone + 一天中的当前时刻;参见 + + + + + + current_timestamp current_timestamp + timestamp with time zone + 当前日期和时间(当前事务开始时);参见 + + + + + + date_part date_part(text, timestamp) + double precision + 获取子字段(等价于 extract);参见 + date_part('hour', timestamp '2001-02-16 20:38:40') + 20 + + + + date_part(text, interval) + double precision + 获取子字段(等价于 extract);参见 + date_part('month', interval '2 years 3 months') + 3 + + + + date_trunc date_trunc(text, timestamp) + timestamp + 截断到指定精度;另见 + date_trunc('hour', timestamp '2001-02-16 20:38:40') + 2001-02-16 20:00:00 + + + + date_trunc(text, interval) + interval + 截断到指定精度;另见 + date_trunc('hour', interval '2 days 3 hours 40 minutes') + 2 days 03:00:00 + + + + extract extract(field from timestamp) + double precision + 获取子字段;参见 + extract(hour from timestamp '2001-02-16 20:38:40') + 20 + + + + extract(field from interval) + double precision + 获取子字段;参见 + extract(month from interval '2 years 3 months') + 3 + + + + isfinite isfinite(date) + boolean + + 测试日期是否有限(不是正负无穷) + + isfinite(date '2001-02-16') + true + + + + isfinite(timestamp) + boolean + 测试时间戳是否有限(不是正负无穷) + isfinite(timestamp '2001-02-16 21:28:30') + true + + + + isfinite(interval) + boolean + 测试时间间隔是否有限 + isfinite(interval '4 hours') + true + + + + justify_days justify_days(interval) + interval + 调整时间间隔,使 30 天的时段表示为月 + justify_days(interval '35 days') + 1 mon 5 days + + + + justify_hours justify_hours(interval) + interval + 调整时间间隔,使 24 小时的时段表示为天 + justify_hours(interval '27 hours') + 1 day 03:00:00 + + + + justify_interval justify_interval(interval) + interval + + 使用 justify_daysjustify_hours调整时间间隔,并额外调整符号 + + justify_interval(interval '1 mon -1 hour') + 29 days 23:00:00 + + + + localtime localtime + time + 一天中的当前时刻;参见 + + + + + + localtimestamp localtimestamp + timestamp + 当前日期和时间(当前事务开始时);参见 + + + + + + make_date make_date(year int, month int, day int) + date + + 从年、月和日字段创建日期 + + make_date(2013, 7, 15) + 2013-07-15 + + + + make_interval make_interval(years int DEFAULT 0, months int DEFAULT 0, weeks int DEFAULT 0, days int DEFAULT 0, hours int DEFAULT 0, mins int DEFAULT 0, secs double precision DEFAULT 0.0) + interval + 从年、月、周、日、小时、分钟和秒字段创建时间间隔 + make_interval(days => 10) + 10 days + + + + make_time make_time(hour int, min int, sec double precision) + time + + 从小时、分钟和秒字段创建时间 + + make_time(8, 15, 23.5) + 08:15:23.5 + + + + make_timestamp make_timestamp(year int, month int, day int, hour int, min int, sec double precision) + timestamp + + 从年、月、日、小时、分钟和秒字段创建时间戳 + + make_timestamp(2013, 7, 15, 8, 15, 23.5) + 2013-07-15 08:15:23.5 + + + + make_timestamptz make_timestamptz(year int, month int, day int, hour int, min int, sec double precision, timezone text ) + timestamp with time zone + 从年、月、日、小时、分钟和秒字段创建带时区的时间戳;如果未指定 timezone,则使用当前时区。 + make_timestamptz(2013, 7, 15, 8, 15, 23.5) + 2013-07-15 08:15:23.5+01 + + + + now now() + timestamp with time zone + 当前日期和时间(当前事务开始时);参见 + + + + + + statement_timestamp statement_timestamp() + timestamp with time zone + + 当前日期和时间(当前语句开始时);参见 + + + + + + + timeofday timeofday() + text + + 当前的日期和时间 + (类似 clock_timestamp, 但是采用 text 字符串);参见 + + + + + + + transaction_timestamp transaction_timestamp() + timestamp with time zone + 当前日期和时间(当前事务开始时);参见 + + + + + to_timestamp to_timestamp(double precision) + timestamp with time zone + 将 Unix 纪元时间(自 1970-01-01 00:00:00+00 起的秒数)转换为时间戳 + to_timestamp(1284352323) + 2010-09-13 04:32:03+00 + + + +
+ + + + OVERLAPS + + 除了这些函数以外,还支持 SQL 操作符OVERLAPS: + +(start1, end1) OVERLAPS (start2, end2) +(start1, length1) OVERLAPS (start2, length2) + + 这个表达式在两个时间段(用它们的端点定义)重叠的时候得到真,当它们不重叠时得到假。端点可以用一对日期、时间或者时间戳来指定;或者是用一个后面跟着一个间隔的日期、时间或时间戳来指定。当一对值被提供时,起点或终点都可以被写在前面,OVERLAPS会自动地把较早的值作为起点。每一个时间段被认为是表示半开区间start <= time < end,除非startend相等,这种情况下它表示单个时刻。例如这表示两个只有一个共同端点的时间段不重叠。 + + + +SELECT (DATE '2001-02-16', DATE '2001-12-21') OVERLAPS + (DATE '2001-10-30', DATE '2002-10-30'); +结果:true +SELECT (DATE '2001-02-16', INTERVAL '100 days') OVERLAPS + (DATE '2001-10-30', DATE '2002-10-30'); +结果:false +SELECT (DATE '2001-10-29', DATE '2001-10-30') OVERLAPS + (DATE '2001-10-30', DATE '2001-10-31'); +结果:false +SELECT (DATE '2001-10-30', DATE '2001-10-30') OVERLAPS + (DATE '2001-10-30', DATE '2001-10-31'); +结果:true + + + 当把一个interval值加到某个时间戳上(或从该时间戳中减去一个interval值)时,如果该时间戳为timestamp with time zone类型,天数部分会相应增加或减少timestamp with time zone的日期,变化天数为所指定的天数,而一天中的时刻保持不变。当跨越夏令时变化时(会话时区设为识别夏令时的时区),这意味着interval '1 day'不一定等于interval '24 hours'。例如,当会话时区设置为America/Denver: + +SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '1 day'; +结果:2005-04-03 12:00:00-06 +SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '24 hours'; +结果:2005-04-03 13:00:00-06 +出现这种情况,是因为夏令时变化跳过了一个小时;变化发生的时间为2005-04-03 02:00:00,所在时区为America/Denver。 + + + + 注意,age返回的months字段可能存在歧义,因为不同月份的天数不同。PostgreSQL在计算不足整月的部分时,会采用两个日期中较早的那个日期所在的月份。例如:age('2004-06-01', '2004-04-30')使用 4 月得到1 mon 1 day,而如果使用 5 月则会得到1 mon 2 days,因为 5 月有 31 天,而 4 月只有 30 天。 + + + + 日期和时间戳的减法也可能很复杂。一种概念上简单的方法是,先用EXTRACT(EPOCH FROM ...)将各值转换为秒数,然后将结果相减;这样得到的是两个值之间的数。这种方法会针对每个月的天数、时区变化和夏令时变化进行调整。 + 用-操作符将日期或时间戳值相减,会返回两个值之间的天数(每一天为 24 小时)和时/分/秒,也会作相同的调整。age函数返回年、月、日和时/分/秒,它会逐字段相减,然后调整负值字段。 + 以下查询显示了这些方法的差异。示例结果在timezone = 'US/Eastern'设置下产生;所用的两个日期之间发生了夏令时切换: + + + +SELECT EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') - + EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00'); +结果:10537200 +SELECT (EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') - + EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00')) + / 60 / 60 / 24; +结果:121.958333333333 +SELECT timestamptz '2013-07-01 12:00:00' - timestamptz '2013-03-01 12:00:00'; +结果:121 days 23:00:00 +SELECT age(timestamptz '2013-07-01 12:00:00', timestamptz '2013-03-01 12:00:00'); +结果:4 mons + + + + <function>EXTRACT</function>, <function>date_part</function> + + + date_part + + + extract + + + +EXTRACT(field FROM source) + + + extract函数从日期/时间值中提取年份、小时等子字段。source必须是以下类型的值表达式:timestamptime,或interval。(date类型的表达式会转换为timestamp,因此也可以使用。)field是一个标识符或字符串,用于选择从源值中提取的字段。extract函数返回的值的类型为double precision。以下是有效的字段名称: + + century + + 世纪 + + +SELECT EXTRACT(CENTURY FROM TIMESTAMP '2000-12-16 12:21:13'); +结果:20 +SELECT EXTRACT(CENTURY FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:21 + + + 第一个世纪始于公元 0001-01-01 00:00:00,尽管当时的人们并不知道。这一定义适用于所有采用格里高利历的国家。世纪编号没有 0,而是从 -1 世纪直接跳到 1 世纪。如果你对此有异议,请向梵蒂冈罗马圣彼得大教堂的教皇投诉。 + + + + + day + + 对于 timestamp 值,表示月份中的日期字段(1 - 31);对于 interval 值,表示天数 + + +SELECT EXTRACT(DAY FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:16 +SELECT EXTRACT(DAY FROM INTERVAL '40 days 1 minute'); +结果:40 + + + + + + + + + decade + + + + 年份字段除以10 + + + +SELECT EXTRACT(DECADE FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:200 + + + + + + dow + + + + 一周的日子从星期天(0)到星期六(6) + + + +SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:5 + + + + 请注意extract函数的星期几编号与to_char(..., 'D')函数不同。 + + + + + + + doy + + 一年中的第几天(1 - 365/366) + + +SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:47 + + + + + + epoch + + + 对于timestamp with time zone值,自1970-01-01 00:00:00 UTC以来的秒数(早于该时刻的时间戳对应负值); + 对于datetimestamp值,自1970-01-01 00:00:00以来的名义秒数,不考虑时区或夏令时规则; + 对于interval值,间隔中的总秒数 + + + +SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40.12-08'); +结果:982384720.12 +SELECT EXTRACT(EPOCH FROM TIMESTAMP '2001-02-16 20:38:40.12'); +结果:982355920.12 +SELECT EXTRACT(EPOCH FROM INTERVAL '5 days 3 hours'); +结果:442800 + + + + 您可以使用to_timestamp将一个 epoch 值转换回timestamp with time zone: + + +SELECT to_timestamp(982384720.12); +结果:2001-02-17 04:38:40.12+00 + + + + 注意,将to_timestamp应用于从datetimestamp值中提取的 epoch 值可能会产生误导性的结果: + 结果实际上会假定原始值是以UTC时间给出的,这可能并非事实。 + + + + + + hour + + 小时字段(0 - 23) + + +SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:20 + + + + + + isodow + + + + 一周的日子从星期一(1)到星期日(7) + + + +SELECT EXTRACT(ISODOW FROM TIMESTAMP '2001-02-18 20:38:40'); +结果:7 + + + + 这与dow相同,除了星期天。这匹配ISO 8601的星期几编号。 + + + + + + + isoyear + + 日期所属的 ISO 8601 周编号年份(不适用于间隔) + + +SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-01'); +结果:2005 +SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-02'); +结果:2006 + + + 每个 ISO 8601 周编号年都从包含 1 月 4 日的那一周的星期一开始,因此在 1 月初或 12 月末,ISO 年可能与格里高利年不同。更多信息请参见 week 字段。 + PostgreSQL 8.3 之前的版本不支持此字段。 + + + + + julian + + 与日期或时间戳对应的儒略日(不适用于间隔)。非本地午夜的时间戳会产生带小数部分的值。更多信息请参见 + + +SELECT EXTRACT(JULIAN FROM DATE '2006-01-01'); +结果:2453737 +SELECT EXTRACT(JULIAN FROM TIMESTAMP '2006-01-01 12:00'); +结果:2453737.5 + + + + + + microseconds + + + + 秒字段,包括小数部分,乘以1 000 000;注意这包括完整的秒数 + + + +SELECT EXTRACT(MICROSECONDS FROM TIME '17:12:28.5'); +结果:28500000 + + + + + + millennium + + 千年 + + +SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:3 + + + 1900 年代的年份属于第二个千年。第三个千年始于 2001 年 1 月 1 日。 + + + + + milliseconds + + + 秒字段,包括小数部分,乘以1000。请注意,这包括完整的秒数。 + + + +SELECT EXTRACT(MILLISECONDS FROM TIME '17:12:28.5'); +结果:28500 + + + + + + minute + + 分钟字段(0 - 59) + + +SELECT EXTRACT(MINUTE FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:38 + + + + + + month + + 对于 timestamp 值,表示一年中的月份编号(1 - 12);对于 interval 值,表示月数对 12 取模(0 - 11) + + +SELECT EXTRACT(MONTH FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:2 +SELECT EXTRACT(MONTH FROM INTERVAL '2 years 3 months'); +结果:3 +SELECT EXTRACT(MONTH FROM INTERVAL '2 years 13 months'); +结果:1 + + + + + + quarter + + 日期所在的一年中的季度(1 - 4) + + +SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:1 + + + + + + second + + 秒字段,包括小数部分(0 - 59如果操作系统实现了闰秒,则为 60 + + +SELECT EXTRACT(SECOND FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:40 +SELECT EXTRACT(SECOND FROM TIME '17:12:28.5'); +结果:28.5 + + + + + timezone + + + + 与UTC的时区偏移量,以秒为单位。正值对应于UTC东部的时区,负值对应于UTC西部的时区。 + (从技术上讲,PostgreSQL不使用UTC,因为不处理闰秒。) + + + + + + timezone_hour + + + + 时区偏移的小时部分 + + + + + + timezone_minute + + + + 时区偏移的分钟部分 + + + + + + week + + + + 一年中按ISO 8601 周编号体系计算的周序号。根据定义,ISO 周从周一开始,一年的第一周包含该年的 1 月 4 日。换句话说,一年的第一个星期四在该年的第 1 周。 + + + + 在 ISO 周编号体系中,1 月初的日期可能属于前一年的第 52 周或第 53 周,而 12 月末的日期可能属于下一年的第一周。例如,2005-01-01属于 2004 年的第 53 周,2006-01-01属于 2005 年的第 52 周,而2012-12-31属于 2013 年的第一周。建议将isoyear字段与week一起使用,以获得一致的结果。 + + + +SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:7 + + + + + + year + + + + 年份字段。请记住,没有0 AD,所以把BC年份从AD年份中减去时需要小心。 + + + +SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40'); +结果:2001 + + + + + + + + + + + 当输入值为 +/-Infinity 时,extract对于单调递增的字段(epochjulianyearisoyeardecadecentury以及millennium)返回 +/-Infinity。对于其他字段返回 NULL。PostgreSQL 9.6 之前的版本对所有输入无穷的情况都返回零。 + + + + + extract函数主要的用途是做计算性处理。对于用于显示的日期/时间值格式化,参阅。 + + + date_part函数仿照传统的Ingres实现,后者对应SQL标准的extract函数: + +date_part('field', source) + + 注意,此处的field参数必须是字符串值,而不能是名称。date_part的有效字段名与extract相同。 + + + +SELECT date_part('day', TIMESTAMP '2001-02-16 20:38:40'); +结果:16 +SELECT date_part('hour', INTERVAL '4 hours 3 minutes'); +结果:4 + + + + + + <function>date_trunc</function> + + + date_trunc + + + + date_trunc函数在概念上和用于数字的trunc函数类似。 + + + + +date_trunc('field', source) + + sourcetimestampinterval类型的值表达式。(类型为datetime的值会自动转换为timestampinterval,分别对应这两种输入类型。)field选择输入值的截断精度。返回值的类型是timestampinterval,所有小于所选精度的字段都设为零(日和月则设为一)。 + + + field的有效值是: + + microseconds + milliseconds + second + minute + hour + day + week + month + quarter + year + decade + century + millennium + + + + 示例: +SELECT date_trunc('hour', TIMESTAMP '2001-02-16 20:38:40'); +结果:2001-02-16 20:00:00 + +SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40'); +结果:2001-01-01 00:00:00 + + + + + + <literal>AT TIME ZONE</literal> + + + 时区 + 转换 + + + + AT TIME ZONE + + + AT TIME ZONE 可在不带时区带时区的时间戳之间相互转换,也可将时间值转换到不同的时区。展示了它的各种变体。 + + + <literal>AT TIME ZONE</literal> 变体 + + + + 表达式 + 返回类型 + 描述 + + + + + + timestamp without time zone AT TIME ZONE zone + timestamp with time zone + 将给定的不带时区时间戳视为指定时区中的时间 + + + + timestamp with time zone AT TIME ZONE zone + timestamp without time zone + 将给定的带时区时间戳转换为新时区的时间,结果不带时区标识 + + + + time with time zone AT TIME ZONE zone + time with time zone + 将给定的带时区时间转换为新时区的时间 + + + +
+ + + 在这些表达式里,所需的时区 zone 可以指定为文本值(例如 'America/Los_Angeles'),也可以指定为一个间隔值(例如 INTERVAL '-08:00')。 + 在文本情况下,时区名称可以按 中描述的任意方式指定。 + + + 示例(假设本地时区为America/Los_Angeles): + +SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'America/Denver'; +结果:2001-02-16 19:38:40-08 + +SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'America/Denver'; +结果:2001-02-16 18:38:40 + +SELECT TIMESTAMP '2001-02-16 20:38:40-05' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Chicago'; +结果:2001-02-16 05:38:40 +第一个示例为不带时区的值添加时区,并使用当前TimeZone设置显示该值。第二个示例将带时区的时间戳值移到指定时区,并返回不带时区的值。这样就可以存储和显示与当前TimeZone设置不同的值。第三个示例将东京时间转换为芝加哥时间。将time值转换到其他时区时,由于没有提供日期,会使用当前生效的时区规则。 + + + 函数timezone(zone, timestamp)等效于符合 SQL 标准的结构timestamp AT TIME ZONE zone。 + +
+ + + 当前日期/时间 + + + 日期 + 当前 + + + + 时间 + 当前 + + + + PostgreSQL提供了许多返回当前日期和时间的函数。这些 SQL 标准的函数全部都按照当前事务的开始时刻返回值: + +CURRENT_DATE +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_TIME(precision) +CURRENT_TIMESTAMP(precision) +LOCALTIME +LOCALTIMESTAMP +LOCALTIME(precision) +LOCALTIMESTAMP(precision) + + + + + CURRENT_TIMECURRENT_TIMESTAMP返回带时区的值;LOCALTIMELOCALTIMESTAMP返回不带时区的值。 + + + + CURRENT_TIMECURRENT_TIMESTAMPLOCALTIMELOCALTIMESTAMP可以有选择地接受一个精度参数,该精度会使结果的秒字段舍入到指定的小数位数。如果没有精度参数,结果将给出可用的全部精度。 + + + 下面是一些示例: +SELECT CURRENT_TIME; +结果: 14:39:53.662522-05 +SELECT CURRENT_DATE; +结果: 2001-12-23 +SELECT CURRENT_TIMESTAMP; +结果: 2001-12-23 14:39:53.662522-05 +SELECT CURRENT_TIMESTAMP(2); +结果: 2001-12-23 14:39:53.66-05 +SELECT LOCALTIMESTAMP; +结果: 2001-12-23 14:39:53.662522 + + + + + 因为这些函数全部都按照当前事务的开始时刻返回结果,所以它们的值在事务运行的整个期间内都不改变。 我们认为这是一个特性:目的是为了允许一个事务在当前时间上有一致的概念, 这样在同一个事务里的多个修改可以保持同样的时间戳。 + + + + + + 其他数据库系统可能会更频繁地推进这些值。 + + + + + PostgreSQL还提供了返回当前语句的开始时间以及 + 调用该函数时的实际当前时间的函数。这些非 SQL 标准的函数列表如下: + +transaction_timestamp() +statement_timestamp() +clock_timestamp() +timeofday() +now() + + + + + transaction_timestamp()等价于CURRENT_TIMESTAMP,但是其命名清楚地反映了它的返回值。statement_timestamp()返回当前语句的开始时刻(更准确地说,是接收到客户端最近一条命令消息的时间)。statement_timestamp()transaction_timestamp()在一个事务的第一条命令期间返回值相同,但是在随后的命令中却不一定相同。 clock_timestamp()返回真正的当前时间,因此它的值甚至在同一条 SQL 命令中都会变化。timeofday()是一个有历史原因的PostgreSQL函数。和clock_timestamp()相似,它也返回真实的当前时间,但是它的结果是一个格式化的text串,而不是timestamp with time zone值。now()PostgreSQL中与transaction_timestamp()等价的传统函数。 + + + + 所有日期/时间数据类型也都接受特殊字面值now来指定当前日期和时间(同样解释为事务开始时间)。因此,下面三种写法都返回相同的结果: + +SELECT CURRENT_TIMESTAMP; +SELECT now(); +SELECT TIMESTAMP 'now'; -- 但请参阅下面的提示 + + + + + + + 当指定以后要计算的值时,不要使用第三种形式,例如在表列的DEFAULT子句中。 + 系统将在分析这个常量的时候把now转换为一个timestamp, 这样需要默认值时就会得到创建表的时间!而前两种形式要到实际使用默认值的时候才被计算, 因为它们是函数调用。因此它们可以给出每次插入行的时刻。 + (参见 。) + + + + + + 延时执行 + + + pg_sleep + + + pg_sleep_for + + + pg_sleep_until + + + 休眠 + + + 延迟 + + + 以下函数可用于延迟服务器进程的执行: +pg_sleep(seconds) +pg_sleep_for(interval) +pg_sleep_until(timestamp with time zone) + + + pg_sleep使当前会话的进程休眠seconds秒。seconds的类型为double precision,因此可以指定带小数部分的秒数作为延迟时间。pg_sleep_for便于指定较长的休眠时间,其参数类型为interval。 + pg_sleep_until是在需要指定唤醒时间时使用的便利函数。例如: +SELECT pg_sleep(1.5); +SELECT pg_sleep_for('5 minutes'); +SELECT pg_sleep_until('tomorrow 03:00'); + + + + + + + 有效的休眠时间间隔精度是平台相关的,通常 0.01 秒是通用值。休眠延迟将至少持续指 + 定的时长, 也有可能由于服务器负荷而比指定的时间长。特别地, + pg_sleep_until并不保证能刚好在指定的时刻被唤醒,但它不会 + 在比指定时刻早的时候醒来。 + + + + + + + 请确保在调用pg_sleep或者其变体时,你的会话没有持有不必要 + 的锁。否则其它会话可能必须等待你的休眠会话,因而减慢整个系统速度。 + + + + +
+ + + + 枚举支持函数 + + + 对于枚举类型(见),有些函数可以避免硬编码枚举类型中的特定值,使程序更简洁。这些函数列在中。以下示例假定枚举类型按如下方式创建: + + +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + + + + + + 枚举支持函数 + + + + 函数 + 描述 + 示例 + 示例结果 + + + + + enum_first enum_first(anyenum) + + 返回输入枚举类型的第一个值。 + + enum_first(null::rainbow) + red + + + enum_last enum_last(anyenum) + + 返回输入枚举类型的最后一个值。 + + enum_last(null::rainbow) + purple + + + enum_range enum_range(anyenum) + + 将输入枚举类型的所有值作为一个有序的数组返回。 + + enum_range(null::rainbow) + {red,orange,yellow,green,blue,purple} + + + enum_range(anyenum, anyenum) + + 以有序数组返回两个给定枚举值之间的范围。两个值必须来自同一枚举类型。如果第一个参数为 null,结果从该枚举类型的第一个值开始;如果第二个参数为 null,结果以该枚举类型的最后一个值结束。 + + enum_range('orange'::rainbow, 'green'::rainbow) + {orange,yellow,green} + + + enum_range(NULL, 'green'::rainbow) + {red,orange,yellow,green} + + + enum_range('orange'::rainbow, NULL) + {orange,yellow,green,blue,purple} + + + +
+ + + 请注意,除了enum_range的双参数形式外,这些函数都忽略传入的具体值,只关心其声明的数据类型。传入 null 或该类型的某个具体值,结果都相同。通常会将这些函数用于表列或函数参数,而不是像示例那样使用硬编码的类型名。 + +
+ + + 几何函数和操作符 + + + 几何类型pointbox、 + lseglinepath、 + polygoncircle有大量内置支持函数和操作符,如中所示。 + + + + 注意,相同操作符 ~= 表示 pointboxpolygoncircle 类型通常意义上的相等。其中一些类型也有 = 操作符,但 = 仅比较面积是否相等。这些类型的其他标量比较操作符(<= 等)同样比较面积。 + + + + 几何操作符 + + + + 操作符 + 描述 + 示例 + + + + + + + 平移 + box '((0,0),(1,1))' + point '(2.0,0)' + + + - + 平移 + box '((0,0),(1,1))' - point '(2.0,0)' + + + * + 缩放/旋转 + box '((0,0),(1,1))' * point '(2.0,0)' + + + / + 缩放/旋转 + box '((0,0),(2,2))' / point '(2.0,0)' + + + # + 相交的点或矩形框 + box '((1,-1),(-1,1))' # box '((1,1),(-2,-2))' + + + # + 路径或多边形中的点数 + # path '((1,0),(0,1),(-1,0))' + + + @-@ + 长度或周长 + @-@ path '((0,0),(1,0))' + + + @@ + 中心 + @@ circle '((0,0),10)' + + + ## + 第二个操作数上距离第一个操作数最近的点 + point '(0,0)' ## lseg '((2,0),(0,2))' + + + <-> + 两者之间的距离 + circle '((0,0),1)' <-> circle '((5,0),1)' + + + && + 是否重叠?(有一个公共点即为真。) + box '((0,0),(1,1))' && box '((0,0),(2,2))' + + + << + 是否严格位于左侧? + circle '((0,0),1)' << circle '((5,0),1)' + + + >> + 是否严格位于右侧? + circle '((5,0),1)' >> circle '((0,0),1)' + + + &< + 是否未超出对方的右边界? + box '((0,0),(1,1))' &< box '((0,0),(2,2))' + + + &> + 是否未超出对方的左边界? + box '((0,0),(3,3))' &> box '((0,0),(2,2))' + + + <<| + 是否严格位于下方? + box '((0,0),(3,3))' <<| box '((3,4),(5,5))' + + + |>> + 是否严格位于上方? + box '((3,4),(5,5))' |>> box '((0,0),(3,3))' + + + &<| + 是否未超出对方的上边界? + box '((0,0),(1,1))' &<| box '((0,0),(2,2))' + + + |&> + 是否未超出对方的下边界? + box '((0,0),(3,3))' |&> box '((0,0),(2,2))' + + + <^ + 是否位于下方(允许接触)? + circle '((0,0),1)' <^ circle '((0,5),1)' + + + >^ + 是否位于上方(允许接触)? + circle '((0,5),1)' >^ circle '((0,0),1)' + + + ?# + 是否相交? + lseg '((-1,0),(1,0))' ?# box '((-2,-2),(2,2))' + + + ?- + 是否水平? + ?- lseg '((-1,0),(1,0))' + + + ?- + 是否水平对齐? + point '(1,0)' ?- point '(0,0)' + + + ?| + 是否竖直? + ?| lseg '((-1,0),(1,0))' + + + ?| + 是否竖直对齐? + point '(0,1)' ?| point '(0,0)' + + + ?-| + 是否互相垂直? + lseg '((0,0),(0,1))' ?-| lseg '((0,0),(1,0))' + + + ?|| + 是否平行? + lseg '((-1,0),(1,0))' ?|| lseg '((-1,2),(1,2))' + + + @> + 是否包含? + circle '((0,0),2)' @> point '(1,1)' + + + <@ + 是否位于内部或边界上? + point '(1,1)' <@ circle '((0,0),2)' + + + ~= + 是否相同? + polygon '((0,0),(1,1))' ~= polygon '((1,1),(0,0))' + + + +
+ + + PostgreSQL 8.2 之前,包含操作符 @><@ 分别称为 ~@。这些名称仍然可用,但已被弃用,最终将被移除。 + + + + area + + + center + + + diameter + + + height + + + isclosed + + + isopen + + + length + + + npoints + + + pclose + + + popen + + + radius + + + width + + + + 几何函数 + + + + 函数 + 返回类型 + 描述 + 示例 + + + + + area(object) + double precision + 面积 + area(box '((0,0),(1,1))') + + + center(object) + point + 中心 + center(box '((0,0),(1,2))') + + + diameter(circle) + double precision + 圆的直径 + diameter(circle '((0,0),2.0)') + + + height(box) + double precision + 矩形框的竖直尺寸 + height(box '((0,0),(1,1))') + + + isclosed(path) + boolean + 是否为闭合路径? + isclosed(path '((0,0),(1,1),(2,0))') + + + isopen(path) + boolean + 是否为开放路径? + isopen(path '[(0,0),(1,1),(2,0)]') + + + length(object) + double precision + 长度 + length(path '((-1,0),(1,0))') + + + npoints(path) + int + 点数 + npoints(path '[(0,0),(1,1),(2,0)]') + + + npoints(polygon) + int + 点数 + npoints(polygon '((1,1),(0,0))') + + + pclose(path) + path + 将路径转换为闭合路径 + pclose(path '[(0,0),(1,1),(2,0)]') + + + + point(lseg, lseg) + point + 交集 + point(lseg '((-1,0),(1,0))',lseg '((-2,-2),(2,2))') + +]]> + + popen(path) + path + 将路径转换为开放路径 + popen(path '((0,0),(1,1),(2,0))') + + + radius(circle) + double precision + 圆的半径 + radius(circle '((0,0),2.0)') + + + width(box) + double precision + 矩形框的水平尺寸 + width(box '((0,0),(1,1))') + + + +
+ + + 几何类型转换函数 + + + + 函数 + 返回类型 + 描述 + 示例 + + + + + box box(circle) + box + 将圆转换为矩形框 + box(circle '((0,0),2.0)') + + + box(point) + box + 将点转换为空矩形框 + box(point '(0,0)') + + + box(point, point) + box + 将点转换为矩形框 + box(point '(0,0)', point '(1,1)') + + + box(polygon) + box + 将多边形转换为矩形框 + box(polygon '((0,0),(1,1),(2,0))') + + + bound_box(box, box) + box + 将两个矩形框转换为边界框 + bound_box(box '((0,0),(1,1))', box '((3,3),(4,4))') + + + circle circle(box) + circle + 将矩形框转换为圆 + circle(box '((0,0),(1,1))') + + + circle(point, double precision) + circle + 由圆心和半径构造圆 + circle(point '(0,0)', 2.0) + + + circle(polygon) + circle + 将多边形转换为圆 + circle(polygon '((0,0),(1,1),(2,0))') + + + line(point, point) + line + 由点构造直线 + line(point '(-1,0)', point '(1,0)') + + + lseg lseg(box) + lseg + 将矩形框的对角线转换为线段 + lseg(box '((-1,0),(1,0))') + + + lseg(point, point) + lseg + 由点构造线段 + lseg(point '(-1,0)', point '(1,0)') + + + path path(polygon) + path + 将多边形转换为路径 + path(polygon '((0,0),(1,1),(2,0))') + + + point point(double precision, double precision) + point + 构造点 + point(23.4, -44.5) + + + point(box) + point + 矩形框的中心 + point(box '((-1,0),(1,0))') + + + point(circle) + point + 圆的中心 + point(circle '((0,0),2.0)') + + + point(lseg) + point + 线段的中心 + point(lseg '((-1,0),(1,0))') + + + point(polygon) + point + 多边形的中心 + point(polygon '((0,0),(1,1),(2,0))') + + + polygon polygon(box) + polygon + 将矩形框转换为 4 个顶点的多边形 + polygon(box '((0,0),(1,1))') + + + polygon(circle) + polygon + 将圆转换为 12 个顶点的多边形 + polygon(circle '((0,0),2.0)') + + + polygon(npts, circle) + polygon + 将圆转换为 npts 个顶点的多边形 + polygon(12, circle '((0,0),2.0)') + + + polygon(path) + polygon + 将路径转换为多边形 + polygon(path '((0,0),(1,1),(2,0))') + + + +
+ + + 可以把一个point当作下标为 0 和 1 的数组,访问它的两个数值分量。例如,如果t.p是一个point列,那么SELECT p[0] FROM t检索 X 坐标而 UPDATE t SET p[1] = ...改变 Y 坐标。同样,box或者lseg类型的值可以当作两个point值组成的数组看待。 + + + area 函数适用于 boxcirclepath 类型。对于 path 数据类型,只有当 path 中的点构成的路径不自相交时,area 函数才能工作。例如,path '((0,0),(0,1),(2,1),(2,2),(1,2),(1,0),(0,0))'::PATH 无法使用;但下面这个视觉上相同的 path '((0,0),(0,1),(1,1),(1,2),(2,2),(2,1),(1,1),(1,0),(0,0))'::PATH 可以使用。如果难以理解自相交与非自相交 path 的区别,可以把上述两条 path 并排画在方格纸上。 + +
+ + + + 网络地址函数和操作符 + + 列出了可用于 cidrinet 类型的操作符。操作符 <<<<=>>>>=&& 测试子网包含关系。它们只考虑两个地址的网络部分(忽略主机部分),并判断一个网络是否与另一个网络相同,或是另一个网络的子网。 + + + <type>cidr</type> 和 <type>inet</type> 操作符 + + + + 操作符 + 描述 + 示例 + + + + + < + 小于 + inet '192.168.1.5' < inet '192.168.1.6' + + + <= + 小于或等于 + inet '192.168.1.5' <= inet '192.168.1.5' + + + = + 等于 + inet '192.168.1.5' = inet '192.168.1.5' + + + >= + 大于或等于 + inet '192.168.1.5' >= inet '192.168.1.5' + + + > + 大于 + inet '192.168.1.5' > inet '192.168.1.4' + + + <> + 不等于 + inet '192.168.1.5' <> inet '192.168.1.4' + + + << + 被包含 + inet '192.168.1.5' << inet '192.168.1/24' + + + <<= + 被包含于或等于 + inet '192.168.1/24' <<= inet '192.168.1/24' + + + >> + 包含 + inet '192.168.1/24' >> inet '192.168.1.5' + + + >>= + 包含或等于 + inet '192.168.1/24' >>= inet '192.168.1/24' + + + && + 包含或被包含于 + inet '192.168.1/24' && inet '192.168.1.80/28' + + + ~ + 按位非 + ~ inet '192.168.1.6' + + + & + 按位与 + inet '192.168.1.6' & inet '0.0.0.255' + + + | + 按位或 + inet '192.168.1.6' | inet '0.0.0.255' + + + + + 加法 + inet '192.168.1.6' + 25 + + + - + 减法 + inet '192.168.1.43' - 36 + + + - + 减法 + inet '192.168.1.43' - inet '192.168.1.19' + + + +
+ + 列出了可用于 cidrinet 类型的函数。abbrevhosttext 函数主要用于提供其他显示格式。 + + + <type>cidr</type> 和 <type>inet</type> 函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + abbrev abbrev(inet) + text + 以文本表示的缩略显示格式 + abbrev(inet '10.1.0.0/16') + 10.1.0.0/16 + + + abbrev(cidr) + text + 以文本表示的缩略显示格式 + abbrev(cidr '10.1.0.0/16') + 10.1/16 + + + broadcast broadcast(inet) + inet + 网络的广播地址 + broadcast('192.168.1.5/24') + 192.168.1.255/24 + + + family family(inet) + int + 提取地址族;IPv4 为 4,IPv6 为 6 + family('::1') + 6 + + + host host(inet) + text + 以文本形式提取 IP 地址 + host('192.168.1.5/24') + 192.168.1.5 + + + hostmask hostmask(inet) + inet + 构造网络的主机掩码 + hostmask('192.168.23.20/30') + 0.0.0.3 + + + masklen masklen(inet) + int + 提取网络掩码长度 + masklen('192.168.1.5/24') + 24 + + + netmask netmask(inet) + inet + 构造网络的网络掩码 + netmask('192.168.1.5/24') + 255.255.255.0 + + + network network(inet) + cidr + 提取地址的网络部分 + network('192.168.1.5/24') + 192.168.1.0/24 + + + set_masklen set_masklen(inet, int) + inet + 设置 inet 值的网络掩码长度 + set_masklen('192.168.1.5/24', 16) + 192.168.1.5/16 + + + set_masklen(cidr, int) + cidr + 设置 cidr 值的网络掩码长度 + set_masklen('192.168.1.0/24'::cidr, 16) + 192.168.0.0/16 + + + text text(inet) + text + 以文本形式提取 IP 地址和网络掩码长度 + text(inet '192.168.1.5') + 192.168.1.5/32 + + + inet_same_family inet_same_family(inet, inet) + boolean + 这些地址是否属于同一地址族? + inet_same_family('192.168.1.5/24', '::1') + false + + + inet_merge inet_merge(inet, inet) + cidr + 包含两个给定网络的最小网络 + inet_merge('192.168.1.5/24', '192.168.2.5/24') + 192.168.0.0/22 + + + +
+ + 任何 cidr 值都可以隐式或显式地转换为 inet;因此,上述处理 inet 的函数也适用于 cidr 值。(如果 inetcidr 各有单独的函数,是因为这两种情况下的行为应当不同。)也允许将 inet 值转换为 cidr。这样做时,网络掩码右侧的所有位都会被直接置零,以创建有效的 cidr 值。此外,还可以用普通的类型转换语法将文本值转换为 inetcidr:例如 inet(expression)colname::cidr + + 列出了可用于 macaddr 类型的函数。函数 trunc(macaddr) 返回将最后 3 个字节置零的 MAC 地址。可以用余下的前缀确定制造商。 + + + <type>macaddr</type> 函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + trunc trunc(macaddr) + macaddr + 将最后 3 个字节设为零 + trunc(macaddr '12:34:56:78:90:ab') + 12:34:56:00:00:00 + + + +
+ + macaddr 类型还支持按字典顺序比较的标准关系操作符(><= 等),以及用于按位非、与、或的位运算操作符(~&|)。 + + + +
+ + + + 文本搜索函数和操作符 + + + 全文检索 + 函数和操作符 + + + + 文本搜索 + 函数和操作符 + + + + 、 + 以及 + + 总结了为全文检索提供的函数和操作符。PostgreSQL的文本搜索功能的详细解释可参考。 + + + + 文本搜索操作符 + + + + 操作符 + 返回类型 + 描述 + 示例 + 结果 + + + + + @@ + boolean + tsvector 是否匹配 tsquery + to_tsvector('fat cats ate rats') @@ to_tsquery('cat & rat') + t + + + @@@ + boolean + @@ 的已弃用同义写法 + to_tsvector('fat cats ate rats') @@@ to_tsquery('cat & rat') + t + + + || + tsvector + 串接 tsvector + 'a:1 b:2'::tsvector || 'c:1 d:2 b:3'::tsvector + 'a':1 'b':2,5 'c':3 'd':4 + + + && + tsquery + tsquery 进行 AND 组合 + 'fat | rat'::tsquery && 'cat'::tsquery + ( 'fat' | 'rat' ) & 'cat' + + + || + tsquery + tsquery 进行 OR 组合 + 'fat | rat'::tsquery || 'cat'::tsquery + ( 'fat' | 'rat' ) | 'cat' + + + !! + tsquery + tsquery 取反 + !! 'cat'::tsquery + !'cat' + + + <-> + tsquery + tsquery 后跟 tsquery + to_tsquery('fat') <-> to_tsquery('rat') + 'fat' <-> 'rat' + + + @> + boolean + tsquery 是否包含另一个查询? + 'cat'::tsquery @> 'cat & rat'::tsquery + f + + + <@ + boolean + tsquery 是否被包含于另一个查询? + 'cat'::tsquery <@ 'cat & rat'::tsquery + t + + + +
+ + + tsquery 包含操作符只考虑两个查询中列出的词位,忽略组合操作符。 + + + 除了表中列出的操作符,tsvectortsquery 类型还定义了普通的 B-树比较操作符(=< 等)。这些操作符对文本搜索用处不大,但可以用于其他用途,例如在这些类型的列上建立唯一索引。 + + + 文本搜索函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + array_to_tsvector array_to_tsvector(text[]) + tsvector + 将词位数组转换为 tsvector + array_to_tsvector('{fat,cat,rat}'::text[]) + 'cat' 'fat' 'rat' + + + get_current_ts_config get_current_ts_config() + regconfig + 获取默认的文本搜索配置 + get_current_ts_config() + english + + + length length(tsvector) + integer + tsvector 中的词位数 + length('fat:2,4 cat:3 rat:5A'::tsvector) + 3 + + + numnode numnode(tsquery) + integer + tsquery 中的词位数与操作符数之和 + numnode('(fat & rat) | cat'::tsquery) + 5 + + + plainto_tsquery plainto_tsquery( config regconfig , query text) + tsquery + 生成 tsquery,忽略标点符号 + plainto_tsquery('english', 'The Fat Rats') + 'fat' & 'rat' + + + phraseto_tsquery phraseto_tsquery( config regconfig , query text) + tsquery + 生成搜索短语的 tsquery,忽略标点符号 + phraseto_tsquery('english', 'The Fat Rats') + 'fat' <-> 'rat' + + + querytree querytree(query tsquery) + text + 获取 tsquery 中可索引的部分 + querytree('foo & ! bar'::tsquery) + 'foo' + + + setweight setweight(vector tsvector, weight "char") + tsvector + vector 中的每个元素赋予 weight + setweight('fat:2,4 cat:3 rat:5B'::tsvector, 'A') + 'cat':3A 'fat':2A,4A 'rat':5A + + + setweight 为指定词位设置权重 setweight(vector tsvector, weight "char", lexemes text[]) + tsvector + vector 中列在 lexemes 内的元素赋予 weight + setweight('fat:2,4 cat:3 rat:5B'::tsvector, 'A', '{cat,rat}') + 'cat':3A 'fat':2,4 'rat':5A + + + strip strip(tsvector) + tsvector + tsvector 中移除位置和权重 + strip('fat:2,4 cat:3 rat:5A'::tsvector) + 'cat' 'fat' 'rat' + + + to_tsquery to_tsquery( config regconfig , query text) + tsquery + 正规化单词并转换为 tsquery + to_tsquery('english', 'The & Fat & Rats') + 'fat' & 'rat' + + + to_tsvector to_tsvector( config regconfig , document text) + tsvector + 将文档文本转换为 tsvector + to_tsvector('english', 'The Fat Rats') + 'fat':2 'rat':3 + + + ts_delete ts_delete(vector tsvector, lexeme text) + tsvector + vector 中移除给定的 lexeme + ts_delete('fat:2,4 cat:3 rat:5A'::tsvector, 'fat') + 'cat':3 'rat':5A + + + ts_delete(vector tsvector, lexemes text[]) + tsvector + vector 中移除 lexemes 所列词位的所有出现 + ts_delete('fat:2,4 cat:3 rat:5A'::tsvector, ARRAY['fat','rat']) + 'cat':3 + + + ts_filter ts_filter(vector tsvector, weights "char"[]) + tsvector + 仅从 vector 中选出具有给定 weights 的元素 + ts_filter('fat:2,4 cat:3b rat:5A'::tsvector, '{a,b}') + 'cat':3B 'rat':5A + + + ts_headline ts_headline( config regconfig, document text, query tsquery , options text ) + text + 显示查询匹配内容 + ts_headline('x y z', 'z'::tsquery) + x y <b>z</b> + + + ts_rank ts_rank( weights float4[], vector tsvector, query tsquery , normalization integer ) + float4 + 计算文档相对于查询的排名分值 + ts_rank(textsearch, query) + 0.818 + + + ts_rank_cd ts_rank_cd( weights float4[], vector tsvector, query tsquery , normalization integer ) + float4 + 使用覆盖密度计算文档相对于查询的排名分值 + ts_rank_cd('{0.1, 0.2, 0.4, 1.0}', textsearch, query) + 2.01317 + + + ts_rewrite ts_rewrite(query tsquery, target tsquery, substitute tsquery) + tsquery + 将查询中的 target 替换为 substitute + ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'foo|bar'::tsquery) + 'b' & ( 'foo' | 'bar' ) + + + ts_rewrite(query tsquery, select text) + tsquery + 使用 SELECT 命令提供的目标和替代内容进行替换 + SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases') + 'b' & ( 'foo' | 'bar' ) + + + tsquery_phrase tsquery_phrase(query1 tsquery, query2 tsquery) + tsquery + 构造搜索 query1 后跟 query2 的查询(与 <-> 操作符相同) + tsquery_phrase(to_tsquery('fat'), to_tsquery('cat')) + 'fat' <-> 'cat' + + + tsquery_phrase(query1 tsquery, query2 tsquery, distance integer) + tsquery + 构造搜索 query1 后跟 query2、间距为 distance 的查询 + tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10) + 'fat' <10> 'cat' + + + tsvector_to_array tsvector_to_array(tsvector) + text[] + tsvector 转换为词位数组 + tsvector_to_array('fat:2,4 cat:3 rat:5A'::tsvector) + {cat,fat,rat} + + + tsvector_update_trigger tsvector_update_trigger() + trigger + 自动更新 tsvector 列的触发器函数 + + CREATE TRIGGER ... tsvector_update_trigger(tsvcol, 'pg_catalog.swedish', title, body) + + + + + tsvector_update_trigger_column tsvector_update_trigger_column() + trigger + 自动更新 tsvector 列的触发器函数 + CREATE TRIGGER ... tsvector_update_trigger_column(tsvcol, configcol, title, body) + + + + unnest 用于 tsvector unnest(tsvector, OUT lexeme text, OUT positions smallint[], OUT weights text) + setof record + 将 tsvector 展开为一组行 + unnest('fat:2,4 cat:3 rat:5A'::tsvector) + (cat,{3},{D}) ... + + + +
+ + + + + 所有接受一个可选的regconfig参数的文本搜索函数在省略该参数时,会使用由指定的配置。 + + + + + 中的函数被单独列出,因为它们通常不被用于日常的文本搜索操作。 + 它们有助于开发和调试新的文本搜索配置。 + + + + 文本搜索调试函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + ts_debug ts_debug( config regconfig, document text, OUT alias text, OUT description text, OUT token text, OUT dictionaries regdictionary[], OUT dictionary regdictionary, OUT lexemes text[]) + setof record + 测试配置 + ts_debug('english', 'The Brightest supernovaes') + (asciiword,"Word, all ASCII",The,{english_stem},english_stem,{}) ... + + + ts_lexize ts_lexize(dict regdictionary, token text) + text[] + 测试词典 + ts_lexize('english_stem', 'stars') + {star} + + + ts_parse ts_parse(parser_name text, document text, OUT tokid integer, OUT token text) + setof record + 测试解析器 + ts_parse('default', 'foo - bar') + (1,foo) ... + + + ts_parse(parser_oid oid, document text, OUT tokid integer, OUT token text) + setof record + 测试解析器 + ts_parse(3722, 'foo - bar') + (1,foo) ... + + + ts_token_type ts_token_type(parser_name text, OUT tokid integer, OUT alias text, OUT description text) + setof record + 获取解析器定义的词元类型 + ts_token_type('default') + (1,asciiword,"Word, all ASCII") ... + + + ts_token_type(parser_oid oid, OUT tokid integer, OUT alias text, OUT description text) + setof record + 获取解析器定义的词元类型 + ts_token_type(3722) + (1,asciiword,"Word, all ASCII") ... + + + ts_stat ts_stat(sqlquery text, weights text, OUT word text, OUT ndoc integer, OUT nentry integer) + setof record + 获取 tsvector 列的统计信息 + ts_stat('SELECT vector from apod') + (foo,10,15) ... + + + +
+ +
+ + + + + XML 函数 + + + + 本节中描述的函数以及类函数表达式都作用于类型 xml 的值。关于 xml 类型的详细信息请查阅。用于在值与类型 xml 之间转换的类函数表达式 xmlparsexmlserialize 记录在该节中,这里不再重复。使用这些函数中的大多数要求安装时使用了configure --with-libxml编译。 + + + + 产生 XML 内容 + + + 有一组函数和类函数的表达式可以用来从 SQL 数据产生 XML 内容。它们特别适合于将查询结果格式化成 XML 文档以便于在客户端应用中处理。 + + + + <literal>xmlcomment</literal> + + + xmlcomment + + + +xmlcomment(text) + + + 函数 xmlcomment 创建一个 XML 值,其中包含以指定文本为内容的 XML 注释。该文本不能包含 --,也不能以 - 结尾,以确保构造出的 XML 注释有效。如果参数为空值,结果也为空值。 + + + 示例: + +]]> + + + + + <literal>xmlconcat</literal> + + + xmlconcat + + + +xmlconcat(xml, ...) + + + + 函数xmlconcat将由单个 XML 值组成的列表串接成一个单独的值,这个值包含一个 XML 内容片断。空值会被忽略,只有当没有参数为非空时结果才为空。 + + + + 示例: +', 'foo'); + + xmlconcat +---------------------- + foo +]]> + + + + 如果 XML 声明存在,它们会按照下面的方式被组合。如果所有的参数值都有相同的 XML 版本声明,该版本将被用在结果中,否则将不使用版本。如果所有参数值有独立声明值yes,那么该值将被用在结果中。如果所有参数值都有一个独立声明值并且至少有一个为no,则no被用在结果中。否则结果中将没有独立声明。如果结果被决定要要求一个独立声明但是没有版本声明,将会使用一个版本 1.0 的版本声明,因为 XML 要求一个 XML 声明要包含一个版本声明。编码声明会被忽略并且在所有情况中都会被移除。 + + + + 示例: +', ''); + + xmlconcat +----------------------------------- + +]]> + + + + + <literal>xmlelement</literal> + + + xmlelement + + + +xmlelement(name name , xmlattributes(value AS attname , ... ) , content, ...) + + + 表达式 xmlelement 使用给定的名称、属性和内容生成一个 XML 元素。 + + 示例: + +SELECT xmlelement(name foo, xmlattributes('xyz' as bar)); + + xmlelement +------------------ + + +SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent'); + + xmlelement +------------------------------------- + content +]]> + + + + 不是合法 XML 名字的元素名和属性名将被转义,转义的方法是将违反的字符用序列_xHHHH_替换,其中HHHH是被替换字符的 Unicode 代码点的十六进制表示。例如: + +]]> + + + + 如果属性值是一个列引用,则不需要指定一个显式的属性名,在这种情况下列的名字将被默认用于属性的名字。在其他情况下,属性必须被给定一个显式名称。因此这个示例是合法的: + +CREATE TABLE test (a xml, b xml); +SELECT xmlelement(name test, xmlattributes(a, b)) FROM test; + + 但是下面这些不合法: + +SELECT xmlelement(name test, xmlattributes('constant'), a, b) FROM test; +SELECT xmlelement(name test, xmlattributes(func(a, b))) FROM test; + + + + + 如果指定了元素内容,它们将被根据其数据类型格式化。如果内容本身也是类型xml,就可以构建复杂的 XML 文档。例如: + +]]> + + 其他类型的内容将被格式化为合法的 XML 字符数据。这意味着字符 <, >, 和 & 将被转换为实体。二进制数据(数据类型bytea)将被表示成 base64 或十六进制编码,具体取决于配置参数的设置。个别数据类型的特殊行为将不断发展,以使SQL和PostgreSQL数据类型与 XML Schema 规范对齐,届时将给出更精确的描述。 + + + + + <literal>xmlforest</literal> + + + xmlforest + + + +xmlforest(content AS name , ...) + + + 表达式 xmlforest 使用给定的名称和内容生成由元素构成的 XML 森林(序列)。 + + 示例:abc123 + + +SELECT xmlforest(table_name, column_name) +FROM information_schema.columns +WHERE table_schema = 'pg_catalog'; + + xmlforest +------------------------------------------------------------------------------------------- + pg_authidrolname + pg_authidrolsuper + ... +]]>如第二个示例所示,如果内容值是一个列引用,可以省略元素名称,此时默认使用列名。否则,必须指定名称。 + + + 如上文xmlelement所示,非法 XML 名字的元素名会被转义。相似地,内容数据也会被转义来产生合法的 XML 内容,除非它已经是一个xml类型。 + + + + 注意如果 XML 森林由多于一个元素组成,那么它不是合法的 XML 文档,因此在xmlelement中包装xmlforest表达式会有用处。 + + + + + <literal>xmlpi</literal> + + + xmlpi + + + +xmlpi(name target , content) + + + 表达式 xmlpi 创建一个 XML 处理指令。如果提供了内容,其中不得包含字符序列 ?> + + + 示例: + +]]> + + + + + <literal>xmlroot</literal> + + + xmlroot + + + +xmlroot(xml, version text | no value , standalone yes|no|no value) + + + + 表达式xmlroot修改一个 XML 值的根结点的属性。如果指定了一个版本,它会替换根节点的版本声明中的值;如果指定了一个独立设置,它会替换根节点的独立声明中的值。 + + + +abc'), + version '1.0', standalone yes); + + xmlroot +---------------------------------------- + + abc +]]> + + + + + <literal>xmlagg</literal> + + + xmlagg + + + +xmlagg(xml) + + + + 和这里描述的其他函数不同,函数xmlagg是一个聚合函数。它将聚合函数调用的输入值串接起来,非常像xmlconcat所做的事情,除了串接是跨行发生的而不是在单一行的多个表达式上发生。聚合表达式的更多信息请见。 + + + + 示例: +abc'); +INSERT INTO test VALUES (2, ''); +SELECT xmlagg(x) FROM test; + xmlagg +---------------------- + abc +]]> + + + + 为了决定串接的顺序,可以为聚合调用增加一个ORDER BY子句,如中所述。例如: + +abc +]]> + + + + 我们推荐在以前的版本中使用下列非标准方法,并且它们在特定情况下仍然有用: + +abc +]]> + + + + + + XML 谓词 + + + 这一节描述的表达式检查xml值的属性。 + + + + <literal>IS DOCUMENT</literal> + + + IS DOCUMENT + + + +xml IS DOCUMENT + + + + 如果参数 XML 值是一个正确的 XML 文档,则IS DOCUMENT返回真,如果不是则返回假(即它是一个内容片断),或者是参数为空时返回空。文档和内容片断之间的区别请见。 + + + + + <literal>IS NOT DOCUMENT</literal> + + + IS NOT DOCUMENT + + + +xml IS NOT DOCUMENT + + + + 如果参数中的XML值是一个正确的XML文档,那么表达式IS NOT DOCUMENT返回假,否则返回真(也就是说它是一个内容片段),如果参数为空则返回空。 + + + + + <literal>XMLEXISTS</literal> + + + XMLEXISTS + + + +XMLEXISTS(text PASSING BY REF xml BY REF) + + + + 如果第一个参数中的XPath表达式返回任何节点,函数xmlexists返回true,否则返回false。(如果任一参数为空,则结果为空。) + + + 示例:TorontoOttawa'); + + xmlexists +------------ + t +(1 row) +]]> + + + BY REF 子句在 PostgreSQL 中没有效果,但为了 SQL 一致性以及与其他实现的兼容性,允许使用它们。按照 SQL 标准,第一个 BY REF 是必需的,第二个是可选的。另请注意,SQL 标准规定 xmlexists 结构接受 XQuery 表达式作为第一个参数,但 PostgreSQL 目前只支持 XPath,它是 XQuery 的一个子集。 + + + + <literal>xml_is_well_formed</literal> + + + xml_is_well_formed + + + + xml_is_well_formed_document + + + + xml_is_well_formed_content + + + +xml_is_well_formed(text) +xml_is_well_formed_document(text) +xml_is_well_formed_content(text) + + + + 这些函数检查一个text串是不是一个良构的 XML,返回一个布尔结果。xml_is_well_formed_document检查一个良构的文档,而xml_is_well_formed_content检查良构的内容。如果配置参数被设置为DOCUMENTxml_is_well_formed会做第一个函数的工作;如果配置参数被设置为CONTENT,它会做第二个函数的工作。这意味着xml_is_well_formed对于检查一个到类型xml的简单类型转换是否会成功非常有用,而其他两个函数对于检查XMLPARSE的对应变体是否会成功有用。 + + + + 示例: + +'); + xml_is_well_formed +-------------------- + f +(1 row) + +SELECT xml_is_well_formed(''); + xml_is_well_formed +-------------------- + t +(1 row) + +SET xmloption TO CONTENT; +SELECT xml_is_well_formed('abc'); + xml_is_well_formed +-------------------- + t +(1 row) + +SELECT xml_is_well_formed_document('bar'); + xml_is_well_formed_document +----------------------------- + t +(1 row) + +SELECT xml_is_well_formed_document('bar'); + xml_is_well_formed_document +----------------------------- + f +(1 row) +]]> + + 最后一个示例显示了检查是否正确匹配命名空间。 + + + + + + 处理 XML + + + XPath + + + + 为了处理xml数据类型的值,PostgreSQL 提供了xpathxpath_exists函数,它们计算 XPath 1.0 表达式。 + + + +xpath(xpath, xml , nsarray) + + + + 函数xpath针对 XML 值xml计算 XPath 表达式xpath(一个text值)。它返回一个由 XPath 表达式产生的节点集所对应的 XML 值组成的数组。如果 XPath 表达式返回的是标量值而不是节点集,则返回一个单元素数组。 + + + + 第二个参数必须是一个良构的 XML 文档。特别地,它必须有单个根节点元素。 + + + + 函数可选的第三个参数是名称空间映射的数组。这个数组应该是一个第二轴长度为 2 的二维text数组(也就是说,它应该是一个数组的数组,其中每个数组恰好由 2 个元素组成)。每个数组条目的第一个元素是名称空间名称(别名),第二个是名称空间 URI。不要求此数组中提供的别名与 XML 文档本身中使用的别名相同(换句话说,在 XML 文档和xpath函数上下文中,别名都是局部的)。 + + + + 示例: +test', + ARRAY[ARRAY['my', 'http://example.com']]); + xpath +--------- + {test} +(1 row) +]]> + + + + 要处理默认(匿名)名称空间,可以这样做: +test', + ARRAY[ARRAY['mydefns', 'http://example.com']]); + xpath +--------- + {test} +(1 row) +]]> + + + + xpath_exists + + + +xpath_exists(xpath, xml , nsarray) + + + + 函数xpath_existsxpath函数的一个特殊形式。它不返回满足 XPath 的各个 XML 值,而是返回一个布尔值,指示查询是否被满足。这个函数等价于标准的XMLEXISTS谓词,只是它还支持名称空间映射参数。 + + + + 示例: +test', + ARRAY[ARRAY['my', 'http://example.com']]); + xpath_exists +-------------- + t +(1 row) +]]> + + + + + 将表映射到 XML + + + XML export + + + 以下函数将关系表的内容映射为 XML 值,可以将它们视为 XML 导出功能: +table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text) +query_to_xml(query text, nulls boolean, tableforest boolean, targetns text) +cursor_to_xml(cursor refcursor, count int, nulls boolean, + tableforest boolean, targetns text) +每个函数的返回类型都是xml。 + + + + table_to_xml映射由参数tbl传递的命名表的内容。 + regclass类型接受使用常见标记标识表的字符串,包括可选的模式限定和双引号。 + query_to_xml执行由参数query传递的查询并且映射结果集。 + cursor_to_xmlcursor指定的游标中取出指定数量的行。 + 如果需要映射一个大型的表,我们推荐这种变体,因为每一个函数都是在内存中构建结果值的。 + + + + 如果tableforest为假,则结果的 XML 文档看起来像这样: + + + data + data + + + + ... + + + ... + +]]> + + 如果tableforest为真,结果是一个看起来像这样的 XML 内容片断: + + data + data + + + + ... + + +... +]]> + + 如果没有表名可用,在映射一个查询或一个游标时,在第一种格式中使用串table,在第二种格式中使用row。 + + + + 这几种格式的选择由用户决定。第一种格式是一个正确的 XML 文档,它在很多应用中都很重要。如果结果值要被重组为一个文档,第二种格式在cursor_to_xml函数中更有用。前文讨论的产生 XML 内容的函数(特别是xmlelement)可以被用来把结果修改成符合用户的要求。 + + + + 数据值会被以前文的函数xmlelement中描述的相同方法映射。 + + + + 参数nulls决定空值是否会被包含在输出中。如果为真,列中的空值被表示为: + +]]> + 其中xsi是 XML 模式实例的 XML 名字空间前缀。一个合适的名字空间声明将被加入到结果值中。如果为假,包含空值的列将被从输出中忽略掉。 + + + + 参数targetns指定想要的结果的 XML 名字空间。如果没有想要的特定名字空间,将会传递一个空串。 + + + 以下函数返回 XML Schema 文档,描述上面相应函数所执行的映射: +table_to_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text) +query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text) +cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text) +必须传入相同的参数,才能得到彼此匹配的 XML 数据映射和 XML Schema 文档。 + + 以下函数在同一个文档(或森林)中生成 XML 数据映射和对应的 XML Schema,并将两者链接在一起。当需要自包含且自描述的结果时,这些函数很有用: +table_to_xml_and_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text) +query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targetns text) + + + + 此外,还可以使用以下函数,为整个模式或整个当前数据库生成类似的映射: +schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text) +schema_to_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text) +schema_to_xml_and_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text) + +database_to_xml(nulls boolean, tableforest boolean, targetns text) +database_to_xmlschema(nulls boolean, tableforest boolean, targetns text) +database_to_xml_and_xmlschema(nulls boolean, tableforest boolean, targetns text) +注意,这些函数可能生成大量数据,需要在内存中构建。请求大型模式或数据库的内容映射时,可以考虑分别映射各个表,甚至通过游标来完成。 + + + 一个模式内容映射的结果看起来像这样: + + + +table1-mapping + +table2-mapping + +... + +]]> + + 其中一个表映射的格式取决于上文解释的tableforest参数。 + + + + 一个数据库内容映射的结果看起来像这样: + + + + + ... + + + + ... + + +... + +]]> + + 其中的模式映射如上所述。 + + + + 作为一个使用这些函数产生的输出的示例,展示了一个 XSLT 样式表,它将table_to_xml_and_xmlschema的输出转换为一个包含表数据的扁平转印的 HTML 文档。以一种相似的方式,这些函数的结果可以被转换成其他基于 XML 的格式。 + + + + + 转换 SQL/XML 输出到 HTML 的 XSLT 样式表 + + + + + + + + + + + + + <xsl:value-of select="name(current())"/> + + + + + + + + + + + + + + + + +
+ + +
+ +
+]]>
+
+
+
+ + + JSON 函数和操作符 + + + JSON + 函数和操作符 + + + 列出了可用于两种 JSON 数据类型的操作符(参见)。 + + + <type>json</type> 和 <type>jsonb</type> 操作符 + + + + 操作符 + 右操作数类型 + 描述 + 示例 + 示例结果 + + + + + -> + int + 获取 JSON 数组元素(索引从零开始,负整数从末尾计数) + '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2 + {"c":"baz"} + + + -> + text + 按键获取 JSON 对象字段 + '{"a": {"b":"foo"}}'::json->'a' + {"b":"foo"} + + + ->> + int + text 形式获取 JSON 数组元素 + '[1,2,3]'::json->>2 + 3 + + + ->> + text + text 形式获取 JSON 对象字段 + '{"a":1,"b":2}'::json->>'b' + 2 + + + #> + text[] + 获取指定路径处的 JSON 对象 + '{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}' + {"c": "foo"} + + + #>> + text[] + text 形式获取指定路径处的 JSON 对象 + '{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}' + 3 + + + +
+ + + 这些操作符针对 jsonjsonb 类型都有相应的变体。字段、元素和路径提取操作符返回的类型与其左侧输入相同(jsonjsonb),但标明返回 text 的操作符会将值转换为文本。如果 JSON 输入的结构不符合请求,例如所需元素不存在,字段、元素和路径提取操作符会返回 NULL,而不会失败。接受整数 JSON 数组下标的字段、元素和路径提取操作符都支持使用负下标从数组末尾计数。 + + + 中给出的常规比较操作符也可用于jsonb,但不适用于json。 + 比较操作符遵循 B-树操作的排序规则,详见。 + + 还有一些操作符只适用于 jsonb,如所示。其中许多操作符可以通过 jsonb 操作符类使用索引。关于 jsonb 包含与存在语义的完整说明,请参见介绍了如何使用这些操作符有效地为 jsonb 数据建立索引。 + + 附加的 <type>jsonb</type> 操作符 + + + + 操作符 + 右操作数类型 + 描述 + 示例 + + + + + @> + jsonb + 左侧 JSON 值是否在顶层包含右侧 JSON 路径/值条目? + '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb + + + <@ + jsonb + 左侧 JSON 路径/值条目是否包含在右侧 JSON 值的顶层? + '{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb + + + ? + text + 字符串是否作为顶层键存在于 JSON 值中? + '{"a":1, "b":2}'::jsonb ? 'b' + + + ?| + text[] + 这些数组字符串中是否有任意一个作为顶层键存在? + '{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'c'] + + + ?& + text[] + 这些数组字符串是否都作为顶层键存在? + '["a", "b"]'::jsonb ?& array['a', 'b'] + + + || + jsonb + 将两个 jsonb 值串接为一个新的 jsonb + '["a", "b"]'::jsonb || '["c", "d"]'::jsonb + + + - + text + 从左操作数中删除键/值对或字符串元素。键/值对按其键进行匹配。 + '{"a": "b"}'::jsonb - 'a' + + + - + integer + 删除指定索引的数组元素(负整数从末尾计数)。如果顶层容器不是数组,则抛出错误。 + '["a", "b"]'::jsonb - 1 + + + #- + text[] + 删除指定路径处的字段或元素(对于 JSON 数组,负整数从末尾计数) + '["a", {"b":1}]'::jsonb #- '{1,b}' + + + +
+ + + || 操作符连接两个 JSON 对象时,会生成一个包含两者键的并集的对象;遇到重复键时,采用第二个对象的值。其他情况都会生成 JSON 数组:首先将任何非数组输入转换为单元素数组,然后连接两个数组。该操作不递归,只合并顶层数组或对象结构。 + + + 列出了可用于创建 jsonjsonb 值的函数。(row_to_jsonarray_to_json 函数没有对应的 jsonb 函数,但 to_jsonb 函数提供了大致相同的功能。) + + + to_json + + + array_to_json + + + row_to_json + + + json_build_array + + + json_build_object + + + json_object + + + to_jsonb + + + jsonb_build_array + + + jsonb_build_object + + + jsonb_object + + + + JSON 创建函数 + + + + 函数 + 描述 + 示例 + 示例结果 + + + + + to_json(anyelement) + to_jsonb(anyelement) + + 将值作为 jsonjsonb 返回。数组和复合值分别递归转换为数组和对象;否则,如果存在从该类型到 json 的类型转换,则使用该转换函数执行转换;否则生成标量值。对于数值、布尔值或 null 以外的任何标量类型,将使用其文本表示,并使其成为有效的 jsonjsonb 值。 + to_json('Fred said "Hi."'::text) + "Fred said \"Hi.\"" + + + + array_to_json(anyarray [, pretty_bool]) + + 将数组作为 JSON 数组返回。PostgreSQL 多维数组会变成由数组组成的 JSON 数组。如果 pretty_bool 为真,则在第一维元素之间添加换行。 + array_to_json('{{1,5},{99,100}}'::int[]) + [[1,5],[99,100]] + + + + row_to_json(record [, pretty_bool]) + + 将行作为 JSON 对象返回。如果 pretty_bool 为真,则在第一层元素之间添加换行。 + row_to_json(row(1,'foo')) + {"f1":1,"f2":"foo"} + + + json_build_array(VARIADIC "any") + jsonb_build_array(VARIADIC "any") + + 从可变参数列表构造 JSON 数组,各元素可以具有不同类型。 + json_build_array(1,2,'3',4,5) + [1, 2, "3", 4, 5] + + + json_build_object(VARIADIC "any") + jsonb_build_object(VARIADIC "any") + + 从可变参数列表构造 JSON 对象。按惯例,参数列表由键和值交替组成。 + json_build_object('foo',1,'bar',2) + {"foo": 1, "bar": 2} + + + json_object(text[]) + jsonb_object(text[]) + + 从文本数组构造 JSON 对象。该数组必须是一维且包含偶数个成员,此时将成员按交替的键/值对处理;或者是二维数组,且每个内部数组恰好有两个元素,将这两个元素作为一个键/值对。 + json_object('{a, 1, b, "def", c, 3.5}') + json_object('{{a, 1},{b, "def"},{c, 3.5}}') + {"a": "1", "b": "def", "c": "3.5"} + + + json_object(keys text[], values text[]) + jsonb_object(keys text[], values text[]) + + 这种形式的 json_object 从两个独立数组中成对获取键和值。除此之外,它与单参数形式完全相同。 + json_object('{a, b}', '{1,2}') + {"a": "1", "b": "2"} + + + +
+ + + 除了提供美化输出选项外,array_to_jsonrow_to_json 的行为与 to_json 相同。针对 to_json 描述的行为同样适用于其他 JSON 创建函数转换的每个值。 + + + + 扩展提供了从 hstorejson 的类型转换,因此经由 JSON 创建函数转换的 hstore 值会表示为 JSON 对象,而不是基本的字符串值。 + + + + 显示可用于处理jsonjsonb值的函数。 + + + + json_array_length + + + jsonb_array_length + + + json_each + + + jsonb_each + + + json_each_text + + + jsonb_each_text + + + json_extract_path + + + jsonb_extract_path + + + json_extract_path_text + + + jsonb_extract_path_text + + + json_object_keys + + + jsonb_object_keys + + + json_populate_record + + + jsonb_populate_record + + + json_populate_recordset + + + jsonb_populate_recordset + + + json_array_elements + + + jsonb_array_elements + + + json_array_elements_text + + + jsonb_array_elements_text + + + json_typeof + + + jsonb_typeof + + + json_to_record + + + jsonb_to_record + + + json_to_recordset + + + jsonb_to_recordset + + + json_strip_nulls + + + jsonb_strip_nulls + + + jsonb_set + + + jsonb_insert + + + jsonb_pretty + + + + JSON 处理函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 示例结果 + + + + + json_array_length(json) + jsonb_array_length(jsonb) + + int + 返回最外层 JSON 数组的元素数量。 + json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]') + 5 + + + json_each(json) + jsonb_each(jsonb) + + setof key text, value json + setof key text, value jsonb + + 将最外层 JSON 对象展开为一组键/值对。 + select * from json_each('{"a":"foo", "b":"bar"}') + + + key | value +-----+------- + a | "foo" + b | "bar" + + + + + json_each_text(json) + jsonb_each_text(jsonb) + + setof key text, value text + 将最外层 JSON 对象展开为一组键/值对。返回的值为 text 类型。 + select * from json_each_text('{"a":"foo", "b":"bar"}') + + + key | value +-----+------- + a | foo + b | bar + + + + + json_extract_path(from_json json, VARIADIC path_elems text[]) + jsonb_extract_path(from_json jsonb, VARIADIC path_elems text[]) + + jsonjsonb + 返回 path_elems 指向的 JSON 值(等价于 #> 操作符)。 + json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4') + {"f5":99,"f6":"foo"} + + + json_extract_path_text(from_json json, VARIADIC path_elems text[]) + jsonb_extract_path_text(from_json jsonb, VARIADIC path_elems text[]) + + text + text 形式返回 path_elems 指向的 JSON 值(等价于 #>> 操作符)。 + json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4', 'f6') + foo + + + json_object_keys(json) + jsonb_object_keys(jsonb) + + setof text + 返回最外层 JSON 对象的键集合。 + json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}') + + + json_object_keys +------------------ + f1 + f2 + + + + + json_populate_record(base anyelement, from_json json) + jsonb_populate_record(base anyelement, from_json jsonb) + + anyelement + from_json 中的对象展开为一行,其列与 base 定义的记录类型相匹配(见下注)。 + select * from json_populate_record(null::myrowtype, '{"a":1,"b":2}') + + + a | b +---+--- + 1 | 2 + + + + + json_populate_recordset(base anyelement, from_json json) + jsonb_populate_recordset(base anyelement, from_json jsonb) + + setof anyelement + from_json 中最外层的对象数组展开为一组行,其列与 base 定义的记录类型相匹配(见下注)。 + select * from json_populate_recordset(null::myrowtype, '[{"a":1,"b":2},{"a":3,"b":4}]') + + + a | b +---+--- + 1 | 2 + 3 | 4 + + + + + json_array_elements(json) + jsonb_array_elements(jsonb) + + setof json + setof jsonb + + 将 JSON 数组展开为一组 JSON 值。 + select * from json_array_elements('[1,true, [2,false]]') + + + value +----------- + 1 + true + [2,false] + + + + + json_array_elements_text(json) + jsonb_array_elements_text(jsonb) + + setof text + 将 JSON 数组展开为一组 text 值。 + select * from json_array_elements_text('["foo", "bar"]') + + + value +----------- + foo + bar + + + + + json_typeof(json) + jsonb_typeof(jsonb) + + text + 以文本字符串形式返回最外层 JSON 值的类型。可能的类型为 objectarraystringnumberbooleannull + json_typeof('-123.4') + number + + + json_to_record(json) + jsonb_to_record(jsonb) + + record + 从 JSON 对象构造任意记录(见下注)。与所有返回 record 的函数一样,调用者必须使用 AS 子句显式定义记录结构。 + select * from json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}') as x(a int, b text, d text) + + + a | b | d +---+---------+--- + 1 | [1,2,3] | + + + + + json_to_recordset(json) + jsonb_to_recordset(jsonb) + + setof record + 从 JSON 对象数组构造任意记录集合(见下注)。与所有返回 record 的函数一样,调用者必须使用 AS 子句显式定义记录结构。 + select * from json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]') as x(a int, b text); + + + a | b +---+----- + 1 | foo + 2 | + + + + + json_strip_nulls(from_json json) + jsonb_strip_nulls(from_json jsonb) + + jsonjsonb + 返回移除了所有值为 null 的对象字段的 from_json。其他 null 值保持不变。 + json_strip_nulls('[{"f1":1,"f2":null},2,null,3]') + [{"f1":1},2,null,3] + + + jsonb_set(target jsonb, path text[], new_value jsonb , create_missing boolean) + jsonb + 返回将 path 指定部分替换为 new_value 后的 target;如果 create_missing 为真(默认为 true),且 path 指定的项不存在,则添加 new_value。与面向路径的操作符一样,path 中的负整数从 JSON 数组末尾计数。 + jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]', false) + jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]') + + [{"f1":[2,3,4],"f2":null},2,null,3] + [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2] + + + + + jsonb_insert(target jsonb, path text[], new_value jsonb , insert_after boolean) + + jsonb + 返回插入 new_value 后的 target。如果 path 指定的 target 部分位于 JSONB 数组中,则将 new_value 插入目标之前;若 insert_after 为真,则插入目标之后(默认为 false)。如果 path 指定的 target 部分位于 JSONB 对象中,则仅在 target 不存在时插入 new_value。与面向路径的操作符一样,path 中的负整数从 JSON 数组末尾计数。 + + + jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"') + + + jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true) + + + {"a": [0, "new_value", 1, 2]} + {"a": [0, 1, "new_value", 2]} + + + + jsonb_pretty(from_json jsonb) + + text + from_json 作为带缩进的 JSON 文本返回。 + jsonb_pretty('[{"f1":1,"f2":null},2,null,3]') + + +[ + { + "f1": 1, + "f2": null + }, + 2, + null, + 3 +] + + + + + +
+ + + 这些函数和操作符中有许多会将 JSON 字符串中的 Unicode 转义转换为相应的单个字符。对于 jsonb 输入,这不成问题,因为转换已经完成;但对于 json 输入,这可能引发错误,如所述。 + + + + + 虽然函数json_populate_recordjson_populate_recordsetjson_to_recordjson_to_recordset的示例使用常量,但典型用法是在 FROM 子句中引用一个表,并将它的某个 jsonjsonb 列用作函数参数。随后可在查询的其他部分(如 WHERE 子句和目标列表)引用提取出的键值。与使用逐键操作符分别提取相比,以这种方式提取多个值可以提高性能。 + + + + JSON 键与目标行类型中相同的列名匹配。这些函数的 JSON 类型强制转换是尽力而为的,对于某些类型可能不会得到期望的值。目标行类型中未出现的 JSON 字段会被从输出中省略,而与任何 JSON 字段都不匹配的目标列将直接为 NULL。 + + + + + jsonb_setjsonb_insertpath 参数中,除最后一项外的所有项都必须已存在于 target 中。如果 create_missing 为假,jsonb_setpath 参数的所有项都必须存在。如果不满足这些条件,则原样返回 target + 如果路径的最后一项是对象键,当该键不存在时会创建它,并赋予新值。如果路径的最后一项是数组下标,正值从左侧计数,负值从右侧计数来确定要设置的项;-1 表示最右侧的元素,以此类推。如果该项超出 -array_length .. array_length -1 的范围,且 create_missing 为真,则在下标为负时将新值添加到数组开头,为正时添加到数组末尾。 + + + + 不要将 json_typeof 函数返回的 null 与 SQL NULL 混淆。调用 json_typeof('null'::json) 会返回 null,而调用 json_typeof(NULL::json) 会返回 SQL NULL。 + + + + 如果 json_strip_nulls 的参数中有任何对象包含重复字段名,则结果的语义可能有所不同,具体取决于这些字段的出现顺序。jsonb_strip_nulls 没有这一问题,因为 jsonb 值不会包含重复的对象字段名。 + + + + 另请参见,了解聚合函数json_agg如何将记录值聚合为 JSON, + 以及聚合函数json_object_agg如何将值对聚合为 JSON 对象,还有它们对应的 jsonb 函数, + jsonb_aggjsonb_object_agg。 + + +
+ + + 序列操作函数 + + + 序列 + + + nextval + + + currval + + + lastval + + + setval + + + + 本节描述用于操作序列对象(也称为序列生成器,或简称序列)的函数。 + 序列对象是使用创建的特殊单行表。 + 序列对象通常用于为表中的行生成惟一标识符。在中列出的序列函数,提供了简单的、多用户安全方法,用于从序列对象中获取连续的序列值。 + + + + 序列函数 + + + 函数 返回类型 描述 + + + + + currval(regclass) + bigint + 返回最近对指定序列调用 nextval 得到的值 + + + lastval() + bigint + 返回最近对任意序列调用 nextval 得到的值 + + + nextval(regclass) + bigint + 推进序列并返回新值 + + + setval(regclass, bigint) + bigint + 设置序列的当前值 + + + setval(regclass, bigint, boolean) + bigint + 设置序列的当前值和 is_called 标志 + + + +
+ + 序列函数要操作的序列由一个regclass参数指定,该参数就是序列在pg_class系统目录中的 OID。不过,不必手动查找 OID,因为regclass数据类型的输入转换函数会替你完成这项工作。只需将序列名用单引号括起来,使其看上去像一个字面常量。为兼容普通SQL名称的处理方式,除非序列名被双引号括起,否则字符串会转换为小写。因此: +nextval('foo') 操作序列 foo +nextval('FOO') 操作序列 foo +nextval('"Foo"') 操作序列 Foo +必要时,序列名可以带模式限定: +nextval('myschema.foo') 操作 myschema.foo +nextval('"myschema".foo') 同上 +nextval('foo') 在搜索路径中查找 foo +参见以获取更多有关以下类型的信息:regclass。 + + + + PostgreSQL 8.1 之前,序列函数的参数类型是 text,而不是 regclass;上述从文本字符串到 OID 值的转换会在每次调用时于运行期间执行。为保持向后兼容,这种能力仍然存在,但内部现在会在调用函数前,通过从 textregclass 的隐式强制转换来处理。 + + 将序列函数的参数写成不加修饰的字符串字面量时,它会成为以下类型的常量:regclass。由于它实际上只是一个 OID,即使后来发生重命名、模式变更等情况,它仍会指向最初标识的序列。这种早绑定行为通常适用于列默认值和视图中的序列引用。但有时你可能希望采用后绑定,在运行时解析序列引用。要获得后绑定行为,应强制将常量存储为text常量,而不是regclass: + +nextval('foo'::text) foo运行时被查找 +请注意,后绑定是下列旧版本唯一支持的行为:PostgreSQL8.1 之前的版本,因此可能需要这样做来保留旧应用的语义。 + + 当然,序列函数的参数既可以是常量,也可以是表达式。如果它是文本表达式,隐式强制转换就会导致运行时查找。 + + + 可用的序列函数如下: + + nextval + + 将序列对象推进到下一个值并返回该值。这个操作是原子的:即使多个会话并发执行 nextval,每个会话也会安全地获得一个不同的序列值。 + + 如果使用默认参数创建序列对象,连续调用 nextval 会返回从 1 开始的连续值。可以在 命令中使用特殊参数获得其他行为;更多信息请参见该命令的参考页。 + + + + + 为了避免阻塞从同一序列获取数值的并发事务,nextval操作永远不会回滚;也就是说,一旦一个值被取出,它就被视为已使用,不会再被返回。即使周围的事务随后中止,或者调用查询最终没有使用该值,也是如此。例如,带 ON CONFLICT 子句的 INSERT 会在检测到任何会使其转而遵循 ON CONFLICT 规则的冲突之前,计算要插入的元组,包括执行任何所需的 nextval 调用。这类情况会在序列的已分配值中留下未使用的空洞。因此,PostgreSQL 序列对象不能用来获得无空洞序列。 + + + + + + + currval + + 返回当前会话中最近一次针对该序列调用 nextval 所获得的值。(如果当前会话从未针对该序列调用过 nextval,则会报错。)由于返回的是会话局部值,无论其他会话是否在当前会话调用之后执行过 nextval,结果都是可预测的。 + + + + + + lastval + + 返回当前会话中最近一次调用 nextval 所返回的值。此函数与 currval 相同,但不接受序列名参数,而是引用当前会话中最近一次调用 nextval 时所操作的序列。如果当前会话尚未调用过 nextval,调用 lastval 会报错。 + + + + + + setval + + 重置序列对象的计数器值。双参数形式将序列的last_value字段设为指定值,并将其is_called字段设为true,这意味着下次调用nextval会在返回值之前推进序列。currval报告的值也会设为指定值。在三参数形式中,is_called可以设为truefalsetrue与双参数形式具有相同的效果。如果将它设为false,下次调用nextval将恰好返回指定值,并从再下一次调用nextval时开始推进序列。此外,在这种情况下,currval报告的值不会改变。例如: +SELECT setval('foo', 42); 下一次 nextval 将返回 43 +SELECT setval('foo', 42, true); 同上 +SELECT setval('foo', 42, false); 下一次 nextval 将返回 42 +函数setval返回的结果就是其第二个参数的值。 + + + + + 由于序列是非事务性的,如果事务回滚,setval所做的更改不会被撤销。 + + + + + + + +
+ + + + 条件表达式 + + + CASE + + + + 条件表达式 + + + + 本节描述在PostgreSQL中可用的SQL兼容的条件表达式。 + + + + + 如果你的需求超过这些条件表达式的能力,你可能会希望用一种更富表现力的编程语言写一个存储过程。 + + + + + <literal>CASE</literal> + + + SQL CASE表达式是一种通用的条件表达式,类似于其它编程语言中的 if/else 语句: + + +CASE WHEN condition THEN result + WHEN ... + ELSE result +END + + + CASE子句可以用于任何表达式可以出现的地方。每一个condition是一个返回boolean结果的表达式。如果结果为真,那么CASE表达式的结果就是紧随该条件后的result,并且剩下的CASE表达式不会被处理。如果条件的结果不为真,那么以相同方式搜寻任何随后的WHEN子句。如果没有WHEN condition为真,那么CASE表达式的值就是在ELSE子句里的result。如果省略了ELSE子句而且没有条件为真,结果为 null。 + + + + 示例: + +SELECT * FROM test; + + a +--- + 1 + 2 + 3 + +SELECT a, + CASE WHEN a=1 THEN 'one' + WHEN a=2 THEN 'two' + ELSE 'other' + END + FROM test; + + a | case +---+------- + 1 | one + 2 | two + 3 | other + + + + + 所有result表达式的数据类型都必须可以转换成单一的输出类型。 参阅获取细节。 + + + + 下面这个简单形式的CASE表达式是上述通用形式的一个变种: + + +CASE expression + WHEN value THEN result + WHEN ... + ELSE result +END + + + 第一个expression会被计算,然后与所有在WHEN子句中的每一个value对比,直到找到一个相等的。如果没有找到匹配的,则返回在ELSE子句中的result(或者 null 值)。 这类似于 C 里的switch语句。 + + + + 上面的示例可以用简单CASE语法来写: + +SELECT a, + CASE a WHEN 1 THEN 'one' + WHEN 2 THEN 'two' + ELSE 'other' + END + FROM test; + + a | case +---+------- + 1 | one + 2 | two + 3 | other + + + + + CASE表达式并不计算任何无助于判断结果的子表达式。例如,下面是一个可以避免被零除错误的方法: + +SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END; + + + + + + + 如所述,在多种情况下,表达式中的子表达式会在不同阶段求值,因此CASE只计算必要的子表达式这一原则并非绝对成立。例如,常量子表达式1/0通常会在规划时导致除零错误,即便它位于一个运行时永远不会进入的CASE分支中也是如此。 + + + + + + <literal>COALESCE</literal> + + + COALESCE + + + + NVL + + + + IFNULL + + + +COALESCE(value , ...) + + + COALESCE函数返回参数中第一个不为 null 的值。只有所有参数都为 null 时,才返回 null。它常用于在检索数据以供显示时,用默认值替换 null 值。例如: + +SELECT COALESCE(description, short_description, '(none)') ... + + 此表达式返回description,前提是它不为 null,否则返回short_description,前提是它不为 null,否则返回(none)。 + + + + 所有参数都必须能转换为同一个数据类型,它将是结果的类型(详情参见)。 + + + + 和CASE表达式一样,COALESCE只计算确定结果所需的参数;也就是说,在第一个不为 null 的参数右边的参数不会被计算。这个 SQL 标准函数提供了类似于NVLIFNULL的能力,它们被用在某些其他数据库系统中。 + + + + + <literal>NULLIF</literal> + + + NULLIF + + + +NULLIF(value1, value2) + + + + 当value1value2相等时,NULLIF返回 null。 + 否则它返回value1。 这些可以用于执行前文给出的COALESCE示例的逆操作: + +SELECT NULLIF(value, '(none)') ... + + 在这个示例中,如果value(none),将返回 null,否则返回value的值。 + + + + 这两个参数必须具有可比较的类型。具体来说,它们的比较与你写的 value1 = value2完全一样,因此必须有一个合适的=操作符可用。 + + + + 结果的类型与第一个参数相同,但有一点细微的区别。实际上返回的是隐含 =操作符的第一个参数,在某些情况下,它将被提升以匹配第二个参数的类型。 + 例如,NULLIF(1, 2.2) 生成 numeric,因为没有integer = numeric操作符,只有numeric = numeric。 + + + + + + + <literal>GREATEST</literal>和<literal>LEAST</literal> + + + GREATEST + + + + LEAST + + + +GREATEST(value , ...) + + + +LEAST(value , ...) + + + + GREATESTLEAST函数从由任意数量的表达式组成的列表中选取最大值或最小值。这些表达式都必须能转换为同一个数据类型,该类型将作为结果类型(详情参见)。列表中的 NULL 值会被忽略。只有所有表达式的求值结果都为 NULL 时,结果才为 NULL。 + + + + 请注意GREATESTLEAST都未包含在 SQL 标准中,但却是很常见的扩展。某些其他数据库让它们在任何参数为 NULL 时返回 NULL,而不是在所有参数都为 NULL 时才返回 NULL。 + + + + + + 数组函数和操作符 + + 列出了可用于数组类型的操作符。 + + + 数组操作符 + + + + 操作符 + 描述 + 示例 + 结果 + + + + + = + 等于 + ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3] + t + + + + <> + 不等于 + ARRAY[1,2,3] <> ARRAY[1,2,4] + t + + + + < + 小于 + ARRAY[1,2,3] < ARRAY[1,2,4] + t + + + + > + 大于 + ARRAY[1,4,3] > ARRAY[1,2,4] + t + + + + <= + 小于等于 + ARRAY[1,2,3] <= ARRAY[1,2,3] + t + + + + >= + 大于等于 + ARRAY[1,4,3] >= ARRAY[1,4,3] + t + + + + @> + 包含 + ARRAY[1,4,3] @> ARRAY[3,1,3] + t + + + + <@ + 被包含 + ARRAY[2,2,7] <@ ARRAY[1,7,4,2,6] + t + + + + && + 重叠(有公共元素) + ARRAY[1,4,3] && ARRAY[2,1] + t + + + + || + 数组与数组串接 + ARRAY[1,2,3] || ARRAY[4,5,6] + {1,2,3,4,5,6} + + + + || + 数组与数组串接 + ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]] + {{1,2,3},{4,5,6},{7,8,9}} + + + + || + 元素与数组串接 + 3 || ARRAY[4,5,6] + {3,4,5,6} + + + + || + 数组与元素串接 + ARRAY[4,5,6] || 7 + {4,5,6,7} + + + +
+ + 数组排序操作符(<>= 等)使用元素数据类型的默认 B-树比较函数,逐个元素比较数组内容,并根据第一个差异决定排序顺序。多维数组的元素按行优先顺序访问(最后一个下标变化最快)。如果两个数组的内容相同,但维度信息不同,则由维度信息中的第一个差异决定排序顺序。(这与 PostgreSQL 8.2 之前的版本不同:旧版本会将内容相同的两个数组视为相等,即使它们的维数或下标范围不同。) + + 数组包含操作符(<@@>)在一个数组的每个元素都出现在另一个数组中时,认为前者包含在后者中。重复元素不会被特殊处理,因此 ARRAY[1]ARRAY[1,1] 被认为彼此包含。 + + + 参阅获取有关数组操作符行为的更多细节。有关哪些操作符支持索引操作,请参阅。 + + + + 展示了可以用于数组类型的函数。 参阅获取更多信息以及使用这些函数的示例。 + + + + array_append + + + array_cat + + + array_ndims + + + array_dims + + + array_fill + + + array_length + + + array_lower + + + array_position + + + array_positions + + + array_prepend + + + array_remove + + + array_replace + + + array_to_string + + + array_upper + + + cardinality + + string_to_array + + unnest + + + + 数组函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + + array_append(anyarray, anyelement) + + + anyarray + 向数组末尾追加一个元素 + array_append(ARRAY[1,2], 3) + {1,2,3} + + + + + array_cat(anyarray, anyarray) + + + anyarray + 串接两个数组 + array_cat(ARRAY[1,2,3], ARRAY[4,5]) + {1,2,3,4,5} + + + + + array_ndims(anyarray) + + + int + 返回数组的维数 + array_ndims(ARRAY[[1,2,3], [4,5,6]]) + 2 + + + + + array_dims(anyarray) + + + text + 返回数组维度的文本表示 + array_dims(ARRAY[[1,2,3], [4,5,6]]) + [1:2][1:3] + + + + + array_fill(anyelement, int[] + , int[]) + + + anyarray + 返回按给定值和维度初始化的数组,可选地使用不为 1 的下界 + array_fill(7, ARRAY[3], ARRAY[2]) + [2:4]={7,7,7} + + + + + array_length(anyarray, int) + + + int + 返回数组指定维度的长度 + array_length(array[1,2,3], 1) + 3 + + + + + array_lower(anyarray, int) + + + int + 返回数组指定维度的下界 + array_lower('[0:2]={1,2,3}'::int[], 1) + 0 + + + + + array_position(anyarray, anyelement , int) + + + int + 返回第二个参数在数组中第一次出现的下标,从第三个参数指定的元素或第一个元素开始搜索(数组必须是一维的) + array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon') + 2 + + + + + array_positions(anyarray, anyelement) + + + int[] + 返回第二个参数在第一个参数所给数组中所有出现位置的下标数组(数组必须是一维的) + array_positions(ARRAY['A','A','B','A'], 'A') + {1,2,4} + + + + + array_prepend(anyelement, anyarray) + + + anyarray + 向数组开头添加一个元素 + array_prepend(1, ARRAY[2,3]) + {1,2,3} + + + + + array_remove(anyarray, anyelement) + + + anyarray + 从数组中移除所有等于给定值的元素(数组必须是一维的) + array_remove(ARRAY[1,2,3,2], 2) + {1,3} + + + + + array_replace(anyarray, anyelement, anyelement) + + + anyarray + 将数组中每个等于给定值的元素替换为新值 + array_replace(ARRAY[1,2,5,4], 5, 3) + {1,2,3,4} + + + + + array_to_string(anyarray, text , text) + + + text + 使用给定分隔符和可选的 null 替代字符串串接数组元素 + array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*') + 1,2,3,*,5 + + + + + array_upper(anyarray, int) + + + int + 返回数组指定维度的上界 + array_upper(ARRAY[1,8,3,7], 1) + 4 + + + + + cardinality(anyarray) + + + int + 返回数组的元素总数;数组为空时返回 0 + cardinality(ARRAY[[1,2],[3,4]]) + 4 + + + + + string_to_array(text, text , text) + + + text[] + 使用给定分隔符和可选的 null 替代字符串将字符串拆分为数组元素 + string_to_array('xx~^~yy~^~zz', '~^~', 'yy') + {xx,NULL,zz} + + + + + unnest(anyarray) + + + setof anyelement + 将数组展开为一组行 + unnest(ARRAY[1,2]) + + 1 + 2 +(2 rows) + + + + + unnest(anyarray, anyarray [, ...]) + + + setof anyelement, anyelement [, ...] + 将多个数组(类型可以不同)展开为一组行。仅允许在 FROM 子句中使用;参见 + unnest(ARRAY[1,2],ARRAY['foo','bar','baz']) + 1 foo +2 bar +NULL baz(3 rows) + + + +
+ + array_positionarray_positions 中,使用 IS NOT DISTINCT FROM 语义将每个数组元素与要搜索的值进行比较。 + + array_position 中,如果找不到该值,则返回 NULL + + array_positions 中,只有数组为 NULL 时才返回 NULL;如果数组中找不到该值,则返回空数组。 + + string_to_array 中,如果分隔符参数为 NULL,输入字符串中的每个字符都会成为结果数组中的独立元素。如果分隔符为空字符串,则将整个输入字符串作为单元素数组返回。否则,在分隔符字符串的每次出现处拆分输入字符串。 + + string_to_array 中,如果省略 null 替代字符串参数,或该参数为 NULL,则不会将任何输入子串替换为 NULL。在 array_to_string 中,如果省略 null 替代字符串参数,或该参数为 NULL,则直接跳过数组中的所有 null 元素,不在输出字符串中表示它们。 + + + string_to_array 的行为与 PostgreSQL 9.1 之前的版本有两点不同。首先,当输入字符串长度为零时,它返回空数组(零元素),而不是 NULL。其次,如果分隔符字符串为 NULL,该函数会将输入拆分为单独的字符,而不是像以前那样返回 NULL。 + + + + 也可参见了解用于数组的聚合函数array_agg。 + +
+ + + 范围函数和操作符 + + + 范围类型的概述可参见 。 + + + 列出了可用于范围类型的操作符。 + + + 范围操作符 + + + + 操作符 + 描述 + 示例 + 结果 + + + + + = + 等于 + int4range(1,5) = '[1,4]'::int4range + t + + + + <> + 不等于 + numrange(1.1,2.2) <> numrange(1.1,2.3) + t + + + + < + 小于 + int4range(1,10) < int4range(2,3) + t + + + + > + 大于 + int4range(1,10) > int4range(1,5) + t + + + + <= + 小于等于 + numrange(1.1,2.2) <= numrange(1.1,2.2) + t + + + + >= + 大于等于 + numrange(1.1,2.2) >= numrange(1.1,2.0) + t + + + + @> + 包含范围 + int4range(2,4) @> int4range(2,3) + t + + + + @> + 包含元素 + '[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp + t + + + + <@ + 范围被包含于 + int4range(2,4) <@ int4range(1,7) + t + + + + <@ + 元素被包含于 + 42 <@ int4range(1,7) + f + + + + && + 重叠(有公共点) + int8range(3,7) && int8range(4,12) + t + + + + << + 严格位于左侧 + int8range(1,10) << int8range(100,110) + t + + + + >> + 严格位于右侧 + int8range(50,60) >> int8range(20,30) + t + + + + &< + 不超出其右边界 + int8range(1,20) &< int8range(18,20) + t + + + + &> + 不超出其左边界 + int8range(7,20) &> int8range(5,10) + t + + + + -|- + 与之相邻 + numrange(1.1,2.2) -|- numrange(2.2,3.3) + t + + + + + + 并集 + numrange(5,15) + numrange(10,20) + [5,20) + + + + * + 交集 + int8range(5,15) * int8range(10,20) + [10,15) + + + + - + 差集 + int8range(5,15) - int8range(10,20) + [5,10) + + + + +
+ + 简单比较操作符 <><=>= 首先比较下界,只有下界相等时才比较上界。这些比较对范围通常用处不大,但提供这些操作符可以在范围上建立 B-树索引。 + + 涉及空范围时,左侧、右侧和相邻操作符总是返回假;也就是说,空范围不被视为位于任何其他范围之前或之后。 + + 如果结果范围需要包含两个不相交的子范围,并集和差集操作符会失败,因为这样的范围无法表示。 + + 列出了可用于范围类型的函数。 + + + lower + + + upper + + + isempty + + + lower_inc + + + upper_inc + + + lower_inf + + + upper_inf + + + + 范围函数 + + + + 函数 + 返回类型 + 描述 + 示例 + 结果 + + + + + + + lower(anyrange) + + + 范围的元素类型 + 范围的下界 + lower(numrange(1.1,2.2)) + 1.1 + + + + + upper(anyrange) + + + 范围的元素类型 + 范围的上界 + upper(numrange(1.1,2.2)) + 2.2 + + + + + isempty(anyrange) + + + boolean + 范围是否为空? + isempty(numrange(1.1,2.2)) + false + + + + + lower_inc(anyrange) + + + boolean + 是否包含下界? + lower_inc(numrange(1.1,2.2)) + true + + + + + upper_inc(anyrange) + + + boolean + 是否包含上界? + upper_inc(numrange(1.1,2.2)) + false + + + + + lower_inf(anyrange) + + + boolean + 下界是否无穷? + lower_inf('(,)'::daterange) + true + + + + + upper_inf(anyrange) + + + boolean + 上界是否无穷? + upper_inf('(,)'::daterange) + true + + + + + range_merge(anyrange, anyrange) + + + anyrange + 包含两个给定范围的最小范围 + range_merge('[1,2)'::int4range, '[3,4)'::int4range) + [1,4) + + + +
+ + 如果范围为空,或请求的边界是无限的,lowerupper 函数返回 null。对于空范围,lower_incupper_inclower_infupper_inf 函数都返回假。 +
+ + + 聚合函数 + + + 聚合函数 + 内置 + + + 聚合函数根据一组输入值计算出单个结果。内置的普通聚合函数列于。内置的有序集聚合函数列于。与聚合函数密切相关的分组操作列于。聚合函数的特殊语法说明见。更多入门信息请参见 + + + 通用聚合函数 + + + + + 函数 + 参数类型 + 返回类型 + 部分模式 + 描述 + + + + + + array_agg array_agg(expression) + 任意非数组类型 + 参数类型的数组 + + 将输入值(包括 null)串接为数组 + + + + array_agg(expression) + 任意数组类型 + 与参数数据类型相同 + + 将输入数组串接为维数多一维的数组(所有输入的维数必须相同,且不能是空数组或 null) + + + + 平均值 avg avg(expression) + smallintintbigintrealdouble precisionnumericinterval + 整数类型参数返回 numeric,浮点参数返回 double precision,其他情况与参数数据类型相同 + + 所有非空输入值的平均值(算术平均值) + + + + bit_and bit_and(expression) + smallintintbigintbit + 与参数数据类型相同 + + 所有非空输入值的按位与;没有非空输入时为 null + + + + bit_or bit_or(expression) + smallintintbigintbit + 与参数数据类型相同 + + 所有非空输入值的按位或;没有非空输入时为 null + + + + bool_and bool_and(expression) + bool + bool + + 所有输入值都为真时返回真,否则返回假 + + + + bool_or bool_or(expression) + bool + bool + + 至少一个输入值为真时返回真,否则返回假 + + + + count count(*) + + bigint + + 输入行数 + + + + count(expression) + 任意 + bigint + + expression 的值不为 null 的输入行数 + + + + every every(expression) + bool + bool + + 等价于 bool_and + + + + json_agg json_agg(expression) + any + json + + 将值(包括 null)聚合为 JSON 数组 + + + + jsonb_agg jsonb_agg(expression) + any + jsonb + + 将值(包括 null)聚合为 JSON 数组 + + + + json_object_agg json_object_agg(name, value) + + (any, any) + + json + + 将名称/值对聚合为 JSON 对象;值可以为 null,名称不能为 null + + + + jsonb_object_agg jsonb_object_agg(name, value) + + (any, any) + + jsonb + + 将名称/值对聚合为 JSON 对象;值可以为 null,名称不能为 null + + + + max max(expression) + 任意数值、字符串、日期/时间、网络或枚举类型,或这些类型的数组 + 与参数类型相同 + + 所有非空输入值中 expression 的最大值 + + + + min min(expression) + 任意数值、字符串、日期/时间、网络或枚举类型,或这些类型的数组 + 与参数类型相同 + + 所有非空输入值中 expression 的最小值 + + + + string_agg string_agg(expression, delimiter) + texttext)或(byteabytea + 与参数类型相同 + + 将非空输入值串接为字符串,以分隔符分隔 + + + + sum sum(expression) + smallintintbigintrealdouble precisionnumericintervalmoney + smallintint 参数返回 bigintbigint 参数返回 numeric,其他情况与参数数据类型相同 + + 对所有非空输入值的 expression 求和 + + + + xmlagg xmlagg(expression) + xml + xml + + 串接非空 XML 值(另见 + + + +
+ + + 需要注意,除了count之外,这些函数在没有选中任何行时都会返回空值。特别地,sum在没有输入行时返回空值,而不是预期中的零;array_agg在没有输入行时返回空值,而不是空数组。必要时,可以用coalesce函数把空值替换成零或空数组。 + + + 支持部分模式的聚合函数可以参与并行聚合等多种优化。 + + + + ANY + + + SOME + + 布尔聚合bool_andbool_or对应于标准 SQL 聚合everyanysome。至于anysome,标准语法似乎存在歧义: +SELECT b1 = ANY((SELECT b2 FROM t2 ...)) FROM t1 ...; +此处的ANY既可以被视为引入一个子查询,也可以在该子查询返回一行布尔值时被视为聚合函数。因此,不能将标准名称用于这些聚合。 + + + + 习惯于其他 SQL 数据库管理系统的用户,可能会对count聚合用于整个表时的性能感到失望。如下查询: +SELECT count(*) FROM sometable; +所需的工作量与表大小成正比:PostgreSQL必须扫描整个表,或者完整扫描一个包含表中所有行的索引。 + + + + 聚合函数array_aggjson_aggjsonb_aggjson_object_aggjsonb_object_aggstring_aggxmlagg,以及类似的用户定义聚合函数,其结果值会随输入值的顺序发生实质性变化。默认情况下,输入顺序未指定,但可以在聚合调用中写入ORDER BY子句来控制,如所示。也可以用已排序的子查询提供输入值,这通常也能奏效。例如: + + 需要注意,如果外层查询包含连接等额外处理,这种方法可能失效,因为子查询的输出可能在计算聚合之前被重新排序。 + + + 列出了统计分析常用的聚合函数。(将它们单独列出只是为了避免让更常用的聚合函数列表过于拥挤。)说明中提到的 N 表示所有输入表达式均非空的输入行数。在所有情况下,如果计算没有意义,例如 N 为零,都会返回空值。 + + + 统计 + + + 线性回归 + + + + 用于统计的聚合函数 + + + + + 函数 + 参数类型 + 返回类型 + 部分模式 + 描述 + + + + + + + 相关性 corr corr(Y, X) + double precision + double precision + + 相关系数 + + + + 协方差 总体 covar_pop covar_pop(Y, X) + double precision + double precision + + 总体协方差 + + + + 协方差 样本 covar_samp covar_samp(Y, X) + double precision + double precision + + 样本协方差 + + + + regr_avgx regr_avgx(Y, X) + double precision + double precision + + 自变量的平均值(sum(X)/N + + + + regr_avgy regr_avgy(Y, X) + double precision + double precision + + 因变量的平均值(sum(Y)/N + + + + regr_count regr_count(Y, X) + double precision + bigint + + 两个表达式都非空的输入行数 + + + + 回归截距 regr_intercept regr_intercept(Y, X) + double precision + double precision + + 由(XY)数值对确定的最小二乘拟合线性方程的 y 轴截距 + + + + regr_r2 regr_r2(Y, X) + double precision + double precision + + 相关系数的平方 + + + + 回归斜率 regr_slope regr_slope(Y, X) + double precision + double precision + + 由(XY)数值对确定的最小二乘拟合线性方程的斜率 + + + + regr_sxx regr_sxx(Y, X) + double precision + double precision + + sum(X^2) - sum(X)^2/N(自变量的平方和 + + + + regr_sxy regr_sxy(Y, X) + double precision + double precision + + sum(X*Y) - sum(X) * sum(Y)/N(自变量与因变量的乘积和 + + + + regr_syy regr_syy(Y, X) + double precision + double precision + + sum(Y^2) - sum(Y)^2/N(因变量的平方和 + + + + 标准差 stddev stddev(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + stddev_samp 的历史别名 + + + + 标准差 总体 stddev_pop stddev_pop(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + 输入值的总体标准差 + + + + 标准差 样本 stddev_samp stddev_samp(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + 输入值的样本标准差 + + + + variance variance(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + var_samp 的历史别名 + + + + 方差 总体 var_pop var_pop(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + 输入值的总体方差(总体标准差的平方) + + + + 方差 样本 var_samp var_samp(expression) + smallintintbigintrealdouble precisionnumeric + 浮点参数返回 double precision,其他情况返回 numeric + + 输入值的样本方差(样本标准差的平方) + + + +
+ + 列出了一些使用有序集聚合语法的聚合函数。这些函数有时被称为逆分布函数。 + + + 有序集聚合 + 内置 + + + 逆分布 + + + + 有序集聚合函数 + + + + + 函数 + 直接参数类型 + 聚合参数类型 + 返回类型 + 部分模式 + 描述 + + + + + + + 众数 统计 mode() WITHIN GROUP (ORDER BY sort_expression) + + + 任意可排序类型 + 与排序表达式相同 + + 返回出现次数最多的输入值(若有多个结果同样频繁,则任意选择第一个) + + + + 百分位点 连续 percentile_cont(fraction) WITHIN GROUP (ORDER BY sort_expression) + double precision + double precisioninterval + 与排序表达式相同 + + 连续百分位点:返回排序中与指定比例对应的值,必要时在相邻输入项之间插值 + + + + percentile_cont(fractions) WITHIN GROUP (ORDER BY sort_expression) + + double precision[] + + double precisioninterval + 排序表达式类型的数组 + + 多个连续百分位点:返回形状与 fractions 参数一致的结果数组,将每个非空元素替换为与该百分位点对应的值 + + + + 百分位数 离散 percentile_disc(fraction) WITHIN GROUP (ORDER BY sort_expression) + double precision + 任意可排序类型 + 与排序表达式相同 + + 离散百分位数:返回排序位置等于或超过指定比例的第一个输入值 + + + + percentile_disc(fractions) WITHIN GROUP (ORDER BY sort_expression) + + double precision[] + + 任意可排序类型 + 排序表达式类型的数组 + + 多个离散百分位数:返回形状与 fractions 参数一致的结果数组,将每个非空元素替换为与该百分位数对应的输入值 + + + + +
+ + 中列出的所有聚合函数都忽略其排序输入中的空值。对于接受 fraction 参数的函数,该比例值必须在 0 和 1 之间,否则会报错。但 null 比例值只会产生 null 结果。 + + + 假想集聚合 + 内置 + + + 中列出的每个聚合函数都与中定义的同名窗口函数相关联。对于每个函数,如果将由 args 构造的假想行加入由 sorted_args 计算出的已排序行组,聚合结果就是相应窗口函数会为该行返回的值。 + + + 假想集聚合函数 + + + + + 函数 + 直接参数类型 + 聚合参数类型 + 返回类型 + 部分模式 + 描述 + + + + + + + rank 假设行 rank(args) WITHIN GROUP (ORDER BY sorted_args) + VARIADIC "any" + VARIADIC "any" + bigint + + 假设行的排名,重复行会造成排名空缺 + + + + dense_rank 假设行 dense_rank(args) WITHIN GROUP (ORDER BY sorted_args) + VARIADIC "any" + VARIADIC "any" + bigint + + 假设行的排名,没有空缺 + + + + percent_rank 假设行 percent_rank(args) WITHIN GROUP (ORDER BY sorted_args) + VARIADIC "any" + VARIADIC "any" + double precision + + 假设行的相对排名,范围为 0 到 1 + + + + cume_dist 假设行 cume_dist(args) WITHIN GROUP (ORDER BY sorted_args) + VARIADIC "any" + VARIADIC "any" + double precision + + 假设行的相对排名,范围为 1/N 到 1 + + + + +
+ + 对于每个假想集聚合函数,args 中给出的直接参数列表必须与 sorted_args 中给出的聚合参数在数量和类型上匹配。与大多数内置聚合不同,这些聚合不是严格的,也就是说,它们不会丢弃包含空值的输入行。空值按 ORDER BY 子句指定的规则排序。 + + + 分组操作 + + + + + 函数 + 返回类型 + 描述 + + + + + + + GROUPING GROUPING(args...) + integer + 表示哪些参数未包含在当前分组集中的整数位掩码 + + + +
+ + 分组操作与分组集配合使用(参见),以区分结果行。传给GROUPING操作的参数不会实际求值,但它们必须与同一查询层级的GROUP BY子句中的表达式完全匹配。位的分配方式是将最右侧的参数对应到最低有效位;如果相应表达式包含在生成结果行的分组集的分组条件中,该位为 0,否则为 1。例如: +=> SELECT * FROM items_sold; + make | model | sales +-------+-------+------- + Foo | GT | 10 + Foo | Tour | 20 + Bar | City | 15 + Bar | Sport | 5 +(4 rows) + +=> SELECT make, model, GROUPING(make,model), sum(sales) FROM items_sold GROUP BY ROLLUP(make,model); + make | model | grouping | sum +-------+-------+----------+----- + Foo | GT | 0 | 10 + Foo | Tour | 0 | 20 + Bar | City | 0 | 15 + Bar | Sport | 0 | 5 + Foo | | 1 | 30 + Bar | | 1 | 20 + | | 3 | 50 +(7 rows) + + + +
+ + + 窗口函数 + + + 窗口函数 + 内置 + + + + 窗口函数提供了对与当前查询行相关的一组行执行计算的能力。 + 该特性的介绍请参见,语法细节请参见。 + + + + 内置的窗口函数列在中。注意,这些函数必须使用窗口函数语法调用,也就是说,需要一个OVER子句。 + + + 除了这些函数外,任何内置或用户定义的普通聚合函数(但不含有序集聚合或假想集聚合)都可以用作窗口函数;内置聚合函数列表见。聚合函数只有在调用后跟随 OVER 子句时才作为窗口函数;否则作为常规聚合。 + + + 通用窗口函数 + + + + + 函数 + 返回类型 + 描述 + + + + + + row_number row_number() + bigint + 当前行在其分区内的编号,从 1 开始计数 + + + + rank rank() + bigint + 当前行的排名,允许空缺;等于其第一个同等行的 row_number + + + + dense_rank dense_rank() + bigint + 当前行的排名,没有空缺;此函数对同等行组计数 + + + + percent_rank percent_rank() + double precision + 当前行的相对排名:(rank - 1)/(总行数 - 1) + + + + cume_dist cume_dist() + double precision + 当前行的相对排名:(当前行之前或与当前行同等的行数)/(总行数) + + + + ntile ntile(num_buckets integer) + integer + 范围为 1 到参数值的整数,将分区尽可能均等地划分 + + + + lag lag(value anyelement [, offset integer [, default anyelement ]]) + value 相同的类型 + 返回在分区内当前行之前 offset 行处计算的 value;如果没有这样的行,则返回 default(其类型必须与 value 相同)。offsetdefault 都针对当前行求值。如果省略,offset 默认为 1,default 默认为 null + + + + lead lead(value anyelement [, offset integer [, default anyelement ]]) + value 相同的类型 + 返回在分区内当前行之后 offset 行处计算的 value;如果没有这样的行,则返回 default(其类型必须与 value 相同)。offsetdefault 都针对当前行求值。如果省略,offset 默认为 1,default 默认为 null + + + + first_value first_value(value any) + value 相同的类型 + 返回在窗口帧第一行处计算的 value + + + + last_value last_value(value any) + value 相同的类型 + 返回在窗口帧最后一行处计算的 value + + + + nth_value nth_value(value any, nth integer) + value 相同的类型 + 返回在窗口帧第 nth 行处计算的 value(从 1 开始计数);没有这样的行时返回 null + + + +
+ + + 在中列出的所有函数都依赖于相关窗口定义的ORDER BY子句指定的排序顺序。 + 在ORDER BY排序中不能区分的行被称为是同等行; + 这四个排名函数的定义使它们对任何两个同等行都给出相同的答案。 + + + + 注意first_valuelast_valuenth_value只考虑窗口帧内的行,它默认情况下包含从分区的开始行直到当前行的最后一个同等行。 + 这对last_value可能不会给出有用的结果,有时对nth_value也一样。 + 你可以通过向OVER子句增加一个合适的帧声明(RANGEROWS)来重定义帧。 + 关于帧声明的更多信息请参考。 + + + + 当一个聚合函数被用作窗口函数时,它将在当前行的窗口帧内的行上聚合。 + 一个使用ORDER BY和默认窗口帧定义的聚合产生一种累计求和类型的行为,这可能是或者不是想要的结果。 + 为了获取在整个分区上的聚合,省略ORDER BY或者使用ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。 + 其它窗口帧声明可以用来获得其它的效果。 + + + + + + SQL 标准为leadlagfirst_valuelast_valuenth_value定义了一个RESPECT NULLSIGNORE NULLS选项。 + 这在PostgreSQL中没有实现:行为总是与标准的默认相同,即RESPECT NULLS。 + 同样,标准中用于nth_valueFROM FIRSTFROM LAST选项没有实现: 只支持默认的FROM FIRST行为(你可以通过反转ORDER BY的排序达到FROM LAST的结果)。 + + + + +
+ + + 子查询表达式 + + + EXISTS + + + + IN + + + + NOT IN + + + + ANY + + + + ALL + + + + SOME + + + + 子查询 + + + + 本节描述PostgreSQL中可用的SQL兼容的子查询表达式。本节介绍的所有表达式形式都返回布尔值(真/假)结果。 + + + + <literal>EXISTS</literal> + + +EXISTS (subquery) + + + + EXISTS的参数是一个任意的SELECT语句, 或者说子查询。系统对子查询进行运算以判断它是否返回行。如果它至少返回一行,那么EXISTS的结果就为; 如果子查询没有返回行,那么EXISTS的结果是。 + + + + 子查询可以引用外层查询的变量,这些变量在该子查询的任何一次计算中都起常量的作用。 + + + + 这个子查询通常只是运行到能判断它是否可以返回至少一行为止, 而不是等到全部结束。在这里写任何有副作用的子查询都是不明智的(例如调用序列函数);这些副作用是否发生是很难判断的。 + + + + 因为结果只取决于是否会返回行,而不取决于这些行的内容, 所以这个子查询的输出列表通常是无关紧要的。一个常用的编码习惯是用EXISTS(SELECT 1 WHERE ...)的形式写所有的EXISTS测试。不过这条规则有例外,例如那些使用INTERSECT的子查询。 + + + + 下面这个简单的示例类似在col2上的一次内连接,但是它为每个 tab1的行最多生成一行输出,即使存在多个匹配tab2的行也如此: + +SELECT col1 +FROM tab1 +WHERE EXISTS (SELECT 1 FROM tab2 WHERE col2 = tab1.col2); + + + + + + <literal>IN</literal> + + +expression IN (subquery) + + + + 右手边是一个圆括号括起来的子查询,它必须恰好返回一列。左手边表达式会被求值,并与子查询结果逐行比较。如果找到任意相等的子查询行,那么IN的结果就是。如果没有找到相等行,那么结果就是(包括子查询不返回任何行的情况)。 + + + + 请注意,如果左侧表达式得到空值,或者右侧没有相等的值且至少有一行得到空值,则IN结构的结果将是空值,而不是假。这符合 SQL 对空值布尔组合的一般规则。 + + + + 和EXISTS一样,假定子查询一定会完整运行并不明智。 + + + +row_constructor IN (subquery) + + + + 这种形式的IN左手边是一个行构造器,如中所述。右手边是一个圆括号括起来的子查询,它必须返回与左手边行中表达式数量完全相同的列数。左手边表达式会被求值,并与子查询结果逐行比较。如果找到任意相等的子查询行,则IN的结果为。如果没有找到相等行,那么结果为(包括子查询不返回任何行的情况)。 + + + + 通常,表达式或者子查询行里的空值是按照 SQL 布尔表达式的一般规则进行组合的。 如果两个行对应的成员都非空并且相等,那么认为这两行相等;如果任意对应成员为非空且不等,那么这两行不等; 否则这样的行比较的结果是未知(空值)。如果所有行的结果要么是不等, 要么是空值,并且至少有一个空值,那么IN的结果是空值。 + + + + + <literal>NOT IN</literal> + + +expression NOT IN (subquery) + + + + 右手边是一个圆括号括起来的子查询,它必须恰好返回一列。左手边表达式会被求值,并与子查询结果逐行比较。如果只找到不相等的子查询行(包括子查询不返回任何行的情况),那么NOT IN的结果是。如果找到任何相等行,则结果为。 + + + + 请注意,如果左侧表达式得到空值,或者右侧没有相等的值且至少有一行得到空值,则NOT IN结构的结果将是空值,而不是真。这符合 SQL 对空值布尔组合的一般规则。 + + + + 和EXISTS一样,假定子查询一定会完整运行并不明智。 + + + +row_constructor NOT IN (subquery) + + + + 这种形式的NOT IN左侧是一个行构造器,如中所述。右侧是一个用圆括号括起来的子查询,必须返回与左侧行中表达式数量完全相同的列数。对左侧表达式求值后,将其按行与子查询结果的每一行比较。如果只找到不相等的子查询行(包括子查询不返回任何行的情况),则NOT IN的结果为。如果找到任何相等行,则结果为。 + + + + 通常,表达式或者子查询行里的空值是按照 SQL 布尔表达式的一般规则进行组合的。 如果两个行对应的成员都非空并且相等,那么认为这两行相等;如果任意对应成员为非空且不等,那么这两行不等; 否则这样的行比较的结果是未知(空值)。如果所有行的结果要么是不等, 要么是空值,并且至少有一个空值,那么NOT IN的结果是空值。 + + + + + <literal>ANY</literal>/<literal>SOME</literal> + + +expression operator ANY (subquery) +expression operator SOME (subquery) + + + + 这种形式的右侧是一个用括号括起来的子查询,它必须恰好返回一列。左侧表达式会被求值,并使用给定的 operator 与子查询结果的每一行进行比较。该操作符必须产生布尔结果。如果得到任何真值结果,那么ANY的结果就是。如果没有找到真值结果,那么结果是(包括子查询没有返回任何行的情况)。 + + + + SOMEANY的同义词。IN等价于= ANY。 + + + + 请注意,如果没有任何比较返回真,并且至少有一个右侧行的操作符结果为空值,则ANY结构的结果将是空值,而不是假。这符合 SQL 对空值布尔组合的一般规则。 + + + + 和EXISTS一样,假定子查询一定会完整运行并不明智。 + + + +row_constructor operator ANY (subquery) +row_constructor operator SOME (subquery) + + + + 这种形式的ANY左侧是一个行构造器,如所述。右侧是一个用括号括起来的子查询,它必须返回与左侧行中表达式数量完全相同的列数。左侧表达式会被求值,并使用给定的operator与子查询结果的每一行逐行比较。如果比较对任何子查询行返回真,则ANY的结果为。如果比较对每一个子查询行都返回假,则结果为(包括子查询不返回行的情况)。如果比较对任何行都不返回真,并且至少有一次比较返回 NULL,则结果为 NULL。 + + + + 关于行构造器比较的详细含义请见。 + + + + + <literal>ALL</literal> + + +expression operator ALL (subquery) + + + + 右侧是一个用圆括号括起来的子查询,必须恰好返回一列。左侧表达式会被求值,并使用给定的operator与子查询结果的每一行进行比较。该操作符必须产生布尔结果。如果所有行都得到真(包括子查询不返回任何行的情况),则ALL的结果为。如果得到任何假值结果,则结果为。如果与任何子查询行的比较都不返回假,并且至少有一次比较返回 NULL,则结果为 NULL。 + + + + NOT IN等价于<> ALL。 + + + + 和EXISTS一样,假定子查询一定会完整运行并不明智。 + + + +row_constructor operator ALL (subquery) + + + + 这种形式的 ALL 左侧是一个行构造器,如所述。右侧是一个用括号括起来的子查询,它必须返回与左侧行中表达式一样多的列。左侧表达式会被求值,并使用给定的 operator 与子查询结果逐行比较。如果该比较对所有子查询行都返回真,那么ALL的结果就是(包括子查询没有返回任何行的情况)。如果对任何子查询行的比较返回假,则结果为。如果比较对任何子查询行都不返回假,并且至少有一次比较返回 NULL,则结果为 NULL。 + + + + 关于行构造器比较的详细含义请见。 + + + + + 单一行比较 + + + 比较 + 子查询结果行 + + + +row_constructor operator (subquery) + + + + 左侧是一个行构造器,如所述。右侧是一个用圆括号括起来的子查询,必须返回与左侧行中表达式数量完全相同的列数。此外,该子查询不能返回超过一行;如果它返回零行,则结果为空值。对左侧求值后,将所得的行与子查询返回的唯一一行进行比较。 + + + + 关于行构造器比较的详细含义请见。 + + + + + + + 行和数组比较 + + + IN + + + + NOT IN + + + + ANY + + + + ALL + + + + SOME + + + + 复合类型 + 比较 + + + + 行比较 + + + + 比较 + 复合类型 + + + + 比较 + 行构造器 + + + + IS DISTINCT FROM + + + + IS NOT DISTINCT FROM + + + + 本节描述几个特殊的结构,用于在值的组之间进行多重比较。这些形式语法上和前面一节的子查询形式相关,但是不涉及子查询。 涉及数组子表达式的形式是PostgreSQL的扩展; 其余形式是SQL兼容的。本节介绍的所有表达式形式都返回布尔(Boolean)结果(真/假)。 + + + + <literal>IN</literal> + + +expression IN (value , ...) + + + 右侧是一个用圆括号括起来的标量表达式列表。如果左侧表达式的结果等于右侧任一表达式的结果,则结果为。这等价于以下写法: + +expression = value1 +OR +expression = value2 +OR +... + + + + + 请注意如果左手边表达式得到空值,或者没有相等的右手边值并且至少有一个右手边的表达式得到空值,那么IN结构的结果将为空值,而不是假。这符合 SQL 处理空值的布尔组合的一般规则。 + + + + + <literal>NOT IN</literal> + + +expression NOT IN (value , ...) + + + 右侧是一个用圆括号括起来的标量表达式列表。如果左侧表达式的结果与右侧所有表达式的结果都不相等,则结果为。这等价于以下写法: + +expression <> value1 +AND +expression <> value2 +AND +... + + + + + 请注意如果左手边表达式得到空值,或者没有相等的右手边值并且至少有一个右手边的表达式得到空值,那么NOT IN结构的结果将为空值, 而不是我们可能天真地认为的真值。这符合 SQL 处理空值的布尔组合的一般规则。 + + + + + + x NOT IN y在所有情况下都等效于NOT (x IN y)。但是,在处理空值的时候,用NOT IN比用IN更可能迷惑新手。最好尽可能用正逻辑来表达你的条件。 + + + + + + <literal>ANY</literal>/<literal>SOME</literal>(数组) + + +expression operator ANY (array expression) +expression operator SOME (array expression) + + + + 右侧是一个用括号括起来的表达式,它必须产生一个数组值。左侧表达式会被求值,并使用给定的operator与数组的每个元素进行比较,该操作符必须产生布尔结果。如果得到了任何真值结果,那么ANY的结果是。如果没有找到真值结果(包括数组有零个元素的情况),那么结果是。 + + + + 如果数组表达式得到的是 null 数组,那么ANY的结果将为 null。如果左手边的表达式得到 null,ANY通常也为 null(尽管非严格比较操作符可能得到不同结果)。另外,如果右手边数组包含任何 null 元素,并且没有得到真值比较结果,那么ANY的结果将为 null 而不是假(同样假设这里使用的是严格比较操作符)。这符合 SQL 处理 null 值布尔组合的一般规则。 + + + + SOMEANY的同义词。 + + + + + <literal>ALL</literal>(数组) + + +expression operator ALL (array expression) + + + + 右侧是一个用括号括起来的表达式,它必须产生一个数组值。左侧表达式会被求值,并使用给定的operator与数组的每个元素进行比较,该操作符必须产生布尔结果。如果所有比较都得到真值结果,那么ALL的结果是(包括数组有零个元素的情况)。如果有任何假值结果,那么结果是。 + + + + 如果数组表达式得到的是 null 数组,那么ALL的结果将为 null。如果左手边的表达式得到 null,ALL通常也为 null(尽管非严格比较操作符可能得到不同结果)。另外,如果右手边数组包含任何 null 元素,并且没有得到假值比较结果,那么ALL的结果将为 null 而不是真(同样假设这里使用的是严格比较操作符)。这符合 SQL 处理 null 值布尔组合的一般规则。 + + + + + 行构造器比较 + + +row_constructor operator row_constructor + + + 两侧都是行构造器,如所述。两个行值必须具有相同数量的字段。对两侧分别求值后,按行进行比较。当 operator=<><<=>>= 时,允许进行行构造器比较。每个行元素的类型都必须具有默认 B-树操作符类,否则尝试比较可能会报错。 + + + 如果通过前面的列就已确定比较结果,则与元素数量或类型有关的错误可能不会发生。 + + + + =<>情况略有不同。如果两行的所有对应成员都是非空且相等则这两行被认为相等;如果任何对应成员是非空但是不相等则这两行不相等;否则行比较的结果为未知(空值)。 + + + + 对于<<=>>=这几种情况,会从左到右比较各行元素,一旦找到一对不相等或含有 null 的元素就立即停止。如果这对元素中的任意一个为 null,那么行比较的结果就是未知(null);否则,这对元素的比较结果决定整个行比较的结果。例如,ROW(1,2,NULL) < ROW(1,3,0)的结果为真,而不是 null,因为第三对元素并不会被考虑。 + + + + + + 在PostgreSQL 8.2 之前,<<=>>=这几种情况并不是按照 SQL 规范处理的。像ROW(a,b) < ROW(c,d)这样的比较会被实现为a < c AND b < d,而正确行为应当等价于a < c OR (a = c AND b < d)。 + + + + +row_constructor IS DISTINCT FROM row_constructor + + + + 这个结构与<>行比较相似,但是它对于空值输入不会得到空值。任何空值被认为和任何非空值不相等(有区别),并且任意两个空值被认为相等(无区别)。因此结果将总是为真或为假,永远不会是空值。 + + + +row_constructor IS NOT DISTINCT FROM row_constructor + + + + 这个结构与=行比较相似,但是它对于空值输入不会得到空值。任何空值被认为和任何非空值不相等(有区别),并且任意两个空值被认为相等(无区别)。因此结果将总是为真或为假,永远不会是空值。 + + + + + + 复合类型比较 + + +record operator record + + + + SQL 规范要求在结果依赖于比较两个 NULL 值或者一个 NULL 与一个非 NULL 时行比较返回 NULL。 + PostgreSQL只有在比较两个行构造器(如)的结果或者比较一个行构造器与一个子查询的输出时才这样做(如中所述)。 + 在其他比较两个复合类型值的环境中,两个 NULL 字段值被认为相等,并且一个 NULL 被认为大于一个非 NULL。 + 为了得到复合类型的一致的排序和索引行为,这样做是必要的。 + + + + 对两侧分别求值后,按行进行比较。当operator是 + =、 + <>、 + <、 + <=、 + >或者 + >=时或者具有与这些类似的语义时,允许复合类型的比较(更准确地说,如果一个操作符是一个 B-树操作符类的成员,或者是一个 B-树操作符类的=成员的否定操作符,它就可以是一个行比较操作符)。 + 上述操作符的默认行为与用于行构造器(见)的IS [ NOT ] DISTINCT FROM相同。 + + + + 为了支持包含无默认 B-树操作符类的元素的行匹配,为复合类型比较定义了下列操作符: + *=、 + *<>、 + *<、 + *<=、 + *>以及 + *>=。 + 这些操作符比较两行的内部二进制表示。即使两行用相等操作符的比较为真,两行也可能具有不同的二进制表示。 + 使用这些比较操作符得到的行排序是确定的,但除此之外没有其他意义。 + 这些操作符在内部被用于物化视图并且可能对其他如复制之类的特殊功能有用,但是它们并不打算用在书写查询这类普通用途中。 + + + + + + 集合返回函数 + + + 集合返回函数 + 函数 + + + + generate_series + + + + 本节描述那些可能返回多于一行的函数。目前这个类中被使用最广泛的是序列生成函数, 如所述。其他更特殊的集合返回函数在本手册的其他地方描述。 + 组合多个集合返回函数的方法可见。 + + + + 序列生成函数 + + + + 函数 + 参数类型 + 返回类型 + 描述 + + + + + + generate_series(start, stop) + intbigintnumeric + setof intsetof bigintsetof numeric(与参数类型相同) + 以 1 为步长,从 startstop 生成一系列值 + + + + generate_series(start, stop, step) + intbigintnumeric + setof intsetof bigintsetof numeric(与参数类型相同) + step 为步长,从 startstop 生成一系列值 + + + + generate_series(start, stop, step interval) + timestamptimestamp with time zone + setof timestampsetof timestamp with time zone(与参数类型相同) + step 为步长,从 startstop 生成一系列值 + + + + +
+ + step为正数时,如果start大于stop,则返回零行。反之,当step为负数时,如果start小于stop则返回零行。如果输入为NULL,也返回零行。如果step为零,则会报错。下面是一些示例: +SELECT * FROM generate_series(2,4); + generate_series +----------------- + 2 + 3 + 4 +(3 rows) + +SELECT * FROM generate_series(5,1,-2); + generate_series +----------------- + 5 + 3 + 1 +(3 rows) + +SELECT * FROM generate_series(4,3); + generate_series +----------------- +(0 rows) + +SELECT generate_series(1.1, 4, 1.3); + generate_series +----------------- + 1.1 + 2.4 + 3.7 +(3 rows) + +-- 此示例使用日期加整数的操作符 +SELECT current_date + s.a AS dates FROM generate_series(0,14,7) AS s(a); + dates +------------ + 2004-02-05 + 2004-02-12 + 2004-02-19 +(3 rows) + +SELECT * FROM generate_series('2008-03-01 00:00'::timestamp, + '2008-03-04 12:00', '10 hours'); + generate_series +--------------------- + 2008-03-01 00:00:00 + 2008-03-01 10:00:00 + 2008-03-01 20:00:00 + 2008-03-02 06:00:00 + 2008-03-02 16:00:00 + 2008-03-03 02:00:00 + 2008-03-03 12:00:00 + 2008-03-03 22:00:00 + 2008-03-04 08:00:00 +(9 rows) + + + + + 下标生成函数 + + + + 函数 + 返回类型 + 描述 + + + + + + generate_subscripts(array anyarray, dim int) + setof int + 生成由给定数组下标组成的序列。 + + + + generate_subscripts(array anyarray, dim int, reverse boolean) + setof int + 生成由给定数组下标组成的序列。当 reverse 为真时,按逆序返回该序列。 + + + + +
+ + + generate_subscripts + + + + generate_subscripts是一个便利函数,用于生成给定数组在指定维度上的有效下标集合。对于没有所请求维度的数组或 NULL 数组,返回零行(但数组中的 NULL 元素仍会返回有效下标)。下面是一些示例: +-- 基本用法 +SELECT generate_subscripts('{NULL,1,NULL,2}'::int[], 1) AS s; + s +--- + 1 + 2 + 3 + 4 +(4 rows) + +-- 展示数组、下标和下标对应的值 +-- 需要使用子查询 +SELECT * FROM arrays; + a +-------------------- + {-1,-2} + {100,200,300} +(2 rows) + +SELECT a AS array, s AS subscript, a[s] AS value +FROM (SELECT generate_subscripts(a, 1) AS s, a FROM arrays) foo; + array | subscript | value +---------------+-----------+------- + {-1,-2} | 1 | -1 + {-1,-2} | 2 | -2 + {100,200,300} | 1 | 100 + {100,200,300} | 2 | 200 + {100,200,300} | 3 | 300 +(5 rows) + +-- 展开二维数组 +CREATE OR REPLACE FUNCTION unnest2(anyarray) +RETURNS SETOF anyelement AS $$ +select $1[i][j] + from generate_subscripts($1,1) g1(i), + generate_subscripts($1,2) g2(j); +$$ LANGUAGE sql IMMUTABLE; +CREATE FUNCTION +SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]); + unnest2 +--------- + 1 + 2 + 3 + 4 +(4 rows) + + + + + 序号 + + + FROM子句中的函数后面加上WITH ORDINALITY时,一个bigint列会追加到输出中,其值从 1 开始,对函数输出的每一行递增 1。这种方式对集合返回函数尤其有用,例如unnest()。 + + +-- 集合返回函数与 WITH ORDINALITY +SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); + ls | n +-----------------+---- + pg_serial | 1 + pg_twophase | 2 + postmaster.opts | 3 + pg_notify | 4 + postgresql.conf | 5 + pg_tblspc | 6 + logfile | 7 + base | 8 + postmaster.pid | 9 + pg_ident.conf | 10 + global | 11 + pg_clog | 12 + pg_snapshots | 13 + pg_multixact | 14 + PG_VERSION | 15 + pg_xlog | 16 + pg_hba.conf | 17 + pg_stat_tmp | 18 + pg_subtrans | 19 +(19 rows) + + + +
+ + + 系统信息函数 + + + 列出了多个用于提取会话和系统信息的函数。 + + + + 除了本节列出的函数外,还有许多与统计系统相关的函数,这些函数也提供系统信息。有关更多信息,请参见。 + + + + 会话信息函数 + + + 名称 返回类型 描述 + + + + + current_catalog + name + 当前数据库名称(在 SQL 标准中称为目录 + + + + current_database() + name + 当前数据库名称 + + + + current_query() + text + 客户端提交的当前执行查询文本(可能包含多条语句) + + + + current_role + name + 等价于 current_user + + + + current_schema[()] + name + 当前模式名称 + + + + current_schemas(boolean) + name[] + 搜索路径中的模式名称,可选地包含隐式模式 + + + + current_user + name + 当前执行上下文的用户名 + + + + inet_client_addr() + inet + 远端连接地址 + + + + inet_client_port() + int + 远端连接端口 + + + + inet_server_addr() + inet + 本地连接地址 + + + + inet_server_port() + int + 本地连接端口 + + + + + pg_backend_pid() + int + 服务当前会话的服务器进程的进程 ID + + + + pg_blocking_pids(int) + int[] + 阻止指定服务器进程的进程 ID + + + + pg_conf_load_time() + timestamp with time zone + 配置加载时间 + + + + + pg_my_temp_schema() + oid + 会话临时模式的 OID;不存在时为 0 + + + + pg_is_other_temp_schema(oid) + boolean + 该模式是否为另一个会话的临时模式? + + + + pg_listening_channels() + setof text + 会话当前监听的通道名称 + + + + pg_notification_queue_usage() + double + 异步通知队列当前已占用的比例(0-1) + + + + pg_postmaster_start_time() + timestamp with time zone + 服务器启动时间 + + + + + pg_trigger_depth() + int + PostgreSQL 触发器的当前嵌套层级(如果不是从触发器内部直接或间接调用,则为 0) + + + + session_user + name + 会话用户名 + + + + user + name + 等价于 current_user + + + + version() + text + PostgreSQL 版本信息。机器可读的版本另见 + + + +
+ + + + current_catalogcurrent_rolecurrent_schemacurrent_usersession_useruserSQL中具有特殊语法:调用时不得在后面加圆括号。在 PostgreSQL 中,current_schema可以选择加圆括号,其他函数则不可以。 + + + + + current_catalog + + + + current_database + + + + current_query + + + + current_role + + + + current_schema + + + + current_schemas + + + + current_user + + + + pg_backend_pid + + + + 模式 + 当前 + + + + 搜索路径 + 当前 + + + + session_user + + + + 用户 + 当前 + + + + user + + + + session_user通常是发起当前数据库连接的用户,但超级用户可以用修改此设置。current_user是用于权限检查的用户标识,通常等于会话用户,但可以用更改。在执行具有SECURITY DEFINER属性的函数期间,它也会改变。用 Unix 的术语来说,会话用户是真实用户,当前用户是有效用户current_roleusercurrent_user的同义词。(SQL 标准区分current_rolecurrent_user,但PostgreSQL不区分,因为它将用户和角色统一为同一种实体。) + + + current_schema 返回搜索路径中第一个模式的名称(如果搜索路径为空,则返回空值)。在创建表或其他命名对象时,如果未指定目标模式,就会使用该模式。current_schemas(boolean) 返回当前搜索路径中所有模式名称的数组。布尔选项决定是否在返回的搜索路径中包含 pg_catalog 等隐式包含的系统模式。 + + + 可以在运行时修改搜索路径,命令如下: +SET search_path TO schema , schema, ... + + + + + + inet_client_addr + + + + inet_client_port + + + + inet_server_addr + + + + inet_server_port + + + inet_client_addr 返回当前客户端的 IP 地址,inet_client_port 返回端口号。inet_server_addr 返回服务器接受当前连接所用的 IP 地址,inet_server_port 返回端口号。如果当前连接通过 Unix 域套接字建立,这些函数都返回 NULL。 + + + pg_blocking_pids + + + pg_blocking_pids 返回一个数组,包含阻塞指定进程 ID 对应的服务器进程的会话进程 ID;如果不存在这样的服务器进程,或该进程未被阻塞,则返回空数组。一个服务器进程会在以下情况下阻塞另一个进程:它持有与被阻塞进程请求的锁冲突的锁(硬阻塞);或者它正在等待一个会与被阻塞进程请求的锁冲突的锁,并且在等待队列中位于被阻塞进程之前(软阻塞)。使用并行查询时,即使实际持锁或等待锁的是子工作进程,结果也始终列出客户端可见的进程 ID(即 pg_backend_pid 的结果)。因此,结果中可能出现重复的 PID。另外,如果持有冲突锁的是一个预备事务,该函数会在结果中用进程 ID 0 表示它。频繁调用此函数可能影响数据库性能,因为它需要短暂地独占访问锁管理器的共享状态。 + + + pg_conf_load_time + + + pg_conf_load_time 返回服务器配置文件最近一次加载的时间,类型为 timestamp with time zone。(如果当时当前会话已经存在,则返回该会话自身重新读取配置文件的时间,因此不同会话中的时间会略有不同。否则,返回 postmaster 进程重新读取配置文件的时间。) + + + pg_my_temp_schema + + + + pg_is_other_temp_schema + + + pg_my_temp_schema 返回当前会话的临时模式的 OID;如果没有临时模式(因为尚未创建任何临时表),则返回零。如果给定 OID 是另一个会话的临时模式的 OID,pg_is_other_temp_schema 返回真。(这可以用于从系统目录显示结果中排除其他会话的临时表等场景。) + + + pg_listening_channels + + + + pg_notification_queue_usage + + + pg_listening_channels 返回当前会话正在监听的异步通知通道名称集合。pg_notification_queue_usage 返回待处理通知当前占用的空间占通知总可用空间的比例,是一个范围为 0-1 的 double 值。更多信息请参见 + + + pg_postmaster_start_time + + + pg_postmaster_start_time 返回服务器启动的时间,类型为 timestamp with time zone + + + version + + + version 返回一个描述 PostgreSQL 服务器版本的字符串。也可以通过 获取此信息,或者通过 获取机器可读的版本。软件开发者应使用 server_version_num(自 8.2 起提供)或 ,而不是解析文本版本。 + + + 权限 + 查询 + + + 列出了允许用户以编程方式查询对象访问权限的函数。关于权限的更多信息,请参见 + + + 访问权限查询函数 + + + 名称 返回类型 描述 + + + + + has_any_column_privilege(user, + table, + privilege) + + boolean + 用户是否对表的至少一列具有权限 + + + has_any_column_privilege(table, + privilege) + + boolean + 当前用户是否对表的至少一列具有权限 + + + has_column_privilege(user, + table, + column, + privilege) + + boolean + 用户是否具有列权限 + + + has_column_privilege(table, + column, + privilege) + + boolean + 当前用户是否具有列权限 + + + has_database_privilege(user, + database, + privilege) + + boolean + 用户是否具有数据库权限 + + + has_database_privilege(database, + privilege) + + boolean + 当前用户是否具有数据库权限 + + + has_foreign_data_wrapper_privilege(user, + fdw, + privilege) + + boolean + 用户是否具有外部数据包装器权限 + + + has_foreign_data_wrapper_privilege(fdw, + privilege) + + boolean + 当前用户是否具有外部数据包装器权限 + + + has_function_privilege(user, + function, + privilege) + + boolean + 用户是否具有函数权限 + + + has_function_privilege(function, + privilege) + + boolean + 当前用户是否具有函数权限 + + + has_language_privilege(user, + language, + privilege) + + boolean + 用户是否具有语言权限 + + + has_language_privilege(language, + privilege) + + boolean + 当前用户是否具有语言权限 + + + has_schema_privilege(user, + schema, + privilege) + + boolean + 用户是否具有模式权限 + + + has_schema_privilege(schema, + privilege) + + boolean + 当前用户是否具有模式权限 + + + has_sequence_privilege(user, + sequence, + privilege) + + boolean + 用户是否具有序列权限 + + + has_sequence_privilege(sequence, + privilege) + + boolean + 当前用户是否具有序列权限 + + + has_server_privilege(user, + server, + privilege) + + boolean + 用户是否具有外部服务器权限 + + + has_server_privilege(server, + privilege) + + boolean + 当前用户是否具有外部服务器权限 + + + has_table_privilege(user, + table, + privilege) + + boolean + 用户是否具有表权限 + + + has_table_privilege(table, + privilege) + + boolean + 当前用户是否具有表权限 + + + has_tablespace_privilege(user, + tablespace, + privilege) + + boolean + 用户是否具有表空间权限 + + + has_tablespace_privilege(tablespace, + privilege) + + boolean + 当前用户是否具有表空间权限 + + + has_type_privilege(user, + type, + privilege) + + boolean + 用户是否具有类型权限 + + + has_type_privilege(type, + privilege) + + boolean + 当前用户是否具有类型权限 + + + pg_has_role(user, + role, + privilege) + + boolean + 用户是否具有角色权限 + + + pg_has_role(role, + privilege) + + boolean + 当前用户是否具有角色权限 + + + row_security_active(table) + + boolean + 该表的行级安全性是否对当前用户生效 + + + +
+ + + has_any_column_privilege + + + has_column_privilege + + + has_database_privilege + + + has_function_privilege + + + has_foreign_data_wrapper_privilege + + + has_language_privilege + + + has_schema_privilege + + + has_server_privilege + + + has_sequence_privilege + + + has_table_privilege + + + has_tablespace_privilege + + + has_type_privilege + + + pg_has_role + + + row_security_active + + + + has_table_privilege检查用户是否可以以某种方式访问表。用户参数可以是名称、OID(pg_authid.oid), + public(表示 PUBLIC 伪角色);如果省略该参数,则使用current_user。表可以通过名称或 OID 指定。(因此,has_table_privilege实际上有六种变体,可以通过参数的数量和类型来区分。)通过名称指定时,如有需要,可以用模式限定名称。所需访问权限类型由文本字符串指定,其值必须为以下值之一:SELECTINSERT, + UPDATEDELETETRUNCATE, + REFERENCES,或TRIGGER。还可以添加WITH GRANT OPTION到权限类型后,以检查该权限是否附带授予选项。也可以用逗号分隔列出多个权限类型;只要拥有列出的任一权限,结果就为true。(权限字符串不区分大小写,权限名之间允许有额外的空白,但权限名内部不允许。)下面是一些示例: +SELECT has_table_privilege('myschema.mytable', 'select'); +SELECT has_table_privilege('joe', 'mytable', 'INSERT, SELECT WITH GRANT OPTION'); + + + + has_sequence_privilege 检查用户是否可以以某种方式访问序列。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 USAGESELECTUPDATE 之一。 + + has_any_column_privilege 检查用户是否可以以某种方式访问表的至少一列。其参数形式与 has_table_privilege 类似,但所需访问权限类型必须为 SELECTINSERTUPDATEREFERENCES 的某种组合。注意,在表级别拥有这些权限中的任意一种,就隐式地对表的每一列拥有该权限,因此对于相同参数,如果 has_table_privilege 返回 truehas_any_column_privilege 也总是返回真。但是,只要至少有一列获得该权限的列级授权,has_any_column_privilege 也会成功。 + + has_column_privilege 检查用户是否可以以某种方式访问列。其参数形式与 has_table_privilege 类似,但还可以通过列名或属性编号指定列。所需访问权限类型必须为 SELECTINSERTUPDATEREFERENCES 的某种组合。注意,在表级别拥有这些权限中的任意一种,就隐式地对表的每一列拥有该权限。 + + has_database_privilege 检查用户是否可以以某种方式访问数据库。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 CREATECONNECTTEMPORARYTEMP(等同于 TEMPORARY)的某种组合。 + + + has_function_privilege检查用户是否可以以某种方式访问函数。其参数形式类似于has_table_privilege。通过文本字符串而不是 OID 指定函数时,允许的输入与regprocedure数据类型相同(参见)。所需访问权限类型必须为EXECUTE。例如: +SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); + + + + has_foreign_data_wrapper_privilege 检查用户是否可以以某种方式访问外部数据包装器。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 USAGE + + has_language_privilege 检查用户是否可以以某种方式访问过程语言。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 USAGE + + has_schema_privilege 检查用户是否可以以某种方式访问模式。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 CREATEUSAGE 的某种组合。 + + has_server_privilege 检查用户是否可以以某种方式访问外部服务器。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 USAGE + + has_tablespace_privilege 检查用户是否可以以某种方式访问表空间。其参数形式与 has_table_privilege 类似。所需访问权限类型必须为 CREATE + + has_type_privilege 检查用户是否可以以某种方式访问类型。其参数形式与 has_table_privilege 类似。通过文本字符串而不是 OID 指定类型时,允许的输入与 regtype 数据类型相同(参见)。所需访问权限类型必须为 USAGE + + pg_has_role 检查用户是否可以以某种方式访问角色。其参数形式与 has_table_privilege 类似,但不允许使用 public 作为用户名。所需访问权限类型必须为 MEMBERUSAGE 的某种组合。MEMBER 表示直接或间接地属于该角色(即有权执行 SET ROLE),而 USAGE 表示无需执行 SET ROLE 就能立即使用该角色的权限。 + + row_security_active 检查在 current_user 和当前环境的上下文中,指定表的行级安全性是否生效。可以通过名称或 OID 指定表。 + + + 列出了判断某个特定对象是否可见的函数,其判断依据是当前模式搜索路径。例如,如果表所在的模式位于搜索路径中,并且在搜索路径的更前面没有同名表,就称该表可见。这等价于说,可以只通过表名引用该表,而不必显式地用模式限定。要列出所有可见表的名称: +SELECT relname FROM pg_class WHERE pg_table_is_visible(oid); + + + + + 搜索路径 + 对象可见性 + + + + 模式可见性查询函数 + + + 名称 返回类型 描述 + + + + + pg_collation_is_visible(collation_oid) + boolean + 排序规则是否在搜索路径中可见 + + + pg_conversion_is_visible(conversion_oid) + boolean + 转换是否在搜索路径中可见 + + + pg_function_is_visible(function_oid) + boolean + 函数是否在搜索路径中可见 + + + pg_opclass_is_visible(opclass_oid) + boolean + 操作符类是否在搜索路径中可见 + + + pg_operator_is_visible(operator_oid) + boolean + 操作符是否在搜索路径中可见 + + + pg_opfamily_is_visible(opclass_oid) + boolean + 操作符族是否在搜索路径中可见 + + + pg_table_is_visible(table_oid) + boolean + 表是否在搜索路径中可见 + + + pg_ts_config_is_visible(config_oid) + boolean + 全文检索配置是否在搜索路径中可见 + + + pg_ts_dict_is_visible(dict_oid) + boolean + 全文检索词典是否在搜索路径中可见 + + + pg_ts_parser_is_visible(parser_oid) + boolean + 全文检索解析器是否在搜索路径中可见 + + + pg_ts_template_is_visible(template_oid) + boolean + 全文检索模板是否在搜索路径中可见 + + + pg_type_is_visible(type_oid) + boolean + 类型(或域)是否在搜索路径中可见 + + + +
+ + + pg_collation_is_visible + + + pg_conversion_is_visible + + + pg_function_is_visible + + + pg_opclass_is_visible + + + pg_operator_is_visible + + + pg_opfamily_is_visible + + + pg_table_is_visible + + + pg_ts_config_is_visible + + + pg_ts_dict_is_visible + + + pg_ts_parser_is_visible + + + pg_ts_template_is_visible + + + pg_type_is_visible + + + 每个函数检查一种数据库对象的可见性。注意,pg_table_is_visible 也可用于视图、物化视图、索引、序列和外部表;pg_type_is_visible 也可用于域。对于函数和操作符,如果搜索路径更前面没有名称和参数数据类型都相同的对象,那么路径中的该对象就是可见的。对于操作符类,会同时考虑名称和关联的索引访问方法。 + + 所有这些函数都需要用对象 OID 标识要检查的对象。如果想按名称测试对象,使用 OID 别名类型会很方便(regclassregtype, + regprocedureregoperatorregconfig,或regdictionary),例如: +SELECT pg_type_is_visible('myschema.widget'::regtype); +注意,用这种方式测试不带模式限定的类型名并没有太大意义:只要该名称能够被识别,它就必然可见。 + + + format_type + + + + pg_get_constraintdef + + + + pg_get_expr + + + + pg_get_functiondef + + + + pg_get_function_arguments + + + + pg_get_function_identity_arguments + + + + pg_get_function_result + + + + pg_get_indexdef + + + + pg_get_keywords + + + + pg_get_ruledef + + + + pg_get_serial_sequence + + + + + pg_get_triggerdef + + + + pg_get_userbyid + + + + pg_get_viewdef + + + + pg_index_column_has_property + + + + pg_index_has_property + + + + pg_indexam_has_property + + + + pg_options_to_table + + + + pg_tablespace_databases + + + + pg_tablespace_location + + + + pg_typeof + + + + collation for + + + + to_regclass + + + + to_regproc + + + + to_regprocedure + + + + to_regoper + + + + to_regoperator + + + + to_regtype + + + + to_regnamespace + + + + to_regrole + + + + 列出从系统目录中提取信息的函数。 + + + + 系统目录信息函数 + + + 名称 返回类型 描述 + + + + + format_type(type_oid, typemod) + text + 获取数据类型的 SQL 名称 + + + pg_get_constraintdef(constraint_oid) + text + 获取约束定义 + + + pg_get_constraintdef(constraint_oid, pretty_bool) + text + 获取约束定义 + + + pg_get_expr(pg_node_tree, relation_oid) + text + 反编译表达式的内部形式,假定其中所有 Var 节点都引用第二个参数指定的关系 + + + pg_get_expr(pg_node_tree, relation_oid, pretty_bool) + text + 反编译表达式的内部形式,假定其中所有 Var 节点都引用第二个参数指定的关系 + + + pg_get_functiondef(func_oid) + text + 获取函数定义 + + + pg_get_function_arguments(func_oid) + text + 获取函数定义的参数列表(包含默认值) + + + pg_get_function_identity_arguments(func_oid) + text + 获取用于标识函数的参数列表(不含默认值) + + + pg_get_function_result(func_oid) + text + 获取函数的 RETURNS 子句 + + + pg_get_indexdef(index_oid) + text + 获取索引的 CREATE INDEX 命令 + + + pg_get_indexdef(index_oid, column_no, pretty_bool) + text + 获取索引的 CREATE INDEX 命令;当 column_no 非零时,仅获取一个索引列的定义 + + + pg_get_keywords() + setof record + 获取 SQL 关键字及其类别的列表 + + + pg_get_ruledef(rule_oid) + text + 获取规则的 CREATE RULE 命令 + + + pg_get_ruledef(rule_oid, pretty_bool) + text + 获取规则的 CREATE RULE 命令 + + + pg_get_serial_sequence(table_name, column_name) + text + 获取 serialsmallserialbigserial 列使用的序列名称 + + + pg_get_triggerdef(trigger_oid) + text + 获取触发器的 CREATE [ CONSTRAINT ] TRIGGER 命令 + + + pg_get_triggerdef(trigger_oid, pretty_bool) + text + 获取触发器的 CREATE [ CONSTRAINT ] TRIGGER 命令 + + + pg_get_userbyid(role_oid) + name + 获取具有给定 OID 的角色名称 + + + pg_get_viewdef(view_name) + text + 获取视图或物化视图的底层 SELECT 命令(已弃用 + + + pg_get_viewdef(view_name, pretty_bool) + text + 获取视图或物化视图的底层 SELECT 命令(已弃用 + + + pg_get_viewdef(view_oid) + text + 获取视图或物化视图的底层 SELECT 命令 + + + pg_get_viewdef(view_oid, pretty_bool) + text + 获取视图或物化视图的底层 SELECT 命令 + + + pg_get_viewdef(view_oid, wrap_column_int) + text + 获取视图或物化视图的底层 SELECT 命令;包含字段的行按指定列数折行,并隐含启用美化输出 + + + pg_index_column_has_property(index_oid, column_no, prop_name) + boolean + 测试索引列是否具有指定属性 + + + pg_index_has_property(index_oid, prop_name) + boolean + 测试索引是否具有指定属性 + + + pg_indexam_has_property(am_oid, prop_name) + boolean + 测试索引访问方法是否具有指定属性 + + + pg_options_to_table(reloptions) + setof record + 获取存储选项的名称/值对集合 + + + pg_tablespace_databases(tablespace_oid) + setof oid + 获取在该表空间中具有对象的数据库 OID 集合 + + + pg_tablespace_location(tablespace_oid) + text + 获取该表空间在文件系统中的路径 + + + pg_typeof(any) + regtype + 获取任意值的数据类型 + + + collation for (any) + text + 获取参数的排序规则 + + + to_regclass(rel_name) + regclass + 获取指定名称关系的 OID + + + to_regproc(func_name) + regproc + 获取指定名称函数的 OID + + + to_regprocedure(func_name) + regprocedure + 获取指定名称函数的 OID + + + to_regoper(operator_name) + regoper + 获取指定名称操作符的 OID + + + to_regoperator(operator_name) + regoperator + 获取指定名称操作符的 OID + + + to_regtype(type_name) + regtype + 获取指定名称类型的 OID + + + to_regnamespace(schema_name) + regnamespace + 获取指定名称模式的 OID + + + to_regrole(role_name) + regrole + 获取指定名称角色的 OID + + + +
+ + format_type 返回数据类型的 SQL 名称,该类型由其类型 OID 和可能存在的类型修饰符标识。如果不知道具体的类型修饰符,请为类型修饰符参数传入 NULL。 + + pg_get_keywords 返回一组记录,描述服务器识别的 SQL 关键字。word 列包含关键字。catcode 列包含类别代码:U 表示非保留关键字,C 表示列名,T 表示类型名或函数名,R 表示保留关键字。catdesc 列包含描述该类别的字符串,该字符串可能已经过本地化。 + + pg_get_constraintdefpg_get_indexdefpg_get_ruledefpg_get_triggerdef分别重建约束、索引、规则或触发器的创建命令。(注意,这是通过反编译重建的,并非命令的原始文本。)pg_get_expr 反编译单个表达式的内部形式,例如列的默认值。这在检查系统目录内容时可能很有用。如果表达式可能包含 Var 节点,请将它们所引用的关系的 OID 指定为第二个参数;如果预计不含 Var 节点,传入零即可。pg_get_viewdef 重建定义视图的 SELECT 查询。这些函数中的大多数有两种变体,其中一种可以选择美化输出结果。美化格式更易读,但默认格式更可能被未来版本的 PostgreSQL 以相同方式解释;转储时应避免使用美化输出。向美化输出参数传入 false,所得结果与完全没有该参数的变体相同。 + + pg_get_functiondef 返回一个函数的完整 CREATE OR REPLACE FUNCTION 语句。pg_get_function_argumentsCREATE FUNCTION 中所需的形式返回函数参数列表。pg_get_function_result 类似地返回该函数相应的 RETURNS 子句。pg_get_function_identity_arguments 返回标识函数所需的参数列表,例如以 ALTER FUNCTION 中所需的形式返回。此形式省略默认值。 + + + pg_get_serial_sequence返回与某一列关联的序列名称;如果该列没有关联的序列,则返回 NULL。第一个输入参数是可以带有模式名的表名,第二个参数是列名。由于第一个参数可能包含模式和表,它不会被当作双引号括起的标识符处理,因此默认转换为小写;第二个参数只包含列名,会被当作带双引号的标识符处理并保留大小写。函数返回的值采用适于传递给序列函数的格式(参见)。这一关联可以使用ALTER SEQUENCE OWNED BY修改或移除。(该函数也许应该叫作pg_get_owned_sequence;它当前的名称反映了它通常用于serialbigserial列这一事实。) + + + pg_get_userbyid 根据给定 OID 获取角色名称。 + + pg_index_column_has_propertypg_index_has_propertypg_indexam_has_property 返回指定的索引列、索引或索引访问方法是否具有给定名称的属性。如果属性名未知或不适用于该对象,或者 OID 或列号不能标识有效对象,则返回 NULL。列属性见,索引属性见,访问方法属性见。(注意,扩展提供的访问方法可以为其索引定义额外的属性名。) + + + + 索引列属性 + + + + 名称描述 + + + + + asc + 在向前扫描时列是按照升序排列吗? + + + + + desc + 在向前扫描时列是按照降序排列吗? + + + + + nulls_first + 在向前扫描时列排序会把空值排在前面吗? + + + + + nulls_last + 在向前扫描时列排序会把空值排在最后吗? + + + + + orderable + 列具有已定义的排序顺序吗? + + + + + distance_orderable + + 能否按距离操作符的结果有序地扫描该列,例如ORDER BY col <-> constant。 + + + + + returnable + 列值是否可以通过一次仅索引扫描返回? + + + + + search_array + 列是否原生支持col = ANY(array)搜索? + + + + + search_nulls + 列是否支持IS NULLIS NOT NULL搜索? + + + + +
+ + + + 索引属性 + + + + 名称描述 + + + + + clusterable + 索引是否可以用于CLUSTER命令? + + + + + index_scan + 索引是否支持普通扫描(非位图)? + + + + + bitmap_scan + 索引是否支持位图扫描? + + + + + backward_scan + 在扫描中扫描方向能否被更改(为了支持游标上无需物化的FETCH BACKWARD)? + + + + +
+ + + 索引访问方法属性 + + + + 名称描述 + + + + can_order + 访问方法是否支持ASCDESC以及CREATE INDEX中的有关关键词? + + + + can_unique + 访问方法是否支持唯一索引? + + + + can_multi_col + 访问方法是否支持多列索引? + + + + can_exclude + 访问方法是否支持排除约束? + + + + +
+ + pg_options_to_table 传入 pg_class.reloptionspg_attribute.attoptions 时,它返回存储选项名称/值对(option_name/option_value)的集合。 + + pg_tablespace_databases 用于检查表空间。它返回在该表空间中存储了对象的数据库 OID 集合。如果此函数返回任何行,则说明该表空间不为空,不能删除。要显示存放在该表空间中的具体对象,需要连接到 pg_tablespace_databases 标识的数据库,并查询它们的 pg_class 系统目录。 + + + pg_typeof返回所传入值的数据类型的 OID。这有助于排查问题或动态构造 SQL 查询。函数声明的返回类型是regtype,它是一种 OID 别名类型(参见);这意味着它在比较时与 OID 相同,但显示为类型名。例如: +SELECT pg_typeof(33); + + pg_typeof +----------- + integer +(1 row) + +SELECT typlen FROM pg_type WHERE oid = pg_typeof(33); + typlen +-------- + 4 +(1 row) + + + + 表达式collation for返回所传入值的排序规则。例如: +SELECT collation for (description) FROM pg_description LIMIT 1; + pg_collation_for +------------------ + "default" +(1 row) + +SELECT collation for ('foo' COLLATE "de_DE"); + pg_collation_for +------------------ + "de_DE" +(1 row) +返回值可能带有引号和模式限定。如果不能为参数表达式推导出排序规则,则返回空值。如果参数的数据类型不支持排序规则,则会报错。 + + to_regclassto_regprocto_regprocedureto_regoperto_regoperatorto_regtypeto_regnamespaceto_regrole 函数将以 text 给出的关系、函数、操作符、类型、模式和角色名称,分别转换为 regclassregprocregprocedureregoperregoperatorregtyperegnamespaceregrole 类型的对象。这些函数与从文本进行类型转换的区别是:它们不接受数值 OID,而且在找不到名称时(对于 to_regprocto_regoper,还包括给定名称匹配多个对象时)返回空值,而不会报错。 + + + pg_describe_object + + + + pg_identify_object + + + + pg_identify_object_as_address + + + + pg_get_object_address + + + + 列出了与数据库对象标识和寻址有关的函数。 + + + + 对象信息和寻址函数 + + + 名称 返回类型 描述 + + + + + pg_describe_object(classid oid, objid oid, objsubid integer) + text + 获取数据库对象的描述 + + + pg_identify_object(classid oid, objid oid, objsubid integer) + type text, schema text, name text, identity text + 获取数据库对象的标识 + + + pg_identify_object_as_address(classid oid, objid oid, objsubid integer) + type text, object_names text[], object_args text[] + 获取数据库对象地址的外部表示 + + + pg_get_object_address(type text, name text[], args text[]) + classid oid, objid oid, objsubid integer + 根据外部表示获取数据库对象的地址 + + + +
+ + pg_describe_object 返回数据库对象的文本描述,对象由系统目录 OID、对象 OID 和子对象 ID 指定(例如表中的列号;引用整个对象时,子对象 ID 为零)。该描述供人阅读,并可能根据服务器配置被翻译。这有助于确定存储在 pg_depend 系统目录中的对象标识。 + + pg_identify_object 返回一行,其中包含足以唯一标识数据库对象的信息,该对象由系统目录 OID、对象 OID 和子对象 ID 指定。这些信息供机器读取,永远不会被翻译。type 标识数据库对象的类型;schema 是对象所属的模式名,对于不属于模式的对象类型则为 NULL;如果对象名(以及适用时的模式名)足以唯一标识该对象,name 就是对象名,并在必要时加引号,否则为 NULLidentity 是完整的对象标识,其具体格式取决于对象类型,格式中的每个名称都会根据需要加上模式限定和引号。 + + pg_identify_object_as_address 返回一行,其中包含足以唯一标识数据库对象的信息,该对象由系统目录 OID、对象 OID 和子对象 ID 指定。返回的信息与当前服务器无关,也就是说,它也能用于标识另一台服务器上名称相同的对象。type 标识数据库对象的类型;object_namesobject_args 是文本数组,共同构成对该对象的引用。将这三个值传给 pg_get_object_address 可以获得对象的内部地址。此函数执行 pg_get_object_address 的逆操作。 + + pg_get_object_address 返回一行,其中包含足以唯一标识数据库对象的信息,该对象由其类型、对象名称数组和参数数组指定。返回的值就是 pg_depend 等系统目录中使用的值,也可以传给 pg_identify_objectpg_describe_object 等其他系统函数。classid 是包含该对象的系统目录的 OID;objid 是对象自身的 OID;objsubid 是子对象 ID,没有子对象时为零。此函数执行 pg_identify_object_as_address 的逆操作。 + + + col_description + + + + obj_description + + + + shobj_description + + + + 注释 + 关于数据库对象 + + + + 中的函数用于提取此前通过命令存储的注释。如果找不到与指定参数对应的注释,则返回空值。 + + + + 注释信息函数 + + + 名称 返回类型 描述 + + + + + col_description(table_oid, column_number) + text + 获取表列的注释 + + + obj_description(object_oid, catalog_name) + text + 获取数据库对象的注释 + + + obj_description(object_oid) + text + 获取数据库对象的注释(已弃用 + + + shobj_description(object_oid, catalog_name) + text + 获取共享数据库对象的注释 + + + +
+ + col_description 返回表列的注释,列由所属表的 OID 和列号指定。(obj_description 不能用于表列,因为列没有自身的 OID。) + + obj_description 的双参数形式返回数据库对象的注释,对象由其 OID 和所在系统目录的名称指定。例如,obj_description(123456,'pg_class') 会获取 OID 为 123456 的表的注释。obj_description 的单参数形式只需要对象 OID。该形式已被弃用,因为无法保证 OID 在不同系统目录之间唯一,因而可能返回错误的注释。 + + shobj_description 的用法与 obj_description 相同,但用于获取共享对象的注释。有些系统目录由数据库集簇中的所有数据库全局共享,其中对象的描述也全局存储。 + + + txid_current + + + + + txid_current_snapshot + + + + txid_snapshot_xip + + + + txid_snapshot_xmax + + + + txid_snapshot_xmin + + + + txid_visible_in_snapshot + + + + 中的函数以可导出的形式提供服务器事务信息。这些函数主要用于确定两个快照之间有哪些事务提交。 + + + 事务 ID 和快照 + + + 名称 返回类型 描述 + + + + + txid_current() + bigint + 获取当前事务 ID;如果当前事务尚无 ID,则分配一个新的 ID + + + txid_current_snapshot() + txid_snapshot + 获取当前快照 + + + txid_snapshot_xip(txid_snapshot) + setof bigint + 获取快照中正在进行的事务 ID + + + txid_snapshot_xmax(txid_snapshot) + bigint + 获取快照的 xmax + + + txid_snapshot_xmin(txid_snapshot) + bigint + 获取快照的 xmin + + + txid_visible_in_snapshot(bigint, txid_snapshot) + boolean + 该事务 ID 是否在快照中可见?(不要用于子事务 ID) + + + +
+ + 内部事务 ID 类型(xid)为 32 位,每经过约 40 亿个事务就会回卷一次。不过,这些函数导出的是用纪元计数器扩展的 64 位格式,在一次安装的整个使用期间都不会回卷。这些函数使用的 txid_snapshot 数据类型存储某一时刻的事务 ID 可见性信息。其组成部分见 + + + 快照组件 + + + + + 名称 + 描述 + + + + + + + xmin + 仍然活动的最早事务 ID(txid)。所有更早的事务要么已经提交且可见,要么已经回滚而失效。 + + + + xmax + 第一个尚未分配的 txid。所有大于或等于此值的 txid 在快照时尚未开始,因此不可见。 + + + + xip_list + 快照时活动的 txid。该列表仅包含 xminxmax 之间的活动 txid;可能存在大于 xmax 的活动 txid。满足 xmin <= txid < xmax 且不在此列表中的 txid 在快照时已经完成,因此根据其提交状态,要么可见,要么失效。该列表不包含子事务的 txid。 + + + + +
+ + txid_snapshot 的文本表示为 xmin:xmax:xip_list。例如,10:20:10,14,15 表示 xmin=10, xmax=20, xip_list=10, 14, 15 + + 中的函数提供已提交事务的信息,主要是事务何时提交。只有启用 配置选项后,这些函数才会提供有用的数据,并且只针对启用该选项之后提交的事务。 + + + 已提交事务信息 + + + 名称 返回类型 描述 + + + + + pg_xact_commit_timestamp pg_xact_commit_timestamp(xid) + timestamp with time zone + 获取事务的提交时间戳 + + + + pg_last_committed_xact pg_last_committed_xact() + xid xid, timestamp timestamp with time zone + 获取最近提交事务的事务 ID 和提交时间戳 + + + +
+ + 中的函数显示在 initdb 期间初始化的信息,例如系统目录版本。它们也显示有关预写式日志和检查点处理的信息。这些信息适用于整个数据库集簇,而非某个特定数据库。它们与 从相同来源提供大部分相同的信息,但采用更适合 SQL 函数的形式。 + + + 控制数据函数 + + + 名称 返回类型 描述 + + + + + pg_control_checkpoint pg_control_checkpoint() + record + 返回当前检查点状态的信息。 + + + + pg_control_system pg_control_system() + record + 返回当前控制文件状态的信息。 + + + + pg_control_init pg_control_init() + record + 返回集簇初始化状态的信息。 + + + + pg_control_recovery pg_control_recovery() + record + 返回恢复状态的信息。 + + + + +
+ + pg_control_checkpoint 返回一条记录,其内容见 + + + <function>pg_control_checkpoint</function> 输出列 + + + + + 列名称 + 数据类型 + + + + + + + checkpoint_location + pg_lsn + + + + prior_location + pg_lsn + + + + redo_location + pg_lsn + + + + redo_wal_file + text + + + + timeline_id + integer + + + + prev_timeline_id + integer + + + + full_page_writes + boolean + + + + next_xid + text + + + + next_oid + oid + + + + next_multixact_id + xid + + + + next_multi_offset + xid + + + + oldest_xid + xid + + + + oldest_xid_dbid + oid + + + + oldest_active_xid + xid + + + + oldest_multi_xid + xid + + + + oldest_multi_dbid + oid + + + + oldest_commit_ts_xid + xid + + + + newest_commit_ts_xid + xid + + + + checkpoint_time + timestamp with time zone + + + + +
+ + pg_control_system 返回一条记录,其内容见 + + + <function>pg_control_system</function> 输出列 + + + + + 列名称 + 数据类型 + + + + + + + pg_control_version + integer + + + + catalog_version_no + integer + + + + system_identifier + bigint + + + + pg_control_last_modified + timestamp with time zone + + + + +
+ + pg_control_init 返回一条记录,其内容见 + + + <function>pg_control_init</function> 输出列 + + + + + 列名称 + 数据类型 + + + + + + + max_data_alignment + integer + + + + database_block_size + integer + + + + blocks_per_segment + integer + + + + wal_block_size + integer + + + + bytes_per_wal_segment + integer + + + + max_identifier_length + integer + + + + max_index_columns + integer + + + + max_toast_chunk_size + integer + + + + large_object_chunk_size + integer + + + + bigint_timestamps + boolean + + + + float4_pass_by_value + boolean + + + + float8_pass_by_value + boolean + + + + data_page_checksum_version + integer + + + + +
+ + pg_control_recovery 返回一条记录,其内容见 + + + <function>pg_control_recovery</function> 输出列 + + + + + 列名称 + 数据类型 + + + + + + + min_recovery_end_location + pg_lsn + + + + min_recovery_end_timeline + integer + + + + backup_start_location + pg_lsn + + + + backup_end_location + pg_lsn + + + + end_of_backup_record_required + boolean + + + + +
+ +
+ + + 系统管理函数 + + + 这一节描述的函数被用来控制和监视一个PostgreSQL安装。 + + + + 配置设定函数 + + + 展示了那些可以用于查询以及修改运行时配置参数的函数。 + + + + 配置设定函数 + + + 名称 返回类型 描述 + + + + + current_setting current_setting(setting_name [, missing_ok ]) + text + 获取配置项的当前值 + + + set_config set_config(setting_name, new_value, is_local) + text + 设置参数并返回新值 + + + +
+ + + SET + + + + SHOW + + + + 配置 + 服务器 + 函数 + + + 函数current_setting返回设置setting_name的当前值。它对应于SQL命令SHOW。例如: +SELECT current_setting('datestyle'); + + current_setting +----------------- + ISO, MDY +(1 row) +如果不存在名为setting_name的设置, + current_setting会报错,除非提供了missing_ok且其值为true。 + + + + set_config将参数setting_name设置为new_value。如果is_localtrue,新值仅适用于当前事务。如果要让新值适用于当前会话,请改用false。该函数对应于 SQL 命令SET。例如: +SELECT set_config('log_statement_stats', 'off', false); + + set_config +------------ + off +(1 row) + + + +
+ + + 服务器信号函数 + + + pg_cancel_backend + + + pg_reload_conf + + + pg_rotate_logfile + + + pg_terminate_backend + + + + 信号 + 后端进程 + + + + 在中展示的函数向其它服务器进程发送控制信号。默认情况下这些函数只能被超级用户使用,但是如果需要,可以利用GRANT把访问权限授予给其他用户(注明的例外除外)。 + + + + 服务器信号函数 + + + 名称 返回类型 描述 + + + + + + pg_cancel_backend(pid int) + boolean + 取消后端的当前查询。如果调用角色是被取消后端所属角色的成员,或者调用角色被授予了 pg_signal_backend,也允许执行此操作;但只有超级用户才能取消超级用户的后端。 + + + + pg_reload_conf() + + boolean + 使服务器进程重新加载配置文件 + + + + pg_rotate_logfile() + + boolean + 轮转服务器日志文件 + + + pg_terminate_backend(pid int) + boolean + 终止后端。如果调用角色是被终止后端所属角色的成员,或者调用角色被授予了 pg_signal_backend,也允许执行此操作;但只有超级用户才能终止超级用户的后端。 + + + +
+ + 这些函数在成功时都返回 true,否则返回 false + + + pg_cancel_backendpg_terminate_backend向由进程 ID 标识的后端进程发送信号(分别是SIGINTSIGTERM)。 + 一个活动后端的进程 ID可以从pg_stat_activity视图的pid列中找到,或者通过在服务器上列出postgres进程(在 Unix 上使用ps或者在Windows上使用任务管理器)得到。 + 一个活动后端的角色可以在pg_stat_activity视图的usename列中找到。 + + + pg_reload_conf 向服务器发送 SIGHUP 信号,使所有服务器进程重新加载配置文件。 + + pg_rotate_logfile 向日志文件管理器发送信号,使其立即切换到新的输出文件。只有内置日志收集器正在运行时才有效,否则不存在日志文件管理器子进程。 + +
+ + + 备份控制函数 + + + 备份 + + + pg_create_restore_point + + + pg_current_xlog_flush_location + + + pg_current_xlog_insert_location + + + pg_current_xlog_location + + + pg_start_backup + + + pg_stop_backup + + pg_is_in_backup + pg_backup_start_time + + pg_switch_xlog + + + pg_xlogfile_name + + + pg_xlogfile_name_offset + + + pg_xlog_location_diff + + + 中列出的函数有助于进行在线备份。这些函数不能在恢复期间执行(非排他模式的 pg_start_backup、非排他模式的 pg_stop_backuppg_is_in_backuppg_backup_start_timepg_xlog_location_diff 除外)。 + + + 备份控制函数 + + + 名称 返回类型 描述 + + + + + + pg_create_restore_point(name text) + pg_lsn + 创建用于恢复的命名点(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数) + + + + pg_current_xlog_flush_location() + + pg_lsn + 获取当前预写式日志刷盘位置 + + + + pg_current_xlog_insert_location() + + pg_lsn + 获取当前预写式日志插入位置 + + + + pg_current_xlog_location() + + pg_lsn + 获取当前预写式日志写入位置 + + + pg_start_backup(label text , fast boolean , exclusive boolean ) + pg_lsn + 准备执行在线备份(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数) + + + + pg_stop_backup() + + pg_lsn + 结束排他在线备份(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数) + + + pg_stop_backup(exclusive boolean) + setof record + 结束排他或非排他在线备份(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数) + + + + pg_is_in_backup() + + bool + 如果在线排他备份仍在进行,则为真。 + + + + pg_backup_start_time() + + timestamp with time zone + 获取正在进行的在线排他备份的开始时间。 + + + + pg_switch_xlog() + + pg_lsn + 强制切换到新的预写式日志文件(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数) + + + pg_xlogfile_name(location pg_lsn) + text + 将事务日志位置字符串转换为文件名 + + + pg_xlogfile_name_offset(location pg_lsn) + text, integer + 将事务日志位置字符串转换为文件名和文件内的十进制字节偏移量 + + + pg_xlog_location_diff(location pg_lsn, location pg_lsn) + numeric + 计算两个事务日志位置之差 + + + +
+ + + pg_start_backup接受任意用户定义的备份标签。(通常是备份转储文件将要保存的名称。)在排他模式下,函数将备份标签文件(backup_label)以及在pg_tblspc/目录中存在链接时生成的表空间映射文件(tablespace_map)写入数据库集簇的数据目录,执行检查点,然后以文本形式返回备份的起始事务日志位置。用户可以忽略该返回值,提供它只是因为它可能有用。在非排他模式下,这些文件的内容改由pg_stop_backup函数返回,并应由调用者写入备份。 +postgres=# select pg_start_backup('label_goes_here'); + pg_start_backup +----------------- + 0/D4445B8 +(1 row) +还可以提供第二个可选参数,其类型为boolean。如果该参数为true,则尽快执行pg_start_backup。这会强制立即执行检查点,使 I/O 操作量陡增,并降低并发执行的查询的速度。 + + 在排他备份中,pg_stop_backup 删除标签文件,以及 pg_start_backup 创建的 tablespace_map 文件(如果存在)。在非排他备份中,backup_labeltablespace_map 的内容在函数结果中返回,应将它们写入备份中的文件,而不是数据目录中的文件。在主库上执行时,只要启用了归档,pg_stop_backup 会等待 WAL 归档。 + + 在备库上,pg_stop_backup 会立即返回而不等待,因此务必确认所有需要的 WAL 段都已完成归档。如果主库的写入活动很少,可以在主库上运行 pg_switch_xlog,触发立即切换日志段。 + + 在主库上执行时,该函数还会在预写式日志归档区域创建备份历史文件。历史文件包括传给 pg_start_backup 的标签、备份的起止事务日志位置以及备份的起止时间。返回值是备份的结束事务日志位置(同样可以忽略)。记录结束位置后,当前事务日志插入点会自动推进到下一个事务日志文件,使包含结束位置的预写式日志文件可以立即归档,从而完成备份。 + + pg_switch_xlog 切换到下一个预写式日志文件,使当前文件可以归档(假设正在使用连续归档)。返回值是刚完成的预写式日志文件中的结束预写式日志位置加 1。如果自上次切换预写式日志以来没有发生任何预写式日志活动,pg_switch_xlog 不执行任何操作,并返回当前使用的预写式日志文件的起始位置。 + + pg_create_restore_point 创建一条可用作恢复目标的命名预写式日志记录,并返回相应的预写式日志位置。随后可以在 中使用该名称,指定恢复到哪一点。应避免创建多个同名恢复点,因为恢复会在第一个名称匹配恢复目标的恢复点停止。 + + pg_current_xlog_location 显示当前预写式日志写入位置,格式与上述函数相同。类似地,pg_current_xlog_insert_location 显示当前预写式日志插入位置,pg_current_xlog_flush_location 显示当前预写式日志刷盘位置。插入位置是预写式日志在任意时刻的逻辑末尾;写入位置是实际从服务器内部缓冲区写出的内容的末尾;刷盘位置则是保证已经写入持久存储的位置。写入位置是能从服务器外部检查到的内容的末尾,如果要归档尚未写满的预写式日志文件,通常需要这个位置。插入位置和刷盘位置主要用于服务器调试。这些都是只读操作,不需要超级用户权限。 + + 可以使用pg_xlogfile_name_offset从上述任一函数的结果中提取相应的预写式日志文件名和字节偏移。例如: +postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); + file_name | file_offset +--------------------------+------------- + 00000001000000000000000D | 4039624 +(1 row) +类似地,pg_xlogfile_name仅提取预写式日志文件名。当指定的预写式日志位置恰好位于预写式日志文件边界时,这两个函数都返回前一个预写式日志文件的名称。这通常正是管理预写式日志归档时所需的行为,因为前一个文件是当前需要归档的最后一个文件。 + + pg_xlog_location_diff 计算两个预写式日志位置之间的字节差。可以将它与 pg_stat_replication中的某些函数配合使用,以获取复制延迟。 + + + 有关正确使用这些函数的详细信息,参见。 + + +
+ + + 恢复控制函数 + + + pg_is_in_recovery + + + pg_last_xlog_receive_location + + + pg_last_xlog_replay_location + + + pg_last_xact_replay_timestamp + + + + 中展示的函数提供有关备库当前状态的信息。 + 这些函数可以在恢复或普通运行过程中被执行。 + + + + 恢复信息函数 + + + 名称 返回类型 描述 + + + + + + + pg_is_in_recovery() + + bool + 如果恢复仍在进行,则为真。 + + + + pg_last_xlog_receive_location() + + pg_lsn + 获取流复制最近接收并同步到磁盘的预写式日志位置。在流复制进行期间,该值单调增加。如果恢复已完成,该值将保持为恢复期间最后接收并同步到磁盘的 WAL 记录的位置。如果禁用了流复制,或者流复制尚未开始,此函数返回 NULL。 + + + + pg_last_xlog_replay_location() + + pg_lsn + 获取恢复期间最近重放的预写式日志位置。如果恢复仍在进行,该值单调增加。如果恢复已完成,该值将保持为该次恢复期间最后应用的 WAL 记录的位置。如果服务器未经恢复而正常启动,此函数返回 NULL。 + + + + pg_last_xact_replay_timestamp() + + timestamp with time zone + 获取恢复期间最近重放事务的时间戳,即该事务的提交或中止 WAL 记录在主库上生成的时间。如果恢复期间尚未重放任何事务,此函数返回 NULL。否则,如果恢复仍在进行,该值单调增加。如果恢复已完成,该值将保持为该次恢复期间最后应用的事务的时间戳。如果服务器未经恢复而正常启动,此函数返回 NULL。 + + + +
+ + + pg_is_xlog_replay_paused + + + pg_xlog_replay_pause + + + pg_xlog_replay_resume + + + + 列出的函数用于控制恢复进度。这些函数只能在恢复期间执行。 + + + + 恢复控制函数 + + + 名称 返回类型 描述 + + + + + + + pg_is_xlog_replay_paused() + + bool + 如果恢复已暂停,则为真。 + + + + pg_xlog_replay_pause() + + void + 立即暂停恢复(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数)。 + + + + pg_xlog_replay_resume() + + void + 如果恢复已暂停,则重新开始恢复(默认仅限超级用户,但可以向其他用户授予EXECUTE权限以运行此函数)。 + + + +
+ + 恢复暂停时,不再应用数据库更改。如果处于热备状态,所有新查询都会看到同一个一致的数据库快照,并且在恢复继续之前不会再产生查询冲突。 + + + 如果禁用了流复制,则暂停状态可能会无限期地持续下去,不会出现问题。 + 如果正在进行流复制,那么将继续接收WAL记录,这将最终填满可用磁盘空间,这取决于暂停持续时间、WAL生成速度和可用磁盘空间。 + + +
+ + + 快照同步函数 + + + pg_export_snapshot + + + + PostgreSQL允许数据库会话同步它们的快照。一个快照决定对于正在使用该快照的事务哪些数据是可见的。当两个或者更多个会话需要看到数据库中的相同内容时,就需要同步快照。如果两个会话独立开始其事务,就总是有可能有某个第三事务在两个START TRANSACTION命令的执行之间提交,这样其中一个会话就可以看到该事务的效果而另一个则看不到。 + + + + 为了解决这个问题,PostgreSQL允许一个事务导出它正在使用的快照。只要导出快照的事务仍然保持打开,其他事务可以导入它的快照,并且因此可以保证它们可以看到和第一个事务看到的完全一样的数据库视图。但是注意这些事务中的任何一个对数据库所作的更改对其他事务仍然保持不可见,和未提交事务所作的修改一样。因此这些事务是针对以前存在的数据同步,而对由它们自己所作的更改则采取正常的动作。 + + + + 如中所示,快照通过pg_export_snapshot函数导出,并且通过命令导入。 + + + + 快照同步函数 + + + 名称 返回类型 描述 + + + + + + + pg_export_snapshot() + + text + 保存当前快照并返回其标识符 + + + +
+ + 函数 pg_export_snapshot 保存当前快照,并返回标识该快照的 text 字符串。必须在数据库之外将此字符串传递给要导入该快照的客户端。快照只能在导出它的事务结束之前导入。如有需要,一个事务可以导出多个快照。注意,这只对 READ COMMITTED 事务有用,因为在 REPEATABLE READ 及更高隔离级别中,事务在整个存续期间都使用同一个快照。事务一旦导出过快照,就不能再通过 进行预备。 + + 关于如何使用导出快照的详细说明,请参见 +
+ + + 复制函数 + + 中的函数用于控制复制功能并与之交互。相关功能的信息见。复制源函数仅限超级用户使用。复制槽函数仅限超级用户和具有 REPLICATION 权限的用户使用。 + + + 很多这些函数在复制协议中都有等价的命令,见 + 。 + + + + 、 + 和 + + 中描述的函数也与复制相关。 + + + + 复制 <acronym>SQL</acronym> 函数 + + + + 函数 + 返回类型 + 描述 + + + + + pg_create_physical_replication_slot pg_create_physical_replication_slot(slot_name name , immediately_reserve boolean ) + + (slot_name name, xlog_position pg_lsn) + + 创建名为 slot_name 的新物理复制槽。可选的第二个参数为 true 时,指定立即为此复制槽保留 LSN;否则在流复制客户端首次连接时保留 LSN。从物理槽流式传输更改只能使用流复制协议 — 参见。此函数对应复制协议命令 CREATE_REPLICATION_SLOT ... PHYSICAL + + + pg_drop_replication_slot pg_drop_replication_slot(slot_name name) + void + 删除名为 slot_name 的物理或逻辑复制槽。与复制协议命令 DROP_REPLICATION_SLOT 相同。对于逻辑槽,必须连接到创建该槽的同一个数据库来调用。 + + + + pg_create_logical_replication_slot pg_create_logical_replication_slot(slot_name name, plugin name) + + (slot_name name, xlog_position pg_lsn) + + 使用输出插件 plugin 创建名为 slot_name 的新逻辑(解码)复制槽。调用此函数与复制协议命令 CREATE_REPLICATION_SLOT ... LOGICAL 效果相同。 + + + + pg_logical_slot_get_changes pg_logical_slot_get_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[]) + + (location pg_lsn, xid xid, data text) + + 返回槽 slot_name 中自上次消费更改的位置起的更改。如果 upto_lsnupto_nchanges 都为 NULL,逻辑解码会持续到 WAL 末尾。如果 upto_lsn 非 NULL,解码仅包含在指定 LSN 之前提交的事务。如果 upto_nchanges 非 NULL,解码产生的行数超过指定值时就会停止。不过,实际返回行数可能更大,因为只有在添加完对每个新事务提交进行解码所产生的行后,才会检查此限制。 + + + + pg_logical_slot_peek_changes pg_logical_slot_peek_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[]) + + (location pg_lsn, xid xid, data text) + + + 行为就像pg_logical_slot_get_changes()函数, + 不过改变不会被消费, 即在未来的调用中还会返回这些改变。 + + + + + pg_logical_slot_get_binary_changes pg_logical_slot_get_binary_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[]) + + (location pg_lsn, xid xid, data bytea) + + + 行为就像pg_logical_slot_get_changes()函数, + 不过改变会以bytea返回。 + + + + + pg_logical_slot_peek_binary_changes pg_logical_slot_peek_binary_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[]) + + (location pg_lsn, xid xid, data bytea) + + 行为与 pg_logical_slot_get_changes() 相同,但更改以 bytea 返回,且不会被消费;也就是说,后续调用还会再次返回这些更改。 + + + + pg_replication_origin_create pg_replication_origin_create(node_name text) + oid + 以给定外部名称创建复制源,并返回分配给它的内部 ID。 + + + + pg_replication_origin_drop pg_replication_origin_drop(node_name text) + void + 删除先前创建的复制源,包括关联的重放进度。 + + + + pg_replication_origin_oid pg_replication_origin_oid(node_name text) + oid + 按名称查找复制源并返回内部 id。如果没有找到对应的复制源,则抛出错误。 + + + + pg_replication_origin_session_setup pg_replication_origin_session_setup(node_name text) + void + 将当前会话标记为从给定复制源重放,以便跟踪重放进度。使用 pg_replication_origin_session_reset 撤销。仅当先前未配置复制源时才能使用。 + + + + pg_replication_origin_session_reset pg_replication_origin_session_reset() + void + 取消 pg_replication_origin_session_setup() 的效果。 + + + + pg_replication_origin_session_is_setup pg_replication_origin_session_is_setup() + bool + 当前会话是否已配置复制源? + + + + pg_replication_origin_session_progress pg_replication_origin_session_progress(flush bool) + pg_lsn + 返回当前会话所配置复制源的重放位置。flush 参数决定是否保证对应的本地事务已刷盘。 + + + + pg_replication_origin_xact_setup pg_replication_origin_xact_setup(origin_lsn pg_lsn, origin_timestamp timestamptz) + void + 将当前事务标记为正在重放一个已在给定 LSN 和时间戳提交的事务。仅当先前已用 pg_replication_origin_session_setup() 配置复制源时才能调用。 + + + + pg_replication_origin_xact_reset pg_replication_origin_xact_reset() + void + 取消 pg_replication_origin_xact_setup() 的效果。 + + + + pg_replication_origin_advance pg_replication_origin_advance(node_name text, pos pg_lsn) + void + 将给定结点的复制进度设置为给定位置。这主要用于设置初始位置,或在配置更改等情况之后设置新位置。请注意,疏忽使用此函数可能导致复制数据不一致。 + + + + pg_replication_origin_progress pg_replication_origin_progress(node_name text, flush bool) + pg_lsn + 返回给定复制源的重放位置。flush 参数决定是否保证对应的本地事务已刷盘。 + + + + pg_logical_emit_message pg_logical_emit_message(transactional bool, prefix text, content text) + pg_lsn + 发出文本逻辑解码消息。这可用于通过 WAL 向逻辑解码插件传递通用消息。transactional 参数指定消息是作为当前事务的一部分,还是立即写入并在逻辑解码读到该记录时立即解码。prefix 是文本前缀,便于逻辑解码插件识别其关注的消息。content 是消息文本。 + + + + pg_logical_emit_message(transactional bool, prefix text, content bytea) + pg_lsn + 发出二进制逻辑解码消息。这可用于通过 WAL 向逻辑解码插件传递通用消息。transactional 参数指定消息是作为当前事务的一部分,还是立即写入并在逻辑解码读到该记录时立即解码。prefix 是文本前缀,便于逻辑解码插件识别其关注的消息。content 是消息的二进制内容。 + + + + +
+ +
+ + + 数据库对象管理函数 + + 中的函数计算数据库对象的磁盘空间用量。 + + + pg_column_size + + + pg_database_size + + + pg_indexes_size + + + pg_relation_size + + + pg_size_bytes + + + pg_size_pretty + + + pg_table_size + + + pg_tablespace_size + + + pg_total_relation_size + + + + 数据库对象尺寸函数 + + + 名称 返回类型 描述 + + + + + + pg_column_size(any) + int + 存储某个值所使用的字节数(可能已压缩) + + + pg_database_size(oid) + bigint + 指定 OID 的数据库占用的磁盘空间 + + + pg_database_size(name) + bigint + 指定名称的数据库占用的磁盘空间 + + + pg_indexes_size(regclass) + bigint + 指定表的索引占用的磁盘空间总量 + + + pg_relation_size(relation regclass, fork text) + bigint + 指定表或索引的指定分支('main''fsm''vm''init')占用的磁盘空间 + + + pg_relation_size(relation regclass) + bigint + pg_relation_size(..., 'main') 的简写 + + + pg_size_bytes(text) + bigint + 将带有大小单位的人类可读格式转换为字节数 + + + pg_size_pretty(bigint) + text + 将以 64 位整数表示的字节大小转换为带有大小单位的人类可读格式 + + + pg_size_pretty(numeric) + text + 将以 numeric 值表示的字节大小转换为带有大小单位的人类可读格式 + + + pg_table_size(regclass) + bigint + 指定表占用的磁盘空间,不包括索引(但包括 TOAST、空闲空间映射和可见性映射) + + + pg_tablespace_size(oid) + bigint + 指定 OID 的表空间占用的磁盘空间 + + + pg_tablespace_size(name) + bigint + 指定名称的表空间占用的磁盘空间 + + + pg_total_relation_size(regclass) + bigint + 指定表占用的磁盘空间总量,包括所有索引和 TOAST 数据 + + + +
+ + pg_column_size 显示存储任意单个数据值所用的空间。 + + pg_total_relation_size 接受表或 TOAST 表的 OID 或名称,返回该表占用的磁盘总空间,包括所有关联索引。此函数等同于 pg_table_size + pg_indexes_size + + pg_table_size 接受表的 OID 或名称,返回该表所需的磁盘空间,不包括索引。(包括 TOAST 空间、空闲空间映射和可见性映射。) + + pg_indexes_size 接受表的 OID 或名称,返回该表所有附属索引占用的磁盘总空间。 + + pg_database_sizepg_tablespace_size 接受数据库或表空间的 OID 或名称,返回其中占用的磁盘总空间。使用 pg_database_size 必须对指定数据库拥有 CONNECT 权限(默认授予)。使用 pg_tablespace_size 必须对指定表空间拥有 CREATE 权限;但如果该表空间是当前数据库的默认表空间,则不受此限制。 + + + pg_relation_size接受表、索引或 TOAST 表的 OID 或名称,并以字节为单位返回该关系某个分支的磁盘大小。(注意,大多数情况下使用更高层的函数会更方便,例如pg_total_relation_sizepg_table_size,它们会将所有分支的大小相加。)只带一个参数时,返回关系的主数据分支大小。可以提供第二个参数来指定要检查的分支: + + 'main' 返回关系的主数据分支大小。 + + + 'fsm' 返回关系所关联的空闲空间映射的大小(参见)。 + + + 'vm' 返回关系所关联的可见性映射的大小(参见)。 + + + 'init' 返回关系所关联的初始化分支的大小(如果存在)。 + + + + + pg_size_pretty 可以将其他函数的结果格式化为便于阅读的形式,适当地使用 bytes、kB、MB、GB 或 TB 单位。 + + pg_size_bytes 可以将便于阅读的大小字符串转换为字节数。输入可以使用 bytes、kB、MB、GB 或 TB 单位,解析时不区分大小写。如果没有指定单位,则默认为字节。 + + + pg_size_prettypg_size_bytes 使用的 kB、MB、GB 和 TB 单位按 2 的幂定义,而不是 10 的幂。因此,1kB 为 1024 字节,1MB 为 10242 = 1048576 字节,以此类推。 + + + + 上述操作表和索引的函数接受一个regclass参数,它是该表或索引在pg_class系统目录中的 OID。 + 你不必手工去查找该 OID,因为regclass数据类型的输入转换器会为你代劳。 + 只写包围在单引号内的表名,这样它看起来像一个字面量。 + 为了与普通SQL名称的处理相兼容,该字符串将被转换为小写形式,除非其中在表名周围包含双引号。 + + + 如果向上述某个函数传入的 OID 不代表现有对象,则返回 NULL。 + + + 中展示的函数帮助标识数据库对象相关的磁盘文件。 + + + + pg_relation_filenode + + + pg_relation_filepath + + + pg_filenode_relation + + + + 数据库对象位置函数 + + + 名称 返回类型 描述 + + + + + + pg_relation_filenode(relation regclass) + oid + 指定关系的文件结点编号 + + + pg_relation_filepath(relation regclass) + text + 指定关系的文件路径名 + + + pg_filenode_relation(tablespace oid, filenode oid) + regclass + 查找与给定表空间和文件结点关联的关系 + + + +
+ + pg_relation_filenode 接受表、索引、序列或 TOAST 表的 OID 或名称,返回当前分配给它的文件结点编号。文件结点是关系文件名的基本组成部分(更多信息见)。对于大多数表,结果与 pg_class.relfilenode 相同;但某些系统目录的 relfilenode 为零,必须使用此函数获取正确值。如果传入的关系没有存储空间(例如视图),函数返回 NULL。 + + pg_relation_filepathpg_relation_filenode 类似,但返回关系的完整文件路径名(相对于数据库集簇的数据目录 PGDATA)。 + + pg_filenode_relation 执行 pg_relation_filenode 的逆操作。给定表空间 OID 和文件结点,它返回关联关系的 OID。对于位于数据库默认表空间中的表,表空间可以指定为 0。 + + +
+ + + 索引维护函数 + + + brin_summarize_new_values + + + + gin_clean_pending_list + + + + + 列出了可用于索引维护任务的函数。这些函数不能在恢复期间执行,仅限超级用户和给定索引的所有者使用。 + + + 索引维护函数 + + + 名称 返回类型 描述 + + + + + brin_summarize_new_values(index regclass) + integer + 对尚未摘要的页范围进行摘要 + + + gin_clean_pending_list(index regclass) + bigint + 将 GIN 待处理列表条目移入主索引结构 + + + +
+ + brin_summarize_new_values 接受 BRIN 索引的 OID 或名称,检查索引,找出基表中尚未生成索引摘要的页面范围;对于每个这样的范围,它通过扫描表页面创建新的摘要索引元组。函数返回插入索引的新页面范围摘要数量。 + + gin_clean_pending_list 接受 GIN 索引的 OID 或名称,通过将待处理列表中的条目批量移到主 GIN 数据结构中,清理指定索引的待处理列表。它返回从待处理列表中移除的页面数。注意,如果参数是禁用了 fastupdate 选项的 GIN 索引,则不执行清理并返回 0,因为该索引没有待处理列表。关于待处理列表和 fastupdate 选项的详细信息,请参见 + +
+ + + 通用文件访问函数 + + 中的函数提供对数据库服务器所在机器上的文件的本地访问。只能访问数据库集簇目录和 log_directory 中的文件。集簇目录中的文件使用相对路径,日志文件使用与 log_directory 配置设置相匹配的路径。除非另有说明,这些函数仅限超级用户使用。 + + + 通用文件访问函数 + + + 名称 返回类型 描述 + + + + + + pg_ls_dir(dirname text [, missing_ok boolean, include_dot_dirs boolean]) + setof text + 列出目录内容。 + + + pg_read_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ]) + text + 返回文本文件的内容。 + + + pg_read_binary_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ]) + bytea + 返回文件内容。 + + + pg_stat_file(filename text[, missing_ok boolean]) + record + 返回文件信息。 + + + +
+ + 这些函数中的一些接受可选参数 missing_ok,用于指定文件或目录不存在时的行为。如果为 true,函数返回 NULL(但 pg_ls_dir 返回空结果集)。如果为 false,则会报错。默认值为 false + + + pg_ls_dir + + pg_ls_dir 返回指定目录中所有文件的名称(也包括目录和其他特殊文件)。 include_dot_dirs 指示结果集是否包含 ...。默认不包含它们(false);但当 missing_oktrue 时,包含它们有助于区分空目录和不存在的目录。 + + + + + pg_read_file + + pg_read_file 从给定的 offset 开始返回文本文件的一部分,最多返回 length 字节(如果先到达文件末尾,则返回更少的字节)。如果 offset 为负,则它相对于文件末尾。如果省略 offsetlength,则返回整个文件。从文件读取的字节按服务器编码解释为字符串;如果它们在该编码下无效,则会报错。 + + + pg_read_binary_file + + + pg_read_binary_file类似于pg_read_file,但结果是bytea值,因此不执行编码检查。与convert_from函数配合使用,可以按指定编码读取文件: +SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); + + + + + pg_stat_file + + + pg_stat_file返回一条记录,包含文件大小、最后访问时间戳、最后修改时间戳、最后文件状态更改时间戳(仅限 Unix 平台)、文件创建时间戳(仅限 Windows),以及一个boolean值,表示它是否是目录。典型用法包括: +SELECT * FROM pg_stat_file('filename'); +SELECT (pg_stat_file('filename')).modification; + + + +
+ + + 咨询锁函数 + + + 中展示的函数管理咨询锁。 + 有关正确使用这些函数的细节请参考。 + + + + 咨询锁函数 + + + 名称 返回类型 描述 + + + + + + pg_advisory_lock(key bigint) + void + 获取排他会话级咨询锁 + + + pg_advisory_lock(key1 int, key2 int) + void + 获取排他会话级咨询锁 + + + pg_advisory_lock_shared(key bigint) + void + 获取共享会话级咨询锁 + + + pg_advisory_lock_shared(key1 int, key2 int) + void + 获取共享会话级咨询锁 + + + pg_advisory_unlock(key bigint) + boolean + 释放排他会话级咨询锁 + + + pg_advisory_unlock(key1 int, key2 int) + boolean + 释放排他会话级咨询锁 + + + + pg_advisory_unlock_all() + + void + 释放当前会话持有的所有会话级咨询锁 + + + pg_advisory_unlock_shared(key bigint) + boolean + 释放共享会话级咨询锁 + + + pg_advisory_unlock_shared(key1 int, key2 int) + boolean + 释放共享会话级咨询锁 + + + pg_advisory_xact_lock(key bigint) + void + 获取排他事务级咨询锁 + + + pg_advisory_xact_lock(key1 int, key2 int) + void + 获取排他事务级咨询锁 + + + pg_advisory_xact_lock_shared(key bigint) + void + 获取共享事务级咨询锁 + + + pg_advisory_xact_lock_shared(key1 int, key2 int) + void + 获取共享事务级咨询锁 + + + pg_try_advisory_lock(key bigint) + boolean + 如果可用,则获取排他会话级咨询锁 + + + pg_try_advisory_lock(key1 int, key2 int) + boolean + 如果可用,则获取排他会话级咨询锁 + + + pg_try_advisory_lock_shared(key bigint) + boolean + 如果可用,则获取共享会话级咨询锁 + + + pg_try_advisory_lock_shared(key1 int, key2 int) + boolean + 如果可用,则获取共享会话级咨询锁 + + + pg_try_advisory_xact_lock(key bigint) + boolean + 如果可用,则获取排他事务级咨询锁 + + + pg_try_advisory_xact_lock(key1 int, key2 int) + boolean + 如果可用,则获取排他事务级咨询锁 + + + pg_try_advisory_xact_lock_shared(key bigint) + boolean + 如果可用,则获取共享事务级咨询锁 + + + pg_try_advisory_xact_lock_shared(key1 int, key2 int) + boolean + 如果可用,则获取共享事务级咨询锁 + + + +
+ + + pg_advisory_lock + + pg_advisory_lock 锁定应用定义的资源,可以用一个 64 位键值或两个 32 位键值标识资源(注意,这两个键空间不重叠)。如果另一个会话已持有相同资源标识符上的锁,此函数会等待,直到该资源可用。该锁是排他的。多个锁请求会叠加,因此如果同一资源被锁定了三次,就必须解锁三次,才能释放给其他会话使用。 + + + pg_advisory_lock_shared + + pg_advisory_lock_shared 的行为与 pg_advisory_lock 相同,但该锁可以与其他请求共享锁的会话共享。只有试图获取排他锁的会话会被阻挡。 + + + pg_try_advisory_lock + + pg_try_advisory_lockpg_advisory_lock 类似,但不会等待锁变得可用。它要么立即获得锁并返回 true,要么在无法立即获得锁时返回 false + + + pg_try_advisory_lock_shared + + pg_try_advisory_lock_shared 的行为与 pg_try_advisory_lock 相同,但尝试获取的是共享锁,而不是排他锁。 + + + pg_advisory_unlock + + pg_advisory_unlock 释放先前获得的会话级排他咨询锁。如果成功释放锁,则返回 true。如果未持有该锁,则返回 false,服务器还会报告一条 SQL 警告。 + + + pg_advisory_unlock_shared + + pg_advisory_unlock_shared 的行为与 pg_advisory_unlock 相同,但释放的是会话级共享咨询锁。 + + + pg_advisory_unlock_all + + pg_advisory_unlock_all 释放当前会话持有的所有会话级咨询锁。(会话结束时会隐式调用此函数,即使客户端非正常断开连接也是如此。) + + + pg_advisory_xact_lock + + pg_advisory_xact_lock 的行为与 pg_advisory_lock 相同,但锁会在当前事务结束时自动释放,不能显式释放。 + + + pg_advisory_xact_lock_shared + + pg_advisory_xact_lock_shared 的行为与 pg_advisory_lock_shared 相同,但锁会在当前事务结束时自动释放,不能显式释放。 + + + pg_try_advisory_xact_lock + + pg_try_advisory_xact_lock 的行为与 pg_try_advisory_lock 相同,但如果获得了锁,该锁会在当前事务结束时自动释放,不能显式释放。 + + + pg_try_advisory_xact_lock_shared + + pg_try_advisory_xact_lock_shared 的行为与 pg_try_advisory_lock_shared 相同,但如果获得了锁,该锁会在当前事务结束时自动释放,不能显式释放。 + +
+ +
+ + + 触发器函数 + + + suppress_redundant_updates_trigger + + + 目前 PostgreSQL 提供一个内置触发器函数 suppress_redundant_updates_trigger,它会阻止那些实际上不会改变行中数据的更新。通常的行为则是无论数据是否改变都执行更新。(这种通常行为不需要检查,因此更新运行得更快,并且在某些情况下也有用。) + + 理想情况下,应尽量避免执行实际上不会改变记录中数据的更新。冗余更新会耗费大量不必要的时间,尤其是在需要改动许多索引时;它们还会使死行占用空间,最终需要通过清理回收。但是,在客户端代码中检测这种情况并不总是容易,甚至可能无法做到,而编写用于检测的表达式也容易出错。另一种办法是使用 suppress_redundant_updates_trigger,跳过不改变数据的更新。但使用时应当谨慎。此触发器处理每条记录的耗时虽小,却不可忽略,因此如果一次更新涉及的大多数记录确实会改变,使用此触发器反而会使更新更慢。 + + suppress_redundant_updates_trigger函数可以按如下方式添加到表中: +CREATE TRIGGER z_min_update +BEFORE UPDATE ON tablename +FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +在大多数情况下,应让该触发器对每一行最后触发。由于触发器按名称顺序触发,应选择一个排序位于表上其他所有触发器名称之后的名称。 + 关于创建触发器的更多信息,请参见 + + + + 事件触发器函数 + + + PostgreSQL提供了这些辅助函数来从事件触发器检索信息。 + + + + 更多有关事件触发器的信息请见。 + + + + 在命令结束处捕捉更改 + + + pg_event_trigger_ddl_commands + + + + 当在一个ddl_command_end事件触发器的函数中调用时,pg_event_trigger_ddl_commands返回被每一个用户动作执行的DDL命令的列表。 + 如果在其他任何环境中调用这个函数,会发生错误。 + pg_event_trigger_ddl_commands为每一个被执行的基本命令返回一行,某些由单条 SQL 语句构成的命令可能会返回多于一行。 + 这个函数返回下面的列: + + + + + + 名称 + 类型 + 描述 + + + + + + classid + oid + 对象所属系统目录的 OID + + + objid + oid + 对象本身的 OID + + + objsubid + integer + 子对象 ID(例如列的属性编号) + + + command_tag + text + 命令标签 + + + object_type + text + 对象的类型 + + + schema_name + text + + 对象所属模式的名称(若有);否则为NULL。不加引号。 + + + + object_identity + text + + 对象标识的文本表示形式,带模式限定。标识中包含的每个标识符在必要时都会加引号。 + + + + in_extension + bool + 如果该命令是一个扩展脚本的一部分则为真 + + + command + pg_ddl_command + + 命令的完整内部表示,不能直接输出,但可以将其传给其他函数以获取关于该命令的不同信息。 + + + + + + + + + + 处理被 DDL 命令删除的对象 + + + pg_event_trigger_dropped_objects + + + 在命令的 sql_drop 事件中调用 pg_event_trigger_dropped_objects 时,它返回该命令删除的所有对象的列表。如果在其他上下文中调用,pg_event_trigger_dropped_objects 会报错。pg_event_trigger_dropped_objects 返回以下列: + + + + + 名称 + 类型 + 描述 + + + + + + classid + oid + 对象原先所属系统目录的 OID + + + objid + oid + 对象本身的 OID + + + objsubid + integer + 子对象 ID(例如列的属性编号) + + + original + bool + 如果这是删除中的一个根对象则为真 + + + normal + bool + + 如果依赖图中存在指向该对象的普通依赖关系,则为真。 + + + + is_temporary + bool + + 如果该对象是一个临时对象则为真 + + + + object_type + text + 对象的类型 + + + schema_name + text + + 对象原先所属模式的名称(若有);否则为NULL。不加引号。 + + + + object_name + text + + 如果模式和名称的组合可用作该对象的唯一标识符,则为对象名称;否则为NULL。不加引号,并且该名称永远不带模式限定。 + + + + object_identity + text + + 对象标识的文本表示形式,带模式限定。标识中包含的每个标识符在必要时都会加引号。 + + + + address_names + text[] + + 一个数组,可与object_typeaddress_args一起,通过pg_get_object_address()函数在包含同类同名对象的远程服务器上重建该对象地址。 + + + + address_args + text[] + + 上述address_names的补充。 + + + + + + + + pg_event_trigger_dropped_objects函数可以按如下方式用于事件触发器: +CREATE FUNCTION test_event_trigger_for_drops() + RETURNS event_trigger LANGUAGE plpgsql AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + RAISE NOTICE '% dropped object: % %.% %', + tg_tag, + obj.object_type, + obj.schema_name, + obj.object_name, + obj.object_identity; + END LOOP; +END; +$$; +CREATE EVENT TRIGGER test_event_trigger_for_drops + ON sql_drop + EXECUTE PROCEDURE test_event_trigger_for_drops(); + + + + + + 处理表重写事件 + + + + 中所示的函数提供刚刚被调用过table_rewrite + 事件的表的信息。如果在任何其他环境中调用,会发生错误。 + + + + 表重写信息 + + + 名称 返回类型 描述 + + + + + pg_event_trigger_table_rewrite_oid pg_event_trigger_table_rewrite_oid() + Oid + 即将重写的表的 OID。 + + + + pg_event_trigger_table_rewrite_reason pg_event_trigger_table_rewrite_reason() + int + 解释重写原因的原因代码。代码的确切含义取决于版本。 + + + +
+ + pg_event_trigger_table_rewrite_oid函数可以按如下方式用于事件触发器: +CREATE FUNCTION test_event_trigger_table_rewrite_oid() + RETURNS event_trigger + LANGUAGE plpgsql AS +$$ +BEGIN + RAISE NOTICE 'rewriting table % for reason %', + pg_event_trigger_table_rewrite_oid()::regclass, + pg_event_trigger_table_rewrite_reason(); +END; +$$; + +CREATE EVENT TRIGGER test_table_rewrite_oid + ON table_rewrite + EXECUTE PROCEDURE test_event_trigger_table_rewrite_oid(); + + +
+
+ +
diff --git a/zh/9.6/fuzzystrmatch.sgml b/zh/9.6/fuzzystrmatch.sgml new file mode 100644 index 00000000..ea2a2d52 --- /dev/null +++ b/zh/9.6/fuzzystrmatch.sgml @@ -0,0 +1,224 @@ + + + + fuzzystrmatch — 确定字符串的相似性和距离 + + + fuzzystrmatch + + + + fuzzystrmatch模块提供多个函数,用于确定字符串的相似性和距离。 + + + + 目前,soundexmetaphonedmetaphonedmetaphone_alt 函数不能很好地处理多字节编码(例如 UTF-8)。 + + + + Soundex + + + Soundex 系统是一种通过将发音相近的名字转换为相同代码来进行匹配的方法。 + 它最初在 1880 年、1900 年和 1910 年的美国人口普查中使用。注意, + Soundex 对非英语名字并不是很有用。 + + + + fuzzystrmatch模块提供两个用于处理 Soundex 代码的函数: + + + + soundex + + + + difference + + + +soundex(text) returns text +difference(text, text) returns int + + + + soundex函数将字符串转换为其 Soundex 代码。 + difference函数将两个字符串转换为各自的 Soundex 代码, + 然后返回代码中相同位置上匹配的个数。由于 Soundex 代码有四个字符, + 因此结果范围为 0 到 4,其中 0 表示没有匹配,4 表示完全匹配。 + (因此,这个函数的命名并不贴切,similarity + 会是更好的名字。) + + + + 下面是一些用法示例: + + + +SELECT soundex('hello world!'); + +SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann'); +SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew'); +SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret'); + +CREATE TABLE s (nm text); + +INSERT INTO s VALUES ('john'); +INSERT INTO s VALUES ('joan'); +INSERT INTO s VALUES ('wobbly'); +INSERT INTO s VALUES ('jack'); + +SELECT * FROM s WHERE soundex(nm) = soundex('john'); + +SELECT * FROM s WHERE difference(s.nm, 'john') > 2; + + + + + Levenshtein + + + 该函数计算两个字符串之间的 Levenshtein 距离: + + + + levenshtein + + + + levenshtein_less_equal + + + +levenshtein(text source, text target, int ins_cost, int del_cost, int sub_cost) returns int +levenshtein(text source, text target) returns int +levenshtein_less_equal(text source, text target, int ins_cost, int del_cost, int sub_cost, int max_d) returns int +levenshtein_less_equal(text source, text target, int max_d) returns int + + + + sourcetarget 都可以是任意非空字符串, + 最大长度为 255 个字符。代价参数分别指定字符插入、删除或替换的代价。 + 可以像该函数的第二种形式那样省略代价参数;此时它们都默认为 1。 + + + + levenshtein_less_equal 是 Levenshtein 函数的加速版本, + 用于只关心较小距离的场景。如果实际距离小于等于 max_d, + 那么 levenshtein_less_equal 会返回正确的距离; + 否则它会返回某个大于 max_d 的值。如果 + max_d 为负,则其行为与 levenshtein + 相同。 + + + + 示例: + + + +test=# SELECT levenshtein('GUMBO', 'GAMBOL'); + levenshtein +------------- + 2 +(1 row) + +test=# SELECT levenshtein('GUMBO', 'GAMBOL', 2,1,1); + levenshtein +------------- + 3 +(1 row) + +test=# SELECT levenshtein_less_equal('extensive', 'exhaustive',2); + levenshtein_less_equal +------------------------ + 3 +(1 row) + +test=# SELECT levenshtein_less_equal('extensive', 'exhaustive',4); + levenshtein_less_equal +------------------------ + 4 +(1 row) + + + + + Metaphone + + + Metaphone 与 Soundex 一样,基于为输入字符串构造一个代表性代码的思想。 + 如果两个字符串具有相同的代码,则认为它们相似。 + + + + 该函数计算输入字符串的 Metaphone 代码: + + + + metaphone + + + +metaphone(text source, int max_output_length) returns text + + + + source 必须是非空字符串,最大长度为 255 个字符。 + max_output_length 设置输出 Metaphone 代码的最大长度; + 如果超过该长度,输出会被截断为该长度。 + + + + 示例: + + + +test=# SELECT metaphone('GUMBO', 4); + metaphone +----------- + KM +(1 row) + + + + + Double Metaphone + + + Double Metaphone 系统会为给定输入字符串计算两个近音代码, + 即一个代码和一个备选代码。在大多数情况下, + 它们相同,但尤其对于非英语名字,这两个代码可能会因发音不同而略有差异。 + 这些函数分别计算主代码和备选代码: + + + + dmetaphone + + + + dmetaphone_alt + + + +dmetaphone(text source) returns text +dmetaphone_alt(text source) returns text + + + + 输入字符串没有长度限制。 + + + + 示例: + + + +test=# select dmetaphone('gumbo'); + dmetaphone +------------ + KMP +(1 row) + + + + diff --git a/zh/9.6/generate-errcodes-table.pl b/zh/9.6/generate-errcodes-table.pl new file mode 100644 index 00000000..2195e0f4 --- /dev/null +++ b/zh/9.6/generate-errcodes-table.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# +# Generate the errcodes-table.sgml file from errcodes.txt +# Copyright (c) 2000-2016, PostgreSQL Global Development Group + +use warnings; +use strict; + +print + "\n"; + +open my $errcodes, $ARGV[0] or die; + +while (<$errcodes>) +{ + chomp; + + # Skip comments + next if /^#/; + next if /^\s*$/; + + # Emit section headers + if (/^Section:/) + { + + # Remove the Section: string + s/^Section: //; + + # Escape dashes for SGML + s/-/—/; + + # Wrap PostgreSQL in + s/PostgreSQL/PostgreSQL<\/>/g; + + print "\n\n"; + print "\n"; + print ""; + print "$_\n"; + print "\n"; + + next; + } + + die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/; + + (my $sqlstate, my $type, my $errcode_macro, my $condition_name) = + ($1, $2, $3, $4); + + # Skip lines without PL/pgSQL condition names + next unless defined($condition_name); + + print "\n"; + print "\n"; + print "$sqlstate\n"; + print "$condition_name\n"; + print "\n"; +} + +close $errcodes; diff --git a/zh/9.6/generic-wal.sgml b/zh/9.6/generic-wal.sgml new file mode 100644 index 00000000..97dad935 --- /dev/null +++ b/zh/9.6/generic-wal.sgml @@ -0,0 +1,108 @@ + + + + 通用 WAL 记录 + + + 虽然所有内置的、会生成 WAL 记录的模块都有各自的 WAL 记录类型,但也有一种通用 WAL 记录类型,可以用通用方式描述对页面的更改。这对于提供自定义访问方法的扩展很有用,因为它们无法注册自己的 WAL 重做例程。 + + + + 用于构造通用 WAL 记录的 API 定义在access/generic_xlog.h中,并在access/transam/generic_xlog.c中实现。 + + + + 要使用通用 WAL 记录机制执行一次需要写入 WAL 的数据更新,请遵循以下步骤: + + + + + state = GenericXLogStart(relation) — 开始为给定关系构造一条通用 WAL 记录。 + + + + + + page = GenericXLogRegisterBuffer(state, buffer, flags) + — 注册一个将在当前通用 WAL 记录中被修改的缓冲区。该函数返回一个指向该缓冲区页面临时副本的指针,应在该副本上进行修改(不要直接修改缓冲区内容)。第三个参数是适用于该操作的标志掩码。目前唯一这样的标志是GENERIC_XLOG_FULL_IMAGE,表示应在 WAL 记录中包含整页镜像,而不是增量更新。通常在页面是新的,或者已经被完全重写时,会设置该标志。如果该操作需要修改多个页面,则可以重复调用GenericXLogRegisterBuffer。 + + + + + + 对上一步得到的页面镜像施加修改。 + + + + + + GenericXLogFinish(state) — 将更改应用到缓冲区,并发出通用 WAL 记录。 + + + + + + + 在上述任意步骤之间,都可以调用GenericXLogAbort(state)取消 WAL 记录的构造。这会丢弃对页面镜像副本所做的全部更改。 + + + + 在使用通用 WAL 记录功能时请注意以下几点: + + + + + 不允许直接修改缓冲区!所有修改都必须在通过GenericXLogRegisterBuffer()取得的副本上完成。换句话说,生成通用 WAL 记录的代码绝不应自行调用BufferGetPage()。不过,在适当的时机对缓冲区执行 pin/unpin 和 lock/unlock,仍然是调用者的责任。对于每个目标缓冲区,从调用GenericXLogRegisterBuffer()之前开始直到GenericXLogFinish()之后,都必须持有排他锁。 + + + + + + 注册缓冲区(步骤 2)和修改页面镜像(步骤 3)可以自由交错进行,也就是说,这两个步骤可以按任意顺序重复。请记住,注册缓冲区的顺序应当与重放时获取其锁的顺序一致。 + + + + + + 一个通用 WAL 记录最多能注册MAX_GENERIC_XLOG_PAGES个缓冲区。如果超过这个限制,就会抛出错误。 + + + + + + 通用 WAL 假定待修改的页面具有标准布局,尤其是假定pd_lowerpd_upper之间没有有用数据。 + + + + + + 由于修改的是缓冲区页面的副本,GenericXLogStart()不会开启临界区。因此,在GenericXLogStart()GenericXLogFinish()之间,可以安全地进行内存分配、抛出错误等操作。唯一真正的临界区位于GenericXLogFinish()内部。此外,也不必担心在错误退出时调用GenericXLogAbort()。 + + + + + + GenericXLogFinish()会负责将缓冲区标记为脏并设置它们的 LSN。你不需要显式执行这些操作。 + + + + + + 对于不记录 WAL 的关系,其他行为都完全相同,只是不会真正发出 WAL 记录。因此,通常不需要专门对不记录 WAL 的关系做任何显式检查。 + + + + + + 通用 WAL 的重做函数会按照缓冲区注册的顺序获取这些缓冲区上的排他锁。在重做完所有更改后,这些锁也会按照同样的顺序释放。 + + + + + + 如果某个已注册缓冲区未指定GENERIC_XLOG_FULL_IMAGE,通用 WAL 记录中包含的就是旧页面镜像与新页面镜像之间的差异。该差异基于逐字节比较。对于在页面内移动数据的情况,这种表示方式并不十分紧凑,未来可能会改进。 + + + + + diff --git a/zh/9.6/geqo.sgml b/zh/9.6/geqo.sgml new file mode 100644 index 00000000..28672fee --- /dev/null +++ b/zh/9.6/geqo.sgml @@ -0,0 +1,225 @@ + + + + 遗传查询优化器 + + + + 作者 + + 由 Martin Utesch utesch@aut.tu-freiberg.de 为德国弗赖贝格矿业和技术大学自动控制研究所编写。 + + + + + + 将查询处理看成是一个复杂的优化问题 + + + 在所有关系操作符中,最难处理和优化的是连接。随着查询中连接数目的增加,可能的查询计划数量会呈指数增长。为了处理单个连接而支持多种连接方法(例如 PostgreSQL 中的嵌套循环、哈希连接和归并连接),以及作为关系访问路径的多种索引(例如 PostgreSQL 中的 B-树、哈希、GiST 和 GIN),也进一步增加了优化工作量。 + + + + 常规的PostgreSQL查询优化器会在可选策略空间中执行近似穷举搜索。该算法最早出现在 IBM 的 System R 数据库中,能够产生近乎最优的连接顺序;但当查询中的连接数变得很大时,它可能耗费极大的时间和内存空间。因此,普通的PostgreSQL查询优化器并不适合需要连接大量表的查询。 + + + + 德国弗赖贝格矿业和技术大学自动控制研究所在尝试将 PostgreSQL 用作一个用于电网维护的基于知识的决策支持系统后端时遇到了一些问题。该 DBMS 需要为该基于知识系统中的推理机处理大型连接查询。这些查询中的连接数量使得使用普通查询优化器变得不可行。 + + + + 下文将介绍一种遗传算法的实现,它以适合涉及大量连接的查询的高效方式来解决连接顺序问题。 + + + + + 遗传算法 + + + 遗传算法(GA)是一种通过随机化搜索进行工作的启发式优化方法。优化问题的可能解集合被视为由若干个体组成的种群。个体对其环境的适应程度由其适应度表示。 + + + + 一个个体在搜索空间中的坐标由染色体表示,本质上是一组字符串。基因是染色体的一个片段,它编码某个待优化参数的值。基因的典型编码可以是二进制整数。 + + + + 通过模拟重组变异选择这些进化操作,可以找到平均适应度高于前代的新一代搜索点。 + + + + 根据comp.ai.genetic FAQ 中的说法,再怎么强调也不过分:GA并不是为了求解问题而进行的纯粹随机搜索。GA会使用随机过程,但其结果显然并非随机的(优于随机)。 + + +
+ 遗传算法的结构图 + + + + + + P(t) + 时刻 t 的祖代 + + + + P''(t) + 时刻 t 的子代 + + + + + + ++=========================================+ +|>>>>>>>>>>> Algorithm GA <<<<<<<<<<<<<<| ++=========================================+ +| INITIALIZE t := 0 | ++=========================================+ +| INITIALIZE P(t) | ++=========================================+ +| evaluate FITNESS of P(t) | ++=========================================+ +| while not STOPPING CRITERION do | +| +-------------------------------------+ +| | P'(t) := RECOMBINATION{P(t)} | +| +-------------------------------------+ +| | P''(t) := MUTATION{P'(t)} | +| +-------------------------------------+ +| | P(t+1) := SELECTION{P''(t) + P(t)} | +| +-------------------------------------+ +| | evaluate FITNESS of P''(t) | +| +-------------------------------------+ +| | t := t + 1 | ++===+=====================================+ + +
+
+ + + PostgreSQL 中的遗传查询优化(<acronym>GEQO</acronym>) + + + GEQO模块把查询优化问题视为著名的旅行商问题(TSP)来处理。可能的查询计划被编码为整数串。每个串表示查询中从一个关系到下一个关系的连接顺序。例如,连接树 + + /\ + /\ 2 + /\ 3 +4 1 + + 会被编码为整数串 '4-1-3-2',这意味着先连接关系 '4' 和 '1',再连接 '3',最后连接 '2';其中 1、2、3、4 是PostgreSQL优化器内部的关系 ID。 + + + + PostgreSQLGEQO 实现的具体特征包括: + + + + + 采用稳态 GA(替换种群中适应度最低的个体,而不是整代替换),能够快速收敛到更优的查询计划。这对于在合理时间内处理查询至关重要; + + + + + + 采用边重组交叉,它特别适合在利用 GA 求解 TSP 时将边损失保持在较低水平; + + + + + + 不使用变异作为遗传操作符,因此无须借助修复机制来生成合法的 TSP 回路。 + + + + + + + GEQO模块的部分内容改编自 D. Whitley 的 Genitor 算法。 + + + + GEQO模块使PostgreSQL查询优化器能够通过非穷举搜索有效支持大型连接查询。 + + + + 使用<acronym>GEQO</acronym>生成可能的计划 + + + GEQO 规划过程使用标准规划器代码来生成各个关系扫描的计划,然后再用遗传方法生成连接计划。如上所示,每个候选连接计划都表示为一个连接基本关系的顺序。在初始阶段,GEQO 代码只是随机生成一些可能的连接序列。对于每一个待考察的连接序列,都会调用标准规划器代码来估算按该序列执行查询的代价。(对于连接序列中的每一步,都会考虑全部三种可能的连接策略;并且所有预先确定的关系扫描计划也都可用。估计代价取这些可能性中最低者。)估计代价较低的连接序列会被认为比代价较高的序列更适合。遗传算法会丢弃最不适合的候选,然后通过组合更适合候选的基因来生成新的候选,也就是从已知低代价连接序列中随机选取片段,构造新的序列以供考察。这个过程会重复进行,直到已经考察的连接序列数量达到预设值;然后,在搜索过程中任何时刻找到的最佳序列都会被用来生成最终计划。 + + + + 由于初始种群选择以及后续对最佳候选的变异都包含随机选择,因此这一过程天生就是非确定性的。为了避免所选计划发生令人意外的变化,每次运行 GEQO 算法时,都会用当前的 参数设置重新初始化其随机数生成器。只要 geqo_seed、其他 GEQO 参数以及统计信息等其他规划器输入保持不变,对于给定查询就会生成相同的计划。若想试验不同的搜索路径,可以尝试修改 geqo_seed。 + + + + + + <productname>PostgreSQL</productname> <acronym>GEQO</acronym>的未来实现任务 + + + 为了改进遗传算法的参数设置,仍有一些工作要做。在文件src/backend/optimizer/geqo/geqo_main.c中的例程gimme_pool_sizegimme_number_generations里,我们必须为参数设置找到一种折中,以满足两个相互竞争的需求: + + + + 查询计划的最优性 + + + + + 计算时间 + + + + + + + 在当前实现中,每个候选连接序列的适应度都是通过从头运行标准规划器的连接选择和代价估算代码来估算的。只要不同候选使用了相似的连接子序列,就会有大量工作被重复执行。如果能保留子连接的代价估算,就可以显著加快这一过程。问题在于,必须避免为了保存这种状态而消耗不合理数量的内存。 + + + + 从更基础的层面看,用一个为 TSP 设计的 GA 算法来解决查询优化问题是否合适,也并不明确。在 TSP 情况下,与任何子串(部分巡回)相关的代价都独立于巡回的其余部分,但对于查询优化显然并非如此。因此,边重组交叉是否是最有效的变异过程,仍然值得怀疑。 + + + + + + + 进一步阅读 + + + 下列资源包含关于遗传算法的更多信息: + + + + + + 《Hitch-Hiker 演化计算指南》comp.ai.genetic FAQ) + + + + + + + 《演化计算及其在艺术和设计中的应用》,Craig Reynolds 著 + + + + + + + + + + + + + + + + + + +
diff --git a/zh/9.6/gin.sgml b/zh/9.6/gin.sgml new file mode 100644 index 00000000..f7c39add --- /dev/null +++ b/zh/9.6/gin.sgml @@ -0,0 +1,809 @@ + + + +GIN 索引 + + + 索引 + GIN + + + + 简介 + + + GIN 是 Generalized Inverted Index(通用倒排索引)的缩写。 + GIN 被设计用于处理这样一类情况:要建立索引的项是组合值, + 而索引需要处理的查询则要搜索出现在这些组合项内部的元素值。例如,这些项可以是文档, + 而查询可以是搜索包含特定词语的文档。 + + + + 我们用来指代要被索引的组合值,用来指代其中的元素值。 + GIN 总是存储并搜索键,而不是项值本身。 + + + + GIN 索引存储一组(键,倒排列表)对,其中一个倒排列表(posting list) + 是一个包含出现该键的行 ID 的集合。由于一个项可以包含多个键,同一个行 ID 可以出现在多个倒排列表中。 + 每个键值只会被存储一次,因此在同一个键大量重复出现的场景下, + GIN 索引会非常紧凑。 + + + + GIN 之所以是通用的,是因为 GIN 访问方法的代码 + 不需要知道它所加速的具体操作。相反,它使用为特定数据类型定义的自定义策略。 + 该策略定义了如何从被索引项和查询条件中提取键,以及如何判断一个包含查询中某些键值的行 + 是否真正满足该查询。 + + + + GIN 的一个优点在于,它允许由数据类型所属领域的专家来开发带有合适访问方法的自定义数据类型, + 而不必依赖数据库专家。这一点与使用 GiST 的优势非常相似。 + + + + PostgreSQL 中的 GIN 实现主要由 + Teodor Sigaev 和 Oleg Bartunov 维护。关于 GIN 的更多信息, + 可在他们的 网站 上找到。 + + + + + 内置操作符类 + + + PostgreSQL 核心发行版包含 + 所示的 GIN 操作符类。 + (在 中描述的一些可选模块还提供额外的 GIN 操作符类。) + + + + 内置 <acronym>GIN</acronym> 操作符类 + + + + + 名称 + 被索引数据类型 + 可索引操作符 + + + + + _abstime_ops + abstime[] + + && + <@ + = + @> + + + + _bit_ops + bit[] + + && + <@ + = + @> + + + + _bool_ops + boolean[] + + && + <@ + = + @> + + + + _bpchar_ops + character[] + + && + <@ + = + @> + + + + _bytea_ops + bytea[] + + && + <@ + = + @> + + + + _char_ops + "char"[] + + && + <@ + = + @> + + + + _cidr_ops + cidr[] + + && + <@ + = + @> + + + + _date_ops + date[] + + && + <@ + = + @> + + + + _float4_ops + float4[] + + && + <@ + = + @> + + + + _float8_ops + float8[] + + && + <@ + = + @> + + + + _inet_ops + inet[] + + && + <@ + = + @> + + + + _int2_ops + smallint[] + + && + <@ + = + @> + + + + _int4_ops + integer[] + + && + <@ + = + @> + + + + _int8_ops + bigint[] + + && + <@ + = + @> + + + + _interval_ops + interval[] + + && + <@ + = + @> + + + + _macaddr_ops + macaddr[] + + && + <@ + = + @> + + + + _money_ops + money[] + + && + <@ + = + @> + + + + _name_ops + name[] + + && + <@ + = + @> + + + + _numeric_ops + numeric[] + + && + <@ + = + @> + + + + _oid_ops + oid[] + + && + <@ + = + @> + + + + _oidvector_ops + oidvector[] + + && + <@ + = + @> + + + + _reltime_ops + reltime[] + + && + <@ + = + @> + + + + _text_ops + text[] + + && + <@ + = + @> + + + + _time_ops + time[] + + && + <@ + = + @> + + + + _timestamp_ops + timestamp[] + + && + <@ + = + @> + + + + _timestamptz_ops + timestamp with time zone[] + + && + <@ + = + @> + + + + _timetz_ops + time with time zone[] + + && + <@ + = + @> + + + + _tinterval_ops + tinterval[] + + && + <@ + = + @> + + + + _varbit_ops + bit varying[] + + && + <@ + = + @> + + + + _varchar_ops + character varying[] + + && + <@ + = + @> + + + + jsonb_ops + jsonb + + ? + ?& + ?| + @> + + + + jsonb_path_ops + jsonb + + @> + + + + tsvector_ops + tsvector + + @@ + @@@ + + + + +
+ + + 在用于类型 jsonb 的两个操作符类中,jsonb_ops + 是默认项。jsonb_path_ops 支持的操作符较少,但对这些操作符提供更好的性能。 + 详见 。 + + +
+ + + 可扩展性 + + + GIN 接口具有很高的抽象层次,访问方法实现者只需实现所访问数据类型的语义。 + GIN 层本身会处理并发、日志记录以及树结构的搜索。 + + + + 要让一个 GIN 访问方法工作起来,只需实现少数几个用户定义的方法, + 它们定义了树中键的行为,以及键、被索引项和可索引查询之间的关系。简言之, + GIN 将可扩展性与通用性、代码重用以及清晰的接口结合在一起。 + + + + GIN 操作符类必须提供三个方法: + + + + int compare(Datum a, Datum b) + + + 比较两个键(不是被索引项!),并返回一个整数:小于零、等于零或大于零, + 分别表示第一个键小于、等于或大于第二个键。null 键绝不会被传递给这个函数。 + + + + + + Datum *extractValue(Datum itemValue, int32 *nkeys, + bool **nullFlags) + + + 给定一个要建立索引的项,返回一个用 palloc 分配的键数组。返回键的数量必须存入 + *nkeys。如果任一键可以为 null,还要另外 palloc 一个包含 + *nkeysbool 字段的数组,将其地址存入 + *nullFlags,并按需设置这些空值标志。如果所有键都不是 null, + *nullFlags 可以保持为 NULL(其初始值)。 + 如果该项不包含任何键,则返回值可以为 NULL。 + + + + + + Datum *extractQuery(Datum query, int32 *nkeys, + StrategyNumber n, bool **pmatch, Pointer **extra_data, + bool **nullFlags, int32 *searchMode) + + + 给定一个待查询的值,返回一个用 palloc 分配的键数组;也就是说, + query 是一个可索引操作符右侧的值,而该操作符左侧是被索引列。 + n 是该操作符在操作符类中的策略号(见 )。 + 通常,extractQuery 需要查看 n,以确定 + query 的数据类型,以及应采用何种方法提取键值。返回键的数量必须存入 + *nkeys。如果任一键可以为 null,还要另外 palloc 一个包含 + *nkeysbool 字段的数组,将其地址存入 + *nullFlags,并按需设置这些空值标志。如果所有键都不是 null, + *nullFlags 可以保持为 NULL(其初始值)。 + 如果 query 不包含任何键,则返回值可以为 NULL。 + + + + searchMode 是一个输出参数,用于让 extractQuery + 指定搜索如何执行。若 *searchMode 被设置为 + GIN_SEARCH_MODE_DEFAULT(调用前它会被初始化为该值), + 则只有至少匹配一个返回键的项才会被视为候选匹配。若 *searchMode + 被设置为 GIN_SEARCH_MODE_INCLUDE_EMPTY,则除至少包含一个匹配键的项之外, + 完全不含任何键的项也会被视为候选匹配。(例如,该模式对于实现是子集操作符很有用。) + 若 *searchMode 被设置为 GIN_SEARCH_MODE_ALL, + 则索引中所有非 null 项都会被视为候选匹配,无论它们是否匹配任一返回键。 + (该模式比前两种选择慢得多,因为它基本上需要扫描整个索引;但为了正确处理某些边界情况, + 可能有此必要。在大多数情况下都需要此模式的操作符,大概并不适合作为 + GIN 操作符类的候选。)用于设置该模式的符号定义在 + access/gin.h 中。 + + + + pmatch 是一个在支持部分匹配时使用的输出参数。要使用它, + extractQuery 必须分配一个包含 *nkeys 个 + 布尔值的数组,并将其地址存入 *pmatch。若相应键需要部分匹配, + 则数组对应元素应设置为 TRUE,否则设置为 FALSE。如果 *pmatch + 被设置为 NULL,那么 GIN 认为不需要部分匹配。 + 该变量在调用前会初始化为 NULL,因此不支持部分匹配的操作符类可以直接忽略此参数。 + + + + extra_data 是一个输出参数,用于让 extractQuery + 向 consistentcomparePartial 方法传递额外数据。 + 要使用它,extractQuery 必须分配一个包含 *nkeys + 个指针的数组,并将其地址存入 *extra_data,然后把所需内容存入各个指针。 + 该变量在调用前会初始化为 NULL,因此不需要额外数据的操作符类可以直接忽略此参数。 + 如果设置了 *extra_data,则整个数组会传给 consistent + 方法,而对应元素会传给 comparePartial 方法。 + + + + + + + 操作符类还必须提供一个函数,用于检查被索引项是否匹配查询。它有两种形式:布尔型 + consistent 函数,以及三值型 triConsistent 函数。 + triConsistent 覆盖了两者的功能,因此仅提供 triConsistent + 就已经足够。不过,如果布尔变体的计算代价明显更低,那么同时提供两者可能更有利。 + 若只提供布尔变体,则一些依赖于在取回所有键之前先排除索引项的优化将被禁用。 + + + + bool consistent(bool check[], StrategyNumber n, Datum query, + int32 nkeys, Pointer extra_data[], bool *recheck, + Datum queryKeys[], bool nullFlags[]) + + + 如果被索引项满足策略号为 n 的查询操作符,则返回 TRUE; + 如果同时返回需要复核的指示,那么 TRUE 也可以只表示它可能满足。 + 该函数无法直接访问被索引项的值,因为 GIN 并不显式存储项。 + 它所能利用的是这样一种信息:从查询中提取出的哪些键值出现在给定的被索引项中。 + check 数组长度为 nkeys,这与先前针对该 + query datum 由 extractQuery 返回的键数量相同。 + 如果被索引项包含相应查询键,则 check 数组中的对应元素为 TRUE; + 也就是说,如果 (check[i] == TRUE),则 extractQuery + 结果数组中的第 i 个键存在于该被索引项中。传入原始 query datum, + 是为了让 consistent 方法在需要时可以查看它;同样也会传入先前由 + extractQuery 返回的 queryKeys[] 和 + nullFlags[] 数组。extra_data 则是 + extractQuery 返回的额外数据数组,如果没有则为 NULL。 + + + + 当 extractQueryqueryKeys[] 中返回一个 null 键时, + 若被索引项包含 null 键,则对应的 check[] 元素为 TRUE;也就是说, + check[] 的语义类似于 IS NOT DISTINCT FROM。 + 如果 consistent 函数需要区分普通值匹配与 null 匹配, + 它可以检查对应的 nullFlags[] 元素。 + + + + 成功时,如果需要根据查询操作符重新检查堆元组,则 *recheck 应设置为 TRUE; + 如果索引测试是精确的,则设置为 FALSE。也就是说,返回 FALSE 保证该堆元组不匹配查询; + 返回 TRUE 且 *recheck 为 FALSE 保证该堆元组匹配查询; + 返回 TRUE 且 *recheck 为 TRUE 则表示该堆元组可能匹配查询, + 因此需要取出该元组,并直接对最初被索引的项值重新计算查询操作符以完成复核。 + + + + + + GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query, + int32 nkeys, Pointer extra_data[], + Datum queryKeys[], bool nullFlags[]) + + + triConsistentconsistent 类似,不过 + check 向量中的元素不是布尔值,而是每个键都有三种可能的值: + GIN_TRUEGIN_FALSEGIN_MAYBE。 + GIN_FALSEGIN_TRUE 与普通布尔值含义相同, + 而 GIN_MAYBE 表示该键是否存在尚不确定。存在 GIN_MAYBE + 值时,只有当无论索引项是否包含对应查询键,该项都确定匹配时,函数才应返回 + GIN_TRUE。同样,只有当无论是否包含 GIN_MAYBE 键, + 该项都确定不匹配时,函数才能返回 GIN_FALSE。如果结果依赖于 + GIN_MAYBE 条目,也就是说,无法根据已知的查询键确认或否定匹配, + 则函数必须返回 GIN_MAYBE。 + + + 当 check 向量中没有 GIN_MAYBE 值时, + 返回 GIN_MAYBE 等价于在布尔型 consistent + 函数中设置 recheck 标志。 + + + + + + 作为可选项,GIN操作符类可以提供以下方法: + + + + int comparePartial(Datum partial_key, Datum key, StrategyNumber n, + Pointer extra_data) + + + 比较部分匹配查询键与索引键。返回一个整数,其符号表示结果:小于零表示索引键不匹配查询, + 但索引扫描应继续;零表示索引键匹配查询;大于零表示索引扫描应停止,因为后续不可能再有匹配项。 + 产生该部分匹配查询的操作符的策略号 n 会被传入,以便在需要其语义来决定何时结束扫描时使用。 + 另外,extra_data 是由 extractQuery 生成的额外数据数组中的对应元素, + 如果没有则为 NULL。null 键绝不会被传递给这个函数。 + + + + + + + + 要支持部分匹配查询,操作符类必须提供 comparePartial 方法, + 并且其 extractQuery 方法在遇到部分匹配查询时必须设置 + pmatch 参数。详见 。 + + + + 上文提到的各种 Datum 值,其实际数据类型会因操作符类而异。 + 传给 extractValue 的项值始终是该操作符类的输入类型,而所有键值都必须是该类的 + STORAGE 类型。传给 extractQuery、 + consistenttriConsistent 的 + query 参数类型,是由该策略号标识的类成员操作符右侧输入类型。 + 只要能够从中提取出正确类型的键值,它就不必与被索引类型相同。不过,建议在这三个支持函数的 + SQL 声明中,对 query 参数使用该操作符类的被索引数据类型, + 尽管依据具体操作符,实际类型可能是别的类型。 + + + + + + 实现 + + + 在内部,一个 GIN 索引包含一个基于键构建的 B-树索引,其中每个键都是一个或多个被索引项中的某个元素 + (例如数组成员),而叶子页中的每个元组要么包含一个指向堆指针 B-树的指针(倒排树), + 要么在列表足够小、能够连同键值一起放入单个索引元组时,直接包含一个简单的堆指针列表(倒排列表)。 + + + + 从 PostgreSQL 9.1 开始,索引中可以包含 null 键值。 + 此外,对于那些自身为 null,或按照 extractValue 的定义不包含任何键的被索引项, + 索引中也会包含占位符 null 值。这样一来,应该找到空项的搜索就能够找到它们。 + + + + 多列 GIN 索引的实现方式,是在组合值(列号,键值)之上构建一个单独的 B-树。 + 不同列的键值可以具有不同类型。 + + + + GIN 快速更新技术 + + + 由于倒排索引的内在性质,更新 GIN 索引往往比较慢:插入或更新一个堆行, + 可能会导致向索引中执行多次插入(从被索引项中提取出的每个键都要插入一次)。 + 从PostgreSQL 8.4 开始,GIN 可以通过把新元组插入一个临时的、未排序的待处理列表, + 来推迟其中的大部分工作。当表被清理或自动分析时,或者调用 + gin_clean_pending_list 函数时,又或者待处理列表增长到大于 + 时,这些项就会被移入主要的 + GIN 数据结构中,所用的是与初始索引创建时相同的批量插入技术。 + 即便把额外的清理开销计算在内,这也会大幅提高 GIN 索引的更新速度。 + 此外,这部分开销工作还可以由后台进程完成,而不是在前台查询处理过程中完成。 + + + + 这种方法的主要缺点是,搜索除了要查找常规索引之外,还必须扫描待处理列表, + 因此大型待处理列表会显著拖慢搜索。另一个缺点是,虽然大多数更新都很快, + 但一旦某次更新使待处理列表变得过大,就会立刻触发一次清理周期, + 因此该次更新会比其他更新慢得多。恰当地使用自动清理可以将这两个问题都尽量减轻。 + + + + 如果稳定的响应时间比更新速度更重要,可以通过关闭 GIN 索引的 + fastupdate 存储参数来禁用待处理列表机制。详见 + 。 + + + + + 部分匹配算法 + + + GIN 可以支持部分匹配查询。在这种查询中, + 查询无法确定一个或多个键的精确匹配,但可能的匹配会落在一个相对狭窄的键值范围内 + (该范围位于由 compare 支持方法确定的键排序顺序中)。 + extractQuery 方法不是返回一个要精确匹配的键值, + 而是返回待搜索范围的下界键值,并将 pmatch 标志设为 true。 + 随后使用 comparePartial 方法扫描该键范围。 + 对于匹配的索引键,comparePartial 必须返回零; + 对于虽然不匹配但仍处在待搜索范围内的索引键,返回小于零; + 如果索引键已经超出了可能匹配的范围,则返回大于零。 + + + + + + +GIN 提示和技巧 + + + + 创建与插入 + + + 向 GIN 索引插入数据可能较慢,因为每个项很可能需要插入许多键。 + 因此,对于向表中执行的批量插入,建议先删除 GIN 索引,待批量插入完成后再重建它。 + + + PostgreSQL 8.4 开始,由于采用了延迟索引,这项建议已不那么必要(详见)。但对于非常大的更新,删除并重建索引仍然可能是最佳选择。 + + + + + + + + GIN 索引的构建时间对 maintenance_work_mem + 设置非常敏感;在创建索引时节省工作内存并不划算。 + + + + + + + + + 在对现有 GIN 索引执行一系列插入且其 fastupdate + 已启用时,系统会在待处理列表增长到超过 gin_pending_list_limit 时清理该列表。 + 为避免观测到的响应时间波动,最好让待处理列表的清理在后台发生(即通过自动清理)。 + 可以通过增大 gin_pending_list_limit 或让自动清理更积极, + 来避免前台清理操作。不过,提高清理触发阈值也意味着,一旦确实发生前台清理,耗时会更长。 + + + 可以通过更改存储参数为单个 GIN 索引覆盖 + gin_pending_list_limit,从而让每个 GIN 索引拥有自己的清理阈值。 + 例如,可以只提高那些更新很频繁的 GIN 索引的阈值,而把其他索引的阈值调低。 + + + + + + + + + 开发 GIN 索引的主要目标,是为 PostgreSQL + 中高度可伸缩的全文检索提供支持,而全文检索返回一个非常大的结果集的情况也很常见。 + 此外,当查询包含非常常见的词时,通常也会出现这种情况,以至于这个大型结果集本身并无多大用处。 + 由于从磁盘读取大量元组并对其排序可能需要很长时间,这在生产环境中是不可接受的。 + (注意,索引搜索本身是非常快的。) + + + 为了便于受控地执行这类查询,GIN 对返回行数设置了一个可配置的软上限, + 即配置参数 gin_fuzzy_search_limit。默认值为 0(表示无限制)。 + 如果设置了非零限制,那么返回集合将是整个结果集的一个随机子集。 + + + 的意思是,实际返回结果的数量可能会与指定的限制略有不同, + 这取决于查询以及系统随机数生成器的质量。 + + + 根据经验,数千量级的值(例如 5000 — 20000)效果不错。 + + + + + + + + + 限制 + + + GIN 假定可索引操作符是严格的。这意味着,当项值为 null 时, + 根本不会对其调用 extractValue(而是自动创建一个占位符索引项); + 当查询值为 null 时,也不会调用 extractQuery(而是认为该查询不可满足)。 + 不过要注意,非 null 的组合项或查询值内部包含的 null 键值仍然受支持。 + + + + + 示例 + + + PostgreSQL 源码发行版包含 tsvector 以及所有内部类型的一维数组的 + GIN 操作符类。tsvector 中的前缀搜索是使用 GIN 部分匹配特性实现的。 + 下列 contrib 模块也包含 GIN 操作符类: + + + + btree_gin + + 为若干数据类型提供等效于 B-树的功能 + + + + + hstore + + 用于存储(键,值)对的模块 + + + + + intarray + + int[] 的增强支持 + + + + + pg_trgm + + 基于三字符组匹配的文本相似度 + + + + + + +
diff --git a/zh/9.6/gist.sgml b/zh/9.6/gist.sgml new file mode 100644 index 00000000..23317615 --- /dev/null +++ b/zh/9.6/gist.sgml @@ -0,0 +1,729 @@ + + + +GiST 索引 + + + 索引 + GiST + + + + 简介 + + + GiST是 Generalized Search Tree(通用搜索树)的缩写。它是一种平衡的树形访问方法,可作为实现任意索引方案的基本模板。B-树、R 树以及许多其他索引方案都可以在GiST中实现。 + + + + GiST的一个优点是,它使数据类型所属领域的专家而不是数据库专家,能够开发带有适当访问方法的自定义数据类型。 + + + + 这里的一些内容来自加州大学伯克利分校的 GiST 索引项目 + 网站以及 + Marcel Kornacker 的论文 + + Access Methods for Next-Generation Database Systems。 + PostgreSQL中的GiST + 实现主要由 Teodor Sigaev 和 Oleg Bartunov 维护,他们的 + 网站上还有更多信息。 + + + + + + 内置操作符类 + + + PostgreSQL核心发行版包含中所示的GiST操作符类。(中描述的一些可选模块还提供了额外的GiST操作符类。) + + + + 内置 <acronym>GiST</acronym> 操作符类 + + + + 名称 + 被索引数据类型 + 可索引操作符 + 排序操作符 + + + + + box_ops + box + && &> &< &<| >> << <<| <@ @> @ |&> |>> ~ ~= + + + + + circle_ops + circle + && &> &< &<| >> << <<| <@ @> @ |&> |>> ~ ~= + <-> + + + inet_ops + inet, cidr + && >> >>= > >= <> << <<= < <= = + + + + + point_ops + point + >> >^ << <@ <@ <@ <^ ~= + <-> + + + poly_ops + polygon + && &> &< &<| >> << <<| <@ @> @ |&> |>> ~ ~= + <-> + + + range_ops + 任意范围类型 + && &> &< >> << <@ -|- = @> @> + + + + + tsquery_ops + tsquery + <@ @> + + + + + tsvector_ops + tsvector + + @@ + + + + + + +
+ + + 出于历史原因,inet_ops操作符类不是类型inetcidr的默认类。要使用它,请在CREATE INDEX中写出该类名,例如 + +CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); + + + +
+ + + 可扩展性 + + + 传统上,实现一种新的索引访问方法意味着大量艰难的工作。必须理解数据库的内部机制,例如锁管理器和预写式日志。GiST接口具有很高的抽象层次,只要求访问方法实现者实现被访问数据类型的语义。GiST层本身会处理并发、日志记录以及树结构的搜索。 + + + + 这种可扩展性不应与其他标准搜索树在可处理数据方面的可扩展性相混淆。例如,PostgreSQL支持可扩展的 B-树和 hash 索引。这意味着你可以用PostgreSQL在任意数据类型上构建 B-树或 hash 索引。但 B-树只支持范围谓词(<=>),而 hash 索引只支持等值查询。 + + + + 因此,如果你用PostgreSQL的 B-树为一个图像集合建立索引,你只能发出诸如imagex 是否等于 imageyimagex 是否小于 imagey以及imagex 是否大于 imagey之类的查询。取决于你如何在这种上下文中定义等于小于大于,这可能仍然有用。不过,使用基于GiST的索引,你就可以构造出能够提出特定领域问题的查询方式,例如找出所有马的图片或者找出所有曝光过度的图片。 + + + + 要让一个GiST访问方法运行起来,只需实现几个用户定义的方法,这些方法定义了树中键的行为。当然,要支持复杂查询,这些方法本身也必须足够巧妙;但对于所有标准查询(B-树、R 树等),它们都相对直接。简而言之,GiST把可扩展性与通用性、代码复用以及清晰的接口结合了起来。 + + + + 一个GiST索引操作符类必须提供七个方法,另外还有两个可选方法。通过正确实现sameconsistentunion方法可以保证索引的正确性,而索引的效率(大小与速度)则取决于penaltypicksplit方法。另外两个基本方法是compressdecompress,它们允许索引的内部树数据使用与其所索引数据不同的类型。叶子必须是被索引数据类型,而其他树节点可以是任意 C 结构体(但这里仍必须遵守PostgreSQL的数据类型规则,关于变长数据可参见varlena)。如果树的内部数据类型在 SQL 层存在,可以使用CREATE OPERATOR CLASS命令的STORAGE选项。可选的第八个方法是distance,若操作符类希望支持有序扫描(最近邻搜索),则需要它。可选的第九个方法fetch在操作符类希望支持仅索引扫描时需要。 + + + + + consistent + + + 给定一个索引项p和一个查询值q,该函数判断该索引项是否与该查询一致;也就是说,该索引项所代表的某一行是否可能使谓词indexed_column + indexable_operator q为真。对于叶子索引项,这等同于测试该可索引条件;而对于内部树节点,这决定是否有必要扫描该树节点所表示的索引子树。当结果为true时,还必须返回一个recheck标志。它表示该谓词是确定为真,还是仅可能为真。如果recheck = false,则该索引已经精确测试了谓词条件;如果recheck = true,则该行只是候选匹配。在这种情况下,系统会自动针对实际行值计算indexable_operator,以判断它是否真的匹配。这种约定使GiST能够同时支持无损和有损的索引结构。 + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_consistent(internal, data_type, smallint, oid, internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_consistent); + +Datum +my_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + data_type *query = PG_GETARG_DATA_TYPE_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + data_type *key = DatumGetDataType(entry->key); + bool retval; + + /* + * 根据 strategy、key 和 query 确定返回值。 + * + * 使用 GIST_LEAF(entry) 判断当前调用位于索引树的哪个位置。 + * 例如,支持 = 操作符时这很有用(可以在非叶节点检查 + * union() 是否非空,在叶节点检查是否相等)。 + */ + + *recheck = true; /* 如果检查是精确的,则为 false */ + + PG_RETURN_BOOL(retval); +} + + + 这里,key是索引中的一个元素,而query是在该索引中查找的值。StrategyNumber参数指示应用的是操作符类中的哪个操作符,它对应于CREATE OPERATOR CLASS命令中的某个操作符编号。 + + + + 取决于你在该类中包含了哪些操作符,query的数据类型可能会随操作符而变化,因为它将是操作符右侧的类型,而这可能不同于左侧出现的被索引数据类型。(上面的代码框架假定只可能有一种类型;如果不是这样,获取query参数值的方式就必须依赖于具体的操作符。)建议在consistent函数的 SQL 声明中,对query参数使用该操作符类的被索引数据类型,即使实际类型可能因为操作符不同而有所不同。 + + + + + + + union + + + 该方法用于汇总树中的信息。给定一组项,该函数生成一个新的索引项,用来表示所有给定项。 + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_union(internal, internal) +RETURNS storage_type +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_union); + +Datum +my_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GISTENTRY *ent = entryvec->vector; + data_type *out, + *tmp, + *old; + int numranges, + i = 0; + + numranges = entryvec->n; + tmp = DatumGetDataType(ent[0].key); + out = tmp; + + if (numranges == 1) + { + out = data_type_deep_copy(tmp); + + PG_RETURN_DATA_TYPE_P(out); + } + + for (i = 1; i < numranges; i++) + { + old = out; + tmp = DatumGetDataType(ent[i].key); + out = my_union_implementation(out, tmp); + } + + PG_RETURN_DATA_TYPE_P(out); +} + + + + + 如你所见,在这个框架里,我们处理的是一种满足union(X, Y, Z) = union(union(X, Y), Z)的数据类型。对于不满足这一性质的数据类型,只需在这个GiST支持方法中实现正确的 union 算法即可。 + + + + union函数的结果必须是索引存储类型的值,不管该类型是什么(它可能与被索引列的类型相同,也可能不同)。union函数应返回一个指向新近通过palloc()分配的内存的指针。即使没有类型变化,也不能原样返回输入值。 + + + + 如上所示,union函数的第一个internal参数实际上是一个GistEntryVector指针。第二个参数是一个指向整数变量的指针,可以忽略。(过去要求union函数把结果值的大小存入该变量,但现在已经不再需要。) + + + + + + compress + + 将一个数据项转换成适合在索引页中物理存储的格式。 + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_compress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_compress); + +Datum +my_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *retval; + + if (entry->leafkey) + { + /* 将 entry->key 替换为压缩后的形式 */ + compressed_data_type *compressed_data = palloc(sizeof(compressed_data_type)); + + /* 根据 entry->key 填充 *compressed_data ... */ + + retval = palloc(sizeof(GISTENTRY)); + gistentryinit(*retval, PointerGetDatum(compressed_data), + entry->rel, entry->page, entry->offset, FALSE); + } + else + { + /* 通常无需对非叶项做任何处理 */ + retval = entry; + } + + PG_RETURN_POINTER(retval); +} + + + + + 当然,为了压缩叶子节点,你必须把compressed_data_type改成要转换成的具体类型。 + + + + + + decompress + + compress方法的逆操作。将数据项的索引表示转换成操作符类中其他 GiST 方法能够操作的格式。 + + SQL声明必须如下所示: +CREATE OR REPLACE FUNCTION my_decompress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +C 模块中相应的代码可以采用以下框架: +PG_FUNCTION_INFO_V1(my_decompress); + +Datum +my_decompress(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); +} +上述框架适用于不需要解压的情况。 + + + + + penalty + + + 返回一个值,指示把新项插入树中特定分支的代价。项会沿着树中penalty最小的路径插入。penalty返回的值应为非负;如果返回负值,它将被按零处理。 + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_penalty(internal, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; -- 某些情况下 penalty 函数不必是严格函数 + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_penalty); + +Datum +my_penalty(PG_FUNCTION_ARGS) +{ + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *penalty = (float *) PG_GETARG_POINTER(2); + data_type *orig = DatumGetDataType(origentry->key); + data_type *new = DatumGetDataType(newentry->key); + + *penalty = my_penalty_implementation(orig, new); + PG_RETURN_POINTER(penalty); +} + + + 出于历史原因,penalty函数并不是直接返回一个float结果;相反,它必须把该值存储到第三个参数指示的位置。返回值本身会被忽略,不过通常会返回该参数所指向的地址。 + + + + penalty函数对于索引的良好性能至关重要。它会在插入时用于决定在树中应沿着哪个分支向下,以便选择把新项加到哪里。在查询时,索引越平衡,查找就越快。 + + + + + + picksplit + + + 当索引页必须分裂时,该函数决定页面上的哪些项留在旧页中,哪些移到新页中。 + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_picksplit(internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_picksplit); + +Datum +my_picksplit(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + OffsetNumber maxoff = entryvec->n - 1; + GISTENTRY *ent = entryvec->vector; + int i, + nbytes; + OffsetNumber *left, + *right; + data_type *tmp_union; + data_type *unionL; + data_type *unionR; + GISTENTRY **raw_entryvec; + + maxoff = entryvec->n - 1; + nbytes = (maxoff + 1) * sizeof(OffsetNumber); + + v->spl_left = (OffsetNumber *) palloc(nbytes); + left = v->spl_left; + v->spl_nleft = 0; + + v->spl_right = (OffsetNumber *) palloc(nbytes); + right = v->spl_right; + v->spl_nright = 0; + + unionL = NULL; + unionR = NULL; + + /* 初始化原始项向量。 */ + raw_entryvec = (GISTENTRY **) malloc(entryvec->n * sizeof(void *)); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + raw_entryvec[i] = &(entryvec->vector[i]); + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + int real_index = raw_entryvec[i] - entryvec->vector; + + tmp_union = DatumGetDataType(entryvec->vector[real_index].key); + Assert(tmp_union != NULL); + + /* + * 选择索引项的存放位置,并相应更新 unionL 和 unionR。 + * 将项追加到 v->spl_left 或 v->spl_right, + * 同时更新计数器。 + */ + + if (my_choice_is_left(unionL, curl, unionR, curr)) + { + if (unionL == NULL) + unionL = tmp_union; + else + unionL = my_union_implementation(unionL, tmp_union); + + *left = real_index; + ++left; + ++(v->spl_nleft); + } + else + { + /* + * 对右侧执行相同操作 + */ + } + } + + v->spl_ldatum = DataTypeGetDatum(unionL); + v->spl_rdatum = DataTypeGetDatum(unionR); + PG_RETURN_POINTER(v); +} + + + 注意,picksplit函数的结果是通过修改传入的v结构体来传递的。返回值本身会被忽略,不过通常会返回v的地址。 + + + + 和penalty一样,picksplit函数对于索引的良好性能至关重要。设计合适的penaltypicksplit实现,正是实现高性能GiST索引的难点所在。 + + + + + + same + + + 如果两个索引项相同则返回真,否则返回假。(索引项是索引存储类型的值,不一定是原始被索引列的类型。) + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_same(storage_type, storage_type, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_same); + +Datum +my_same(PG_FUNCTION_ARGS) +{ + prefix_range *v1 = PG_GETARG_PREFIX_RANGE_P(0); + prefix_range *v2 = PG_GETARG_PREFIX_RANGE_P(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = my_eq(v1, v2); + PG_RETURN_POINTER(result); +} + + + 出于历史原因,same函数并不是直接返回一个布尔结果;相反,它必须把该标志存储到第三个参数指示的位置。返回值本身会被忽略,不过通常会返回该参数所指向的地址。 + + + + + + distance + + + 给定一个索引项p和一个查询值q,该函数确定索引项与查询值之间的距离。如果操作符类包含任何排序操作符,就必须提供此函数。使用排序操作符的查询会优先返回距离值最小的索引项,因此结果必须与该操作符的语义一致。对于叶子索引项,结果仅表示到该索引项的距离;对于内部树节点,结果必须是其任意子项可能具有的最小距离。 + + + + 该函数的SQL声明必须如下所示: + + +CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal) +RETURNS float8 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_distance); + +Datum +my_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + data_type *query = PG_GETARG_DATA_TYPE_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + /* Oid subtype = PG_GETARG_OID(3); */ + /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */ + data_type *key = DatumGetDataType(entry->key); + double retval; + + /* + * 根据 strategy、key 和 query 确定返回值。 + */ + + PG_RETURN_FLOAT8(retval); +} + + + distance函数的参数与consistent函数的参数完全相同。 + + + + 在确定距离时允许有一定近似,只要结果永不大于该项的实际距离即可。因此,例如在几何应用中,到包围盒的距离通常就足够了。对于内部树节点,返回的距离不能大于其任一子节点的距离。如果返回的距离不精确,函数必须将*recheck设为 true。(对内部树节点则不必这样做;对它们总是假定计算结果不精确。)在这种情况下,执行器会在从堆中取出元组后计算准确距离,并在必要时重新排序这些元组。 + + + + 如果距离函数对任意一个叶节点返回*recheck = true,原始排序操作符的返回类型必须是float8float4,且距离函数的结果值必须能与原始排序操作符的结果进行比较,因为执行器会同时使用距离函数结果和重新计算得到的排序操作符结果进行排序。否则,距离函数的结果值可以是任意有限的float8值,只要这些结果值的相对顺序与排序操作符返回的顺序一致即可。(无穷大和负无穷在内部用于处理空值等情况,因此不建议distance函数返回这些值。) + + + + + + + fetch + + + 为了支持仅索引扫描,将数据项的压缩索引表示转换为原始数据类型。返回的数据必须是最初被索引值的精确、无损副本。 + + + SQL声明必须如下所示: +CREATE OR REPLACE FUNCTION my_fetch(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +参数是一个指向GISTENTRY结构体的指针。进入该函数时,它的key字段包含一个压缩形式的非 NULL 叶子 datum。返回值是另一个GISTENTRY结构体,其中的key字段以原始、未压缩形式包含同一个 datum。如果该操作符类的 compress 函数对叶子项不做任何处理,fetch方法可以原样返回该参数。 + + + 而 C 模块中的对应代码则可以遵循如下框架: + + +PG_FUNCTION_INFO_V1(my_fetch); + +Datum +my_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + input_data_type *in = DatumGetPointer(entry->key); + fetched_data_type *fetched_data; + GISTENTRY *retval; + + retval = palloc(sizeof(GISTENTRY)); + fetched_data = palloc(sizeof(fetched_data_type)); + + /* + * 将 'fetched_data' 转换为原始数据类型的 Datum。 + */ + + /* 根据 fetched_data 填充 *retval。 */ + gistentryinit(*retval, PointerGetDatum(converted_datum), + entry->rel, entry->page, entry->offset, FALSE); + + PG_RETURN_POINTER(retval); +} + + + + + 如果 compress 方法对叶子项是有损的,该操作符类就不能支持仅索引扫描,并且不得定义fetch函数。 + + + + + + + + 所有 GiST 支持方法通常都在短生命周期的内存上下文中被调用;也就是说,每处理完一个元组,CurrentMemoryContext都会被重置。因此通常无需过分担心释放所有通过 palloc 分配的内容。不过,在某些情况下,让支持方法在重复调用之间缓存数据是有用的。要做到这一点,可将寿命更长的数据分配在fcinfo->flinfo->fn_mcxt中,并在fcinfo->flinfo->fn_extra中保存指向它的指针。这类数据会在一次索引操作期间存活(例如一次 GiST 索引扫描、索引构建或索引元组插入)。在替换fn_extra值时要注意 pfree 旧值,否则泄漏会在整个操作期间不断累积。 + + + + + + 实现 + + + GiST 缓冲构建 + 仅靠把所有元组逐个插入来构建大型 GiST 索引往往很慢,因为如果索引元组分散在整个索引中,而索引又大到无法放入缓存,插入时就需要大量随机 I/O。从版本 9.2 开始,PostgreSQL 支持一种更高效的、基于缓冲的 GiST 索引构建方法,对于非有序数据集,它可以显著减少所需的随机 I/O 次数。对于顺序良好的数据集,收益较小或者根本没有,因为一次只有少量页面会接收新元组,而这些页面即使整个索引放不进缓存,也能够放进缓存。 + + + 不过,缓冲索引构建需要更频繁地调用penalty函数,这会消耗一些额外的 CPU 资源。此外,缓冲区需要临时磁盘空间,最多可达最终索引的大小。缓冲还可能正面或负面地影响最终索引的质量。这种影响取决于多种因素,例如输入数据的分布以及操作符类的实现。 + + + + 默认情况下,当索引大小达到时,GiST 索引构建会切换到缓冲方法。也可以通过 CREATE INDEX 命令的buffering参数手工强制启用或禁止缓冲。默认行为在大多数情况下都不错,但如果输入数据是有序的,关闭缓冲模式可能会略微加快构建速度。 + + + + + + + 示例 + + + PostgreSQL源代码发行包包含了若干使用GiST实现的索引方法示例。核心系统目前提供了文本搜索支持(为tsvectortsquery建立索引),并为某些内置几何数据类型提供了与 R 树等价的功能(见src/backend/access/gist/gistproc.c)。下列contrib模块中也包含GiST操作符类: + + + + btree_gist + + 为多种数据类型提供与 B-树等价的功能 + + + + + cube + + 多维立方体的索引 + + + + + hstore + + 用于存储(键,值)对的模块 + + + + + intarray + + 一维 int4 值数组的 RD 树 + + + + + ltree + + 树状结构的索引 + + + + + pg_trgm + + 基于三字符组匹配的文本相似度 + + + + + seg + + float 范围的索引 + + + + + + + +
diff --git a/zh/9.6/high-availability.sgml b/zh/9.6/high-availability.sgml new file mode 100644 index 00000000..3679eb3d --- /dev/null +++ b/zh/9.6/high-availability.sgml @@ -0,0 +1,1293 @@ + + + + 高可用、负载均衡和复制 + + 高可用 + 故障切换 + 复制 + 负载均衡 + 集簇 + 数据分区 + + + 数据库服务器可以协同工作,从而在主库失效时让第二台服务器快速接管其任务(高可用性),或者让多台计算机提供同一份数据(负载均衡)。理想情况下,数据库服务器应当能够无缝协同工作。提供静态网页服务的 Web 服务器只需把 Web 请求分发到多台机器,就能很容易地组合起来。事实上,只读数据库服务器也相对容易组合起来。不幸的是,大多数数据库服务器同时处理读写请求,而读/写服务器要更难组合。这是因为,只读数据只需要在每台服务器上放置一次,而对任意一台服务器的写入都必须传播到所有服务器,以保证后续对这些服务器的读取请求返回一致的结果。 + + + + 这种同步问题是服务器协同工作的根本难点。由于不存在一种能够对所有使用场景都消除同步问题影响的单一方案,因此出现了多种不同的解决方案。每一种方案都以不同方式处理这个问题,并且针对特定负载将其影响降到最低。 + + + + 某些方案通过只允许一台服务器修改数据来处理同步。能够修改数据的服务器称为读/写、主库(master)主库(primary)服务器。跟踪主库变更的服务器称为备库(standby)备库(slave)。只能在被提升为主库之后才能连接的备库称为温备,而能够接受连接并提供只读查询的备库称为热备。 + + + + 某些方案是同步的,即一个修改数据的事务只有在所有服务器都提交该事务之后才被视为已提交。这保证一次故障切换不会丢失任何数据,并且所有负载均衡的服务器无论查询哪一台都将返回一致的结果。相反,异步方案允许一次提交与其传播到其他服务器之间存在一定延迟,这就带来了切换到备库时丢失某些事务的可能性,也意味着负载均衡的服务器可能返回略微陈旧的结果。当同步通信过慢时,就会使用异步通信。 + + + + 这些方案还可以按粒度分类。有些方案只能处理整个数据库服务器,而另一些则允许在每个表或者每个数据库级别进行控制。 + + + + 无论做出哪种选择,都必须考虑性能。功能与性能之间通常存在权衡。例如,在低速网络上采用完全同步的方案,性能可能下降一半以上,而异步方案对性能的影响则可能很小。 + + + + 本节其余部分将概述多种故障切换、复制和负载均衡方案。 + + + + 不同方案的比较 + + + + + 共享磁盘故障切换 + + + + 共享磁盘故障切换通过只保留一份数据库拷贝来避免同步开销。它使用一个由多个服务器共享的单一磁盘阵列。如果主库失效,备库就可以挂载并启动数据库,就好像它正在从一次数据库崩溃中恢复一样。这允许快速故障切换而不会丢失数据。 + + + + 共享硬件功能常见于网络存储设备。使用网络文件系统也是可行的,但必须注意文件系统是否具备完整的 POSIX 行为(见)。这种方法的一大局限是,如果共享磁盘阵列失效或损坏,主库和备库都会无法工作。另一个问题是,在主库运行期间,备库绝不应访问共享存储。 + + + + + + + 文件系统(块设备)复制 + + + + 共享硬件功能的一种变体是文件系统复制,其中对一个文件系统的所有更改都会镜像到位于另一台计算机上的文件系统。唯一的限制是,这种镜像必须以能够保证备库拥有该文件系统一致拷贝的方式完成 — 特别是,对备库的写入必须与主库上的写入保持相同顺序。DRBD 是 Linux 上一种流行的文件系统复制方案。 + + + + + + + + + 事务日志传送 + + + + 温备和热备服务器能够通过读取预写式日志(WAL)记录流来保持最新状态。如果主库失效,备库拥有主库的几乎全部数据,并且能够迅速成为新的主库。这可以是同步的,也可以是异步的,并且只能用于整个数据库服务器。 + + + 可以使用基于文件的日志传送()、流复制(见 )或两者结合的方式来实现备库。有关热备的信息,见 。 + + + + + + + 基于触发器的主备复制 + + + 主备复制配置会将所有修改数据的查询发送到主库。主库将数据更改异步发送到备库。主库运行期间,备库可以响应只读查询。备库很适合处理数据仓库查询。 + + + Slony-I 是这种复制类型的一个示例。它按表粒度工作,并且支持多个备库。由于它会以批处理方式异步更新备库,因此在故障切换期间可能会发生数据丢失。 + + + + + + 基于语句的复制中间件 + + + 使用基于语句的复制中间件时,一个程序会截获每条 SQL 查询,并将其发送到一台或所有服务器。每台服务器独立运行。读写查询必须发送到所有服务器,确保每台服务器都收到所有更改。但只读查询可以只发送到一台服务器,从而将读取负载分散到这些服务器上。 + + 如果只是原样广播查询,random()CURRENT_TIMESTAMP 等函数以及序列在不同服务器上可能产生不同的值。这是因为每台服务器独立运行,且广播的是 SQL 查询,而不是实际修改后的行。如果无法接受这种情况,中间件或应用就必须从同一台服务器查询这些值,然后在写入查询中使用这些值。另一种选择是将这种复制方式与传统主备配置结合使用,即只向主库发送修改数据的查询,再通过主备复制将其传播到备库,而不是通过复制中间件传播。还必须确保所有事务在所有服务器上要么全部提交,要么全部中止,为此可以使用两阶段提交()。Pgpool-IIContinuent Tungsten 就是这类复制的例子。 + + + + + 异步多主复制 + + + + 对于那些并不经常连接或通信链路较慢的服务器,例如笔记本电脑或远程服务器,保持服务器间的数据一致是一个挑战。使用异步多主复制时,每台服务器都独立工作,并定期与其他服务器通信,以识别冲突事务。这些冲突可以由用户或冲突解决规则来解决。Bucardo 是这种复制类型的一个示例。 + + + + + + 同步多主复制 + + + + 在同步多主复制中,每台服务器都能接受写请求,并且在每个事务提交之前,修改过的数据都会从原始服务器传送给其他每台服务器。繁重的写入活动可能导致过多锁定和提交延迟,进而带来较差的性能。读请求可以发送给任意服务器。某些实现使用共享磁盘来减少通信开销。同步多主复制尤其适合以读为主的负载,尽管它的一大优势是任意服务器都能接受写请求 — 无需在主库和备库之间划分负载,并且由于数据更改是从一台服务器传送到另一台服务器,因此不会出现非确定函数(如 random())的问题。 + + + + PostgreSQL 本身不提供这种复制,不过可以在应用代码或中间件中利用 PostgreSQL 的两阶段提交()来实现这一功能。 + + + + + + 商业解决方案 + + + 由于 PostgreSQL 是开源的,而且易于扩展,许多公司以 PostgreSQL 为基础,创建了具有独特故障切换、复制和负载均衡功能的商业闭源解决方案。 + + + + + + + 总结了上述多种方案的能力。 + + + + 高可用、负载均衡和复制特性矩阵 + + + + 特性 + 共享磁盘故障切换 + 文件系统复制 + 事务日志传送 + 基于触发器的主备复制 + 基于语句的复制中间件 + 异步多主复制 + 同步多主复制 + + + + + + + 最常见的实现 + NAS + DRBD + 流复制 + Slony + pgpool-II + Bucardo + + + + + 通信方法 + 共享磁盘 + 磁盘块 + WAL + 表行 + SQL + 表行 + 表行和行锁 + + + + 不要求特殊硬件 + + + + + + + + + + + 允许多个主库 + + + + + + + + + + + 主库无额外开销 + + + + + + + + + + + 不等待多个服务器 + + + 关闭同步时 + + + + + + + + 主库失效时绝不丢失数据 + + + 启用同步时 + + + + + + + + 备库接受只读查询 + + + 启用热备时 + + + + + + + + 每个表粒度 + + + + + + + + + + + 不需要冲突解决 + + + + + + + + + + + +
+ + + 有一些方案不适合上述的类别: + + + + + + 数据分区 + + + + 数据分区会把表拆分成多个数据集。每个数据集只能由一台服务器修改。例如,可以按办公室划分数据,如伦敦和巴黎,每个办公室各有一台服务器。如果有必要执行组合伦敦和巴黎数据的查询,应用可以同时查询两台服务器,或者可以使用主库/备库复制,在每台服务器上保留其他办公室数据的只读副本。 + + + + + + 多服务器并行查询执行 + + + + 上述许多方案都允许多台服务器处理多个查询,但没有一种允许单个查询同时使用多台服务器来更快完成。这种方案允许多台服务器在同一个查询上并发工作。通常的做法是把数据分散到各台服务器上,让每台服务器执行该查询中属于自己的部分,然后把结果返回给一台中心服务器,由它汇总结果并返回给用户。这种方案也可以使用PL/Proxy工具集来实现。 + + + + + + + +
+ + + + 日志传送备库 + + + + 持续归档可用于创建一种高可用性(HA)集簇配置,其中有一个或多个备库随时准备在主库失效时接管操作。这种能力通常称为温备日志传送。 + + + + 主库和备库协同工作以提供这种能力,不过两者之间只是松耦合。主库运行在持续归档模式下,而每台备库都运行在持续恢复模式下,从主库读取 WAL 文件。启用这种能力不需要修改数据库表,因此与其他一些复制方案相比,它的管理开销较低。这种配置对主库的性能影响也相对较小。 + + + + 将 WAL 记录直接从一台数据库服务器移动到另一台数据库服务器,通常称为日志传送。PostgreSQL 通过一次传送一个文件(WAL 段)中的 WAL 记录来实现基于文件的日志传送。WAL 文件(16MB)可以轻松且低成本地传送到任意距离,无论是邻近系统、同一站点的另一台系统,还是地球另一端的系统。该技术所需的带宽取决于主库的事务速率。基于记录的日志传送粒度更细,会通过网络连接增量地流式传输 WAL 变更(见 )。 + + + + 需要注意的是,日志传送是异步的,也就是说 WAL 记录是在事务提交之后才被传送的。因此,如果主库发生灾难性故障,就会存在一个数据丢失窗口;尚未传送的事务将会丢失。对于基于文件的日志传送,可以通过使用 archive_timeout 参数来限制这个数据丢失窗口,它可以设置为低至数秒。不过,如此低的设置会显著增加文件传送所需的带宽。流复制(见 )允许把数据丢失窗口缩得更小。 + + + + 恢复性能已经足够好,因此一旦备库被激活,通常只需片刻就能达到完全可用状态。因此,这种配置被称为温备配置,它提供了高可用性。从归档的基础备份中恢复服务器并向前重放则需要更长时间,因此这种技术只能用于灾难恢复,而不是高可用性。备库还可以用于只读查询,这种情况下它被称为热备服务器。更多信息见 。 + + + + 温备 + + + + PITR 备库 + + + + 备库 + + + + 日志传送 + + + + 见证服务器 + + + + STONITH + + + + 规划 + + + 通常,最好让主库和备库尽可能相似,至少从数据库服务器的角度看应如此。尤其是,与表空间相关的路径名会原样传递,因此如果使用该特性,主库和备库必须为表空间配置完全相同的挂载路径。请记住,如果在主库上执行 ,则它所需的任何新挂载点都必须在执行该命令之前先在主库和所有备库上创建好。硬件不必完全相同,但经验表明,在应用和系统的整个生命周期内,维护两个相同的系统要比维护两个不同的系统更容易。无论如何,硬件架构必须相同 — 例如,从 32 位系统向 64 位系统传送日志是不可行的。 + + + + 一般来说,不能在运行不同主版本 PostgreSQL 的服务器之间传送日志。PostgreSQL 全球开发组的策略是在次版本升级期间不改变磁盘格式,因此主库和备库运行不同次版本通常也能正常工作。不过,这方面并没有正式支持,因此仍建议主库和备库尽量保持在相同的发行级别。当升级到新的次版本时,最安全的策略是先升级备库 — 新的次版本更有可能兼容读取前一个次版本生成的 WAL 文件,反过来则未必。 + + + + + + 备库操作 + + 在备库模式下,服务器会持续应用从主库收到的 WAL。备库可以从 WAL 归档读取 WAL(参见 ),也可以通过 TCP 连接直接从主库读取(流复制)。备库还会尝试恢复其集簇 pg_xlog 目录中找到的所有 WAL。这通常发生在服务器重启后,此时备库会重新重放重启前从主库接收的 WAL;不过,也可以随时手动将文件复制到 pg_xlog 中,让备库重放它们。 + + 启动时,备库首先调用 restore_command,恢复归档位置中所有可用的 WAL。当读到归档中可用 WAL 的末尾,且 restore_command 失败后,它会尝试恢复 pg_xlog 目录中的所有可用 WAL。如果这也失败,且已配置流复制,备库就会尝试连接主库,从归档或 pg_xlog 中找到的最后一条有效记录开始接收 WAL 流。如果连接失败、未配置流复制,或连接后来断开,备库就会返回第一步,再次尝试从归档恢复文件。这种依次从归档、pg_xlog 和流复制重试的循环,会一直持续到服务器停止,或触发文件触发故障切换。 + + 运行 pg_ctl promote 或发现触发文件(trigger_file)时,服务器会退出备库模式,切换到正常运行。在故障切换前,会恢复归档或 pg_xlog 中能立即获取的所有 WAL,但不会尝试连接主库。 + + + + 为备库准备主库 + + + 如 所述,在主库上设置持续归档,将日志归档到备库可访问的归档目录。即使主库宕机,该归档位置也应该对备库可访问;也就是说,它应位于备库本身或另一台可信服务器上,而不是位于主库上。 + + + + 如果想使用流复制,就需要在主库上配置认证,以允许来自备库的复制连接;也就是说,创建一个角色,并在 pg_hba.conf 中添加一个或多个把数据库字段设置为 replication 的合适项。还要确保主库配置文件中的 max_wal_senders 被设置为足够大的值。如果要使用复制槽,还应确保 max_replication_slots 也设置得足够高。 + + + + 如 所述,获取一个基础备份来引导备库。 + + + + + 设置备库 + + 要配置备库,请恢复从主库取得的基础备份(参见 )。在备库的集簇数据目录中创建恢复命令文件 recovery.conf,并启用 standby_mode。将 restore_command 设为一个从 WAL 归档复制文件的简单命令。如果计划使用多个备库提供高可用,请将 recovery_target_timeline 设为 latest,使备库能够跟随故障切换到另一个备库时发生的时间线变化。 + + + 不要将 pg_standby 或类似工具与这里介绍的内置备库模式一起使用。如果文件不存在,restore_command 应立即返回;服务器会在必要时重试该命令。有关 pg_standby 等工具的使用,参见 + + + 如果希望使用流复制,请在 primary_conninfo 中填写 libpq 连接字符串,包含连接主库所需的主机名(或 IP 地址)及其他信息。如果主库使用密码认证,还需要在 primary_conninfo 中指定密码。 + + + 如果你是为了高可用目的设置备库,那么也应像主库一样设置 WAL 归档、连接和认证,因为故障切换后该备库将作为主库工作。 + + + + 如果使用 WAL 归档,可以借助 参数删除备库不再需要的文件,以尽量缩小归档大小。pg_archivecleanup 工具专门设计用于在典型的单备库配置中与 archive_cleanup_command 配合使用,见 。不过请注意,如果你还把归档用于备份目的,即使这些文件对备库已经不再需要,也必须至少保留从最新基础备份恢复所需的那些文件。 + + + 一个简单的 recovery.conf 示例如下: +standby_mode = 'on' +primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' +restore_command = 'cp /path/to/archive/%f %p' +archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r' + + + + + 备库的数量可以任意多,但如果使用流复制,请确保主库上的 max_wal_senders 设置得足够高,以允许它们同时连接。 + + + + + + 流复制 + + + 流复制 + + + + 与基于文件的日志传送相比,流复制可以让备库保持得更接近最新状态。备库连接到主库,主库在生成 WAL 记录时就会把它们流式发送给备库,而不必等待 WAL 文件被写满。 + + + + 默认情况下流复制是异步的(见 ),在这种情况下主库上提交一个事务与该变化在备库上变得可见之间存在短暂的延迟。不过这种延迟比基于文件的日志传送方式中要小得多,在备库的能力足以跟得上负载的前提下,延迟通常低于一秒。在流复制中,不需要 archive_timeout 来缩减数据丢失窗口。 + + + + 如果你使用流复制,但没有启用基于文件的持续归档,服务器可能会在备库收到旧的 WAL 段之前就把它们回收掉。如果发生这种情况,备库就需要重新通过新的基础备份进行初始化。可以通过把 wal_keep_segments 设置得足够大,以确保 WAL 段不会过早被回收,或者为备库配置一个复制槽,从而避免这种情况。如果配置了一个备库可访问的 WAL 归档,就不需要这些方案,因为只要归档保留了足够多的段,备库始终可以利用归档追赶上来。 + + + 要使用流复制,请先按 所述配置基于文件的日志传送备库。将这种备库转为流复制备库的关键步骤,是在 recovery.conf 文件中设置 primary_conninfo,使其指向主库。在主库上设置 和认证选项(参见 pg_hba.conf),使备库能够连接主库的 replication 伪数据库(参见 )。 + + + 在支持 keepalive 套接字选项的系统上,设置 有助于主库迅速注意到断开的连接。 + + + + 设置来自备库的最大并发连接数(详见 )。 + + + 备库启动后,如果正确设置了 primary_conninfo,备库会在重放归档中所有可用的 WAL 文件后连接主库。如果连接成功,就会在备库上看到 walreceiver 进程,在主库上看到对应的 walsender 进程。 + + + 认证 + 务必正确设置复制访问权限,使只有受信任的用户才能读取 WAL 流,因为很容易从中提取需要权限才能访问的信息。备库必须以超级用户或具有 REPLICATION 权限的账户向主库认证。建议为复制创建专用用户账户,并授予 REPLICATIONLOGIN 权限。虽然 REPLICATION 权限赋予的权限很高,但它不允许用户修改主库系统上的任何数据,而 SUPERUSER 权限允许这样做。 + + + 复制的客户端认证由 pg_hba.conf 中的一条记录控制,该记录需要把 replication 指定在 database 字段中。例如,如果备库运行在主机 IP 192.168.1.100 上,并且用于复制的账户名为 foo,管理员可以在主库上的 pg_hba.conf 文件中加入下列行: + + +# Allow the user "foo" from host 192.168.1.100 to connect to the primary +# as a replication standby if the user's password is correctly supplied. +# +# TYPE DATABASE USER ADDRESS METHOD +host replication foo 192.168.1.100/32 md5 + + + 主库的主机名、端口号、连接用户名和密码在 recovery.conf 文件中指定。也可以将密码设置在备库的 ~/.pgpass 文件中(将 replication 指定为 database 字段的值)。例如,如果主库所在主机的 IP 地址为 192.168.1.50,端口为 5432,用于复制的账户名为 foo,密码为 foopass,管理员可以将以下行添加到 recovery.conf 文件(位于备库)中: +# The standby connects to the primary that is running on host 192.168.1.50 +# and port 5432 as the user "foo" whose password is "foopass". +primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' + + + + + + 监控 + + 流复制的一个重要健康指标,是主库上已经生成但尚未在备库上应用的 WAL 记录量。你可以通过比较主库上的当前 WAL 写入位置和备库收到的最后一个 WAL 位置来计算这种滞后。这些位置分别可以用主库上的 pg_current_xlog_location 和备库上的 pg_last_xlog_receive_location 取得(详见 )。备库上的最后一个 WAL 接收位置也会显示在 WAL 接收进程的进程状态中,即通过 ps 命令显示的状态(详见 )。 + + 可以通过 pg_stat_replication 视图获取 WAL 发送进程列表。pg_current_xlog_locationsent_location 字段相差较大,可能表示主库负载很高;而 sent_location 与备库上 pg_last_xlog_receive_location 之间存在差异,则可能表示网络延迟或备库负载很高。 + + + + + 复制槽 + + 复制槽 + 流复制 + + + 复制槽提供了一种自动化方法,以确保主库在所有备库都收到 WAL 段之前不会删除它们,并且即使备库处于断开状态,主库也不会删除那些一旦删除就可能导致恢复冲突的行。 + + 除了使用复制槽,也可以通过 防止旧 WAL 段被删除,或者使用 将这些段保存在归档中。不过,这些方法通常会保留多于实际所需的 WAL 段,而复制槽只保留已知必需的段数。这些方法的优点是能限制 pg_xlog 的空间需求;目前使用复制槽还无法做到这一点。 + 类似地, 可以保护相关行,避免它们被清理删除,但前者在备库未连接期间无法提供保护,后者则通常需要设为较大的值才能提供充分保护。复制槽克服了这些缺点。 + + 查询和管理复制槽 + + 每个复制槽都有一个名称,该名称可以包含小写字母、数字和下划线字符。 + + + 现有复制槽及其状态可以在 + pg_replication_slots + 视图中查看。 + + + 复制槽可以通过流复制协议(见)或者 SQL 函数(见)创建和删除。 + + + + 配置示例 + 可以这样创建复制槽: +postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot'); + slot_name | xlog_position +-------------+--------------- + node_a_slot | + +postgres=# SELECT slot_name, slot_type, active FROM pg_replication_slots; + slot_name | slot_type | active +-------------+-----------+-------- + node_a_slot | physical | f +(1 row) +要配置备库使用此复制槽,需要将 primary_slot_name 配置在备库的 recovery.conf 中。下面是一个简单的示例: +standby_mode = 'on' +primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' +primary_slot_name = 'node_a_slot' + + + + + + + 级联复制 + + + 级联复制 + + + + 级联复制特性允许一台备库接受复制连接,并像中继器一样把 WAL 记录流式发送给其他备库。这可以用来减少直接连接到主库的连接数,并使站点间的带宽开销最小化。 + + + + 一台同时扮演接收者和发送者角色的备库称为级联备库。与主库连接更直接(经过更少级联备库)的备库称为上游服务器,而距离更远的备库称为下游服务器。级联复制并不限制下游服务器的数量和拓扑,不过每台备库只连接到一台上游服务器,而这条链路最终都会通向同一台主库。 + + + + 级联备库不仅发送从主库接收到的 WAL 记录,也会发送那些从归档中恢复的记录。因此,即使某条上游复制连接被中断,只要仍有新的 WAL 记录可用,下游的流复制就会继续。 + + + + 级联复制目前是异步的。同步复制(见)设置当前对级联复制无影响。 + + + + 热备反馈会向上传播,无论级联拓扑如何。 + + + 如果某个上游备库被提升为新主库,只要 recovery_target_timeline 设为 'latest',下游服务器就会继续从新主库接收流。 + + + 要使用级联复制,需要把级联备库设置为能够接受复制连接(也就是设置 ,并配置 基于主机的认证)。你还需要把下游备库中的 primary_conninfo 设置为指向级联备库。 + + + + + 同步复制 + + + 同步复制 + + + + PostgreSQL 的流复制默认是异步的。如果主库崩溃,则某些已提交的事务可能尚未复制到备库,从而导致数据丢失。数据丢失量与故障切换时的复制延迟成正比。 + + + + 同步复制能够确认一个事务所做的全部修改已经被传送到一台或多台同步备库。这扩展了事务提交所提供的标准持久性级别。在计算机科学理论中,这种保护级别被称为 2-safe 复制;而当synchronous_commit被设置为remote_write时,则称为 group-1-safe(group-safe 和 1-safe)。 + + + + 请求同步复制时,每个写事务的提交都会等待,直到收到确认,表明该提交已被写入主库和备库磁盘上的事务日志。数据唯一可能丢失的情况,是主库和备库同时崩溃。这可以提供更高的持久性级别,不过前提是系统管理员必须谨慎地部署和管理这两台服务器。等待确认会增强用户对服务器崩溃时更改不会丢失的信心,但也必然会增加请求事务的响应时间。最短等待时间是主库与备库之间的往返时间。 + + + + 只读事务和事务回滚不需要等待备库的回应。子事务提交也不需要等待备库响应,只有顶层提交才需要等待。数据装载或索引构建等长时间运行的动作,直到最终提交时才会等待。所有两阶段提交操作都需要等待提交,包括准备和提交两个阶段。 + + + + + 基本配置 + + + 一旦流复制已经配置好,配置同步复制只需要额外一步:必须把设置为非空值。synchronous_commit也必须设置为on,但由于这是默认值,通常无需更改(见)。这样的配置会导致每次提交都等待确认,以保证备库已经把提交记录写入持久存储。synchronous_commit可以由单个用户设置,因此既可以在配置文件中配置,也可以针对特定用户或数据库配置,或者由应用动态配置,从而在每事务级别控制持久性保证。 + + + + 当提交记录已经在主库上写入磁盘之后,WAL 记录就会被发送到备库。每当新的一批 WAL 数据被写入磁盘时,备库就会发送回复消息,除非备库上的wal_receiver_status_interval被设置为零。如果synchronous_commit被设置为remote_apply,那么备库会在提交记录被重放、该事务变得可见时发送回复消息。如果根据主库上的synchronous_standby_names优先级列表,该备库被选为同步备库,那么它发出的回复会与其他同步备库的回复一起,用于决定何时释放那些正在等待确认提交记录已被收到的事务。这些参数允许管理员指定哪些备库应作为同步备库。注意,同步复制的配置主要在主库上进行。被命名的备库必须直接连接到主库;主库并不知道使用级联复制的下游备库。 + + + + 把synchronous_commit设置为remote_write,会使每次提交都等待,直到备库确认已经收到提交记录并把它写入自己的操作系统,但不会等待数据被刷到备库磁盘上。与on相比,这种设置提供较弱一些的持久性保证:在操作系统崩溃时,备库可能丢失数据,尽管在PostgreSQL崩溃时不会。不过在实践中,这是一种有用的设置,因为它可以降低事务响应时间。只有当主库和备库都崩溃,并且主库数据库同时发生损坏时,才可能发生数据丢失。 + + + + 把synchronous_commit设置为remote_apply,会使每次提交都等待,直到当前同步备库报告它们已经重放了该事务,从而使它对用户查询可见。在简单场景下,这可以支持具备因果一致性的负载均衡。 + + + + 如果请求快速关闭,用户将停止等待。不过,与使用异步复制时一样,在所有尚未传送的 WAL 记录传输到当前已连接的备库之前,服务器不会完全关闭。 + + + + + + 多个同步备库 + + + 同步复制支持一台或多台同步备库;事务将一直等待,直到所有被视为同步的备库确认已收到其数据。事务需要等待多少台同步备库的回复,由synchronous_standby_names指定。该参数还指定一个备库名称列表,它决定了每台备库被选为同步备库的优先级。列表中出现得越早的备库优先级越高,并会被视为同步备库。该列表中更靠后的备库则是潜在的同步备库。如果当前某个同步备库因任何原因断开连接,它将立刻由下一个优先级最高的备库替代。 + + + 多个同步备库的synchronous_standby_names示例如下: + +synchronous_standby_names = '2 (s1, s2, s3)' + + 在这个例子中,如果四台备库s1s2s3s4都在运行,则s1s2会被选为同步备库,因为它们的名字在备库名称列表中出现得更早。s3是潜在的同步备库,当s1s2中的任意一台失效时,它就会接替其角色。由于s4的名称不在列表中,因此它是异步备库。 + + + + + 性能规划 + + + 同步复制通常要求对备库进行仔细规划和部署,才能保证应用具有可接受的性能。等待本身不会占用系统资源,但事务锁会一直保持到传输得到确认。因此,若不谨慎使用同步复制,数据库应用的性能会因为响应时间增加和争用加剧而下降。 + + + + PostgreSQL允许应用开发者通过复制来指定所需的持久性级别。这可以在整个系统级别指定,也可以针对特定用户、特定连接,甚至单个事务指定。 + + + + 例如,一个应用负载可能由如下部分组成:10% 的变更是重要的客户资料,而 90% 的变更是不太重要、即使丢失业务也较容易承受的数据,例如用户之间的聊天消息。 + + + + 通过在应用级别(在主库上)指定同步复制选项,我们可以只对最重要的变更提供同步复制,而不会拖慢大部分工作负载。应用级别选项是让高性能应用获得同步复制收益的一种重要而实用的工具。 + + + + 你应当确保网络带宽高于 WAL 数据的生成速率。 + + + + + + 高可用规划 + + + synchronous_standby_names指定了当synchronous_commit设置为onremote_applyremote_write时,事务提交需要等待其响应的同步备库的数量和名称。如果任意一台同步备库崩溃,此类事务提交就可能永远无法完成。 + + + + 对于高可用而言,最好的办法是确保你始终保有所要求数量的同步备库。这可以通过在synchronous_standby_names中命名多个潜在同步备库来实现。列表中出现较早的备库将被用作同步备库。列在它们之后的备库,会在当前同步备库失效时接替其角色。 + + + + 当一台备库第一次连接到主库时,它还没有正确同步。这种状态称为catchup模式。一旦备库与主库之间的滞后第一次变为零,它就会进入实时的streaming状态。备库刚创建之后,追赶阶段可能会持续较长时间。如果备库被关闭,则追赶阶段会随着它停机时间的延长而变长。只有在到达streaming状态后,备库才能成为同步备库。 + + + + 如果主库在提交正等待确认时重启,这些等待中的事务会在主库恢复后被标记为已完全提交。无法确定在主库崩溃时,所有备库是否已经收到全部待传送的 WAL 数据。因此,某些事务可能不会在备库上显示为已提交,即使它们在主库上显示为已提交。我们所提供的保证是:只有在确认 WAL 数据已经被所有同步备库安全接收之后,应用才会收到事务成功提交的显式确认。 + + + + 如果你确实无法维持所要求数量的同步备库,那么就应当在synchronous_standby_names中减少事务提交需要等待其响应的同步备库数量(或者禁用它),然后在主库上重新加载配置文件。 + + + + 如果主库与剩余的备库隔离开了,你应当故障切换到那些剩余备库中最佳的候选者。 + + + 如果需要在事务等待期间重新创建备库,请确保在 synchronous_commit = off 的会话中运行 pg_start_backup() 和 pg_stop_backup() 命令,否则这些请求会一直等待备库出现。 + + + + + + 在备库中持续归档 + + + 持续归档 + 在备库中 + + + + 当在备库中使用持续 WAL 归档时,有两种不同的场景:WAL 归档可以由主库和备库共享,或者备库可以拥有自己的 WAL 归档。当备库拥有自己的 WAL 归档时,应把archive_mode设置为always,这样备库就会为它接收到的每个 WAL 段调用归档命令,无论该 WAL 段是通过从归档恢复得到的,还是通过流复制得到的。共享归档也可以类似处理,但archive_command必须检查正在归档的文件是否已经存在,以及已有文件的内容是否完全相同。这就要求在archive_command中更加小心:既不能用不同内容覆盖现有文件,又要在同一个文件被归档两次且内容完全相同时返回成功。如果两台服务器同时尝试归档同一个文件,还必须确保整个过程不存在竞争条件。 + + + + 如果archive_mode被设置为on,那么归档器在恢复期间或备库模式下不会启用。如果备库被提升,它会在提升后开始归档,但不会归档任何不是由它自己生成的 WAL 或时间线历史文件。要在归档中获得完整的一系列 WAL 文件,就必须确保所有 WAL 在到达备库之前已经被归档。对于基于文件的日志传送,这天然成立,因为备库只能恢复归档中找到的文件;但在启用流复制时则不是这样。当服务器不处于恢复模式时,onalways模式之间没有区别。 + + + + + + 故障切换 + + + 如果主库失效,备库就应该开始执行故障切换过程。 + + + + 如果备库失效,则不需要发生故障切换。如果备库能够重新启动,即使是在稍后某个时间点,恢复过程也可以立即重新开始,从而利用可重启恢复的优势。如果备库无法重新启动,则应创建一个全新的备库实例。 + + + + 如果主库失效,而备库成为新的主库,那么旧主库之后如果重新启动,你必须有一种机制通知它,它已经不再是主库。这有时被称为STONITH(Shoot The Other Node In The Head),它对于避免两个系统都认为自己是主库的情况至关重要,因为那种情况会导致混乱,并最终造成数据丢失。 + + + + 许多故障切换系统只使用两个系统,即主库和备库,并通过某种心跳机制连接它们,以持续验证两者之间的连通性以及主库的可用性。也可以使用第三个系统(称为见证服务器)来防止某些不恰当的故障切换,但除非设置得足够谨慎并经过严格测试,否则额外增加的复杂性可能并不值得。 + + + + PostgreSQL并不提供用于识别主库故障并通知备库的系统软件。现在已经存在许多这样的工具,并且它们通常能很好地与成功故障切换所需的操作系统设施整合在一起,例如 IP 地址迁移。 + + + + 一旦故障切换到备库,系统中就只剩下一台服务器在运行。这被称为退化状态。原来的备库现在成为主库,而原来的主库已经停机,并且可能持续停机。要恢复到正常运行状态,就必须重新创建一台备库:要么在原主库恢复后在其上重建,要么在第三台可能是全新的服务器上重建。在大型集簇上,可以使用工具来加快这一过程。一旦完成,就可以认为主库和备库已经交换了角色。有些人会选择使用第三台服务器,在新的备库重建完成之前为新的主库提供后备支持,但显然这会让系统配置和操作流程更加复杂。 + + + + 因此,从主库切换到备库可以很快,但重新准备故障切换集簇仍然需要时间。定期在主库与备库之间进行切换是有益的,因为它允许每个系统定期停机维护。这也相当于对故障切换机制进行测试,以确保真正需要它时它能够正常工作。建议编写书面的管理操作规程。 + + + 要触发日志传送备库的故障切换,可以运行 pg_ctl promote,或按照 recovery.conftrigger_file 设置指定的文件名和路径创建触发文件。如果计划使用 pg_ctl promote 进行故障切换,就不需要设置 trigger_file。如果所配置的报表服务器只是用来分担主库的只读查询,而不用于高可用,则不需要提升它。 + + + + 日志传送的另一种方法 + + 除了前几节介绍的内置备库模式,也可以使用一个轮询归档位置的 restore_command。在 8.4 及更早版本中,这是唯一可用的方法。采用这种配置时,应关闭 standby_mode,因为备库运行所需的轮询由你自己实现。其参考实现参见 模块。 + + 注意,在这种模式下,服务器每次应用一整个 WAL 文件。因此,如果使用备库处理查询(参见热备),主库上的操作与备库上能看到该操作的结果之间,会有一段延迟,其长度相当于写满一个 WAL 文件所需的时间。可以用 archive_timeout 缩短这一延迟。还要注意,这种方法不能与流复制结合使用。 + + 主库和备库上执行的操作都是普通的持续归档和恢复任务。两台数据库服务器之间唯一的联系,就是它们共享的 WAL 文件归档:主库写入归档,备库从归档读取。必须确保不同主库的 WAL 归档不会混在一起或弄错。如果归档仅用于备库运行,则无需保留很大的归档。 + + 让这两台松散耦合的服务器协同工作的关键,只是备库上的 restore_command:当请求下一个 WAL 文件时,它会等待主库提供该文件。restore_command 在备库的 recovery.conf 文件中指定。正常恢复处理会从 WAL 归档请求文件,如果文件不可用,就报告失败。对备库处理而言,下一个 WAL 文件尚不可用是正常情况,因此备库必须等待它出现。对于以 .history 结尾的文件,则无需等待,必须返回非零返回码。可以编写一个自定义脚本,循环检查下一个 WAL 文件是否存在,从而实现会等待的 restore_command。还必须提供触发故障切换的方法,用来中断 restore_command、跳出循环,并向备库返回文件未找到错误。这会结束恢复,随后备库就会作为普通服务器启动。 + + 一个合适的 restore_command 的伪代码如下: +triggered = false; +while (!NextWALFileReady() && !triggered) +{ + sleep(100000L); /* wait for ~0.1 sec */ + if (CheckForExternalTrigger()) + triggered = true; +} +if (!triggered) + CopyWALFileForRecovery(); + + + + 模块提供了会等待的 restore_command 的可用示例。应参考它来正确实现上述逻辑,也可以按需扩展,以支持特定配置和环境。 + + 触发故障切换的方法是规划和设计的重要部分。一个可能的选择是 restore_command 命令。每个 WAL 文件都会执行一次该命令,但运行 restore_command 的进程针对每个文件单独创建和结束,因此不存在守护进程或服务器进程,也无法使用信号或信号处理程序。所以,restore_command 不适合触发故障切换。可以使用简单的超时机制,尤其是在已知主库 archive_timeout 设置的情况下配合使用。不过,这容易出错,因为网络问题或主库繁忙就可能导致故障切换。如果能够安排,显式创建触发文件之类的通知机制是理想的方式。 + + + 实现 + + 使用这种替代方法配置备库的简要流程如下。各步骤的完整细节,参见所注明的前面章节。 + + 尽可能将主库和备库系统配置得相同,包括安装两个完全相同、发行版本一致的 PostgreSQL 副本。 + + + 配置持续归档,将主库的 WAL 归档到备库上的一个目录。确保在主库上正确设置 (参见 )。 + + + 制作主库的基础备份(参见 ),并将这些数据装载到备库上。 + + + 在备库上从本地 WAL 归档开始恢复,在 recovery.conf 中指定前面所述的会等待的 restore_command(参见 )。 + + + + + 恢复过程将 WAL 归档视为只读,因此,WAL 文件一旦复制到备库系统,就可以在备库读取它的同时,将其复制到磁带上。这样,既能运行备库提供高可用,又能保存文件用于更长期的灾难恢复。 + + 为了测试,可以在同一个系统上同时运行主库和备库。这不会对服务器的稳健性带来有意义的提升,也不能称为高可用。 + + + + 基于记录的日志传送 + + 也可以使用这种替代方法实现基于记录的日志传送,但需要自行开发,而且只有整个 WAL 文件传送完成后,更改才会对热备上的查询可见。 + + 外部程序可以调用 pg_xlogfile_name_offset() 函数(参见 ),取得当前 WAL 末尾所在的文件名及其在文件中的精确字节偏移。随后就可以直接访问 WAL 文件,将上次已知的 WAL 末尾到当前末尾之间的数据复制到备库。采用这种方法,可能丢失数据的时间窗口就是复制程序的轮询周期,可以很短,而且不会因为强制归档未写满的段文件而浪费带宽。注意,备库的 restore_command 脚本只能处理完整的 WAL 文件,因此这些增量复制的数据通常不会提供给备库使用。它们只有在主库失效时才有用 — 此时,在允许备库启动前,将最后一个不完整的 WAL 文件交给备库。要正确实现这一过程,restore_command 脚本必须与数据复制程序配合。 + + PostgreSQL 9.0 开始,可以使用流复制(参见 ),更省力地获得同样的收益。 + + + + + 热备 + + + 热备 + + + + 热备是用来描述服务器在归档恢复或备库模式下仍然可以接受连接并运行只读查询的能力的术语。这对于复制用途以及把备份以极高精度恢复到目标状态都很有用。术语热备还指服务器能够在用户持续运行查询和/或保持连接不断开的同时,从恢复状态切换到正常运行状态的能力。 + + + + 在热备模式下运行查询与正常查询操作类似,不过正如下文所述,在使用和管理上存在一些差异。 + + + + 用户概览 + + + 当备库上的参数被设置为真时,一旦恢复把系统带到一致状态,它就会开始接受连接。所有这类连接都严格是只读的,甚至不能写入临时表。 + + + + 备库上的数据需要一些时间才能从主库到达,因此主库和备库之间会有可测量的延迟。因此,在主库和备库上几乎同时运行同一查询,可能会返回不同的结果。我们说备库上的数据与主库是最终一致的。一旦某个事务的提交记录在备库上被重放,该事务所做的修改就会对备库上之后取得的所有新快照可见。快照可以在每个查询开始时取得,也可以在每个事务开始时取得,这取决于当前的事务隔离级别。详见。 + + + + 在热备期间启动的事务可以发出下列命令: + + + + + 查询访问:SELECTCOPY TO + + + + + 游标命令:DECLAREFETCHCLOSE + + + + 参数:SHOWSETRESET + + + + 事务管理命令: + + + + BEGINENDABORTSTART TRANSACTION + + + + + SAVEPOINTRELEASEROLLBACK TO SAVEPOINT + + + + + EXCEPTION块和其他内部子事务 + + + + + + + + LOCK TABLE,不过只限于显式指定下列模式之一时: + ACCESS SHAREROW SHAREROW EXCLUSIVE + + + + + 计划和资源:PREPAREEXECUTE、 + DEALLOCATEDISCARD + + + + + 插件和扩展:LOAD + + + + + UNLISTEN + + + + + + + 在热备期间启动的事务永远不会被分配事务 ID,也不能写入系统预写式日志。因此,下列动作都会产生错误消息: + + + + + 数据操纵语言(DML):INSERT、 + UPDATEDELETE、 + COPY FROM、 + TRUNCATE。请注意,恢复期间不存在任何允许执行触发器的动作。这个限制甚至适用于临时表,因为不分配事务 ID 就无法读取或写入表行,而目前热备环境中无法分配事务 ID。 + + + + + 数据定义语言(DDL):CREATE、 + DROPALTERCOMMENT。这个限制甚至适用于临时表,因为执行这些操作需要更新系统目录表。 + + + + + SELECT ... FOR SHARE | UPDATE,因为不更新底层数据文件就无法获取行锁。 + + + + + 作用在SELECT语句上、会生成 DML 命令的规则。 + + + + + LOCK,如果它显式请求高于 ROW EXCLUSIVE MODE 的模式。 + + + + + 简写默认形式的LOCK,因为它请求的是 ACCESS EXCLUSIVE MODE。 + + + + + 显式设置为非只读状态的事务管理命令: + + + + BEGIN READ WRITE、 + START TRANSACTION READ WRITE + + + + + SET TRANSACTION READ WRITE、 + SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE + + + + + SET transaction_read_only = off + + + + + + + + 两阶段提交命令:PREPARE TRANSACTION、 + COMMIT PREPAREDROLLBACK PREPARED, + 因为即使是只读事务,在准备阶段(两阶段提交的第一阶段)也需要写入 WAL。 + + + + + 序列更新:nextval()setval() + + + + + LISTENNOTIFY + + + + + + + 在正常运行中,只读事务允许使用LISTENNOTIFY,因此热备会话受到的限制要比普通只读会话稍微更严格一些。将来的版本中,这些限制中的某些可能会被放宽。 + + + + 在热备期间,参数transaction_read_only始终为真,并且不能更改。不过,只要不尝试修改数据库,热备期间的连接行为与其他数据库连接大致相同。如果发生故障切换或计划内切换,数据库将切换到正常处理模式。服务器切换模式时,会话仍会保持连接。一旦热备结束,就可以发起读写事务(即使该会话是在热备期间开始的)。 + + + 用户可以执行 SHOW transaction_read_only,判断自己的会话是否只读。此外,还有一组函数()可用于获取备库信息。借助它们,可以编写感知数据库当前状态的程序,用来监控恢复进度,或编写将数据库恢复到特定状态的复杂程序。 + + + + 处理查询冲突 + + + 主库和备库在许多方面都是松耦合的。主库上的动作会对备库产生影响,因此它们之间可能出现负面交互或冲突。最容易理解的冲突是性能:如果主库上正在执行一次大规模数据装载,那么备库上也会产生类似的 WAL 记录流,因此备库查询可能会争用系统资源,例如 I/O。 + + + + 热备还可能发生其他类型的冲突。这些冲突属于硬冲突,因为查询可能需要被取消,并且在某些情况下还需要断开会话来解决冲突。系统为用户提供了若干种处理这些冲突的方法。冲突场景包括: + + + + + 主库上获取的 Access Exclusive 锁,包括显式的LOCK命令和各种DDL操作,会与备库查询中的表访问发生冲突。 + + + + + 主库上删除表空间,会与备库查询把该表空间用于临时工作文件发生冲突。 + + + + + 主库上删除数据库,会与备库上连接到该数据库的会话发生冲突。 + + + + + 来自 WAL 的清理记录在应用时,会与那些快照仍然能够看到将被删除行的备库事务发生冲突。 + + + + + 来自 WAL 的清理记录在应用时,会与备库上访问目标页面的查询发生冲突,而不管待删除的数据是否可见。 + + + + + + + 在主库上,这些情况只会导致等待;用户可以选择取消冲突动作中的任意一方。但是在备库上没有这种选择:已经被写入 WAL 的动作早已在主库上发生,因此备库在应用它时不能失败。此外,让 WAL 应用无限期等待通常是很不可取的,因为备库的状态会越来越落后于主库。因此,系统提供了一种机制,用来强制取消那些与即将应用的 WAL 记录发生冲突的备库查询。 + + + + 一个典型例子是,主库上的管理员在某个表上执行DROP TABLE,而备库上正有查询访问该表。显然,如果DROP TABLE在备库上被应用,该备库查询就无法继续。如果这种情况发生在主库上,DROP TABLE会等待直到其他查询结束。但在主库上执行DROP TABLE时,主库并不知道备库上正在运行哪些查询,因此它不会等待这些备库查询。于是,WAL 变更记录会在备库查询仍在运行时到达备库,从而引发冲突。备库要么延迟应用这条 WAL 记录(以及之后的所有记录),要么取消冲突查询,以便应用DROP TABLE。 + + + + 当冲突查询很短时,通常最好通过稍微延迟 WAL 应用来让它完成;但 WAL 应用长时间延迟通常并不可取。因此,取消机制提供了这两个参数,用于定义 WAL 应用允许的最大延迟。一旦应用新收到的 WAL 数据所花费的时间超过相应延迟设置,冲突查询就会被取消。之所以有两个参数,是为了分别针对从归档读取 WAL 数据的场景(即从基础备份进行初始恢复,或者追赶一台已经远远落后的备库)以及通过流复制读取 WAL 数据的场景指定不同的延迟值。 + + + + 如果一台备库主要用于高可用性,那么最好把这些延迟参数设置得较短,这样服务器就不会因为备库查询导致的延迟而远远落后于主库。但是,如果该备库的用途是执行长时间运行的查询,那么较高甚至无限的延迟值可能更合适。不过要记住,如果某个长时间运行的查询延迟了 WAL 记录的应用,它也可能使备库上的其他会话看不到主库上的最新变更。 + + + + 一旦max_standby_archive_delaymax_standby_streaming_delay指定的延迟被超越,冲突查询将被取消。这通常仅导致一个取消错误,尽管在重放一个DROP DATABASE的情况下整个冲突会话都将被终止。另外,如果冲突发生在一个被空闲事务持有的锁上,该冲突会话会被终止(这种行为可能在未来被改变)。 + + + 被取消的查询可以立即重试(当然,要先开始一个新事务)。由于查询取消取决于正在重放的 WAL 记录的性质,被取消的查询再次执行时完全可能成功。 + + + 请记住,延迟参数要与备库收到 WAL 数据之后经过的时间进行比较。因此,留给备库上任何一个查询的宽限期从不会超过延迟参数,并且如果备库已经由于等待之前的查询完成而落后或者因为过重的更新负载而无法跟上主库,宽限期可能会更少。 + + + + 备库查询与 WAL 重放发生冲突的最常见原因是过早清理。正常情况下,当不再有事务需要看到旧行版本来保证符合 MVCC 规则的数据可见性时,PostgreSQL允许清理这些旧行版本。不过,这条规则只能应用于在主库上执行的事务。因此,主库上的清理有可能移除某个备库事务仍然可见的行版本。 + + + 有经验的用户应注意,行版本清理和行版本冻结都有可能与备库查询冲突。手动运行 VACUUM FREEZE 很可能导致冲突,即使表中没有更新过或删除过的行也是如此。 + + + 用户应当清楚,主库上经常并且大量更新的表,会很快导致备库上的长时间运行查询被取消。在这种情况下,把max_standby_archive_delaymax_standby_streaming_delay设置为有限值,可以视作类似于设置statement_timeout。 + + + + 如果备库查询被取消的次数多得令人无法接受,也存在补救办法。第一种选择是设置hot_standby_feedback参数,它会阻止VACUUM移除最近死亡的行,因此不会发生清理冲突。如果这样做,你应当注意这会延迟主库上死行的清理,从而可能导致不希望出现的表膨胀。不过,清理情况不会比直接在主库上运行这些备库查询更糟,而且你仍然可以获得把执行卸载到备库上的好处。如果备库经常连接又断开,你可能还需要进行一些调整,以应对无法提供hot_standby_feedback反馈的那段时间。例如,可以考虑增大max_standby_archive_delay,使查询在断开期间不会因为 WAL 归档文件中的冲突而被迅速取消。你也应考虑增大max_standby_streaming_delay,以避免重新连接后由于新到达的流式 WAL 条目而被快速取消。 + + + 另一种选择是增大主库上的 ,使死行不会像通常那样很快被清理。这样,无需将 max_standby_streaming_delay 设得很大,就能让备库查询在被取消前有更多执行时间。不过,这种方法难以保证具体的执行时间窗口,因为 vacuum_defer_cleanup_age 是按主库上执行的事务数来衡量的。 + + + 查询取消的数量及其原因可以通过备库上的pg_stat_database_conflicts系统视图查看。pg_stat_database系统视图也包含汇总信息。 + + + + + 管理员概览 + + 如果 hot_standbyon(默认值,设置在 postgresql.conf 中),且存在 recovery.conf 文件,服务器就会以热备模式运行。不过,可能还要过一段时间才允许热备连接,因为服务器只有完成足够的恢复,达到可供查询运行的一致状态后,才会接受连接。在此期间,尝试连接的客户端会被拒绝,并收到错误消息。要确认服务器已启动,可以在应用中循环尝试连接,或在服务器日志中查找以下消息: +LOG: entering standby mode + +... then some time later ... + +LOG: consistent recovery state reached +LOG: database system is ready to accept read only connections +主库每次检查点都会记录一致性信息。如果主库的 wal_level 未设为 replicalogical,则读取这一期间写入的 WAL 时不能启用热备。如果同时满足以下两个条件,达到一致状态也可能延迟: + + + 某个写事务包含超过 64 个子事务 + + + + + 非常长生命周期的写事务 + + + 如果运行基于文件的日志传送(“温备”),可能需要等到下一个 WAL 文件到达,最长等待时间可达到主库上的 archive_timeout 设置值。 + + 如果在主库上更改了某些参数,就需要重新配置备库上的相应设置。对于这些参数,备库上的值必须大于或等于主库上的值。如果这些参数设置得不够大,备库会拒绝启动。此时可以提高参数值,然后重启服务器,再次开始恢复。这些参数是: + + + max_connections + + + + + max_prepared_transactions + + + + + max_locks_per_transaction + + + + + max_worker_processes + + + + + + + 管理员为选择合适的设置非常重要。最佳选择取决于业务优先级。例如,如果服务器的主要任务是充当高可用服务器,那么你会希望延迟设置较低,甚至可能设为零,尽管这是一个非常激进的设置。如果备库承担的是决策支持查询的附加服务器角色,那么把最大延迟设置为数小时甚至 -1(意味着永远等待查询完成)也可能是可以接受的。 + + + + 主库上写出的事务状态“提示位” 不会被 WAL 记录,因此备库上的数据很可能会再次写出这些提示位。这样一来,即使所有用户都是只读的,备库仍然会执行磁盘写操作;不过数据值本身并不会发生变化。用户仍然会写出大型排序临时文件,并重新生成 relcache 信息文件,因此在热备模式下,数据库没有任何部分是真正只读的。还要注意,使用dblink模块写入远程数据库,以及借助 PL 函数执行其他数据库外部操作,依然是可能的,即使该事务在本地是只读的。 + + + + 在恢复模式下,不接受下列类型的管理命令: + + + + + 数据定义语言(DDL):例如 CREATE INDEX + + + + + 权限和所有权:GRANTREVOKE、 + REASSIGN + + + + + 维护命令:ANALYZEVACUUM、 + CLUSTERREINDEX + + + + + + + 再次注意,这些命令中的某些在主库上的“只读”事务中实际上是被允许的。 + + + + 因此,你不能创建只存在于备库上的额外索引,也不能创建只存在于备库上的统计信息。如果需要这些管理命令,应在主库上执行,最终这些变更会传播到备库。 + + + + pg_cancel_backend()pg_terminate_backend()可以作用于用户后端,但不能作用于执行恢复操作的启动进程。pg_stat_activity不会把正在恢复的事务显示为活动状态。因此,恢复期间pg_prepared_xacts始终为空。如果你希望解决处于不确定状态的预备事务,请查看主库上的pg_prepared_xacts,并在那里发出命令解决这些事务,或者在恢复结束后再解决它们。 + + + + 与正常情况一样,pg_locks会显示后端持有的锁。pg_locks还会显示一个由启动进程管理的虚拟事务,它拥有所有正在被恢复重放的事务所持有的AccessExclusiveLocks。请注意,启动进程不会为了修改数据库而获取锁,因此除了AccessExclusiveLocks之外,其他锁不会在启动进程的pg_locks中显示;它们只是被假定存在。 + + + + Nagioscheck_pgsql插件可以工作,因为它检查的简单信息是存在的。check_postgres监控脚本也可以工作,尽管其中某些被报告的值可能会给出不同或令人困惑的结果。例如,最近一次清理时间不会被维护,因为备库上不会发生清理。不过,主库上执行的清理仍然会把其变更发送到备库。 + + + + 恢复期间,WAL 文件控制命令不可用,例如pg_start_backuppg_switch_xlog等。 + + + + 可动态载入的模块可以工作,包括pg_stat_statements。 + + + + 咨询锁在恢复期间可以正常工作,包括死锁检测。注意,咨询锁从不会被 WAL 记录,因此主库或备库上的咨询锁都不可能与 WAL 重放发生冲突。同样,也不可能在主库上获取一个咨询锁,却在备库上触发一个类似的咨询锁。咨询锁只与获取它们的那台服务器相关。 + + + + 基于触发器的复制系统,如SlonyLondisteBucardo,根本无法在备库上运行;不过只要它们的变更不会被发送到备库并在那里应用,它们在主库上就可以正常工作。WAL 重放并不是基于触发器的,因此你不能把备库作为任何需要额外数据库写操作或依赖触发器的系统的中继节点。 + + + + 不能分配新的 OID,不过某些UUID生成器仍可工作,只要它们不依赖于向数据库写入新的状态。 + + + + 目前,在只读事务期间不允许创建临时表,因此某些现有脚本在这种情况下将无法正常运行。这个限制可能会在未来版本中放宽。这既涉及 SQL 标准兼容性问题,也涉及技术问题。 + + + + 只有在表空间为空时DROP TABLESPACE才能成功。某些备库用户可能正在通过他们的temp_tablespaces参数使用该表空间。如果该表空间中存在临时文件,所有活动查询都将被取消,以确保临时文件被移除,这样该表空间才能被移除并且 WAL 重放可以继续。 + + + + 在主库上执行DROP DATABASEALTER DATABASE ... SET TABLESPACE会生成一条 WAL 记录,从而强制断开备库上所有连接到该数据库的用户。这个动作会立即发生,而不受max_standby_streaming_delay设置的影响。注意,ALTER DATABASE ... RENAME不会断开用户,这在大多数情况下不会被注意到,但如果程序依赖某种基于数据库名的机制,在某些情况下可能会导致混乱。 + + + + 在普通(非恢复)模式下,如果你对一个具有登录能力的角色执行DROP USERDROP ROLE,而该用户仍然处于连接状态,那么已连接用户不会发生任何变化 — 他们会继续保持连接,不过之后不能重新连接。这种行为在恢复期间同样适用,因此在主库上执行一次DROP USER并不会断开备库上的该用户连接。 + + + 统计收集器在恢复期间保持活动状态。所有扫描、读取、块、索引使用情况等,都会在备库上正常记录。重放操作不会重复记录其在主库上产生的统计影响,因此重放一次插入不会增加 pg_stat_user_tables 的 Inserts 列。统计文件会在恢复开始时被删除,因此主库和备库的统计信息不同;这是特性,不是缺陷。 + + + 恢复期间自动清理不会运行。它会在恢复结束时正常启动。 + + + + 检查点进程和后台写入进程在恢复期间是活动的。检查点进程会执行重启点(类似于主库上的检查点),后台写入进程会执行正常的块清理活动。这可能包括更新存储在备库上的提示位信息。恢复期间接受CHECKPOINT命令,不过它执行的是重启点,而不是新的检查点。 + + + + + 热备参数参考 + + + 多个参数已经在中提到过。 + + + 在主库上,可以使用 参数。 在主库上设置时没有效果。 + + 在备库上,可以使用 参数。只要服务器仍处于备库模式, 就没有效果,不过备库成为主库后,它就会起作用。 + + + + 注意事项 + + + 热备有若干限制。 + 这些限制在未来的版本中可以、也很可能会被修复: + + + + + 哈希索引上的操作目前不会写入 WAL 日志,因此重放不会更新这些索引。 + + + + + 在能够取得快照之前,必须完整了解正在运行的事务。使用大量子事务(目前超过 64 个)的事务,会把只读连接的启动推迟到持续时间最长的写事务完成之后。如果发生这种情况,服务器日志中会发送解释性消息。 + + + + + 备库查询的有效起始点是在主库的每个检查点上生成的。如果主库处于关闭状态时备库被关闭,那么在主库再次启动并在 WAL 日志中生成更多起始点之前,备库可能无法重新进入热备状态。不过在最常见的场景中,这通常不是问题。一般来说,如果主库关闭且不再可用,很可能是出现了严重故障,此时无论如何都需要把备库提升为新的主库。而在主库被有意关闭的情况下,协调好备库顺利成为新主库,本来也就是标准流程。 + + + + + 在恢复结束时,由预备事务持有的AccessExclusiveLocks将需要正常数量两倍的锁表项。如果你计划运行大量并发的预备事务,而这些事务通常会持有AccessExclusiveLocks,或者你计划运行一个会持有许多AccessExclusiveLocks的大事务,那么建议选择更大的max_locks_per_transaction值,可能要达到主库上该参数值的两倍。如果你的max_prepared_transactions设置为 0,则完全不需要考虑这一点。 + + + + + 热备中尚不支持可串行化事务隔离级别。(详见。)在热备模式下尝试把事务设置为可串行化隔离级别会产生错误。 + + + + + + + + + +
diff --git a/zh/9.6/history.sgml b/zh/9.6/history.sgml new file mode 100644 index 00000000..e2d0c195 --- /dev/null +++ b/zh/9.6/history.sgml @@ -0,0 +1,160 @@ + + + + <productname>PostgreSQL</productname>简史 + + + 历史 + PostgreSQL 的 + + + + 如今名为PostgreSQL的对象关系数据库管理系统, + 源自加州大学伯克利分校编写的POSTGRES软件包。 + 经过二十多年的发展,PostgreSQL如今已成为当今最先进的开源数据库。 + + + + 伯克利 <productname>POSTGRES</productname> 项目 + + + POSTGRES + + + + 由 Michael Stonebraker 教授领导的 POSTGRES 项目, + 得到了国防高级研究计划局(DARPA)、陆军研究办公室(ARO)、 + 国家科学基金会(NSF)以及 ESL, Inc. 的资助。 + POSTGRES 的实现始于 1986 年。 + 该系统的最初概念见于 ,而最初数据模型的定义见于 + 。当时规则系统的设计见于 。 + 存储管理器的设计动机和体系结构详见 。 + + + + 自那以后,POSTGRES 经历了几次重大版本发布。 + 第一个演示版系统于 1987 年开始运行,并在 1988 年的 + ACM-SIGMOD 大会上进行了展示。版本 1 在 中有描述, + 并于 1989 年 6 月发布给少数外部用户。 + 针对第一代规则系统所受到的批评(),规则系统被重新设计(), + 版本 2 连同新的规则系统于 1990 年 6 月发布。 + 版本 3 于 1991 年问世,增加了对多个存储管理器的支持、改进后的查询执行器以及重写后的规则系统。 + 此后直到 Postgres95(见下文)之前的后续版本, + 主要聚焦于可移植性和可靠性。 + + + + POSTGRES 被用于实现许多不同的研究和生产应用。 + 这些应用包括金融数据分析系统、喷气发动机性能监测软件包、小行星跟踪数据库、 + 医疗信息数据库以及若干地理信息系统。 + POSTGRES 还在多所大学中被用作教学工具。 + 最后,Illustra Information Technologies(后来并入 + Informix, + 该公司现归 IBM 所有)接手了这套代码并将其商业化。 + 在 1992 年末,POSTGRES 成为Sequoia 2000 科学计算项目的主要数据管理器。 + + + + 1993 年期间,外部用户社区的规模几乎翻了一番。 + 人们越来越清楚地意识到,对原型代码的维护和支持工作占用了大量本应用于数据库研究的时间。 + 为了减轻这一支持负担,伯克利 POSTGRES 项目在版本 4.2 时正式结束。 + + + + + <productname>Postgres95</productname> + + + Postgres95 + + + + 1994 年,Andrew Yu 和 Jolly Chen 为 POSTGRES 添加了一个 SQL 语言解释器。 + 随后,它以 Postgres95 这一新名称发布到网络上, + 作为最初的 POSTGRES 伯克利代码的开源后继者自行发展。 + + + + Postgres95的代码完全采用 ANSI C 编写,并将体积缩减了 25%。许多内部更改提升了性能和可维护性。Postgres951.0.x 版本在 Wisconsin Benchmark 上快了大约 30-50%,比较对象是POSTGRES4.2 版。除了错误修复之外,主要增强还包括: + + + 查询语言 PostQUEL 被 SQL 所取代(在服务器中实现)。 + (接口库 libpq 的名称来源于 PostQUEL。) + 直到 PostgreSQL(见下文)才支持子查询, + 但在 Postgres95 中可以用用户定义的 SQL 函数来模拟。 + 聚合函数也得到了重新实现,并增加了对 GROUP BY 查询子句的支持。 + + + + + + 新增了一个用于交互式 SQL 查询的程序(psql), + 它使用了 GNU Readline。 + 这在很大程度上取代了旧的 monitor 程序。 + + + + + + 新增的前端库 libpgtcl 支持基于 Tcl 的客户端。 + 示例 shell pgtclsh 提供了新的 Tcl 命令, + 用于让 Tcl 程序与 Postgres95 服务器交互。 + + + + + + 大对象接口经过了全面改造。反转大对象成为存储大对象的唯一机制。 + (反转文件系统已被移除。) + + + + + + 实例级规则系统被移除。规则仍然可以作为重写规则使用。 + + + + + + 源代码中附带了一份简短教程,介绍常规 SQL 特性以及 + Postgres95 的特性。 + + + + + + 构建时使用的是 GNU make(而不是 BSD make)。 + 此外,Postgres95 可以用未经修改的 GCC 编译 + (双精度浮点数的数据对齐问题已修复)。 + + + + + + + + <productname>PostgreSQL</productname> + + + 到了 1996 年,很明显 Postgres95 这个名字经不起时间的考验。 + 我们选择了一个新名字 PostgreSQL, + 以体现最初的 POSTGRES 与后来具备 SQL 能力的版本之间的关系。 + 同时,我们将版本号从 6.0 开始,使编号重新回到最初由伯克利 POSTGRES 项目开启的序列中。 + + + 许多人出于传统,或因为更容易发音,仍然将PostgreSQL称为Postgres(如今很少全部大写)。这一用法作为昵称或别名已被广泛接受。 + + + 在开发 Postgres95 期间,重点是识别并理解服务器代码中已有的问题。 + 到了 PostgreSQL 阶段,重点则转向增强特性和能力, + 不过各个方面的工作仍在继续。 + + + + 自那以后 PostgreSQL 发生了哪些变化,可见 。 + + + diff --git a/zh/9.6/hstore.sgml b/zh/9.6/hstore.sgml new file mode 100644 index 00000000..9c34fd93 --- /dev/null +++ b/zh/9.6/hstore.sgml @@ -0,0 +1,622 @@ + + + + hstore + + + hstore + + + + 此模块实现了hstore数据类型,用于在单个 + PostgreSQL值中存储一组键/值对。这在多种场景中 + 都很有用,例如属性很多但很少查看的行,或者半结构化数据。键和值都只是文 + 本字符串。 + + + + <type>hstore</type> 外部表示 + + + + 用于输入和输出的hstore文本表示包含零个或多个以逗号分隔的 + key => + value 对。一些示例: + + +k => v +foo => bar, baz => whatever +"1-a" => "anything at all" + + + 键/值对的顺序并不重要(而且在输出时可能不会按原样重现)。键/值对之间或 + => 号周围的空白会被忽略。包含空白、逗号、 + => 的键和值必须用双引号括起来。 + 要在键或值中包含双引号或反斜线,请用反斜线转义。 + + + + 每个hstore中的键都是唯一的。如果声明的hstore + 带有重复键,则在该hstore中只会存储其中一个,而且无法保证保 + 留的是哪一个: + + +SELECT 'a=>1,a=>2'::hstore; + hstore +---------- + "a"=>"1" + + + + + 值(但键不能)可以是 SQL NULL。例如: + + +key => NULL + + + NULL关键字不区分大小写。若要将NULL + 视为普通字符串NULL,请用双引号括起来。 + + + + + 请注意,当hstore文本格式用于输入时,它会在任何必需的加引号 + 或转义之前应用。如果通过参数传递一个 + hstore字面量,则不需要额外处理。但如果将其作为带引号的字面 + 量常量传递,那么其中的单引号字符以及(取决于 + standard_conforming_strings配置参数的设置)反斜线字 + 符都需要被正确转义。关于字符串常量的处理,见 + 。 + + + + + 在输出时,即使严格来说并非必需,键和值也总是带有双引号。 + + + + + + <type>hstore</type> 操作符和函数 + + + hstore模块提供的操作符见, + 函数见。 + + + + <type>hstore</type> 操作符 + + + + + 操作符 + + 描述 + + 示例 + 结果 + + + + + + hstore -> text + 获取键对应的值(如果不存在则为NULL + 'a=>x, b=>y'::hstore -> 'a' + x + + + + hstore -> text[] + 获取各键对应的值(如果不存在则为NULL + 'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a'] + {"z","x"} + + + + hstore || hstore + 连接hstore + 'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore + "a"=>"b", "c"=>"x", "d"=>"q" + + + + hstore ? text + hstore是否包含该键? + 'a=>1'::hstore ? 'a' + t + + + + hstore ?& text[] + hstore是否包含所有指定的键? + 'a=>1,b=>2'::hstore ?& ARRAY['a','b'] + t + + + + hstore ?| text[] + hstore是否包含指定键中的任意一个? + 'a=>1,b=>2'::hstore ?| ARRAY['b','c'] + t + + + + hstore @> hstore + 左操作数是否包含右操作数? + 'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1' + t + + + + hstore <@ hstore + 左操作数是否被包含在右操作数中? + 'a=>c'::hstore <@ 'a=>b, b=>1, c=>NULL' + f + + + + hstore - text + 从左操作数中删除键 + 'a=>1, b=>2, c=>3'::hstore - 'b'::text + "a"=>"1", "c"=>"3" + + + + hstore - text[] + 从左操作数中删除键 + 'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b'] + "c"=>"3" + + + + hstore - hstore + 从左操作数中删除匹配的键值对 + 'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore + "a"=>"1", "c"=>"3" + + + + record #= hstore + hstore中匹配的值替换record中的字段 + 参见示例部分 + + + + + %% hstore + hstore转换为交替排列的键和值数组 + %% 'a=>foo, b=>bar'::hstore + {a,foo,b,bar} + + + + %# hstore + hstore转换为二维键/值数组 + %# 'a=>foo, b=>bar'::hstore + {{a,foo},{b,bar}} + + + + +
+ + + 在 PostgreSQL 8.2 之前,包含操作符 @><@ 分别称为 @~。这些名称仍然可用,但已弃用,最终将被删除。请注意,旧名称与核心几何数据类型以前采用的约定正好相反! + + + + <type>hstore</type> 函数 + + + + + 函数 + 返回类型 + + 描述 + + 示例 + 结果 + + + + + + hstore(record)hstore + hstore + 从记录或行构造hstore + hstore(ROW(1,2)) + f1=>1,f2=>2 + + + + hstore(text[]) + hstore + 从数组构造hstore,数组可以是键/值数组,也可以是二维数组 + hstore(ARRAY['a','1','b','2']) || hstore(ARRAY[['c','3'],['d','4']]) + a=>1, b=>2, c=>3, d=>4 + + + + hstore(text[], text[]) + hstore + 从分开的键数组和值数组构造hstore + hstore(ARRAY['a','b'], ARRAY['1','2']) + "a"=>"1","b"=>"2" + + + + hstore(text, text) + hstore + 生成只含一个项的hstore + hstore('a', 'b') + "a"=>"b" + + + + akeys(hstore)akeys + text[] + hstore的键作为数组获取 + akeys('a=>1,b=>2') + {a,b} + + + + skeys(hstore)skeys + setof text + hstore的键作为集合获取 + skeys('a=>1,b=>2') + + +a +b + + + + + avals(hstore)avals + text[] + hstore的值作为数组获取 + avals('a=>1,b=>2') + {1,2} + + + + svals(hstore)svals + setof text + hstore的值作为集合获取 + svals('a=>1,b=>2') + + +1 +2 + + + + + hstore_to_array(hstore)hstore_to_array + text[] + hstore的键和值作为交替排列的键和值数组获取 + hstore_to_array('a=>1,b=>2') + {a,1,b,2} + + + + hstore_to_matrix(hstore)hstore_to_matrix + text[] + hstore的键和值作为二维数组获取 + hstore_to_matrix('a=>1,b=>2') + {{a,1},{b,2}} + + + + hstore_to_json(hstore)hstore_to_json + json + hstore作为json值获取,把所有非空值转换为 JSON 字符串 + hstore_to_json('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') + {"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"} + + + + hstore_to_jsonb(hstore)hstore_to_jsonb + jsonb + hstore作为jsonb值获取,把所有非空值转换为 JSON 字符串 + hstore_to_jsonb('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') + {"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"} + + + + hstore_to_json_loose(hstore)hstore_to_json_loose + json + hstore作为json值获取,但尝试区分数值和布尔值,使它们在 JSON 中不带引号 + hstore_to_json_loose('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') + {"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4} + + + + hstore_to_jsonb_loose(hstore)hstore_to_jsonb_loose + jsonb + hstore作为jsonb值获取,但尝试区分数值和布尔值,使它们在 JSON 中不带引号 + hstore_to_jsonb_loose('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') + {"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4} + + + + slice(hstore, text[])slice + hstore + 提取hstore的子集 + slice('a=>1,b=>2,c=>3'::hstore, ARRAY['b','c','x']) + "b"=>"2", "c"=>"3" + + + + each(hstore)each + setof(key text, value text) + hstore的键和值作为集合获取 + select * from each('a=>1,b=>2') + + + key | value +-----+------- + a | 1 + b | 2 + + + + + exist(hstore,text)exist + boolean + hstore是否包含该键? + exist('a=>1','a') + t + + + + defined(hstore,text)defined + boolean + hstore是否包含该键的非NULL值? + defined('a=>NULL','a') + f + + + + delete(hstore,text)delete + hstore + 删除键匹配的键值对 + delete('a=>1,b=>2','b') + "a"=>"1" + + + + delete(hstore,text[]) + hstore + 删除键匹配的键值对 + delete('a=>1,b=>2,c=>3',ARRAY['a','b']) + "c"=>"3" + + + + delete(hstore,hstore) + hstore + 删除与第二个参数中的键值对匹配的键值对 + delete('a=>1,b=>2','a=>4,b=>2'::hstore) + "a"=>"1" + + + + populate_record(record,hstore)populate_record + record + hstore中匹配的值替换record中的字段 + 参见示例部分 + + + + + +
+ + + hstore值转换为json时使用函数hstore_to_json。同样,将hstore值转换为jsonb时使用hstore_to_jsonb + + + + populate_record函数的第一个参数实际声明为anyelement,而不是record,但它会在运行时出错并拒绝非记录类型。 + +
+ + + 索引 + + + hstore支持针对@>?、 + ?&?|操作符的 GiST 和 GIN 索 + 引。例如: + + +CREATE INDEX hidx ON testhstore USING GIST (h); + +CREATE INDEX hidx ON testhstore USING GIN (h); + + + + hstore也支持用于=操作符的 + btreehash索引。这允许hstore列被声明为 + UNIQUE,或者用于GROUP BY、 + ORDER BYDISTINCT表达式。 + hstore值的排序顺序本身并没有特别实用的意义,但这些索引可能适合 + 用于等值查找。可按如下方式为=比较创建索引: + + +CREATE INDEX hidx ON testhstore USING BTREE (h); + +CREATE INDEX hidx ON testhstore USING HASH (h); + + + + + 示例 + + 添加一个键,或用新值更新现有键: +UPDATE tab SET h = h || hstore('c', '3'); + + + + + 删除一个键: + +UPDATE tab SET h = delete(h, 'k1'); + + + + + 将record转换为hstore: + +CREATE TABLE test (col1 integer, col2 text, col3 text); +INSERT INTO test VALUES (123, 'foo', 'bar'); + +SELECT hstore(t) FROM test AS t; + hstore +--------------------------------------------- + "col1"=>"123", "col2"=>"foo", "col3"=>"bar" +(1 row) + + + + + 将hstore转换为预定义的record类型: + +CREATE TABLE test (col1 integer, col2 text, col3 text); + +SELECT * FROM populate_record(null::test, + '"col1"=>"456", "col2"=>"zzz"'); + col1 | col2 | col3 +------+------+------ + 456 | zzz | +(1 row) + + + + + 使用hstore中的值修改现有记录: + +CREATE TABLE test (col1 integer, col2 text, col3 text); +INSERT INTO test VALUES (123, 'foo', 'bar'); + +SELECT (r).* FROM (SELECT t #= '"col3"=>"baz"' AS r FROM test t) s; + col1 | col2 | col3 +------+------+------ + 123 | foo | baz +(1 row) + + + + + + 统计信息 + + + 由于hstore类型本身比较宽松,它可能包含大量不同的键。检查键 + 是否合法是应用程序的任务。下面的示例展示了检查键并获取统计信息的几种技 + 术。 + + + + 简单示例: + +SELECT * FROM each('aaa=>bq, b=>NULL, ""=>1'); + + + + 使用表: +SELECT (each(h)).key, (each(h)).value INTO stat FROM testhstore; + + + + + 在线统计信息: + +SELECT key, count(*) FROM + (SELECT (each(h)).key FROM testhstore) AS stat + GROUP BY key + ORDER BY count DESC, key; + key | count +-----------+------- + line | 883 + query | 207 + pos | 203 + node | 202 + space | 197 + status | 195 + public | 194 + title | 190 + org | 189 +................... + + + + + + 兼容性 + + + 自 PostgreSQL 9.0 起,hstore使用了与更早版本不同的内部表 + 示。这不会妨碍转储/恢复升级,因为文本表示(即转储中使用的表示)没有改变。 + + + + 在进行二进制升级时,通过让新代码识别旧格式数据,维持了向上兼容性。这会在 + 处理尚未被新代码修改过的数据时带来轻微的性能损失。可以通过执行如下 + UPDATE语句,强制升级表列中的所有值: + +UPDATE tablename SET hstorecol = hstorecol || ''; + + + + + 另一种方式是: + +ALTER TABLE tablename ALTER hstorecol TYPE hstore USING hstorecol || ''; + + 使用ALTER TABLE方法需要对表加 + ACCESS EXCLUSIVE锁,但不会因旧行版本而导致表膨胀。 + + + + + + 转换 + + + 另外还有一些扩展可用,它们为 PL/Perl 和 PL/Python 语言中的 + hstore类型实现了转换。PL/Perl 的扩展分别名为 + hstore_plperlhstore_plperlu, + 对应受信任的和不受信任的 PL/Perl。如果安装这些转换并在创建函数时指定它 + 们,则hstore值会映射为 Perl 哈希。PL/Python 的扩展分别名为hstore_plpythonuhstore_plpython2uhstore_plpython3u(关于 PL/Python 的命名约定请见)。如果使用它们,hstore值会映射为 Python 字典。 + + + + 强烈建议将转换扩展安装在与 hstore 相同的模式中。否则,如果转换扩展所在的模式包含由恶意用户定义的对象,就会在安装时产生安全隐患。 + + + + + 作者 + + + Oleg Bartunov oleg@sai.msu.su,俄罗斯莫斯科,莫斯科大学 + + + + Teodor Sigaev teodor@sigaev.ru,俄罗斯莫斯科,Delta-Soft Ltd. + + + + Andrew Gierth andrew@tao11.riddles.org.uk,英国,对本模块作 + 了额外增强 + + + +
diff --git a/zh/9.6/indexam.sgml b/zh/9.6/indexam.sgml new file mode 100644 index 00000000..59aca486 --- /dev/null +++ b/zh/9.6/indexam.sgml @@ -0,0 +1,681 @@ + + + + 索引访问方法接口定义 + + + 本章定义 PostgreSQL 核心系统与管理各个索引类型的索引访问方法之间的接口。除这里规定的内容之外,核心系统对索引一无所知,因此可以通过编写附加代码来开发全新的索引类型。 + + + + PostgreSQL 中所有索引在技术上都称为二级索引;也就是说,索引与它所描述的表文件在物理上是分离的。每个索引都存储为独立的物理关系,因此在 pg_class 目录中都有一个条目。索引的内容完全由其索引访问方法控制。实践中,所有索引访问方法都会把索引划分为标准大小的页面,以便使用常规的存储管理器和缓冲区管理器访问索引内容。(现有的所有索引访问方法还都使用 中描述的标准页面布局,而且大多数还对索引元组头使用相同的格式;但这些决定并不是访问方法必须遵循的。) + + + + 索引本质上是从一些数据键值到行版本(元组)在索引父表中的元组标识符TIDs)的映射。一个 TID 由块号以及该块中的项号组成(见 )。这些信息足以从表中取出某个特定的行版本。索引并不直接知道在 MVCC 之下同一逻辑行可能会存在多个现存版本;对索引来说,每个元组都是一个独立对象,需要它自己的索引条目。因此,对一行的更新总会为该行创建全新的索引条目,即使键值并未改变也是如此。(HOT 元组是这一说法的例外;但索引同样不直接处理它们。)当死元组自身被回收时(通过清理),它们对应的索引条目也会被回收。 + + + + 索引的基本 API 结构 + + + 每种索引访问方法都由系统目录 pg_am 中的一行描述。pg_am 条目指定了该访问方法的名称以及一个处理器函数。这些条目可以通过 SQL 命令创建和删除。 + + + + 索引访问方法的处理器函数必须声明为接受一个 internal 类型的参数,并返回伪类型 index_am_handler。这个参数只是一个占位值,用来防止处理器函数被 SQL 命令直接调用。该函数的结果必须是一个通过 palloc 分配的 IndexAmRoutine 结构体,其中包含核心代码使用该索引访问方法所需的全部信息。IndexAmRoutine 结构体也称为该访问方法的API 结构体,其中的字段指定了访问方法的各种固定属性,例如它是否支持多列索引。更重要的是,它包含该访问方法的支持函数指针,而这些支持函数完成了实际访问索引的全部工作。这些支持函数是普通 C 函数,在 SQL 层不可见也不可调用。支持函数将在 中介绍。 + + + 结构体IndexAmRoutine定义如下: +typedef struct IndexAmRoutine +{ + NodeTag type; + + /* + * 可用于遍历或搜索此 AM 的策略(操作符)总数。 + * 如果 AM 没有固定的一组策略分配,则为零。 + */ + uint16 amstrategies; + /* 此 AM 使用的支持函数总数 */ + uint16 amsupport; + /* AM 是否支持按被索引列的值进行 ORDER BY? */ + bool amcanorder; + /* AM 是否支持按被索引列上操作符的结果进行 ORDER BY? */ + bool amcanorderbyop; + /* AM 是否支持反向扫描? */ + bool amcanbackward; + /* AM 是否支持 UNIQUE 索引? */ + bool amcanunique; + /* AM 是否支持多列索引? */ + bool amcanmulticol; + /* AM 是否要求扫描必须约束第一个索引列? */ + bool amoptionalkey; + /* AM 是否处理 ScalarArrayOpExpr 限定条件? */ + bool amsearcharray; + /* AM 是否处理 IS NULL/IS NOT NULL 限定条件? */ + bool amsearchnulls; + /* 索引存储数据类型是否可以不同于列数据类型? */ + bool amstorage; + /* 是否可以按此类型的索引进行聚簇? */ + bool amclusterable; + /* AM 是否处理谓词锁? */ + bool ampredlocks; + /* 索引中存储的数据类型;如果可变,则为 InvalidOid */ + Oid amkeytype; + + /* 接口函数 */ + ambuild_function ambuild; + ambuildempty_function ambuildempty; + aminsert_function aminsert; + ambulkdelete_function ambulkdelete; + amvacuumcleanup_function amvacuumcleanup; + amcanreturn_function amcanreturn; /* 可以为 NULL */ + amcostestimate_function amcostestimate; + amoptions_function amoptions; + amproperty_function amproperty; /* 可以为 NULL */ + amvalidate_function amvalidate; + ambeginscan_function ambeginscan; + amrescan_function amrescan; + amgettuple_function amgettuple; /* 可以为 NULL */ + amgetbitmap_function amgetbitmap; /* 可以为 NULL */ + amendscan_function amendscan; + ammarkpos_function ammarkpos; /* 可以为 NULL */ + amrestrpos_function amrestrpos; /* 可以为 NULL */ +} IndexAmRoutine; + + + + + 要使一种索引访问方法真正可用,还必须在 pg_opfamilypg_opclasspg_amoppg_amproc 中定义一个或多个操作符族操作符类。这些条目使规划器能够确定该访问方法的索引可以使用哪些查询限定条件。关于操作符族和操作符类的说明见 ;阅读本章前应先掌握这些内容。 + + + + 单个索引由两个系统目录条目共同定义:一个 pg_class 条目把它描述为一个物理关系,另一个 pg_index 条目给出索引的逻辑内容,也就是它包含哪些索引列,以及由相关操作符类定义的这些列的语义。索引列(键值)既可以是底层表的简单列,也可以是基于表行的表达式。索引访问方法通常并不关心索引键值来自何处(它拿到的总是预先计算好的键值),但会非常关心 pg_index 中的操作符类信息。这两个目录条目都可以作为传递给索引上所有操作的 Relation 数据结构的一部分来访问。 + + + + IndexAmRoutine 的某些标志字段还有一些不那么直观的含义。amcanunique 的要求见 amcanmulticol 标志表明该访问方法支持多列索引,而 amoptionalkey 表明它允许在第一索引列上没有给出可索引限制子句时进行扫描。当 amcanmulticol 为假时,amoptionalkey 实质上表示该访问方法是否支持没有任何限制子句的全索引扫描。支持多个索引列的访问方法必须支持在第一列之后省略任意一个或全部列限制条件的扫描;但它们可以要求第一索引列必须出现某种限制条件,这通过把 amoptionalkey 设为假来表示。索引 AM 可能把 amoptionalkey 设为假的一个原因,是它不索引 NULL 值。由于大多数可索引操作符都是严格的(strict),因此对 NULL 输入不可能返回 true,所以乍看之下不存储 NULL 值的索引条目似乎很有吸引力:无论如何,这些条目似乎都不可能被索引扫描返回。然而,当某个索引扫描对给定索引列没有限制子句时,这个论证就不成立了。实践中这意味着,若索引把 amoptionalkey 设为真,就必须索引 NULL 值,因为规划器可能决定在完全没有扫描键的情况下使用这种索引。与此相关的另一个限制是,支持多个索引列的索引访问方法必须支持对第一列之后各列中的 NULL 值建立索引,因为规划器会假定该索引可用于不限制这些列的查询。例如,考虑一个在 (a,b) 上的索引以及查询 WHERE a = 4。系统会假定该索引可用于扫描满足 a = 4 的行;如果索引省略了 b 为 NULL 的行,这个假定就是错误的。不过,省略第一索引列为 NULL 的行是可以的。对 NULL 值建立索引的索引访问方法还可以设置 amsearchnulls,表示它支持把 IS NULLIS NOT NULL 子句作为搜索条件。 + + + + + + 索引访问方法函数 + + + 索引访问方法必须在 IndexAmRoutine 中提供如下索引构建和维护函数: + + + + +IndexBuildResult * +ambuild (Relation heapRelation, + Relation indexRelation, + IndexInfo *indexInfo); +构建新索引。索引关系已在物理上创建,但内容为空。必须填入访问方法所需的固定数据,以及表中所有已有元组对应的条目。通常,ambuild函数会调用IndexBuildHeapScan()扫描表中现有的元组,并计算需要插入索引的键。函数必须返回一个通过 palloc 分配的结构体,其中包含新索引的统计信息。 + + + +void +ambuildempty (Relation indexRelation); + + 构建一个空索引,并将其写入给定关系的初始化分支(INIT_FORKNUM)。只有不记录 WAL 的索引才会调用此方法;写入初始化分支的空索引会在每次服务器重启时复制到主关系分支上。 + + + + +bool +aminsert (Relation indexRelation, + Datum *values, + bool *isnull, + ItemPointer heap_tid, + Relation heapRelation, + IndexUniqueCheck checkUnique); +将一个新元组插入已有索引。valuesisnull数组给出要索引的键值,heap_tid则是要索引的 TID。如果访问方法支持唯一索引(其amcanunique标志为 true),则checkUnique指明要执行的唯一性检查类型,具体取决于唯一约束是否可延迟;详情参见。通常,访问方法只在执行唯一性检查时才需要heapRelation参数(因为此时必须查看堆以确认元组是否存活)。 + + 只有当checkUniqueUNIQUE_CHECK_PARTIAL时,该函数的布尔返回值才有意义。在这种情况下,TRUE 表示已知新条目唯一,而 FALSE 表示它可能不唯一(并且必须安排一次延迟唯一性检查)。对于其他情况,建议始终返回 FALSE。 + + + 某些索引可能不会为所有元组建立索引。如果某个元组不应被索引,aminsert 应当直接返回而不做任何事。 + + + + +IndexBulkDeleteResult * +ambulkdelete (IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + IndexBulkDeleteCallback callback, + void *callback_state); + + 从索引中删除元组。这是一个批量删除操作,旨在通过扫描整个索引并检查每个条目是否应被删除来实现。必须调用传入的 callback 函数,其调用形式为 callback(TID, callback_state) returns bool,以确定由其引用 TID 标识的某个索引条目是否应删除。该函数必须返回 NULL,或者返回一个通过 palloc 分配的结构体,其中包含此次删除操作影响的统计信息。如果不需要向 amvacuumcleanup 传递信息,返回 NULL 也是可以的。 + + + + 由于 maintenance_work_mem 有限,当待删除的元组很多时,ambulkdelete 可能需要被调用多次。参数 stats 是此前对该索引上一次调用的结果(在一次 VACUUM 操作中的第一次调用时它为 NULL)。这使得 AM 可以在整个操作过程中累积统计信息。通常,如果传入的 stats 非 NULL,ambulkdelete 会修改并返回同一个结构体。 + + + + +IndexBulkDeleteResult * +amvacuumcleanup (IndexVacuumInfo *info, + IndexBulkDeleteResult *stats); + + 在一次 VACUUM 操作(零次或多次 ambulkdelete 调用)之后执行清理。它不一定要做返回索引统计信息之外的事情,但也可能执行批量清理,例如回收空索引页面。stats 是最后一次 ambulkdelete 调用返回的结果;如果因为没有需要删除的元组而没有调用 ambulkdelete,则为 NULL。如果结果不是 NULL,它必须是一个通过 palloc 分配的结构体。其中的统计信息将用于更新 pg_class,并在指定了 VERBOSE 时由 VACUUM 报告。如果索引在整个 VACUUM 操作期间完全没有变化,返回 NULL 也是可以的;否则应返回正确的统计信息。 + + + + 从PostgreSQL 8.4 开始,amvacuumcleanup 也会在 ANALYZE 操作完成时被调用。在这种情况下,stats 总是 NULL,而且任何返回值都会被忽略。这种情况可以通过检查 info->analyze_only 来区分。我们建议访问方法在这种调用中除了做插入后的清理外什么都不做,而且只在自动清理工作进程中这样做。 + + + + +bool +amcanreturn (Relation indexRelation, int attno); +检查索引能否通过以 IndexTuple 形式返回某个索引条目的被索引列值,在给定列上支持仅索引扫描。属性编号从 1 开始,也就是说第一列的 attno 为 1。若支持则返回 TRUE,否则返回 FALSE。如果访问方法根本不支持仅索引扫描,那么amcanreturn字段在其IndexAmRoutine结构体中可以设为 NULL。 + + + +void +amcostestimate (PlannerInfo *root, + IndexPath *path, + double loop_count, + Cost *indexStartupCost, + Cost *indexTotalCost, + Selectivity *indexSelectivity, + double *indexCorrelation, + double *indexPages); + + 估计一次索引扫描的代价。该函数将在后面的 中详细讨论。 + + + + +bytea * +amoptions (ArrayType *reloptions, + bool validate); + + 解析并验证索引的 reloptions 数组。只有当该索引存在非 NULL 的 reloptions 数组时才会调用此函数。reloptions 是一个 text 数组,其中的条目形如 name=value。该函数应构造一个 bytea 值,并将其复制到索引 relcache 条目的 rd_options 字段中。这个 bytea 值中的数据内容由访问方法自行定义;大多数标准访问方法使用结构体 StdRdOptions。当 validate 为真时,若存在未识别选项或无效取值,函数应报告合适的错误消息;当 validate 为假时,无效条目应被静默忽略。(当装载已存储在 pg_catalog 中的选项时,validate 为假;此时只有在访问方法改变了选项规则时才可能发现无效条目,而忽略过时条目是合适的。)如果希望采用默认行为,返回 NULL 也是可以的。 + + + + +bool +amproperty (Oid index_oid, int attno, + IndexAMProperty prop, const char *propname, + bool *res, bool *isnull); +amproperty方法允许索引访问方法覆盖pg_index_column_has_property及其相关函数的默认行为。如果访问方法对索引属性查询没有任何特殊行为,那么amproperty字段在其IndexAmRoutine结构体中可以设为 NULL。否则,amproperty方法会收到如下调用参数:index_oid和attno均为零,对应pg_indexam_has_property调用;或者index_oid有效且attno为零,对应pg_index_has_property调用;或者index_oid有效且attno大于零,对应pg_index_column_has_property调用。prop是一个枚举值,用来标识当前测试的属性;propname则是原始属性名字符串。如果核心代码不认识该属性名,那么propAMPROP_UNKNOWN。访问方法可以通过检查propname是否匹配来定义自定义属性名(使用pg_strcasecmp进行匹配,以与核心代码保持一致);对于核心代码已知的名称,最好检查prop。如果amproperty方法返回true,则表示它已确定属性测试结果:它必须将*res设为要返回的布尔值,或者把*isnull设为true以返回 NULL。(所引用的两个变量在调用前都初始化为false。)如果amproperty方法返回false,核心代码就会按其正常逻辑确定属性测试结果。 + + + 支持排序操作符的访问方法应当实现 AMPROP_DISTANCE_ORDERABLE 属性测试,因为核心代码不知道如何完成该测试,只会返回 NULL。若完成该测试的代价低于打开索引并调用 amcanreturn(这正是核心代码的默认行为),那么实现 AMPROP_RETURNABLE 测试也可能是有利的。对于其他所有标准属性,默认行为应当已经足够。 + + + + +bool +amvalidate (Oid opclassoid); +在访问方法能够合理验证的范围内,验证指定操作符类的目录条目。例如,可以检查是否提供了所有必需的支持函数。如果操作符类无效,amvalidate函数必须返回 false。应通过ereport消息报告。 + + + + 索引的目的当然是支持扫描那些匹配可索引 WHERE 条件的元组,这种条件常被称为限定词扫描键。关于索引扫描的语义,将在下面的 中更详细地说明。一种索引访问方法可以支持普通索引扫描、位图索引扫描,或者两者都支持。索引访问方法必须或可以提供的扫描相关函数如下: + + + + +IndexScanDesc +ambeginscan (Relation indexRelation, + int nkeys, + int norderbys); + + 为一次索引扫描做准备。nkeysnorderbys 参数表示扫描中将使用的限定条件和排序操作符数量,这些信息可能有助于空间分配。请注意,此时还没有提供扫描键的实际值。结果必须是一个通过 palloc 分配的结构体。出于实现上的原因,索引访问方法必须通过调用 RelationGetIndexScan() 来创建这个结构体。大多数情况下,ambeginscan 除了做这次调用以及也许获取一些锁之外,不会做太多工作;索引扫描启动中真正有意思的部分在 amrescan 中。 + + + + +void +amrescan (IndexScanDesc scan, + ScanKey keys, + int nkeys, + ScanKey orderbys, + int norderbys); + + 开始或重新开始一次索引扫描,并且可以使用新的扫描键。(若要用之前传入的键重新开始,则为 keys 和/或 orderbys 传递 NULL。)请注意,键的数量或排序操作符的数量都不允许超过传给 ambeginscan 的数量。实践中,这种重启特性通常用于嵌套循环连接选中了新的外层元组,因此需要新的键比较值,但扫描键结构体本身保持不变的场景。 + + + + +bool +amgettuple (IndexScanDesc scan, + ScanDirection direction); +沿给定方向(在索引中向前或向后)取出给定扫描中的下一个元组。若取得元组则返回 TRUE,若没有剩余匹配元组则返回 FALSE。返回 TRUE 时,元组 TID 存入scan结构体中。注意,成功仅表示索引中包含与扫描键匹配的条目,并不表示该元组一定仍存在于堆中,或者能够通过调用方的快照测试。成功时,amgettuple还必须将scan->xs_recheck设为 TRUE 或 FALSE。FALSE 表示可以确定该索引条目匹配扫描键。TRUE 表示无法确定,因此在取出堆元组后必须重新检查扫描键所代表的条件。这一规定支持有损索引操作符。注意,重新检查仅针对扫描条件;部分索引谓词(如果有)永远不会由amgettuple的调用方重新检查。 + + + 如果索引支持 仅索引扫描(即 amcanreturn 对它返回 TRUE),那么成功时 AM 还必须检查 scan->xs_want_itup;若其为真,就必须以如下形式返回该索引条目的原始被索引数据:把 IndexTuple 指针存入 scan->xs_itup,其元组描述符为 scan->xs_itupdesc。(指针所引用数据的管理责任在访问方法一侧。这份数据必须至少保持有效,直到该扫描下一次调用 amgettupleamrescanamendscan。) + + + + 只有当访问方法支持普通索引扫描时,才需要提供 amgettuple 函数。如果不支持,其 IndexAmRoutine 结构体中的 amgettuple 字段必须设为 NULL。 + + + + +int64 +amgetbitmap (IndexScanDesc scan, + TIDBitmap *tbm); + + 取出给定扫描中的所有元组,并将其加入调用者提供的 TIDBitmap 中(也就是把这组元组 ID 与位图中已有的集合做 OR)。返回值是取得的元组数量(这可能只是近似计数,例如某些 AM 不会检测重复项)。在把元组 ID 插入位图时,amgetbitmap 可以指出某些具体的元组 ID 需要重新检查扫描条件。这类似于 amgettuplexs_recheck 输出参数。注意:在当前实现中,对这一特性的支持与位图自身的有损存储支持混在一起,因此调用者会对需要重检的元组同时重新检查扫描条件和部分索引谓词(如果有)。不过,这并不一定永远如此。amgetbitmapamgettuple 不能在同一次索引扫描中同时使用;使用 amgetbitmap 时还有其他限制,详见 。 + + + + 只有当访问方法支持位图索引扫描时,才需要提供 amgetbitmap 函数。如果不支持,其 IndexAmRoutine 结构体中的 amgetbitmap 字段必须设为 NULL。 + + + + +void +amendscan (IndexScanDesc scan); + + 结束一次扫描并释放资源。scan 结构体本身不应被释放,但必须释放访问方法内部获取的所有锁、解除所有钉住状态,并释放由 ambeginscan 和其他扫描相关函数分配的其他内存。 + + + + +void +ammarkpos (IndexScanDesc scan); + + 标记当前扫描位置。访问方法只需要为每次扫描支持一个被记住的扫描位置。 + + + + 只有当访问方法支持有序扫描时,才需要提供 ammarkpos 函数。如果不支持,其 IndexAmRoutine 结构体中的 ammarkpos 字段可以设为 NULL。 + + + + +void +amrestrpos (IndexScanDesc scan); + + 将扫描恢复到最近一次标记的位置。 + + + + 只有当访问方法支持有序扫描时,才需要提供 amrestrpos 函数。如果不支持,其 IndexAmRoutine 结构体中的 amrestrpos 字段可以设为 NULL。 + + + + + + + 索引扫描 + + + 在索引扫描中,索引访问方法负责返回它所知道的、匹配扫描键的所有元组的 TID。访问方法负责从索引父表中实际取出这些元组,也不负责判断它们是否能通过扫描的可见性测试或其他条件。 + + + + 扫描键是如下形式的 WHERE 子句的内部表示:index_key operator constant。其中,索引键是该索引的一列,而操作符是与该索引列关联的操作符族成员之一。一次索引扫描可以有零个或多个扫描键,这些扫描键会被隐式地以 AND 连接,也就是说,返回的元组预期应满足所有给定条件。 + + + + 对于某个特定查询,访问方法可以报告该索引是有损的,或者要求重新检查。这意味着索引扫描会返回所有通过扫描键的条目,并可能额外返回一些未通过扫描键的条目。随后,核心系统的索引扫描机制会再次把索引条件应用到堆元组上,以验证它是否真的应被选中。如果没有指定重检选项,索引扫描就必须精确返回匹配条目的集合。 + + + + 请注意,确保正确找出通过全部给定扫描键的所有且仅有的条目,这项工作完全由访问方法负责。此外,核心系统只是简单地把所有与索引键和操作符族匹配的 WHERE 子句交给访问方法,而不会做任何语义分析来判断它们是否冗余或互相矛盾。例如,给定 WHERE x > 4 AND x > 14,其中 x 是一个 B-树索引列,那么由 B-树的 amrescan 函数负责识别第一个扫描键是冗余的并可被丢弃。amrescan 期间需要做多少预处理,取决于索引访问方法需要在多大程度上把扫描键归约成一种正规化形式。 + + + + 某些访问方法会按明确定义的顺序返回索引条目,另一些则不会。实际上,访问方法支持有序输出有两种不同方式: + + + + + 总是按数据自然顺序返回条目的访问方法(例如 btree)应把 amcanorder 设为真。目前,这类访问方法必须对其等值和排序操作符使用与 btree 兼容的策略号。 + + + + + 支持排序操作符的访问方法应把 amcanorderbyop 设为真。这表示索引能够按满足 ORDER BY index_key operator constant 的顺序返回条目。前面已经提到,这种形式的扫描修饰符可以传给 amrescan。 + + + + + + + amgettuple 函数有一个 direction 参数,它可以是 ForwardScanDirection(通常情况)或 BackwardScanDirection。如果 amrescan 之后的第一次调用指定了 BackwardScanDirection,那么匹配条件的索引条目集合就要按从后到前的方向扫描,而不是通常的从前到后,因此 amgettuple 必须返回索引中最后一个匹配元组,而不是通常情况下的第一个。(这只会发生在把 amcanorder 设为真的访问方法上。)第一次调用之后,amgettuple 必须准备好从最近一次返回条目的位置继续按任一方向推进扫描。(但如果 amcanbackward 为假,则后续所有调用的方向都必须与第一次相同。) + + + + 支持有序扫描的访问方法必须支持在扫描中标记一个位置,并在之后返回到该标记位置。同一个位置可能会被恢复多次。不过,每次扫描只需要记住一个位置;新的 ammarkpos 调用会覆盖先前标记的位置。不支持有序扫描的访问方法无须在 IndexAmRoutine 中提供 ammarkposamrestrpos 函数;把这些指针设为 NULL 即可。 + + + + 无论是扫描位置还是标记位置(如果有),在面对索引中的并发插入或删除时都必须保持一致。如果某个新插入的条目没有被一次扫描返回,而假如该条目在扫描开始前就已存在则本应被返回,这也是可以接受的;同样,即使某个条目在第一次扫描通过时未被返回,后续重新扫描或反向扫描时返回它也是可以接受的。类似地,并发删除也可能会或者不会反映在扫描结果中。重要的是,插入或删除本身不能导致扫描漏掉,或者重复返回那些本身并未被插入或删除的条目。 + + + + 如果索引存储的是原始被索引数据值(而不是它们的某种有损表示),那么它就适合支持 仅索引扫描,在这种扫描中索引返回的是实际数据,而不只是堆元组的 TID。只有当可见性映射显示该 TID 位于一个全部可见的页面上时,这才能避免 I/O;否则仍然必须访问堆元组来检查 MVCC 可见性。不过,这并不是访问方法需要关心的事。 + + + + 除了使用 amgettuple 外,索引扫描也可以使用 amgetbitmap,在一次调用中取出所有元组。与 amgettuple 相比,这可能明显更高效,因为它可以避免访问方法内部的反复加锁和解锁。原则上,amgetbitmap 应具有与重复调用 amgettuple 相同的效果,但为了简化实现,我们施加了若干限制。首先,amgetbitmap 一次性返回全部元组,因此不支持标记或恢复扫描位置。其次,元组是通过一个位图返回的,没有特定顺序,这也是为什么 amgetbitmap 不带 direction 参数的原因。(这种扫描也永远不会提供排序操作符。)此外,使用 amgetbitmap 时也没有仅索引扫描的支持,因为没有办法返回索引元组的内容。最后,amgetbitmap 不保证对返回的元组做任何锁定,其影响见 。 + + + + 请注意,如果访问方法的内部实现并不适合某一个 API,那么它可以只实现 amgetbitmap 而不实现 amgettuple,反之亦然。 + + + + + + 索引锁定注意事项 + + + 索引访问方法必须处理多个进程并发更新同一个索引的情况。PostgreSQL 核心系统会在索引扫描期间对索引获取 AccessShareLock,并在更新索引时(包括普通的 VACUUM)获取 RowExclusiveLock。由于这些锁类型彼此不冲突,所以访问方法必须自行处理它可能需要的任何细粒度锁定。对整个索引的 ACCESS EXCLUSIVE 锁只会在索引创建、销毁或 REINDEX 期间获取。 + + + + 构建一种支持并发更新的索引类型,通常需要对所需行为进行大量而细致的分析。对于 B-树和 hash 索引类型,可以阅读 src/backend/access/nbtree/READMEsrc/backend/access/hash/README 中涉及的设计决策。 + + + + 除了索引自身内部一致性的要求外,并发更新还会带来父表(即)与索引之间一致性的问题。由于 PostgreSQL 把对堆的访问和更新与对索引的访问和更新分离开来,因此存在一些窗口期,在这些窗口期内索引可能与堆不一致。我们通过以下规则处理这个问题: + + + + + 先创建新的堆条目,再创建它对应的索引条目。(因此并发索引扫描很可能看不到这个堆条目。这是可以接受的,因为索引读取者本来也不会关心未提交的行。但也请见 。) + + + + + 当某个堆条目将要被删除(由 VACUUM 执行)时,必须先删除它的所有索引条目。 + + + + + 索引扫描必须在保存 amgettuple 最近一次返回条目的索引页面上保持钉住状态,而 ambulkdelete 不能从被其他后端钉住的页面中删除条目。下面会解释为什么需要这条规则。 + + + + + 如果没有第三条规则,索引读取者就有可能在某个索引条目被 VACUUM 删除之前先看到它,然后在对应的堆条目已被 VACUUM 删除之后才到达那里。如果读取者到达时,该项号仍未被重新使用,就不会造成严重问题,因为空项槽位会被 heap_fetch() 忽略。但如果第三个后端已经把这个项槽位重新用作别的东西呢?在使用 MVCC 兼容快照时不会有问题,因为该槽位的新占用者一定太新,无法通过快照测试。然而,对于非 MVCC 兼容的快照(例如 SnapshotAny),就可能错误地接受并返回一行实际上并不匹配扫描键的数据。我们可以要求在所有情况下都对堆行重新检查扫描键,以防范这种情形,但那样代价太高。于是,我们改为把索引页面的钉住状态作为一种代理,表示读取者从索引条目到匹配堆条目的访问过程可能仍在进行中。让 ambulkdelete 因这种钉住状态而阻塞,能够保证 VACUUM 不会在读取者处理完之前删除对应的堆条目。这种方案运行时开销很小,只有在真正发生冲突的少数情况下才会增加阻塞开销。 + + + + 这种解决方案要求索引扫描是同步的:我们必须在扫描到相应索引条目之后立刻取出对应的堆元组。由于多种原因,这会比较昂贵。相反,异步扫描可以先从索引中收集许多 TID,再在稍后某个时刻访问堆元组,这样索引锁定的开销要小得多,也能实现更高效的堆访问模式。根据前面的分析,对于非 MVCC 兼容快照,我们必须采用同步方式;而对于使用 MVCC 快照的查询,异步扫描则是可行的。 + + + + 在 amgetbitmap 索引扫描中,访问方法不会为任何返回的元组保持索引页面的钉住状态。因此,只有把这种扫描与 MVCC 兼容的快照一起使用才是安全的。 + + + + 当未设置 ampredlocks 标志时,在可串行化事务中使用该索引访问方法的任何扫描,都会在整个索引上获取一个非阻塞谓词锁。这会与并发可串行化事务向该索引插入任何元组形成读写冲突。如果在一组并发可串行化事务之间检测到某些特定模式的读写冲突,为了保护数据完整性,其中某个事务可能会被取消。设置该标志则表示该索引访问方法实现了更细粒度的谓词锁,这通常会降低这类事务取消的频率。 + + + + + + 索引唯一性检查 + + + PostgreSQL 使用唯一索引来强制执行 SQL 唯一性约束;所谓唯一索引,就是不允许存在多个具有相同键值条目的索引。支持这一特性的访问方法会把 amcanunique 设为真。(目前只有 B-树支持这一点。) + + + + 由于 MVCC 的存在,索引中在物理上总是必须允许存在重复条目:这些条目可能指向同一个逻辑行的连续版本。我们真正想强制的行为是,任何 MVCC 快照都不能同时包含两行拥有相同索引键的记录。在向唯一索引插入一行新数据时,必须检查以下几种情况: + + + + + 如果某一条冲突的有效行已被当前事务删除,那么这是允许的。(特别是,因为一次 UPDATE 总是在插入新版本前删除旧版本,这就允许对某一行执行不改变键值的 UPDATE。) + + + + + 如果某条冲突的行是由一个尚未提交的事务插入的,那么当前准备插入的事务必须等待,看看那个事务是否提交。如果它回滚,就不存在冲突;如果它提交并且没有再次删除那条冲突行,就发生了唯一性违背。(实际上,我们只是等待另一个事务结束,然后把可见性检查整个重新做一遍。) + + + + + 类似地,如果某条冲突的有效行是由一个尚未提交的事务删除的,那么当前准备插入的事务必须等待该事务提交或中止,然后重新执行测试。 + + + + + + + 此外,就在按照上述规则报告唯一性违背之前,访问方法必须重新检查正在插入那一行的存活性。如果它已经是提交后死亡的状态,就不应报告违背。(这种情况不会出现在插入由当前事务刚创建的行这一普通场景中,但在 CREATE UNIQUE INDEX CONCURRENTLY 期间却可能发生。) + + + + 我们要求索引访问方法自行应用这些测试,这意味着它必须深入堆中检查那些根据索引内容显示为具有重复键的行的提交状态。毫无疑问,这样做既丑陋又不够模块化,但它避免了重复工作:如果我们单独再做一次探测,那么在寻找新行索引条目插入位置时,查找冲突行的索引搜索实际上就会被重复执行。更何况,除非把冲突检查作为插入新索引条目动作的一个组成部分,否则也没有明显的方法可以避免竞争条件。 + + + + 如果唯一约束是可延迟的,情况会更复杂:我们需要能够为新行插入一个索引条目,但把任何唯一性违背错误延迟到语句结束时甚至更晚才报告。为了避免对索引进行不必要的重复搜索,索引访问方法应在初始插入期间执行一次初步唯一性检查。如果这表明确实不存在冲突的存活元组,那么事情就结束了。否则,我们会安排在真正强制约束时再做一次重检。若在重检时,插入的元组与另外某个具有相同键值的元组都仍然存活,就必须报告错误。(注意,就此用途而言,存活实际上是指索引条目 HOT 链中至少有一个元组是存活的。)为实现这一点,传给 aminsert 函数的 checkUnique 参数会取以下值之一: + + + + + UNIQUE_CHECK_NO 表示不应执行唯一性检查(这不是唯一索引)。 + + + + + UNIQUE_CHECK_YES 表示这是一个不可延迟的唯一索引,必须按前述方式立即执行唯一性检查。 + + + + + UNIQUE_CHECK_PARTIAL 表示该唯一约束是可延迟的。PostgreSQL 会使用此模式为每一行插入索引条目。访问方法必须允许索引中出现重复条目,并通过让 aminsert 返回 FALSE来报告任何潜在的重复。对于每一行返回 FALSE的情况,系统都会安排一次延迟重检。 + + + + 访问方法必须识别任何可能违反唯一约束的行,但报告误报并不算错误。这使得检查可以在不等待其他事务结束的情况下完成;此处报告的冲突不会被当作错误处理,而会在之后重新检查,到那时它们可能已不再冲突。 + + + + + UNIQUE_CHECK_EXISTING 表示这是对某一行的延迟重检,此前该行被报告为可能违反唯一性。虽然这同样是通过调用 aminsert 来实现,但访问方法在这种情况下不得插入新的索引条目。该索引条目已经存在。相反,访问方法必须检查是否存在另一个存活的索引条目;如果存在,并且目标行本身也仍然存活,就应报告错误。 + + + + 建议在一次 UNIQUE_CHECK_EXISTING 调用中,访问方法进一步验证目标行在索引中确实已有现存条目;若没有,就报告错误。这样做是个好主意,因为传给 aminsert 的索引元组值会被重新计算。如果索引定义涉及并非真正 immutable 的函数,我们就可能在索引的错误区域上做检查。确认重检时确实找到了目标行,可以验证我们扫描的仍是原始插入时使用的同一组元组值。 + + + + + + + + + 索引代价估算函数 + + + amcostestimate 函数会收到描述某种可能索引扫描方式的信息,其中包括已经确定可用于该索引的 WHERE 子句和 ORDER BY 子句列表。它必须返回访问该索引的代价估算,以及 WHERE 子句选择率的估计值(也就是在索引扫描期间将从父表中检索出的行所占比例)。对于简单情况,代价估算器几乎所有工作都可以通过调用优化器中的标准例程来完成;之所以提供 amcostestimate 函数,是为了让索引访问方法能够提供与索引类型有关的专门知识,以便在可能时改进标准估计。 + + + + 每个 amcostestimate 函数都必须具有如下签名: + + +void +amcostestimate (PlannerInfo *root, + IndexPath *path, + double loop_count, + Cost *indexStartupCost, + Cost *indexTotalCost, + Selectivity *indexSelectivity, + double *indexCorrelation); + + + 前三个参数是输入参数: + + + + root + + + 规划器关于当前正在处理查询的信息。 + + + + + + path + + + 当前正在考虑的索引访问路径。除代价和选择率字段外,其余字段都有效。 + + + + + + loop_count + + + 在代价估算中应计入的索引扫描重复次数。当考虑在嵌套循环连接内部使用参数化扫描时,这个参数通常会大于 1。请注意,代价估算仍应只针对一次扫描;更大的 loop_count 只表示可以适当考虑多次扫描之间的一些缓存效应。 + + + + + + + + 最后四个参数是按引用传递的输出参数: + + + + *indexStartupCost + + + 设为索引启动处理的代价。 + + + + + + *indexTotalCost + + + 设为索引处理的总代价。 + + + + + + *indexSelectivity + + + 设为索引选择率。 + + + + + + *indexCorrelation + + + 设为索引扫描顺序与底层表顺序之间的相关系数。 + + + + + + + + + 请注意,代价估算函数必须用 C 编写,而不能用 SQL 或任何可用的过程语言,因为它们必须访问规划器/优化器的内部数据结构。 + + + + 索引访问代价应使用 src/backend/optimizer/path/costsize.c 所采用的参数来计算:顺序磁盘块读取的代价为 seq_page_cost,非顺序读取的代价为 random_page_cost,处理一条索引行的代价通常应取为 cpu_index_tuple_cost。此外,在索引处理期间调用的任何比较操作符(特别是对 indexquals 本身的求值)都应计入适当倍数的 cpu_operator_cost。 + + + + 访问代价应包括与扫描索引本身有关的全部磁盘和 CPU 代价,但包括取出或处理由索引标识出的父表行的代价。 + + + + 启动代价是整个扫描总代价中必须在开始取第一行之前先付出的那一部分。对大多数索引来说,这可以视为零;但启动代价较高的索引类型可能希望把它设为非零。 + + + + indexSelectivity 应设为在索引扫描期间将从父表中检索出的行的估计比例。对于有损查询,这个值通常会高于实际通过给定限定条件的行比例。 + + + + indexCorrelation 应设为索引顺序与表顺序之间的相关性(范围从 -1.0 到 1.0)。该值用于调整从父表取行代价的估计。 + + + + 当 loop_count 大于 1 时,返回的数字应是该索引任意一次扫描的期望平均值。 + + + + 代价估算 + + 一个典型的代价估算器会按如下步骤进行: + + + + + 基于给定的限定条件,估计并返回将被访问的父表行比例。如果没有任何与索引类型相关的专门知识,可以使用优化器的标准函数 clauselist_selectivity(): + + +*indexSelectivity = clauselist_selectivity(root, path->indexquals, + path->indexinfo->rel->relid, + JOIN_INNER, NULL); + + + + + + + 估计扫描期间将访问的索引行数。对许多索引类型来说,这等于 indexSelectivity 乘以索引中的行数,但也可能更多。(请注意,索引的页面数和行数可以从 path->indexinfo 结构体中取得。) + + + + + + 估计扫描期间将读取的索引页面数。它可能仅仅是 indexSelectivity 乘以索引总页面数。 + + + + + + 计算索引访问代价。一个通用估计器可能会这样做: + + +/* + * 通用假设是索引页面将按顺序读取, + * 因此每页代价为 seq_page_cost,而非 random_page_cost。 + * 此外,还要计入在每个索引行上对 indexquals 求值的代价。 + * 假定所有代价都在扫描过程中逐步付出。 + */ +cost_qual_eval(&index_qual_cost, path->indexquals, root); +*indexStartupCost = index_qual_cost.startup; +*indexTotalCost = seq_page_cost * numIndexPages + + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples; + + + 不过,上述做法没有考虑重复索引扫描之间索引读取的摊销效果。 + + + + + + 估计索引的相关性。对于单列上的简单有序索引,这个值可以从 pg_statistic 中取得。如果相关性未知,保守估计应为零(即无相关性)。 + + + + + + 代价估算器函数的示例可在 src/backend/utils/adt/selfuncs.c 中找到。 + + + diff --git a/zh/9.6/indices.sgml b/zh/9.6/indices.sgml new file mode 100644 index 00000000..48de681d --- /dev/null +++ b/zh/9.6/indices.sgml @@ -0,0 +1,759 @@ + + + + 索引 + + + 索引 + + + + 索引是增强数据库性能的一种常见方式。索引允许数据库服务器比没有索引时更快地找到并检索特定行。但索引也会给整个数据库系统带来额外开销,因此应当合理使用。 + + + + + 简介 + + + 假设我们有一个类似如下的表: + +CREATE TABLE test1 ( + id integer, + content varchar +); + + 并且应用会发出很多如下形式的查询: + +SELECT content FROM test1 WHERE id = constant; + + 如果没有预先准备,系统就必须逐行扫描整个test1表,以找出所有匹配项。如果test1中有很多行,而这种查询只会返回很少几行(甚至可能一行也没有,或者只有一行),这显然是低效的办法。但是如果系统被要求维护一个基于id列的索引,它就可以用更高效的方法定位匹配行。例如,它可能只需要在一棵搜索树中向下走几层。 + + + + 大多数非虚构类书籍也采用了类似的方法:读者经常查找的术语和概念会被收集到书末按字母顺序排列的索引中。感兴趣的读者可以相对快速地浏览索引并翻到相应页面,而不必通读整本书才能找到自己感兴趣的内容。正如作者需要预判读者可能查找哪些条目一样,数据库程序员也需要预见哪些索引会有用。 + + + + 如前所述,下列命令可用于在id列上创建索引: + +CREATE INDEX test1_id_index ON test1 (id); + + test1_id_index这个名字可以自由选择,但最好选一个以后仍能让你记住该索引用途的名字。 + + + + 要删除索引,使用DROP INDEX命令。索引可以在任何时候添加到表上,也可以在任何时候从表上删除。 + + + + 一旦创建了索引,就无需再进行额外干预:系统会在表被修改时更新索引,并且会在它认为这样做比顺序扫描表更高效时,在查询中使用该索引。不过,你可能仍需要定期运行ANALYZE命令来更新统计信息,以便查询规划器作出更有根据的决策。关于如何判断某个索引是否被使用,以及规划器何时和为何可能选择使用索引,可参见。 + + + + 索引还可以让带有搜索条件的UPDATEDELETE命令受益。索引也可以用于连接搜索。因此,定义在连接条件组成列上的索引也能显著加速带连接的查询。 + + + + 在大表上创建索引可能需要很长时间。默认情况下,PostgreSQL允许在创建索引期间并行执行读取(SELECT语句),但写入(INSERTUPDATEDELETE)会被阻塞,直到索引构建完成。在生产环境中,这往往不可接受。可以允许在创建索引时并行写入,但有若干注意事项需要了解,详见。 + + + + 索引创建之后,系统还必须使其与表保持同步。这会给数据操作增加开销。因此,那些在查询中很少使用或从不使用的索引应当被移除。 + + + + + + 索引类型 + + PostgreSQL提供了多种索引类型:B-树、Hash、GiST、SP-GiST、GIN 和 BRIN。每种索引类型都采用不同的算法,分别最适合不同类型的查询。默认情况下,CREATE INDEX命令创建的是 B-树索引,它适用于最常见的场景。 + + 索引 B-树 B-树 索引 + B-树能够处理可以按某种顺序排序的数据上的等值查询和范围查询。特别是,只要已索引列参与了下列任一操作符的比较,PostgreSQL查询规划器就会考虑使用 B-树索引: + + < <= = >= > + + 与这些操作符组合等价的构造,例如BETWEENIN,也可以通过 B-树索引搜索来实现。此外,索引列上的IS NULLIS NOT + NULL条件也可以配合 B-树索引使用。 + + + + 如果模式是常量,并且锚定在字符串起始位置,优化器也可以对涉及模式匹配操作符LIKE~的查询使用 B-树索引,例如col LIKE + 'foo%'col ~ '^foo',但不能用于col LIKE '%bar'。不过,如果你的数据库没有使用 C 区域设置,就需要以一个特殊操作符类来创建该索引,才能支持模式匹配查询的索引化,详见下文。B-树索引也可以用于ILIKE~*,但前提是模式以非字母字符开头,也就是不会受到大小写转换影响的字符。 + + + + B-树索引还可以用于按排序顺序取回数据。这并不总是比简单扫描再排序更快,但通常会很有帮助。 + + + + + 索引 + hash + + + hash + 索引 + Hash 索引只能处理简单的等值比较。只要已索引列参与的是使用=操作符的比较,查询规划器就会考虑使用 Hash 索引。使用以下命令创建 Hash 索引: +CREATE INDEX name ON table USING HASH (column); + + + + + + 哈希索引操作目前不会写入 WAL 日志,因此如果发生过未写入的更改,数据库崩溃后可能需要用REINDEX重建哈希索引。此外,在初始基础备份之后,哈希索引的更改不会通过流复制或基于文件的复制进行复制,因此它们会对随后使用它们的查询给出错误的答案。由于这些原因,目前不建议使用哈希索引。 + + + + 索引 GiST GiST 索引 + GiST 索引并不是某一种单独的索引,而是一种基础设施,可在其中实现许多不同的索引策略。因此,GiST 索引可使用哪些具体操作符,取决于所采用的索引策略(即操作符类)。例如,PostgreSQL标准发布版中包含了若干二维几何数据类型的 GiST 操作符类,它们支持使用下列操作符的索引化查询: + + << &< &> >> <<| &<| |&> |>> @> <@ ~= && + + (这些操作符的含义见。)标准发布版自带的 GiST 操作符类记录在中。还有许多其他 GiST 操作符类可在contrib集合或独立项目中获得。更多信息见。 + + + + GiST 索引还能够优化最近邻搜索,例如: + point '(101,456)' LIMIT 10; +]]> + + 这会找出距离给定目标点最近的十个地点。是否能做到这一点,同样取决于所使用的具体操作符类。在中,可以按这种方式使用的操作符列在排序操作符这一列中。 + + + 索引 SP-GiST SP-GiST 索引 + 与 GiST 一样,SP-GiST 索引也提供一种支持多种搜索的基础设施。SP-GiST 允许实现大量不同的、非平衡的、基于磁盘的数据结构,例如四叉树、k-d 树和基数树(trie)。例如,PostgreSQL标准发布版中包含了用于二维点的 SP-GiST 操作符类,它支持使用下列操作符的索引化查询: + + << >> ~= <@ <^ >^ + + (这些操作符的含义见。)标准发布版自带的 SP-GiST 操作符类记录在中。更多信息见。 + + + 索引 GIN GIN 索引 + GIN 索引是倒排索引,适用于包含多个组成值的数据值,例如数组。倒排索引会为每个组成值保存单独的项,因此能够高效处理测试特定组成值是否存在的查询。 + + + + 与 GiST 和 SP-GiST 一样,GIN 也能支持多种不同的用户定义索引策略,GIN 索引可使用哪些具体操作符同样取决于索引策略。例如,PostgreSQL标准发布版中包含用于一维数组的 GIN 操作符类,它们支持使用下列操作符的索引化查询: + + <@ @> = && + + (这些操作符的含义见。)标准发布版自带的 GIN 操作符类记录在中。还有许多其他 GIN 操作符类可在contrib集合或独立项目中获得。更多信息见。 + + + 索引 BRIN BRIN 索引 + BRIN 索引(块范围索引,Block Range Indexes)存储的是关于表中连续物理块范围内所保存值的摘要信息。与 GiST、SP-GiST 和 GIN 一样,BRIN 也可以支持多种不同的索引策略,而 BRIN 索引可使用哪些具体操作符取决于所采用的索引策略。对于具有线性排序顺序的数据类型,每个块范围上被索引的数据对应于该列值的最小值和最大值。这支持使用下列操作符的索引化查询: + + < <= = >= > + + 标准发布版自带的 BRIN 操作符类记录在中。更多信息见。 + + + + + + 多列索引 + + + 索引 + 多列 + + + + 一个索引可以定义在表的多个列上。例如,如果你有一个如下形式的表: + +CREATE TABLE test2 ( + major int, + minor int, + name varchar +); + + (假设你把/dev目录保存在数据库里……)并且经常发出如下查询: + +SELECT name FROM test2 WHERE major = constant AND minor = constant; + + 那么将majorminor两列一起建一个索引可能是合适的,例如: + +CREATE INDEX test2_mm_idx ON test2 (major, minor); + + + + 当前,只有 B-树、GiST、GIN 和 BRIN 索引类型支持多列索引。最多可以指定 32 列。(这个限制可以在构建 PostgreSQL 时修改;参见文件 pg_config_manual.h。) + + 多列 B-树索引可以用于涉及索引任意列子集的查询条件,但当对前导(最左)列存在约束时,索引效率最高。精确的规则是:对前导列的等值约束,再加上第一个没有等值约束的列上的任何不等约束,会被用来限制索引扫描的范围。这些列右侧列上的约束会在索引中进行检查,因此可以减少访问表本体的次数,但不会缩小必须扫描的索引范围。例如,给定一个 (a, b, c) 上的索引和查询条件 WHERE a = 5 AND b >= 42 AND c < 77,索引必须从第一个 a = 5 且 b = 42 的条目扫描到最后一个 a = 5 的条目。c >= 77 的索引项会被排除,但仍然必须扫描这些项。原则上,这个索引也可以用于只对 b 和/或 c 有约束、而对 a 没有约束的查询,但必须扫描整个索引,因此在大多数情况下,规划器会倾向于顺序扫描表,而不是使用该索引。 + + + 多列 GiST 索引可以用于涉及索引任意列子集的查询条件。附加列上的条件会限制索引返回的项,但决定索引需要扫描多少内容的,最重要的仍是第一列上的条件。如果第一列只有很少几个非重复值,即使其他列有很多非重复值,GiST 索引也会相对低效。 + + + + 多列 GIN 索引可以用于涉及索引任意列子集的查询条件。与 B-树或 GiST 不同,无论查询条件使用的是哪些索引列,GIN 的索引搜索效果都一样。 + + + + 多列 BRIN 索引可以用于涉及索引任意列子集的查询条件。和 GIN 一样、不同于 B-树或 GiST,无论查询条件使用的是哪些索引列,索引搜索效果都一样。在单个表上使用多个 BRIN 索引,而不是使用一个多列 BRIN 索引的唯一理由,是需要不同的pages_per_range存储参数。 + + + + 当然,每一列都必须配合适合该索引类型的操作符使用;涉及其他操作符的子句不会被考虑。 + + + + 多列索引应谨慎使用。在大多数情况下,单列索引已经足够,而且更省空间、也更省时间。除非表的使用方式极其程式化,否则超过三列的索引通常不会有帮助。关于不同索引配置优缺点的讨论,还可参见。 + + + + + + 索引和<literal>ORDER BY</literal> + + + 索引 + ORDER BY + + + + 除了简单地找到查询要返回的行之外,索引还可能能够以某种特定排序顺序交付这些行。这使得查询中的ORDER BY要求无需额外排序步骤即可满足。在PostgreSQL当前支持的索引类型中,只有 B-树 能产生有序输出,其他索引类型返回匹配行的顺序则未指定,并且依赖具体实现。 + + + + 规划器在满足ORDER BY要求时,会考虑两种方案:扫描一个与该要求匹配的可用索引,或者按物理顺序扫描表再显式排序。对于需要扫描表中很大一部分内容的查询,显式排序通常会比使用索引更快,因为它遵循顺序访问模式,需要的磁盘 I/O 更少。只需取出少量行时,索引更有价值。一个重要的特例是ORDER BYLIMIT n组合使用:显式排序必须处理所有数据才能找出最前面的n行,而如果有一个与ORDER BY匹配的索引,就可以直接取回前n行,而完全不必扫描剩余数据。 + + + + 默认情况下,B-树索引按升序存储其项,并将空值放在最后。这意味着,对列x上的索引进行前向扫描,会产生满足ORDER BY x的输出(更完整地说,是ORDER BY x ASC NULLS LAST)。索引也可以反向扫描,从而产生满足ORDER BY x DESC的输出(更完整地说,是ORDER BY x DESC NULLS FIRST,因为NULLS FIRSTORDER BY DESC的默认行为)。 + + + + 你可以在创建 B-树索引时通过指定ASCDESCNULLS FIRST和/或NULLS LAST选项来调整索引的排序方式,例如: + +CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST); +CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST); + + 一个按升序且空值在前存储的索引,根据扫描方向不同,可以满足ORDER BY x ASC NULLS FIRSTORDER BY x DESC NULLS LAST。 + + + + 你可能会疑惑,既然借助两个选项再加上反向扫描的可能性就能覆盖所有ORDER BY变体,为什么还要提供四个选项。对于单列索引,这些选项确实是冗余的,但对于多列索引它们就可能很有用。考虑一个(x, y)上的两列索引:前向扫描时它能满足ORDER BY x, y,反向扫描时能满足ORDER BY x DESC, y DESC。但应用也可能经常需要ORDER BY x ASC, y DESC。普通索引无法产生这种顺序,而如果把索引定义成(x ASC, y DESC)(x DESC, y ASC),就可以做到。 + + + + 显然,具有非默认排序方式的索引是一项相当专门的特性,但在某些查询上它们有时能带来巨大的加速。是否值得维护这样的索引,取决于你需要这种特殊排序的查询出现得有多频繁。 + + + + + + 组合多个索引 + + + 索引 + 组合多个索引 + + + + 位图扫描 + + + + 单次索引扫描只能利用那些在索引列上使用了该索引操作符类中操作符、并且彼此以AND连接的查询子句。例如,给定一个(a, b)上的索引,查询条件WHERE a = 5 AND b = 6可以使用该索引,而WHERE a = 5 OR b = 6这样的查询就不能直接使用该索引。 + + + + 幸运的是,PostgreSQL能够组合多个索引(包括同一个索引的多次使用),以处理单次索引扫描无法实现的情况。系统可以在多个索引扫描之间形成ANDOR条件。例如,WHERE x = 42 OR x = 47 OR x = 53 OR x = 99这样的查询,可以拆分成对x上的一个索引进行四次独立扫描,每次扫描使用一个查询子句。然后将这些扫描的结果做 OR 运算以得到最终结果。另一个例子是,如果我们在xy上分别有索引,那么WHERE x = 5 AND y = 6这样的查询,其一种可能实现方式就是分别用两个索引处理相应条件,然后将索引结果做 AND 运算,以确定结果行。 + + + + 为了组合多个索引,系统会扫描每个所需索引,并在内存中准备一个位图,给出满足该索引条件的表行位置。然后按查询需要对这些位图进行 AND 和 OR 运算。最后,再访问并返回实际的表行。表行是按物理顺序访问的,因为位图就是这样布局的;这意味着原始索引中的任何排序都会丢失,所以如果查询带有ORDER + BY子句,就需要单独的排序步骤。也正因为如此,再加上每多一次索引扫描都会增加额外时间,规划器有时会选择使用简单的单一索引扫描,即使还有其他本来也可以利用的索引。 + + + + 除了最简单的应用之外,通常会有多种可能有用的索引组合,数据库开发者必须权衡决定提供哪些索引。有时多列索引最好,但有时创建独立索引并依赖索引组合功能会更合适。例如,如果你的工作负载包含一组查询:有时只涉及列x,有时只涉及列y,有时同时涉及两列,那么你可以选择分别在xy上创建两个独立索引,并依赖索引组合来处理同时使用两列的查询。你也可以创建一个(x, y)上的多列索引。对于同时涉及两列的查询,这个索引通常会比索引组合更高效,但正如中所讨论的,它对于只涉及y的查询几乎没有用处,因此不应只创建这一个索引。将这个多列索引与一个单独的y索引组合起来,能够取得相当好的效果。对于只涉及x的查询,多列索引也可以使用,但它会比单独的x索引更大,因此更慢。最后一种选择是同时创建这三个索引,但这大概只有在该表被搜索的频率远高于被更新的频率,并且三类查询都很常见时才合理。如果其中一种查询远没有另外两种常见,那么你大概只需创建最适合常见查询类型的两个索引即可。 + + + + + + + 唯一索引 + + + 索引 + 唯一 + + + + 索引还可以用于强制列值唯一,或者强制多个列组合值唯一。 + +CREATE UNIQUE INDEX name ON table (column , ...); + + 当前,只有 B-树索引可以声明为唯一。 + + + + 当一个索引被声明为唯一时,就不允许存在多个具有相同索引值的表行。空值不被视为相等。多列唯一索引只会拒绝那种在多行中所有索引列都相等的情况。 + + + + 当为表定义唯一约束或主键时,PostgreSQL会自动创建一个唯一索引。该索引覆盖构成主键或唯一约束的列(必要时是多列索引),并且正是它在强制执行该约束。 + + + + + 无需手工在唯一列上创建索引;那样做只会重复自动创建的索引。 + + + + + + + 表达式索引 + + + 索引 + 基于表达式 + + + + 索引列不一定非得是底层表中的某一列,也可以是一个由表中一列或多列计算得到的函数或标量表达式。这个特性有助于基于计算结果来快速访问表。 + + + + 例如,进行大小写不敏感比较的一种常见方式,是使用lower函数: + +SELECT * FROM test1 WHERE lower(col1) = 'value'; + + 如果为lower(col1)函数的结果定义了索引,这个查询就可以使用该索引: + +CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1)); + + + + + 如果我们把这个索引声明为UNIQUE,那么它不仅会阻止创建那些col1值仅在大小写上不同的行,也会阻止创建col1值完全相同的行。因此,表达式索引可用于强制那些无法定义为简单唯一约束的约束。 + + + + 再例如,如果你经常执行如下查询: + +SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith'; + + 那么创建一个如下索引可能是值得的: + +CREATE INDEX people_names ON people ((first_name || ' ' || last_name)); + + + + + 正如第二个示例所示,CREATE INDEX命令的语法通常要求对索引表达式写上圆括号。当表达式只是一个函数调用时,则可以像第一个示例那样省略这些圆括号。 + + + + 维护索引表达式的代价相对较高,因为在插入每一行时以及每次更新时都必须计算派生表达式。不过,在使用索引搜索时,索引表达式不会被重新计算,因为它们已经存储在索引中。在上面的两个示例里,系统看到的查询只是WHERE indexedcolumn = 'constant',因此搜索速度与其他简单索引查询相同。因此,当检索速度比插入和更新速度更重要时,表达式索引会很有用。 + + + + + + 部分索引 + + + 索引 + 部分 + + + + 部分索引是建立在表的一个子集上的索引;这个子集由一个条件表达式定义(称为部分索引的谓词)。索引中只包含满足该谓词的表行的项。部分索引是一种专门特性,但有几种场景下它会很有用。 + + + + 使用部分索引的一个主要原因是避免索引常见值。由于搜索常见值(即占全部表行百分之几以上的值)的查询反正也不会使用索引,因此完全没有必要把这些行保留在索引里。这会减小索引尺寸,从而加快那些确实会使用该索引的查询。它也会加快很多表更新操作,因为索引并不需要在所有情况下都更新。展示了这种思路的一种可能应用。 + + + + 建立一个部分索引以排除常见值 + + + 假设你把 Web 服务器访问日志存储在数据库中。大多数访问来自你所在组织的 IP 地址范围,但也有一些来自其他地方(例如使用拨号连接的员工)。如果你按 IP 搜索时主要关心外部访问,那么你大概没有必要为对应于组织内子网的 IP 范围建索引。 + + + + 假设有一个如下表: + +CREATE TABLE access_log ( + url varchar, + client_ip inet, + ... +); + + + + + 要创建一个适合这个例子的部分索引,可使用如下命令: + +CREATE INDEX access_log_client_ip_ix ON access_log (client_ip) +WHERE NOT (client_ip > inet '192.168.100.0' AND + client_ip < inet '192.168.100.255'); + + + + 一个能够使用此索引的典型查询如下: +SELECT * +FROM access_log +WHERE url = '/index.html' AND client_ip = inet '212.78.10.32'; +以下查询无法使用此索引: +SELECT * +FROM access_log +WHERE client_ip = inet '192.168.100.23'; + + + + + 请注意,这类部分索引要求常见值事先可知,因此最适合用于数据分布不会变化的场景。也可以偶尔重建这类索引以适应新的数据分布,但那会增加维护工作量。 + + + + + 部分索引的另一种可能用途,是把典型查询工作负载不感兴趣的值排除在索引之外,如所示。这样会得到与上面相同的好处,但也意味着这些不感兴趣的值无法通过该索引访问,即使在那种情况下索引扫描可能是有利的。显然,为这种场景建立部分索引需要大量的谨慎和实验。 + + + + 建立一个部分索引以排除不感兴趣的值 + + + 如果你有一张表,其中同时包含已开账单和未开账单的订单,而未开账单订单只占整张表的一小部分,却是最常被访问的那些行,那么你可以通过只为未开账单的行创建索引来提升性能。创建该索引的命令如下: + +CREATE INDEX orders_unbilled_index ON orders (order_nr) + WHERE billed is not true; + + + + + 一个可能会使用该索引的查询是: + +SELECT * FROM orders WHERE billed is not true AND order_nr < 10000; + + 不过,这个索引也可以用于那些完全不涉及order_nr的查询,例如: + +SELECT * FROM orders WHERE billed is not true AND amount > 5000.00; + + 这不像在amount列上建立部分索引那样高效,因为系统必须扫描整个索引。不过,如果未开账单的订单相对较少,那么即使只是用这个部分索引来找出未开账单订单,也可能是值得的。 + + + + 请注意,下面这个查询不能使用该索引: + +SELECT * FROM orders WHERE order_nr = 3501; + + 因为订单 3501 可能属于已开账单订单,也可能属于未开账单订单。 + + + + + 也说明了:索引列和谓词中使用的列不必一致。PostgreSQL支持带任意谓词的部分索引,只要其中只涉及正在建立索引的那张表的列。不过要记住,谓词必须与那些希望从该索引受益的查询中使用的条件相匹配。更准确地说,只有当系统能够识别出查询的WHERE条件在数学上蕴含该索引的谓词时,部分索引才能用于该查询。PostgreSQL并没有一个复杂的定理证明器,来识别那些写法不同但数学上等价的表达式。(不仅构建这样一个通用定理证明器极其困难,而且它很可能也会慢到失去实际用途。)系统可以识别简单的不等式蕴含,例如x < 1蕴含x < 2;否则,谓词条件必须与查询WHERE条件的某一部分完全匹配,否则索引不会被识别为可用。匹配发生在查询规划阶段,而不是运行时。因此,参数化查询子句无法与部分索引配合工作。例如,一个带参数的预备查询可能写成x < ?,而它无法保证在参数的所有可能取值下都蕴含x < 2。 + + + + 部分索引的第三种可能用途,甚至不要求索引被查询使用。这里的思路是像那样,在表的一个子集上创建唯一索引。这样就能在满足索引谓词的那些行之间强制唯一性,而不会约束不满足谓词的行。 + + + + 建立一个部分唯一索引 + + + 假设我们有一张描述测试结果的表。我们希望确保对于给定的测试对象和目标组合,只有一条成功记录,但可以有任意多条失败记录。实现方法之一如下: + +CREATE TABLE tests ( + subject text, + target text, + success boolean, + ... +); + +CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) + WHERE success; + + 当成功测试很少而失败测试很多时,这是一种特别高效的方法。也可以通过创建一个带IS NULL限制的唯一部分索引,来让某一列只允许出现一个空值。 + + + + + + 最后,部分索引还可以用来影响系统的查询计划选择。同样,分布异常的数据集可能导致系统在实际上不应该使用索引的时候选择使用它。在这种情况下,可以把索引建成对那个有问题的查询不可用。通常,PostgreSQL会对索引使用作出合理选择(例如,它会在检索常见值时避免使用索引,因此前面的例子实际上只是节省索引空间,而不是为了避免使用索引所必需的),如果出现明显错误的计划选择,那就应当提交 bug 报告。 + + + + 请记住,建立部分索引意味着你至少与查询规划器一样了解情况,尤其是你知道索引何时可能是有利的。形成这种认识需要经验,以及对PostgreSQL中索引工作方式的理解。在大多数情况下,部分索引相比普通索引的优势都很小。 + + + + + 关于部分索引的更多信息可参见。 + + + + + + 操作符类和操作符族 + + + 操作符类 + + + + 操作符族 + + + 索引定义可以为索引的每一列指定一个操作符类 +CREATE INDEX name ON table (column opclass sort options , ...); +操作符类确定索引针对该列使用哪些操作符。例如,建立在类型int4上的 B-树索引会使用int4_ops类;该操作符类包含针对以下类型的值的比较函数:int4。实际中,列数据类型的默认操作符类通常就足够了。之所以需要操作符类,主要是因为对于某些数据类型,可能存在不止一种有意义的索引行为。例如,可能希望按绝对值或实部对复数数据类型排序。可以为该数据类型定义两个操作符类,并在创建索引时选择合适的类。操作符类决定基本排序顺序(随后可以通过添加排序选项进行调整:COLLATE, + ASC/DESC和/或NULLS FIRST/NULLS LAST)。 + + + + 除了默认操作符类之外,还有一些内置操作符类: + + + + + 操作符类text_pattern_opsvarchar_pattern_opsbpchar_pattern_ops分别支持类型textvarcharchar上的 B-树索引。它们与默认操作符类的区别在于,值是严格按字符逐个比较,而不是按照区域设置相关的排序规则比较。因此,当数据库未使用标准C区域设置时,这些操作符类适用于涉及模式匹配表达式(LIKE或 POSIX 正则表达式)的查询。例如,你可以像这样为一个varchar列建立索引: + +CREATE INDEX test_index ON test_table (col varchar_pattern_ops); + + 注意,如果你希望涉及普通<<=>>=比较的查询也能使用索引,那么还应该再创建一个使用默认操作符类的索引。这类查询不能使用xxx_pattern_ops操作符类。(不过普通等值比较是可以使用这些操作符类的。)同一列上可以创建多个使用不同操作符类的索引。如果你使用的是 C 区域设置,则不需要xxx_pattern_ops操作符类,因为带默认操作符类的索引在 C 区域设置下就可以用于模式匹配查询。 + + + + + + + 下面的查询会显示所有已定义的操作符类: + + +SELECT am.amname AS index_method, + opc.opcname AS opclass_name, + opc.opcintype::regtype AS indexed_type, + opc.opcdefault AS is_default + FROM pg_am am, pg_opclass opc + WHERE opc.opcmethod = am.oid + ORDER BY index_method, opclass_name; + + + + + 操作符类其实只是一个更大结构的子集,这个结构称为操作符族。当若干数据类型具有相似行为时,定义跨数据类型操作符并让索引支持它们,往往会很有用。为此,每种类型对应的操作符类都必须归入同一个操作符族。跨类型操作符属于该族,但不与该族中任何单独一个类关联。 + + + + 前一个查询的扩展版本会显示每个操作符类所属的操作符族: + +SELECT am.amname AS index_method, + opc.opcname AS opclass_name, + opf.opfname AS opfamily_name, + opc.opcintype::regtype AS indexed_type, + opc.opcdefault AS is_default + FROM pg_am am, pg_opclass opc, pg_opfamily opf + WHERE opc.opcmethod = am.oid AND + opc.opcfamily = opf.oid + ORDER BY index_method, opclass_name; + + + + + 下面这个查询会显示所有已定义的操作符族,以及每个族中包含的全部操作符: + +SELECT am.amname AS index_method, + opf.opfname AS opfamily_name, + amop.amopopr::regoperator AS opfamily_operator + FROM pg_am am, pg_opfamily opf, pg_amop amop + WHERE opf.opfmethod = am.oid AND + amop.amopfamily = opf.oid + ORDER BY index_method, opfamily_name, opfamily_operator; + + + + + + + 索引和排序规则 + + + 每个索引列只能支持一种排序规则。如果你需要关心多种排序规则,就可能需要多个索引。 + + + + 考虑以下语句: + +CREATE TABLE test1c ( + id integer, + content varchar COLLATE "x" +); + +CREATE INDEX test1c_content_index ON test1c (content); + + 该索引会自动使用底层列的排序规则。因此,下面这种形式的查询: + +SELECT * FROM test1c WHERE content > constant; + + 可以使用该索引,因为比较默认会使用该列的排序规则。不过,这个索引不能加速涉及其他排序规则的查询。因此,如果你也关心如下形式的查询: + +SELECT * FROM test1c WHERE content > constant COLLATE "y"; + + 那么可以额外创建一个支持"y"排序规则的索引,例如: + +CREATE INDEX test1c_content_y_index ON test1c (content COLLATE "y"); + + + + + + + 仅索引扫描 + + + 索引 + 仅索引扫描 + + + 仅索引扫描 + + + + PostgreSQL中的所有索引都是二级索引,也就是说,每个索引都与表的主数据区分开存储(在PostgreSQL术语中,这个主数据区称为表的)。这意味着,在普通索引扫描中,每次取回一行都需要同时从索引和堆中取数据。此外,尽管满足某个可索引WHERE条件的索引项通常在索引中彼此接近,但它们引用的表行却可能分布在堆中的任何位置。因此,索引扫描的堆访问部分会涉及大量对堆的随机访问,这可能很慢,尤其是在传统旋转介质上。(正如中所述,位图扫描试图通过按排序顺序进行堆访问来缓解这项代价,但那也只能缓解到一定程度。) + + + + 为了解决这个性能问题,PostgreSQL支持仅索引扫描,它可以仅凭索引而不访问堆来回答查询。基本思路是直接从每个索引项中返回值,而不是再去查对应的堆项。要使用这种方法,有两个根本限制: + + + + + 索引类型必须支持仅索引扫描。B-树索引总是支持。GiST 和 SP-GiST 索引对某些操作符类支持仅索引扫描,但对另一些则不支持。其他索引类型则完全不支持。底层要求是,索引必须实际存储原始数据值,或者至少能够重建出每个索引项对应的原始数据值。反例是 GIN 索引,它不能支持仅索引扫描,因为每个索引项通常只保存原始数据值的一部分。 + + + + + + 查询只能引用存储在索引中的列。例如,假设某个表的xy列上有一个索引,且该表还有一列z,那么下面这些查询可以使用仅索引扫描: + +SELECT x, y FROM tab WHERE x = 'key'; +SELECT x FROM tab WHERE x = 'key' AND y < 42; + + 但下面这些查询则不能: + +SELECT x, z FROM tab WHERE x = 'key'; +SELECT x FROM tab WHERE x = 'key' AND z < 42; + + (表达式索引和部分索引会让这条规则变得更复杂,下文会讨论。) + + + + + + + 如果这两个基本要求满足,那么查询所需的所有数据值都能从索引中取得,因此从物理上说仅索引扫描是可行的。不过,在PostgreSQL中,任何表扫描还有一个额外要求:它必须验证每个取回的行对该查询的 MVCC 快照是否可见,如所述。可见性信息并不保存在索引项中,而只保存在堆项中;因此乍看之下,似乎每次取回行无论如何都要访问堆。这在表行最近被修改过时的确如此。然而,对于很少变化的数据,这个问题有办法绕开。PostgreSQL会跟踪表堆中每个页面是否其中所有行都已经足够老,以至于对当前和未来所有事务都可见。这个信息保存在该表的可见性映射中的一个位里。仅索引扫描在找到候选索引项后,会检查对应堆页面的可见性映射位。如果该位已设置,那么这行就已知可见,数据可以直接返回而无需进一步工作。如果没有设置,就必须访问堆项来判断该行是否可见,这样相对标准索引扫描就没有性能优势。即使在成功的情况下,这种做法也是用访问可见性映射来替代访问堆;但由于可见性映射比它描述的堆小四个数量级,访问它所需的物理 I/O 要少得多。在大多数场景下,可见性映射始终都会缓存于内存中。 + + + + 简而言之,尽管满足那两个基本要求时就有可能使用仅索引扫描,但只有当表中相当一部分堆页的全部可见(all-visible)映射位已被设置时,它才会带来收益。不过,很多表都会有相当大一部分行长期不变,因此这种扫描方式在实践中非常有用。 + + + 为了有效利用仅索引扫描,可以创建这样的索引:只有前导列用于匹配WHERE子句,而后面的列保存查询需要返回的负载数据。例如,如果经常执行如下查询: +SELECT y FROM tab WHERE x = 'key'; +加快此类查询的传统做法,是仅在以下列上创建索引:x。但是,在(x, y)上创建索引,就可能通过仅索引扫描实现该查询。如前所述,这种索引会比仅在x上创建的索引更大,因而代价也更高,所以只有在已知表基本静态的情况下,这种做法才有吸引力。注意,索引必须声明在(x, y)上,而不是(y, x)上,因为对于大多数索引类型(尤其是 B-树),不约束索引前导列的搜索效率不高。 + + + 原则上,仅索引扫描也可以和表达式索引一起使用。例如,给定一个f(x)上的索引,其中x是表的一列,那么按理说应该可以把 + +SELECT f(x) FROM tab WHERE f(x) < 1; + + 执行成一次仅索引扫描;如果f()是一个计算代价很高的函数,这会非常有吸引力。不过,PostgreSQL的规划器目前在这种情况上还不够聪明。它只会在查询所需的所有都能从索引取得时,才认为查询可能通过仅索引扫描执行。在这个例子里,除了在f(x)这个上下文中,x本身并不需要,但规划器意识不到这一点,因此得出无法做仅索引扫描的结论。如果仅索引扫描看起来足够值得,可以通过在(f(x), x)上声明索引来绕过这一点。第二列实际上并不预期会被使用,加入它只是为了让规划器认为仅索引扫描是可行的。 + 还有一个额外注意事项:如果目标是避免重新计算f(x),那么规划器不一定会把那些不在可索引WHERE子句中的f(x)用法与索引列匹配起来。对于上面展示的简单查询,它通常能做对,但对于涉及连接的查询则不能。未来版本的PostgreSQL可能会修复这些不足。 + + + + 部分索引与仅索引扫描之间也有有趣的相互作用。考虑中展示的这个部分索引: + +CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) + WHERE success; + + 原则上,我们可以在这个索引上做仅索引扫描,以满足如下查询: + +SELECT target FROM tests WHERE subject = 'some-subject' AND success; + + 但这里有个问题:WHERE子句引用了success,而它并不能作为索引的结果列取得。尽管如此,仍然可能做仅索引扫描,因为执行计划在运行时不需要重新检查WHERE子句的这一部分:索引中找到的所有项都必然满足success = true,因此计划里无需显式检查它。PostgreSQL 9.6 及更高版本能够识别这种情况,并允许生成仅索引扫描;更早的版本则不能。 + + + + + + 检查索引使用情况 + + + 索引 + 检查使用情况 + + + + 尽管PostgreSQL中的索引不需要维护或调优,但检查真实查询工作负载到底实际使用了哪些索引,仍然很重要。检查某个单独查询的索引使用情况,可以使用命令;它在这方面的应用见。也可以像中描述的那样,在运行中的服务器上收集索引使用的总体统计信息。 + + + + 很难给出一个确定应该创建哪些索引的通用流程。前面各节的示例已经展示了若干典型情形。通常需要做大量实验。下面这部分会给出一些相关提示: + + + + + + 一定要先运行。这个命令会收集表中值分布的统计信息。估计查询会返回多少行,需要这些信息;而规划器要为每种可能的查询计划分配现实的代价,也需要这些信息。如果没有真实统计信息,系统就会假定一些默认值,而这些默认值几乎肯定并不准确。因此,在没有运行ANALYZE的情况下去检查应用的索引使用情况,基本上是徒劳的。更多信息见。 + + + + + + 用真实数据做实验。使用测试数据来设置索引,只会告诉你测试数据需要什么索引,仅此而已。 + + + + 特别要避免使用非常小的测试数据集。虽然在 100000 行里选 1000 行可能适合用索引,但在 100 行里选 1 行几乎不适合,因为这 100 行大概都能放进一个磁盘页,而没有任何计划能比顺序取一个磁盘页更快。 + + + + 另外,在编造测试数据时也要小心;如果应用尚未投入生产,这往往无法避免。非常相似、完全随机、或者按排序顺序插入的值,都会让统计信息偏离真实数据应有的分布。 + + + + + + 当索引没有被使用时,强制使用它们对测试会很有帮助。有一些运行时参数可以关闭不同的计划类型(见)。例如,关闭顺序扫描(enable_seqscan)和嵌套循环连接(enable_nestloop)这两种最基本的计划,就会迫使系统使用不同的计划。如果系统仍然选择顺序扫描或嵌套循环连接,那么索引未被使用很可能有更根本的原因,例如查询条件并不匹配该索引。(前面各节已经解释了什么样的查询可以使用什么样的索引。) + + + + + + 如果强制使用索引后确实使用了索引,那么就有两种可能:要么系统是对的,使用索引确实不合适;要么查询计划的代价估计并未反映现实。因此,你应该分别在有索引和无索引的情况下对查询计时。EXPLAIN ANALYZE命令在这里会很有帮助。 + + + + + + 如果发现代价估计是错的,那么又有两种可能。总代价是根据每个计划节点的逐行代价乘以该计划节点的选择率估计计算出来的。计划节点的估计代价可以通过运行时参数来调整(见)。而选择率估计不准确,则是因为统计信息不足。可以尝试通过调整统计信息收集参数来改进这一点(见)。 + + + + 如果你无法把这些代价调得更合适,那么可能就不得不退而求其次,显式强制使用索引。你也可能希望联系PostgreSQL开发者来研究这个问题。 + + + + + diff --git a/zh/9.6/info.sgml b/zh/9.6/info.sgml new file mode 100644 index 00000000..9880ab4f --- /dev/null +++ b/zh/9.6/info.sgml @@ -0,0 +1,55 @@ + + + + 更多信息 + + + 除了本文档,也就是本书之外,还有其他关于PostgreSQL的资源: + + + + + Wiki + + + PostgreSQL wiki 收录了该项目的FAQ + (常见问题解答)列表、TODO 列表,以及许多其他主题的详细信息。 + + + + + + 网站 + + + PostgreSQL + 网站 + 提供最新版本的详细信息,以及其他能让你在工作中或日常使用 + PostgreSQL时更加高效的信息。 + + + + + + 邮件列表 + + + 邮件列表是一个让你的问题得到解答、与其他用户分享经验以及联系开发者的好地方。详情请参阅PostgreSQL网站。 + + + + + + 你自己! + + + PostgreSQL是一个开源项目。因此,它依赖用户社区提供持续支持。随着你开始使用PostgreSQL,无论是通过本文档还是通过邮件列表,你都会依赖他人的帮助。请考虑把你的知识回馈给社区。阅读邮件列表并回答问题。如果你学到了一些文档中尚未包含的内容,就把它写出来并贡献出去。如果你为代码增加了功能,也请将其贡献出来。 + + + + + + diff --git a/zh/9.6/information_schema.sgml b/zh/9.6/information_schema.sgml new file mode 100644 index 00000000..b36f2fc3 --- /dev/null +++ b/zh/9.6/information_schema.sgml @@ -0,0 +1,6949 @@ + + + + 信息模式 + + + 信息模式 + + + + 信息模式由一组视图组成,其中包含当前数据库中定义的对象的信息。信息模式由 SQL 标准定义,因此可以预期它具有可移植性并保持稳定;而系统目录则不同,它们是 PostgreSQL 特有的,并且是围绕实现方面的考虑建模的。不过,信息模式视图不包含 PostgreSQL 特有功能的信息;要查询这些信息,需要查询系统目录或其他 PostgreSQL 特有视图。 + + + + + 当在数据库中查询约束信息时,一个期望返回一行的标准兼容的查询可能返回多行。这是因为 SQL 标准要求约束名在一个模式中唯一,但是PostgreSQL并不强制这种限制。PostgreSQL自动产生的约束名避免在相同的模式中重复,但是用户能够指定这种重复的名称。 + + + + 这个问题可能在查询信息模式视图时出现,例如check_constraint_routine_usage、 + check_constraintsdomain_constraints和 + referential_constraints。一些其他视图也有相似的问题,但是它们包含了表名来帮助区分重复行,例如constraint_column_usage、 + constraint_table_usagetable_constraints。 + + + + + + 模式 + + + 信息模式本身是一个名为information_schema的模式。这个模式自动存在于所有数据库中。这个模式的拥有者是集簇中的初始数据库用户,并且该用户自然地拥有这个模式上的所有权限,包括删除它的能力(但是这样节省的空间是很小的)。 + + + + 默认情况下,信息模式不在模式搜索路径中,因此你需要使用限定名访问其中的所有对象。因为信息模式中的某些对象的名称是可能出现在用户应用中的一般名称,如果你想把该信息模式放在路径中,你应该小心。 + + + + + 数据类型 + + + 信息模式视图的列使用在信息模式中定义的特殊数据类型。这些类型被定义为普通内置类型之上的简单域。不应在信息模式之外使用这些类型,但如果应用要从信息模式中查询数据,就必须能够处理它们。 + + + 这些类型为: + + cardinal_number + + + 一种非负整数。 + + + + + + character_data + + + 一种字符串(没有指定最大长度)。 + + + + + + sql_identifier + + + 一种字符串。该类型用于 SQL 标识符,其他任何文本数据则使用类型character_data。 + + + + + + time_stamp + + + 在类型timestamp with time zone之上的一个域。 + + + + + + yes_or_no + + + 一种字符串域,只包含YESNO。它用于在信息模式中表示布尔(真/假)数据。(信息模式是在类型boolean被纳入 SQL 标准之前设计出来的,因此需要采用这种约定以保持向后兼容。) + + + + 信息模式中的每一列都具有这五种类型之一。 + + + + <literal>information_schema_catalog_name</literal> + + + information_schema_catalog_name 是一个表,它始终只包含一行一列,用于存放当前数据库的名称(按 SQL 术语称为当前目录)。 + + + + <literal>information_schema_catalog_name</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + catalog_name + sql_identifier + + 包含这个信息模式的数据库名称 + + + + +
+
+ + + <literal>administrable_role_authorizations</literal> + + + 视图administrable_role_authorizations标识当前用户对其有管理选项的所有角色。 + + + + <literal>administrable_role_authorizations</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantee + sql_identifier + + 被授予该角色成员资格的角色名称(可以是当前用户,也可以是在嵌套角色成员资格情况下的另一个角色) + + + + + role_name + sql_identifier + + 一个角色的名称 + + + + + is_grantable + yes_or_no + + 总是 YES + + + + +
+
+ + + <literal>applicable_roles</literal> + + + 视图applicable_roles标识当前用户可以使用其权限的所有角色。这意味着从当前用户到相关角色之间存在某条角色授予链。当前用户本身也是一个适用角色。适用角色的集合通常用于权限检查。 + applicable role + roleapplicable + + + + <literal>applicable_roles</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantee + sql_identifier + + 被授予该角色成员资格的角色名称(可以是当前用户,也可以是在嵌套角色成员资格情况下的另一个角色) + + + + + role_name + sql_identifier + + 一个角色的名称 + + + + + is_grantable + yes_or_no + + 如果被授权人在角色上有管理选项则为YES ,如果没有则为NO + + + + +
+
+ + + <literal>attributes</literal> + + + 视图attributes包含数据库中定义的组合数据类型的属性信息。(注意,该视图不提供表列的信息,而表列在 PostgreSQL 的语境中有时也被称为属性。)只有当前用户能够访问的那些属性才会显示出来(通过拥有该类型或拥有其上的某种权限)。 + + + + <literal>attributes</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + udt_catalog + sql_identifier + + 包含该数据类型的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 包含该数据类型的模式名称 + + + + + udt_name + sql_identifier + + 数据类型名称 + + + + + attribute_name + sql_identifier + + 属性名称 + + + + + ordinal_position + cardinal_number + + 属性在该数据类型内部的顺序位置(从 1 开始计算) + + + + + attribute_default + character_data + + 该属性的默认表达式 + + + + + is_nullable + yes_or_no + + 如果该属性是可能为空的,值为YES,否则为NO。 + + + + + data_type + character_data + + 如果该属性是一个内置类型,此列值为该属性的数据类型; + 如果该属性是某种数组,此列值为ARRAY(在这种情况下,见视图element_types); + 其他情况,此列值为USER-DEFINED(在这种情况下,该类型在attribute_udt_name和相关列中标识)。 + + + + + character_maximum_length + cardinal_number + + 如果data_type标识字符类型或位串类型,这里是声明的最大长度;如果未声明最大长度,或者对于所有其他数据类型,则为空。 + + + + + character_octet_length + cardinal_number + + 如果data_type标识字符类型,这里是一个数据值可能的最大字节长度;对于所有其他数据类型则为空。 + 最大字节长度取决于声明的字符最大长度(见上文)和服务器编码。 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 包含该属性排序规则的数据库名称(总是当前数据库);如果使用默认排序规则,或该属性的数据类型不支持排序规则,则为 null。 + + + + + collation_schema + sql_identifier + + 包含该属性排序规则的模式名称;如果使用默认排序规则,或该属性的数据类型不支持排序规则,则为 null。 + + + + + collation_name + sql_identifier + + 该属性排序规则的名称;如果使用默认排序规则,或该属性的数据类型不支持排序规则,则为 null。 + + + + + numeric_precision + cardinal_number + + 如果data_type标识一种数字类型,这列包含这个属性类型的(声明的或隐式的)精度。精度指示了有效位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。对于所有其他数据类型,这一列为空。 + + + + + numeric_precision_radix + cardinal_number + + 如果data_type标识一种数字类型,这一列指示numeric_precisionnumeric_scale列中的值是基于什么来表示。 + 该值为 2 或 10。对于所有其他数据类型,这一列为空。 + + + + + numeric_scale + cardinal_number + + 如果data_type标识精确数字类型,这一列包含该属性类型的标度(声明的或隐式的)。 + 标度表示小数点右侧的有效数字位数。它可以按列numeric_precision_radix的指定表示为十进制(基数 10)或二进制(基数 2)。 + 对于所有其他数据类型,这一列为空。 + + + + + datetime_precision + cardinal_number + + 如果data_type标识一种日期、时间、时间戳或时间间隔类型,这一列包含这个属性类型的(声明的或隐式的)分数秒的精度,也就是秒值的小数点后的十进制位数。 + 对于所有其他数据类型,这一列为空。 + + + + + interval_type + character_data + + 如果data_type标识一种时间间隔类型,这一列包含时间间隔为这个属性包括哪些域的声明,例如YEAR TO MONTHDAY TO SECOND等等。 + 如果没有指定域限制(也就是该时间间隔接受所有域),并且对于所有其他数据类型,这个域为空。 + + + + + interval_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性(关于 interval 类型属性的分数秒精度,见datetime_precision) + + + + + attribute_udt_catalog + sql_identifier + + 属性数据类型被定义的数据库名(总是当前数据库) + + + + + attribute_udt_schema + sql_identifier + + 属性数据类型被定义的模式名 + + + + + attribute_udt_name + sql_identifier + + 属性数据类型的名称 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该列的数据类型描述符的一个标识符,在从属于该表的数据类型标识符之中唯一。 + 这主要用于与这类标识符的其他实例进行连接(该标识符的指定格式没有被定义并且不保证在未来的版本中保持相同)。 + + + + + is_derived_reference_attribute + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + +
+ + + 关于某些列的详情,参见之下的一个相似结构的视图。 + +
+ + + <literal>character_sets</literal> + + + 视图character_sets标识当前数据库中可用的字符集。因为 PostgreSQL 不支持在同一个数据库中有多个字符集,这个视图只显示一个字符集,它就是数据库编码。 + + + 请注意 SQL 标准中以下术语的用法: + + character repertoire + + + 一个抽象的字符集合,例如UNICODEUCS或 + LATIN1。不作为SQL对象公开,但在此视图中可见。 + + + + + + character encoding form + + + 一种字符集的编码形式。大多数旧的字符集只使用一种编码形式,因此它们没有单独的名称 + (例如,LATIN1是适用于LATIN1字符集的编码形式)。 + 但是例如Unicode有编码形式UTF8UTF16等 + (并非所有都受PostgreSQL支持)。编码形式不作为SQL对象公开,但在此视图中可见。 + + + + + + character set + + + 一个命名的SQL对象,用于标识字符集、字符编码和默认排序规则。预定义的字符集通常与编码形式 + 同名,但用户可以定义其他名称。例如,字符集UTF8通常会标识字符集 + UCS,编码形式UTF8和一些默认排序规则。 + + + + 在 PostgreSQL 中,可以把编码视为字符集,也可以视为字符编码形式。它们具有相同的名称,并且一个数据库中只能有一个。 + + + <literal>character_sets</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + character_set_catalog + sql_identifier + + 当前字符集并未被实现为模式对象,因此这一列为空。 + + + + + character_set_schema + sql_identifier + + 当前字符集并未被实现为模式对象,因此这一列为空。 + + + + + character_set_name + sql_identifier + + 该字符集的名字,当前实现为显示该数据库编码的名字 + + + + + character_repertoire + sql_identifier + + 字符集合;如果编码为UTF8则显示UCS,否则仅显示编码名称 + + + + + form_of_use + sql_identifier + + 字符编码形式,与数据库编码相同 + + + + + default_collate_catalog + sql_identifier + + 包含该默认排序规则的数据库名(如果任意排序规则被标识,总是当前数据库) + + + + + default_collate_schema + sql_identifier + + 包含该默认排序规则的模式名 + + + + + default_collate_name + sql_identifier + + 默认排序规则的名字。该默认排序规则被标识为匹配当前数据库的COLLATECTYPE设置的排序规则。 + 如果没有这样的排序规则,则这一列以及相关的模式列和目录列都为空。 + + + + +
+
+ + + <literal>check_constraint_routine_usage</literal> + + + 视图check_constraint_routine_usage标识检查约束所使用的例程(函数和过程)。只有当前启用角色拥有的那些例程才会显示出来。 + + + + <literal>check_constraint_routine_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含约束的模式的名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + +
+
+ + + <literal>check_constraints</literal> + + + 视图check_constraints包含所有检查约束,无论它们是定义在表上还是定义在域上,只要它们归当前启用角色所有即可显示(表或域的拥有者就是约束的拥有者)。 + + + + <literal>check_constraints</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含约束的模式的名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + check_clause + character_data + + 该检查约束的检查表达式 + + + + +
+
+ + + <literal>collations</literal> + + + 视图collations包含在当前数据库中可用的排序规则。 + + + + <literal>collations</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + collation_catalog + sql_identifier + + 包含该排序规则的数据库名称(总是当前数据库) + + + + + collation_schema + sql_identifier + + 包含该排序规则的模式名称 + + + + + collation_name + sql_identifier + + 默认排序规则的名称 + + + + + pad_attribute + character_data + + 总是NO PAD(另一种选择PAD SPACE没有被 PostgreSQL 支持) + + + + +
+
+ + + <literal>collation_character_set_applicability</literal> + + + 视图collation_character_set_applicability标识可用的排序规则适用于哪些字符集。在 PostgreSQL 中,每个数据库中只有一种字符集(解释见),因此这个视图没有提供很有用的信息。 + + + + <literal>collation_character_set_applicability</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + collation_catalog + sql_identifier + + 包含该排序规则的数据库名称(总是当前数据库) + + + + + collation_schema + sql_identifier + + 包含该排序规则的模式名称 + + + + + collation_name + sql_identifier + + 默认排序规则的名称 + + + + + character_set_catalog + sql_identifier + + 字符集当前还未被实现为模式对象,所以这一列为空 + + + + + character_set_schema + sql_identifier + + 字符集当前还未被实现为模式对象,所以这一列为空 + + + + + character_set_name + sql_identifier + + 字符集名称 + + + + +
+
+ + + <literal>column_domain_usage</literal> + + + 视图column_domain_usage标识所有使用定义在当前数据库中并且被一个当前启用的角色拥有的域的列(表列或视图列)。 + + + + <literal>column_domain_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + domain_catalog + sql_identifier + + 包含该域的数据库名称(总是当前数据库) + + + + + domain_schema + sql_identifier + + 包含该域的模式名称 + + + + + domain_name + sql_identifier + + 域名称 + + + + + table_catalog + sql_identifier + + 包含表的数据库的名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含表的模式的名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + column_name + sql_identifier + + 列名称 + + + + +
+
+ + + <literal>column_options</literal> + + + 视图column_options包含为当前数据库中外部表列定义的所有选项。只有当前用户能够访问(作为拥有者或具有某些权限)的那些外部表列才被显示。 + + + + <literal>column_options</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含该外部表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该外部表的模式名称 + + + + + table_name + sql_identifier + + 外部表名称 + + + + + column_name + sql_identifier + + 列名称 + + + + + option_name + sql_identifier + + 一个选项名称 + + + + + option_value + character_data + + 该选项的值 + + + + +
+
+ + + <literal>column_privileges</literal> + + + 视图column_privileges标识所有授予给一个当前启用的角色或者被一个当前启用的角色授予的权限。对每一个列、授予者、被授予者的组合只有一行。 + + + + 如果一个权限被授予在一整个表上,它在这个视图中被显示为在每一列上授予,但是只有可用于列粒度的权限类型才会这样: + SELECTINSERT、 + UPDATEREFERENCES。 + + + + <literal>column_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + table_catalog + sql_identifier + + 包含该列的表所在的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该列的表所在的模式名称 + + + + + table_name + sql_identifier + + 包含该列的表名称 + + + + + column_name + sql_identifier + + 列名称 + + + + + privilege_type + character_data + + 权限类型:SELECTINSERTUPDATEREFERENCES + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>column_udt_usage</literal> + + + 视图column_udt_usage标识所有使用被一个当前启用的角色拥有的数据类型的列。注意在PostgreSQL中,内置数据类型的行为和用户定义的类型相似,因此它们也被包括在这里。详见。 + + + + <literal>column_udt_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + udt_catalog + sql_identifier + + 该列数据类型(如果适用,底层的域类型)被定义的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 该列数据类型(如果适用,底层的域类型)被定义的模式名称 + + + + + udt_name + sql_identifier + + 该列数据类型(如果适用,底层的域类型)的名称 + + + + + table_catalog + sql_identifier + + 包含表的数据库的名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含表的模式的名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + column_name + sql_identifier + + 列名称 + + + + +
+
+ + + <literal>columns</literal> + + + 视图columns包含数据库中有关所有表列(或视图列)的信息。系统列(oid等)不被包括在内。只有那些当前用户能够访问(作为拥有者或具有某些权限)的列才被显示。 + + + + <literal>columns</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含表的数据库的名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含表的模式的名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + column_name + sql_identifier + + 列名称 + + + + + ordinal_position + cardinal_number + + 该列在表内的顺序位置(从 1 开始计) + + + + + column_default + character_data + + 该列的默认表达式 + + + + + is_nullable + yes_or_no + + 如果该列可以为空,则为YES,否则为NO。一个非空约束是让一列成为不能为空的方法,但还有其他方法。 + + + + + data_type + character_data + + 如果该列的数据类型是一种内置类型,则为该列的数据类型;如果是某种数组(此种情况见视图element_types),则为ARRAY;否则为USER-DEFINED(此种情况下该类型被标识在udt_name和相关列中)。 + 如果该列基于一个域,这一列引用该域底层的类型(该列被标识在domain_name和相关列中)。 + + + + + character_maximum_length + cardinal_number + + 如果data_type标识一个字符或位串类型,这里是声明的最大长度;如果没有声明最大长度,则对于所有其他数据类型为空。 + + + + + character_octet_length + cardinal_number + + 如果data_type标识一个字符类型,这里是一个数据的最大可能长度(以字节计);对其他所有数据类型为空。 + 最大字节长度取决于声明的字符最大长度(见上文)和服务器编码。 + + + + + numeric_precision + cardinal_number + + 如果data_type标识一种数字类型,这列包含这个属性类型的(声明的或隐式的)精度。精度指示了有效位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。对于所有其他数据类型,这一列为空。 + + + + + numeric_precision_radix + cardinal_number + + 如果data_type标识一种数字类型,这一列指示numeric_precisionnumeric_scale列中的值是基于什么来表示。 + 该值为 2 或 10。对于所有其他数据类型,这一列为空。 + + + + + numeric_scale + cardinal_number + + 如果data_type标识一种准确数字类型,这列包含这个属性类型的(声明的或隐式的)比例。 + 标度表示小数点右侧的有效数字位数。它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。 + 对于所有其他数据类型,这一列为空。 + + + + + datetime_precision + cardinal_number + + 如果data_type标识一种日期、时间、时间戳或时间间隔类型,这一列包含这个属性类型的(声明的或隐式的)分数秒的精度,也就是秒值的小数点后的十进制位数。 + 对于所有其他数据类型,这一列为空。 + + + + + interval_type + character_data + + 如果data_type标识一种时间间隔类型,这一列包含时间间隔为这个属性包括哪些域的声明,例如YEAR TO MONTHDAY TO SECOND等等。 + 如果没有指定域限制(也就是该时间间隔接受所有域),并且对于所有其他数据类型,这个域为空。 + + + + + interval_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性(关于时间间隔类型属性的分数秒精度可见datetime_precision) + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 包含该列排序规则的数据库名称(总是当前数据库);如果使用默认排序规则,或该列的数据类型不支持排序规则,则为 null。 + + + + + collation_schema + sql_identifier + + 包含该列排序规则的模式名称;如果使用默认排序规则,或该列的数据类型不支持排序规则,则为 null。 + + + + + collation_name + sql_identifier + + 该列排序规则的名称;如果使用默认排序规则,或该列的数据类型不支持排序规则,则为 null。 + + + + + domain_catalog + sql_identifier + + 如果该列有一个域类型,这里是该域所在的数据库名(总是当前数据库),否则为空。 + + + + + domain_schema + sql_identifier + + 如果该列有一个域类型,这里是该域所在的模式名,否则为空。 + + + + + domain_name + sql_identifier + + 如果该列有一个域类型,这里是该域的名称,否则为空。 + + + + + udt_catalog + sql_identifier + + 该列数据类型(如果适用,底层的域类型)被定义的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 该列数据类型(如果适用,底层的域类型)被定义的模式名称 + + + + + udt_name + sql_identifier + + 该列数据类型(如果适用,底层的域类型)的名称 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该列的数据类型描述符的一个标识符,在从属于该表的数据类型标识符之中唯一。 + 这主要用于与这类标识符的其他实例进行连接(该标识符的指定格式没有被定义并且不保证在未来的版本中保持相同)。 + + + + + is_self_referencing + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + is_identity + yes_or_no + Applies to a feature not supported by PostgreSQL + + + + identity_generation + character_data + Applies to a feature not supported by PostgreSQL + + + + identity_start + character_data + Applies to a feature not supported by PostgreSQL + + + + identity_increment + character_data + Applies to a feature not supported by PostgreSQL + + + + identity_maximum + character_data + Applies to a feature not supported by PostgreSQL + + + + identity_minimum + character_data + Applies to a feature not supported by PostgreSQL + + + + identity_cycle + yes_or_no + Applies to a feature not supported by PostgreSQL + + + + is_generated + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + generation_expression + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + is_updatable + yes_or_no + + 如果该列是可更新的,则为YES,否则为NO(基表中的列总是可更新的,视图中的列则不一定) + + + + +
+ + + 因为在 SQL 中有多种方式定义数据类型,而PostgreSQL还包含额外的方式来定义数据类型,它们在信息模式中的表示可能有点困难。列data_type应该标识列的底层内置类型。在PostgreSQL中,这表示定义在系统目录模式pg_catalog中的类型。如果应用能够特别地(例如以不同方式格式化数字类型或使用精度列中的数据)处理众所周知的内置类型,这列可能会有用。列udt_nameudt_schemaudt_catalog总是标识列的底层数据类型,即使该列是基于一个域的(因为PostgreSQL对待内置类型和用户定义类型的方式是一样的,内置类型也出现在这里。这是 SQL 标准的一种扩展)。如果一个应用想要根据该类型以不同的方式处理数据,就应该使用这些列,因为在那种情况下即使该列真地基于一个域也没有关系。如果该列是基于一个域,该域的标识被存储在列domain_namedomain_schemadomain_catalog。如果你想要把列和它们相关的数据类型配对并且把域视作单独的类型,你可以写coalesce(domain_name, + udt_name)等等。 + +
+ + + <literal>constraint_column_usage</literal> + + + 视图constraint_column_usage标识当前数据库中被某种约束使用的所有列。只显示那些位于当前启用角色所拥有表中的列。对于检查约束,此视图标识检查表达式中使用的列。对于非空约束,此视图标识定义该约束的列。对于外键约束,此视图标识外键所引用的列。对于唯一约束或主键约束,此视图标识受该约束限制的列。 + + + + <literal>constraint_column_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含被某个约束使用的列的表所在的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含被某个约束使用的列的表所在的模式名称 + + + + + table_name + sql_identifier + + 包含被某个约束使用的列的表名称 + + + + + column_name + sql_identifier + + 包含被某个约束使用的列名称 + + + + + constraint_catalog + sql_identifier + + 包含该约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含该约束的模式名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + +
+
+ + + <literal>constraint_table_usage</literal> + + + 视图constraint_table_usage标识在当前数据库中被某个约束使用的所有表(这与视图table_constraints不同,它标识哪些表约束定义在哪些表上)。对于一个外键约束,这个视图标识该外键引用的表。对于一个唯一或主键约束,这个视图仅标识该约束属于的表。检查约束和非空约束不被包括在这个视图中。 + + + + <literal>constraint_table_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含被某个约束使用的表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含被某个约束使用的表的模式名称 + + + + + table_name + sql_identifier + + 包含被某个约束使用的表名称 + + + + + constraint_catalog + sql_identifier + + 包含该约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含该约束的模式名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + +
+
+ + + <literal>data_type_privileges</literal> + + + 视图data_type_privileges标识当前用户能够访问(作为被描述对象的拥有者或者具有其上的某种权限)的所有数据类型描述符。只要一个数据类型被用在一个表列、一个域或一个函数(作为参数或返回类型)就会生成一个数据类型描述符并且在那个实例中存储一些有关该数据类型如何被使用的信息(例如,声明的最大长度,如果适用)。每一个数据类型描述符被赋予一个任意的标识符,它在被赋予给一个对象(表、域、函数)的数据类型描述符中唯一。这个视图对于应用可能没什么用,但是它被用于定义信息模式中的一些其他视图。 + + + + <literal>data_type_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + object_catalog + sql_identifier + + 包含该被描述对象的数据库名称(总是当前数据库) + + + + + object_schema + sql_identifier + + 包含该被描述对象的模式名称 + + + + + object_name + sql_identifier + + 该描述对象的名称 + + + + + object_type + character_data + + 被描述对象的类型:TABLE(从属于表的一列的数据类型描述符)、DOMAIN (从属于域的数据类型描述符)、ROUTINE(从属于函数的一个参数或返回数据类型的数据类型描述符)。 + + + + + dtd_identifier + sql_identifier + + 数据类型描述符的标识符,它在同一对象的数据类型描述符之间唯一。 + + + + +
+
+ + + <literal>domain_constraints</literal> + + + 视图domain_constraints包含所有属于当前数据库中定义的域的约束。只有当前用户能访问的那些域才被显示(作为拥有者或具有某些权限)。 + + + + <literal>domain_constraints</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含该约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含该约束的模式名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + domain_catalog + sql_identifier + + 包含该域的数据库名称(总是当前数据库) + + + + + domain_schema + sql_identifier + + 包含该域的模式名称 + + + + + domain_name + sql_identifier + + 域名称 + + + + + is_deferrable + yes_or_no + + 如果该约束是可延迟的,则为YES,否则为NO + + + + + initially_deferred + yes_or_no + + 如果该约束是可延迟的且初始就被延迟,则为YES,否则为NO + + + + +
+
+ + + <literal>domain_udt_usage</literal> + + + 视图domain_udt_usage标识所有基于被一个当前启用的角色拥有的数据类型的域。注意在PostgreSQL中,内置数据类型的行为相似于用户定义的类型,因此它们也被包括在这里。 + + + + <literal>domain_udt_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + udt_catalog + sql_identifier + + 该域数据类型被定义的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 该域数据类型被定义的模式名称 + + + + + udt_name + sql_identifier + + 该域数据类型的名称 + + + + + domain_catalog + sql_identifier + + 包含该域的数据库名称(总是当前数据库) + + + + + domain_schema + sql_identifier + + 包含该域的模式名称 + + + + + domain_name + sql_identifier + + 域名称 + + + + +
+
+ + + <literal>domains</literal> + + + 视图domains包含当前数据库中定义的所有域。 + 仅显示当前用户有权限访问的域(通过拥有者或某些权限)。 + + + + <literal>domains</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + domain_catalog + sql_identifier + + 包含该域的数据库名称(总是当前数据库) + + + + + domain_schema + sql_identifier + + 包含该域的模式名称 + + + + + domain_name + sql_identifier + + 域名称 + + + + + data_type + character_data + + 该域的数据类型如果是一种内置类型,这里是该域的数据类型;如果是某种数组(此种情况见视图element_types),则为ARRAY; + 否则为USER-DEFINED(此种情况中,该类型被标识在udt_name和相关列中)。 + + + + + character_maximum_length + cardinal_number + + 如果该域有一个字符或位串类型,这里是声明的最大长度;如果没有声明最大长度,则对于所有其他数据类型为空。 + + + + + character_octet_length + cardinal_number + + 如果该域有一个字符类型,这里是一个数据的最大可能长度(以字节计);对其他所有数据类型为空。 + 最大字节长度取决于声明的字符最大长度(见上文)和服务器编码。 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 包含该域排序规则的数据库名称(总是当前数据库);如果使用默认排序规则,或该域的数据类型不支持排序规则,则为 null。 + + + + + collation_schema + sql_identifier + + 包含该域排序规则的模式名称;如果使用默认排序规则,或该域的数据类型不支持排序规则,则为 null。 + + + + + collation_name + sql_identifier + + 该域排序规则的名称;如果使用默认排序规则,或该域的数据类型不支持排序规则,则为 null。 + + + + + numeric_precision + cardinal_number + + 如果该域有一种数字类型,这列包含这个域类型的(声明的或隐式的)精度。精度指示了有效位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。 + 对于所有其他数据类型,这一列为空。 + + + + + numeric_precision_radix + cardinal_number + + 如果该域有一种数字类型,这一列指示numeric_precisionnumeric_scale列中的值是基于什么来表示。 + 该值为 2 或 10。对于所有其他数据类型,这一列为空。 + + + + + numeric_scale + cardinal_number + + 如果该域有一种准确数字类型,这列包含这个域类型的(声明的或隐式的)比例。标度表示小数点右侧的有效数字位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。 + 对于所有其他数据类型,这一列为空。 + + + + + datetime_precision + cardinal_number + + 如果data_type标识一种日期、时间、时间戳或时间间隔类型, + 这一列包含这个域类型的(声明的或隐式的)分数秒的精度,也就是秒值的小数点后的十进制位数。对于所有其他数据类型,这一列为空。 + + + + + interval_type + character_data + + 如果data_type标识一种时间间隔类型,这一列包含时间间隔为这个域包括哪些域的声明,例如YEAR TO MONTHDAY TO SECOND等等。 + 如果没有指定域限制(也就是该时间间隔接受所有域),并且对于所有其他数据类型,这个域为空。 + + + + + interval_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性(关于时间间隔类型域的分数秒精度可见datetime_precision) + + + + + domain_default + character_data + + 该域的默认表达式 + + + + + udt_catalog + sql_identifier + + 该域数据类型被定义的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 该域数据类型被定义的模式名称 + + + + + udt_name + sql_identifier + + 该域数据类型的名称 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该域的数据类型描述符的一个标识符,在从属于该域的数据类型标识符之中唯一(这不重要,因为一个域只包含一个数据类型描述符)。 + 这主要用于与这类标识符的其他实例进行连接(该标识符的指定格式没有被定义并且不保证在未来的版本中保持相同)。 + + + + +
+
+ + + <literal>element_types</literal> + + + 视图element_types包含数组元素的数据类型描述符。当一个表列、复合类型属性、域、函数参数或函数返回值被定义为一种数组类型时,相应的信息模式视图只在列data_type中包含ARRAY。要获得该数组元素类型的信息,你可以连接该相应的视图和这个视图。例如,要显示一个表的列及其数据类型和数组元素类型(如果适用),你可以: + +SELECT c.column_name, c.data_type, e.data_type AS element_type +FROM information_schema.columns c LEFT JOIN information_schema.element_types e + ON ((c.table_catalog, c.table_schema, c.table_name, 'TABLE', c.dtd_identifier) + = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier)) +WHERE c.table_schema = '...' AND c.table_name = '...' +ORDER BY c.ordinal_position; + + 这个视图只包括当前用户能够访问(作为拥有者或具有某些权限)的对象。 + + + + <literal>element_types</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + object_catalog + sql_identifier + + 包含使用被描述的数组的对象的数据库名称(总是当前数据库) + + + + + object_schema + sql_identifier + + 包含使用被描述的数组的对象的模式名称 + + + + + object_name + sql_identifier + + 使用被描述的模式的对象名称 + + + + + object_type + character_data + + 使用被描述的数组的对象的类型:TABLE(被一个表列使用的数组)、USER-DEFINED TYPE(被复合类型的一个属性使用的数组)、DOMAIN (被域使用的数组)、ROUTINE(被函数的一个参数或返回数据类型使用的数组)。 + + + + + collection_type_identifier + sql_identifier + + 被描述的数组的数据类型描述符的标识符。使用这个去与其他信息模式视图的dtd_identifier列连接。 + + + + + data_type + character_data + + 如果数组元素的数据类型是内置类型,这里是数组元素的数据类型,否则为USER-DEFINED(在那种情况下,该类型被标识在udt_name和相关列中)。 + + + + + character_maximum_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + character_octet_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 包含该元素类型排序规则的数据库名称(总是当前数据库);如果使用默认排序规则,或该元素类型不支持排序规则,则为 null。 + + + + + collation_schema + sql_identifier + + 包含该元素类型排序规则的模式名称;如果使用默认排序规则,或该元素类型不支持排序规则,则为 null。 + + + + + collation_name + sql_identifier + + 该元素类型排序规则的名称;如果使用默认排序规则,或该元素类型不支持排序规则,则为 null。 + + + + + numeric_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + numeric_precision_radix + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + numeric_scale + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + datetime_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + interval_type + character_data + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + interval_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的数组元素数据类型 + + + + + domain_default + character_data + 尚未实现 + + + + udt_catalog + sql_identifier + + 元素的数据类型所在的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 定义元素的数据类型的模式的名称 + + + + + udt_name + sql_identifier + + 元素的数据类型名 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该元素的数据类型描述符的标识符。当前没有用。 + + + + +
+
+ + + <literal>enabled_roles</literal> + + + 视图enabled_roles标识当前已启用的角色。已启用的角色递归地定义为:当前用户,以及所有通过自动继承方式授予给已启用角色的角色。换句话说,这些角色都是当前用户直接或间接自动继承其成员资格的角色。 + enabled role + roleenabled + + + + 对于权限检查,使用的是一组适用角色,其范围可能比已启用角色的集合更广。因此,一般来说最好使用视图applicable_roles而不是这个视图;有关applicable_roles视图的详细信息,见。 + + + + <literal>enabled_roles</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + role_name + sql_identifier + + 一个角色的名称 + + + + +
+
+ + + <literal>foreign_data_wrapper_options</literal> + + + 视图foreign_data_wrapper_options包含为当前数据库中外部数据包装器定义的所有选项。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部数据包装器被显示。 + + + + <literal>foreign_data_wrapper_options</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_data_wrapper_catalog + sql_identifier + + 该外部数据包装器所在的数据库名称(总是当前数据库) + + + + + foreign_data_wrapper_name + sql_identifier + + 该外部数据包装器的名称 + + + + + option_name + sql_identifier + + 一个选项名称 + + + + + option_value + character_data + + 该选项的值 + + + + +
+
+ + + <literal>foreign_data_wrappers</literal> + + + 视图foreign_data_wrappers包含定义在当前数据库中的所有外部数据包装器。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部数据包装器才会被显示。 + + + + <literal>foreign_data_wrappers</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_data_wrapper_catalog + sql_identifier + + 包含该外部数据包装器的数据库名称(总是当前数据库) + + + + + foreign_data_wrapper_name + sql_identifier + + 该外部数据包装器的名称 + + + + + authorization_identifier + sql_identifier + + 外部服务器拥有者的名称 + + + + + library_name + character_data + + 实现这个外部数据包装器的库文件名称 + + + + + foreign_data_wrapper_language + character_data + + 用于实现这个外部数据包装器的语言 + + + + +
+
+ + + <literal>foreign_server_options</literal> + + + 视图foreign_server_options包含为当前数据库中外部服务器定义的所有选项。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部服务器才会被显示。 + + + + <literal>foreign_server_options</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_server_catalog + sql_identifier + + 该外部服务器所在的数据库名称(总是当前数据库) + + + + + foreign_server_name + sql_identifier + + 该外部服务器的名称 + + + + + option_name + sql_identifier + + 一个选项名称 + + + + + option_value + character_data + + 该选项的值 + + + + +
+
+ + + <literal>foreign_servers</literal> + + + 视图foreign_servers包含当前数据库中定义的所有外部服务器。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部服务器才会被显示。 + + + + <literal>foreign_servers</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_server_catalog + sql_identifier + + 该外部服务器所在的数据库名称(总是当前数据库) + + + + + foreign_server_name + sql_identifier + + 该外部服务器的名称 + + + + + foreign_data_wrapper_catalog + sql_identifier + + 包含被该外部服务器使用的外部数据包装器的数据库名称(总是当前数据库) + + + + + foreign_data_wrapper_name + sql_identifier + + 被该外部服务器所使用的外部数据包装器的名称 + + + + + foreign_server_type + character_data + + 外部服务器类型信息,如果在创建时指定过 + + + + + foreign_server_version + character_data + + 外部服务器版本信息,如果在创建时指定过 + + + + + authorization_identifier + sql_identifier + + 外部服务器拥有者的名称 + + + + +
+
+ + + <literal>foreign_table_options</literal> + + + 视图foreign_table_options包含为当前数据库中外部表定义的所有选项。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部表才会被显示。 + + + + <literal>foreign_table_options</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_table_catalog + sql_identifier + + 包含该外部表的数据库名称(总是当前数据库) + + + + + foreign_table_schema + sql_identifier + + 包含该外部表的模式名称 + + + + + foreign_table_name + sql_identifier + + 外部表的名称 + + + + + option_name + sql_identifier + + 一个选项名称 + + + + + option_value + character_data + + 该选项的值 + + + + +
+
+ + + <literal>foreign_tables</literal> + + + 视图foreign_tables包含定义在当前数据库中的所有外部表。只有那些当前用户能够访问(作为拥有者或具有某些权限)的外部表才会被显示。 + + + + <literal>foreign_tables</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + foreign_table_catalog + sql_identifier + + 该外部表所在的数据库名称(总是当前数据库) + + + + + foreign_table_schema + sql_identifier + + 包含该外部表的模式名称 + + + + + foreign_table_name + sql_identifier + + 外部表的名称 + + + + + foreign_server_catalog + sql_identifier + + 该外部服务器所在的数据库名称(总是当前数据库) + + + + + foreign_server_name + sql_identifier + + 该外部服务器的名称 + + + + +
+
+ + + <literal>key_column_usage</literal> + + + 视图key_column_usage标识当前数据库中所有被某种唯一、主键或外键约束限制的列。检查约束不被包括在这个视图中。只有那些当前用户能够访问的列才会被显示(作为拥有者或具有某些权限)。 + + + + <literal>key_column_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含该约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含该约束的模式名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + table_catalog + sql_identifier + + 包含被这个约束限制的列的表所在的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含被这个约束限制的列的表所在的模式名称 + + + + + table_name + sql_identifier + + 包含被这个约束限制的列的表的名称 + + + + + column_name + sql_identifier + + 被这个约束限制的列的名称 + + + + + ordinal_position + cardinal_number + + 该列在约束键中的顺序位置(从 1 开始计数) + + + + + position_in_unique_constraint + cardinal_number + + 对于一个外键约束,被引用行在其唯一约束中的顺序位置(从 1 开始计数);对于其他约束为空 + + + + +
+
+ + + <literal>parameters</literal> + + + 视图parameters包含当前数据库中所有函数的参数的有关信息。只有那些当前用户能够访问(作为拥有者或具有某些权限)的函数才会被显示。 + + + + <literal>parameters</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + + ordinal_position + cardinal_number + + 该参数在函数参数列表中的顺序位置(从 1 开始计数) + + + + + parameter_mode + character_data + + IN表示输入参数,OUT表示输出参数,INOUT表示输入/输出参数。 + + + + + is_result + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + as_locator + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + parameter_name + sql_identifier + + 参数名称,如果参数没有名称则为空 + + + + + data_type + character_data + + 该参数的数据类型如果是一种内置类型,这里是该参数的数据类型;如果是某种数组(此种情况见视图element_types),则为ARRAY; + 否则为USER-DEFINED(此种情况中,该类型被标识在udt_name和相关列中)。 + + + + + character_maximum_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + character_octet_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + collation_schema + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + collation_name + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_precision_radix + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_scale + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + datetime_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + interval_type + character_data + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + interval_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + udt_catalog + sql_identifier + + 该参数的数据类型所在的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 该参数的数据类型所在的模式名称 + + + + + udt_name + sql_identifier + + 该参数的数据类型的名称 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该参数的数据类型描述符的一个标识符,在从属于该函数的数据类型标识符之中唯一(这不重要,因为一个域只包含一个数据类型描述符)。 + 这主要用于与这类标识符的其他实例进行连接(该标识符的指定格式没有被定义并且不保证在未来的版本中保持相同)。 + + + + + parameter_default + character_data + + 该参数的默认表达式,如果没有或者该函数不被一个当前启用的角色拥有则为空值。 + + + + +
+
+ + + <literal>referential_constraints</literal> + + + 视图referential_constraints包含当前数据库中的所有引用(外键)约束。只有那些当前用户具有其引用表上写权限(作为拥有者或具有某些除SELECT之外的权限)的约束才会被显示。 + + + + <literal>referential_constraints</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含约束的模式的名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + unique_constraint_catalog + sql_identifier + + 包含该外键约束所引用的唯一或主键约束的数据库名称(总是当前数据库) + + + + + unique_constraint_schema + sql_identifier + + 包含该外键约束所引用的唯一或主键约束的模式名称 + + + + + unique_constraint_name + sql_identifier + + 包含该外键约束所引用的唯一或主键约束的名称 + + + + + match_option + character_data + + 外键约束的匹配选项:FULLPARTIALNONE。 + + + + + update_rule + character_data + + 外键约束的更新规则: + CASCADESET NULLSET DEFAULTRESTRICTNO ACTION。 + + + + + delete_rule + character_data + + 外键约束的删除规则: + CASCADESET NULLSET DEFAULTRESTRICTNO ACTION。 + + + + +
+ +
+ + + <literal>role_column_grants</literal> + + + 视图role_column_grants标识所有在列上授予的权限,这些权限的授予者或者被授予者是当前已启用的角色。更多信息可以在column_privileges中找到。这个视图和column_privileges之间的唯一实质性区别是:这个视图忽略那些以授予给PUBLIC的方式使当前用户获得其访问权限的列。 + + + + <literal>role_column_grants</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + table_catalog + sql_identifier + + 包含该列的表所在的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该列的表所在的模式名称 + + + + + table_name + sql_identifier + + 包含该列的表名称 + + + + + column_name + sql_identifier + + 列名称 + + + + + privilege_type + character_data + + 权限类型:SELECTINSERTUPDATEREFERENCES + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>role_routine_grants</literal> + + + 视图role_routine_grants标识所有在函数上授予的权限,这些权限的授予者或者被授予者是当前已启用的角色。更多信息可以在routine_privileges中找到。这个视图和routine_privileges之间的唯一实质性区别是:这个视图忽略那些以授予给PUBLIC的方式使当前用户获得其访问权限的函数。 + + + + <literal>role_routine_grants</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + + routine_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + routine_schema + sql_identifier + + 包含函数的模式的名称 + + + + + routine_name + sql_identifier + + 该函数的名称(在重载的情况下可能会重复) + + + + + privilege_type + character_data + + 总是为EXECUTE(函数唯一的权限类型) + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>role_table_grants</literal> + + + 视图role_table_grants标识所有在表或视图上授予的权限,这些权限的授予者或者被授予者是当前已启用的角色。更多信息可以在table_privileges中找到。这个视图和table_privileges之间的唯一实质性区别是:这个视图忽略那些以授予给PUBLIC的方式使当前用户获得其访问权限的表。 + + + + <literal>role_table_grants</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + table_catalog + sql_identifier + + 包含该表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该表的模式名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + privilege_type + character_data + + 该权限的类型:SELECT、 + INSERTUPDATE、 + DELETETRUNCATE、 + REFERENCESTRIGGER + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + + with_hierarchy + yes_or_no + + 在 SQL 标准中,WITH HIERARCHY OPTION是一个独立的(子)权限,它允许在表继承层级上的特定操作。 + 在 PostgreSQL 中,这被包括在SELECT权限中,因此这一列在权限为SELECT时显示YES,其他时候显示NO。 + + + + +
+
+ + + <literal>role_udt_grants</literal> + + + 视图role_udt_grants标识所有在用户定义类型上授予的USAGE权限,这些权限的授予者或者被授予者是当前已启用的角色。更多信息可以在udt_privileges中找到。这个视图和udt_privileges之间的唯一实质性区别是:这个视图忽略那些以授予给PUBLIC的方式使当前用户获得其访问权限的对象。因为数据类型在 PostgreSQL 中并没有真正的权限,而是只有一个给PUBLIC的隐式授予,这个视图为空。 + + + + <literal>role_udt_grants</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + udt_catalog + sql_identifier + + 包含该类型的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 包含该类型的模式名称 + + + + + udt_name + sql_identifier + + 类型的名称 + + + + + privilege_type + character_data + + 总是TYPE USAGE + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>role_usage_grants</literal> + + + 视图role_usage_grants标识所有在多种对象上授予的USAGE权限,这些权限的授予者或者被授予者是当前已启用的角色。更多信息可以在usage_privileges中找到。这个视图和usage_privileges之间的唯一实质性区别是:这个视图忽略那些以授予给PUBLIC的方式使当前用户获得其访问权限的对象。 + + + + <literal>role_usage_grants</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + object_catalog + sql_identifier + + 包含该对象的数据库名称(总是当前数据库) + + + + + object_schema + sql_identifier + + 如果适用,则为包含该对象的模式名称,否则为一个空字符串 + + + + + object_name + sql_identifier + + 对象的名称 + + + + + object_type + character_data + + COLLATIONDOMAINFOREIGN DATA WRAPPERFOREIGN SERVERSEQUENCE + + + + + privilege_type + character_data + + 总是USAGE + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>routine_privileges</literal> + + + 视图routine_privileges标识所有在函数上授予的权限,其授予者或被授予者是当前已启用的角色。对于每一种函数、授予者和被授予者的组合,这里都有一行。 + + + + <literal>routine_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + + routine_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + routine_schema + sql_identifier + + 包含函数的模式的名称 + + + + + routine_name + sql_identifier + + 该函数的名称(在重载的情况下可能会重复) + + + + + privilege_type + character_data + + 总是为EXECUTE(函数唯一的权限类型) + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>routines</literal> + + + 视图routines包含当前数据库中所有的函数。只有那些当前用户能够访问(作为拥有者或具有某些权限)的函数才会被显示。 + + + + <literal>routines</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 该函数的专用名。这是一个在模式中唯一标识该函数的名称,即使该函数真正的名称已经被重载。 + 专用名的格式尚未被定义,它应当仅被用来与指定例程名称的其他实例进行比较。 + + + + + routine_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + routine_schema + sql_identifier + + 包含函数的模式的名称 + + + + + routine_name + sql_identifier + + 该函数的名称(在重载的情况下可能会重复) + + + + + routine_type + character_data + 总是FUNCTION(将来可能会有其他类型的例程。) + + + + module_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + module_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + module_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + udt_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + udt_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + udt_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + data_type + character_data + + 该函数的返回数据类型如果是一种内置类型,这里是该数据类型; + 如果是某种数组(此种情况见视图element_types),则为ARRAY; + 否则为USER-DEFINED(此种情况中,该类型被标识在type_udt_name和相关列中)。 + + + + + + character_maximum_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + character_octet_length + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + collation_schema + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + collation_name + sql_identifier + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_precision_radix + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + numeric_scale + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + datetime_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + interval_type + character_data + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + interval_precision + cardinal_number + + 总是为空,因为这种信息不适用于PostgreSQL中的返回数据类型 + + + + + type_udt_catalog + sql_identifier + + 该函数的返回数据类型所在的数据库名(总是当前数据库)。 + + + + + type_udt_schema + sql_identifier + + 该函数的返回数据类型所在的模式名。 + + + + + type_udt_name + sql_identifier + + 该函数的返回数据类型的名字。 + + + + + scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + maximum_cardinality + cardinal_number + + 总是为空,因为 PostgreSQL 中数组的最大基数始终不受限制 + + + + + dtd_identifier + sql_identifier + + 该函数返回数据类型的数据类型描述符的一个标识符,在从属于该函数的数据类型标识符之中唯一。 + 这主要用于与这类标识符的其他实例进行连接(该标识符的指定格式没有被定义并且不保证在未来的版本中保持相同)。 + + + + + routine_body + character_data + + 如果该函数是一个 SQL 函数,则为SQL,否则为EXTERNAL。 + + + + + routine_definition + character_data + + 该函数的源文本(如果该函数不归当前已启用的角色所有,则为空) + (根据 SQL 标准,只有routine_bodySQL时这一列才适用。 + 但是在PostgreSQL中,它将会包含该函数被创建时所指定的任何源文本。) + + + + + external_name + character_data + + 如果这个函数是一个 C 函数,则为该函数的外部名称(链接符号),否则为空(这会产生和显示在routine_definition中相同的值)。 + + + + + external_language + character_data + + 该函数所用的语言 + + + + + parameter_style + character_data + + 总是GENERAL(SQL 标准定义了其他参数风格,但在PostgreSQL中不可用) + + + + + is_deterministic + yes_or_no + + 如果该函数被声明为不变(在 SQL 标准中被称为确定性的),则为YES,否则为NO + (你不能通过该信息模式查询在PostgreSQL中可用的其他易变级别)。 + + + + + sql_data_access + character_data + + 总是MODIFIES,表示该函数可能修改 SQL 数据。这种信息对PostgreSQL没有用处。 + + + + + is_null_call + yes_or_no + + 如果该函数在任一参数为空时自动返回空值,则为YES,否则为NO。 + + + + + sql_path + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + schema_level_routine + yes_or_no + + 总是YES(反例是一个用户定义类型的方法,这是在PostgreSQL不可用的一种特性)。 + + + + + max_dynamic_result_sets + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + is_user_defined_cast + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + is_implicitly_invocable + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + security_type + character_data + + 如果该函数以当前用户的权限运行,则为INVOKER;如果该函数以定义它的用户的权限运行,则为DEFINER。 + + + + + to_sql_specific_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + to_sql_specific_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + to_sql_specific_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + as_locator + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + created + time_stamp + + 对应 PostgreSQL 不支持的特性 + + + + + last_altered + time_stamp + + 对应 PostgreSQL 不支持的特性 + + + + + new_savepoint_level + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + is_udt_dependent + yes_or_no + + 当前总是NO。另一个选项YES对应 PostgreSQL 不支持的特性。 + + + + + result_cast_from_data_type + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_as_locator + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_char_max_length + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_char_octet_length + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_char_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_char_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_char_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_collation_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_collation_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_collation_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_numeric_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_numeric_precision_radix + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_numeric_scale + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_datetime_precision + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_interval_type + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_interval_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_type_udt_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_type_udt_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_type_udt_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_scope_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_scope_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_scope_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_maximum_cardinality + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + result_cast_dtd_identifier + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + +
+
+ + + <literal>schemata</literal> + + + 视图schemata包含当前数据库中被当前用户(作为属主或具有某些权限)可访问的所有模式。 + + + + <literal>schemata</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + catalog_name + sql_identifier + + 该模式所在的数据库名称(总是当前数据库) + + + + + schema_name + sql_identifier + + 模式的名称 + + + + + schema_owner + sql_identifier + + 模式拥有者的名称 + + + + + default_character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + default_character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + default_character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + sql_path + character_data + + 对应 PostgreSQL 不支持的特性 + + + + +
+
+ + + <literal>sequences</literal> + + + 视图sequences包含所有定义在当前数据库中的序列。只有那些当前用户能够访问(作为拥有者或具有某些权限)的序列才会被显示。 + + + + <literal>sequences</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + sequence_catalog + sql_identifier + + 包含该序列的数据库名称(总是当前数据库) + + + + + sequence_schema + sql_identifier + + 包含该序列的模式名称 + + + + + sequence_name + sql_identifier + + 序列的名称 + + + + + data_type + character_data + + 序列的数据类型。在 PostgreSQL + 中,它目前总是 bigint。 + + + + + numeric_precision + cardinal_number + + 这列包含这个序列数据类型(见上文)的(声明的或隐式的)精度。精度指示了有效位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。 + + + + + numeric_precision_radix + cardinal_number + + 这一列指示numeric_precisionnumeric_scale列中的值是基于什么来表示。该值为 2 或 10。 + + + + + numeric_scale + cardinal_number + + 这一列包含该序列数据类型(见上文)的标度(声明的或隐式的)。标度表示小数点右侧的有效数字位数。 + 它可以按照列numeric_precision_radix中指定的被表示为十进制(基于 10)或二进制(基于 2)。 + + + + + start_value + character_data + + 该序列的开始值 + + + + + minimum_value + character_data + + 该序列的最小值 + + + + + maximum_value + character_data + + 该序列的最大值 + + + + + increment + character_data + + 该序列的增量 + + + + + cycle_option + yes_or_no + + 如果该序列会循环,则为YES,否则为NO + + + + +
+ + + 注意依照 SQL 标准,开始值、最小值、最大值和增量值被作为字符串返回。 + +
+ + + <literal>sql_features</literal> + + + 表sql_features包含的信息指示了哪些 SQL 标准中定义的正式特性被PostgreSQL所支持。这和中的信息一样。这里你也能找到一些额外的背景信息。 + + + + <literal>sql_features</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + feature_id + character_data + + 特性的标识符字符串 + + + + + feature_name + character_data + + 特性的描述性名称 + + + + + sub_feature_id + character_data + + 该子特性的标识符字符串,或者如果不是一个子特性则为一个长度为零的字符串 + + + + + sub_feature_name + character_data + + 该子特性的描述性名称,或者如果不是一个子特性则为一个长度为零的字符串 + + + + + is_supported + yes_or_no + + 如果当前版本的 PostgreSQL 完全支持该特性,则为YES,否则为NO + + + + + is_verified_by + character_data + + 总是为空,因为PostgreSQL开发组没有对特性的一致性执行正式的测试 + + + + + comments + character_data + + 可能会是关于该特性被支持状态的一段注释 + + + + +
+
+ + + <literal>sql_implementation_info</literal> + + + 表sql_implementation_info包含的信息指示剩下的由 SQL 标准实现定义的多个方面。这类信息主要用来在 ODBC 接口的情境中使用;其它接口的用户可能将发现这类信息用处不大。由于这个原因,个体实现信息项没有在这里描述,你将会在 ODBC 接口的描述中找到它们。 + + + + <literal>sql_implementation_info</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + implementation_info_id + character_data + + 实现信息项的标识符字符串 + + + + + implementation_info_name + character_data + + 实现信息项的描述性名称 + + + + + integer_value + cardinal_number + + 实现信息项的整数值;如果该值在列character_value中给出,则本列为空 + + + + + character_value + character_data + + 实现信息项的字符值;如果该值在列integer_value中给出,则本列为空 + + + + + comments + character_data + + 可能是从属于该实现信息项的一段注释 + + + + +
+
+ + + <literal>sql_languages</literal> + + sql_languagesPostgreSQL支持的每一种 SQL 语言绑定包含一行。PostgreSQL支持直接 SQL 和 C 中的嵌入式 SQL;从此表中只能了解到这些信息。 + + 此表已在 SQL:2008 中从 SQL 标准移除,因此没有引用 SQL:2003 之后标准的条目。 + + + <literal>sql_languages</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + sql_language_source + character_data + 语言定义来源的名称;总是ISO 9075,即 SQL 标准 + + + + sql_language_year + character_data + sql_language_source引用的标准获批的年份。 + + + + sql_language_conformance + character_data + 该语言绑定的标准符合性级别。对于 ISO 9075:2003,此值总是CORE + + + + sql_language_integrity + character_data + 总是为空(此值与 SQL 标准的较早版本有关。) + + + + sql_language_implementation + character_data + 总是为空 + + + + sql_language_binding_style + character_data + 语言绑定方式,为DIRECTEMBEDDED + + + + sql_language_programming_language + character_data + 如果绑定方式为EMBEDDED,则为所用编程语言,否则为空。PostgreSQL只支持 C 语言。 + + + +
+
+ + + <literal>sql_packages</literal> + + sql_packages包含PostgreSQL支持 SQL 标准所定义的哪些功能包的信息。功能包的背景信息请参阅 + + + <literal>sql_packages</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + feature_id + character_data + 功能包的标识符字符串 + + + + feature_name + character_data + 功能包的描述性名称 + + + + is_supported + yes_or_no + 如果当前版本的PostgreSQL完全支持此功能包,则为YES,否则为NO + + + + is_verified_by + character_data + + 总是为空,因为PostgreSQL开发组没有对特性的一致性执行正式的测试 + + + + + comments + character_data + 关于此功能包支持状态的可选注释 + + + +
+
+ + + <literal>sql_parts</literal> + + + 表sql_parts包含的信息指示哪些定义在 SQL 标准中的部分被PostgreSQL支持。 + + + + <literal>sql_parts</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + feature_id + character_data + + 包含该部分编号的一个标识符字符串 + + + + + feature_name + character_data + + 该部分的描述性名称 + + + + + is_supported + yes_or_no + + 如果当前版本的 PostgreSQL 完全支持该部分,则为YES,否则为NO + + + + + is_verified_by + character_data + + 总是为空,因为PostgreSQL开发组没有对特性的一致性执行正式的测试 + + + + + comments + character_data + + 可能会是关于该部分被支持状态的一段注释 + + + + +
+
+ + + <literal>sql_sizing</literal> + + + 表sql_sizing包含有关PostgreSQL中多种尺寸限制和最大值的信息。这类信息主要用来在 ODBC 接口的情境中使用;其它接口的用户可能将发现这类信息用处不大。由于这个原因,个体实现信息项没有在这里描述,你将会在 ODBC 接口的描述中找到它们。 + + + + <literal>sql_sizing</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + sizing_id + cardinal_number + + 尺寸项的标识符 + + + + + sizing_name + character_data + + 尺寸项的描述性名称 + + + + + supported_value + cardinal_number + + 尺寸项的值,如果尺寸是不受限制或不能确定的则为 0,如果尺寸项适用的特性不受支持则为空 + + + + + comments + character_data + + 可能是从属于尺寸项的一段注释 + + + + +
+
+ + + <literal>sql_sizing_profiles</literal> + + sql_sizing_profiles包含 SQL 标准的各类配置规范所要求的sql_sizing值的信息。PostgreSQL不跟踪任何 SQL 配置规范,因此此表为空。 + + + <literal>sql_sizing_profiles</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + sizing_id + cardinal_number + + 尺寸项的标识符 + + + + + sizing_name + character_data + + 尺寸项的描述性名称 + + + + + profile_id + character_data + 配置规范的标识符字符串 + + + + required_value + cardinal_number + SQL 配置规范对该尺寸限制项要求的值;如果配置规范对此项不设限制,则为 0;如果配置规范不要求此项所适用的任何功能,则为空 + + + + comments + character_data + 关于此配置规范中该尺寸限制项的可选注释 + + + +
+
+ + + <literal>table_constraints</literal> + + + 视图table_constraints包含属于特定表的所有约束,这些表要满足的条件是:当前用户拥有表或者是当前用户在表上具有某种除SELECT之外的权限。 + + + + <literal>table_constraints</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + constraint_catalog + sql_identifier + + 包含该约束的数据库名称(总是当前数据库) + + + + + constraint_schema + sql_identifier + + 包含该约束的模式名称 + + + + + constraint_name + sql_identifier + + 约束的名称 + + + + + table_catalog + sql_identifier + + 包含该表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该表的模式名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + constraint_type + character_data + + 约束类型:CHECK(包括非空约束)、FOREIGN KEYPRIMARY KEYUNIQUE + + + + + is_deferrable + yes_or_no + + 如果该约束是可延迟的,则为YES,否则为NO + + + + + initially_deferred + yes_or_no + + 如果该约束是可延迟的且初始就被延迟,则为YES,否则为NO + + + + +
+
+ + + <literal>table_privileges</literal> + + + 视图table_privileges标识在表或视图上所有被授予的权限,这些权限必须是被当前已启用角色授出或者被授予给当前已启用角色。对每一个表、授予者和被授予者的组合都有一行。 + + + + <literal>table_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + table_catalog + sql_identifier + + 包含该表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该表的模式名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + privilege_type + character_data + + 该权限的类型:SELECT、 + INSERTUPDATE、 + DELETETRUNCATE、 + REFERENCESTRIGGER + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + + with_hierarchy + yes_or_no + + 在 SQL 标准中,WITH HIERARCHY OPTION是一个独立的(子)权限,它允许在表继承层级上的特定操作。 + 在 PostgreSQL 中,这被包括在SELECT权限中,因此这一列在权限为SELECT时显示YES,其他时候显示NO。 + + + + +
+
+ + + <literal>tables</literal> + + + 视图tables包含定义在当前数据库中的所有表和视图。只有那些当前用户能够访问(作为拥有者或具有某些权限)的表和视图才会被显示。 + + + + <literal>tables</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含该表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该表的模式名称 + + + + + table_name + sql_identifier + + 表的名称 + + + + + table_type + character_data + + 该表的类型:BASE TABLE表示一个持久的基本表(常见表类型),VIEW表示一个视图,FOREIGN TABLE表示一个外部表,LOCAL TEMPORARY表示一个临时表 + + + + + self_referencing_column_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + reference_generation + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + user_defined_type_catalog + sql_identifier + + 如果该表是一个有类型的表,则是包含其底层数据类型的数据库名称(总是当前数据库),否则为空。 + + + + + user_defined_type_schema + sql_identifier + + 如果该表是一个有类型的表,则是包含其底层数据类型的模式名,否则为空。 + + + + + user_defined_type_name + sql_identifier + + 如果该表是一个有类型的表,则是其底层数据类型的名称,否则为空。 + + + + + is_insertable_into + yes_or_no + + 如果该表能够被插入,则为YES,否则为NO(基本表总是能被插入,而视图则不一定)。 + + + + + is_typed + yes_or_no + + 如果该表是一个有类型的表,则为YES,否则为NO + + + + + commit_action + character_data + + 还未被实现 + + + + +
+
+ + + <literal>transforms</literal> + + + 视图transforms包含定义在当前数据库中的转换的信息。更准确 + 来说, 包含在转换中的每一个函数(FROM SQL或者 + TO SQL函数)在其中都有一行。 + + + + <literal>transforms</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + udt_catalog + sql_identifier + + 包含该转换所适用类型的数据库的名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 包含该转换所适用类型的模式的名称 + + + + + udt_name + sql_identifier + + 该转换所适用类型的名称 + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + + group_name + sql_identifier + + SQL 标准允许在中定义转换,并且在运行时选择一个 + 组。PostgreSQL 不支持这种做法,转换是与一种语言相关的。作为一种折衷, + 这个字段包含该转换所适用的语言。 + + + + + transform_type + character_data + + FROM SQL或者TO SQL + + + + +
+
+ + + <literal>triggered_update_columns</literal> + + + 对于当前数据库中指定一个列列表(如UPDATE OF column1, column2)的触发器,视图triggered_update_columns标识这些列。没有指定一个列列表的触发器不被包括在这个视图中。只有那些当前用户拥有或具有某种除SELECT之外权限的列才会被显示。 + + + + <literal>triggered_update_columns</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + trigger_catalog + sql_identifier + + 包含该触发器的数据库名称(总是当前数据库) + + + + + trigger_schema + sql_identifier + + 包含该触发器的模式名称 + + + + + trigger_name + sql_identifier + + 触发器的名称 + + + + + event_object_catalog + sql_identifier + + 包含触发器所在的表的数据库名称(总是当前数据库) + + + + + event_object_schema + sql_identifier + + 包含触发器所在的表的模式名称 + + + + + event_object_table + sql_identifier + + 触发器所在的表的名称 + + + + + event_object_column + sql_identifier + + 触发器所在的列的名称 + + + + +
+
+ + + <literal>triggers</literal> + + + 视图triggers包含所有定义在当前数据库中表和视图上的触发器,并且只显示当前用户拥有的触发器或者是当前用户在其上具有某种除SELECT之外权限的触发器。 + + + + <literal>triggers</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + trigger_catalog + sql_identifier + + 包含该触发器的数据库名称(总是当前数据库) + + + + + trigger_schema + sql_identifier + + 包含该触发器的模式名称 + + + + + trigger_name + sql_identifier + + 触发器的名称 + + + + + event_manipulation + character_data + + 触发该触发器的事件(INSERTUPDATEDELETE) + + + + + event_object_catalog + sql_identifier + + 包含触发器所在的表的数据库名称(总是当前数据库) + + + + + event_object_schema + sql_identifier + + 包含触发器所在的表的模式名称 + + + + + event_object_table + sql_identifier + + 触发器所在的表的名称 + + + + + action_order + cardinal_number + 尚未实现 + + + + action_condition + character_data + + 触发器的WHEN条件,如果没有则为空(如果该表不被当前已启用角色拥有也是为空) + + + + + action_statement + character_data + + 触发器执行的语句(当前总是 EXECUTE PROCEDURE function(...)) + + + + + action_orientation + character_data + + 标识触发器是对每个被处理的行触发一次还是为每个语句触发一次(ROWSTATEMENT) + + + + + action_timing + character_data + + 触发器在什么时候触发(BEFOREAFTERINSTEAD OF) + + + + + action_reference_old_table + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + action_reference_new_table + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + action_reference_old_row + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + action_reference_new_row + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + created + time_stamp + + 对应 PostgreSQL 不支持的特性 + + + + +
+ + + PostgreSQL中的触发器有两点与 SQL 标准不兼容,这会影响在该信息模式中的表示。第一,在PostgreSQL中触发器的名字是局限于每个表的,而不是独立于模式对象。因此可能在一个模式中会有重复的触发器名称,只要它们属于不同的表(trigger_catalogtrigger_schema才真正标识了触发器被定义在哪个表上)。第二,在PostgreSQL中触发器可以被定义为在多个事件上触发(例如ON INSERT OR + UPDATE),而在 SQL 标准中只允许一个。如果一个触发器被定义为在多个事件上触发,它在信息模式中被表示为多行,每一行对应于一类事件。作为这两个问题的结果,视图triggers的主键实际上是(trigger_catalog, trigger_schema, event_object_table, + trigger_name, event_manipulation),而不是(trigger_catalog, trigger_schema, trigger_name)(这是 SQL 标准指定的)。尽管如此,如果你以符合 SQL 标准(在模式中触发器名称唯一并且每个触发器只能有一种事件类型)的方式定义你的触发器,这将不会影响你。 + + + + + 在PostgreSQL 9.1 之前,这个视图的列 + action_timing、 + action_reference_old_table、 + action_reference_new_table、 + action_reference_old_row和 + action_reference_new_row + 分别被命名为 + condition_timing、 + condition_reference_old_table、 + condition_reference_new_table、 + condition_reference_old_row和 + condition_reference_new_row。 + 那也是它们在 SQL:1999 标准中的命名。新的命名遵循 SQL:2003 及其后的版本。 + + +
+ + + <literal>udt_privileges</literal> + + + 视图udt_privileges标识所有在用户定义类型上授予的USAGE权限,这些权限的授予者或者被授予者是当前已启用的角色。 + 对每一个类型、授予者和被授予者的组合都有一行。 + 这个视图只显示复合类型(原因见下面的)。 + 域权限见。 + + + + <literal>udt_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + udt_catalog + sql_identifier + + 包含该类型的数据库名称(总是当前数据库) + + + + + udt_schema + sql_identifier + + 包含该类型的模式名称 + + + + + udt_name + sql_identifier + + 类型的名称 + + + + + privilege_type + character_data + + 总是TYPE USAGE + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>usage_privileges</literal> + + + 视图usage_privileges标识在多种对象上授予的所有USAGE权限,这些权限的授予者或被授予者是当前已启用的角色。在PostgreSQL中,这目前适用于排序规则、域、外部数据包装器、外部服务器和序列。对于每个对象、授予者和被授予者的组合,这里都有一行。 + + + + 由于在PostgreSQL中排序规则并没有真正的权限,这个视图对所有排序规则显示由拥有者授予给PUBLIC的隐式非可授予的USAGE权限。但是对其他对象类型则显示真实的权限。 + + + + 在 PostgreSQL 中,序列也支持除USAGE之外的SELECTUPDATE权限。这些是非标准的并且因此在该信息模式中不可见。 + + + + <literal>usage_privileges</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + grantor + sql_identifier + + 授予该权限的角色名称 + + + + + grantee + sql_identifier + + 被授予该权限的角色名称 + + + + + object_catalog + sql_identifier + + 包含该对象的数据库名称(总是当前数据库) + + + + + object_schema + sql_identifier + + 如果适用,则为包含该对象的模式名称,否则为一个空字符串 + + + + + object_name + sql_identifier + + 对象的名称 + + + + + object_type + character_data + + COLLATIONDOMAINFOREIGN DATA WRAPPERFOREIGN SERVERSEQUENCE + + + + + privilege_type + character_data + + 总是USAGE + + + + + is_grantable + yes_or_no + + 如果该权限是可授予的,则为YES,否则为NO + + + + +
+
+ + + <literal>user_defined_types</literal> + + + 视图user_defined_types目前包含定义在当前数据库中的所有复合类型。只有那些当前用户能够访问(作为拥有者或具有某些权限)的类型才会被显示。 + + + + SQL 中有两种用户定义类型:结构类型(在PostgreSQL中也称为复合类型)以及独立类型(distinct type,PostgreSQL 尚未实现)。为兼顾将来,请使用列user_defined_type_category来区分它们。其他用户定义类型,如基础类型和枚举(两者都是PostgreSQL的扩展),不会在这里显示。域的相关信息见。 + + + + <literal>user_defined_types</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + user_defined_type_catalog + sql_identifier + + 包含该类型的数据库名称(总是当前数据库) + + + + + user_defined_type_schema + sql_identifier + + 包含该类型的模式名称 + + + + + user_defined_type_name + sql_identifier + + 类型的名称 + + + + + user_defined_type_category + character_data + + 当前总是STRUCTURED + + + + + is_instantiable + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + is_final + yes_or_no + + 对应 PostgreSQL 不支持的特性 + + + + + ordering_form + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + ordering_category + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + ordering_routine_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + ordering_routine_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + ordering_routine_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + reference_type + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + data_type + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + character_maximum_length + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + character_octet_length + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + character_set_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_catalog + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_schema + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + collation_name + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + numeric_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + numeric_precision_radix + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + numeric_scale + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + datetime_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + interval_type + character_data + + 对应 PostgreSQL 不支持的特性 + + + + + interval_precision + cardinal_number + + 对应 PostgreSQL 不支持的特性 + + + + + source_dtd_identifier + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + + ref_dtd_identifier + sql_identifier + + 对应 PostgreSQL 不支持的特性 + + + + +
+
+ + + <literal>user_mapping_options</literal> + + + 视图user_mapping_options包含在当前数据库中为用户映射定义的所有选项。只有那些当前用户能够访问其相应外部服务器(作为拥有者或具有某些权限)的用户映射才会被显示。 + + + + <literal>user_mapping_options</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + authorization_identifier + sql_identifier + + 被映射的用户名称,如果映射是公共的则为PUBLIC + + + + + foreign_server_catalog + sql_identifier + + 这个映射所使用的外部服务器所在的数据库名称(总是当前数据库) + + + + + foreign_server_name + sql_identifier + + 这个映射所使用的外部服务器的名称 + + + + + option_name + sql_identifier + + 一个选项名称 + + + + + option_value + character_data + + 选项的值。除非当前用户是被映射的用户或者映射是PUBLIC的并且当前用户是服务器拥有者或者超级用户,这一列将显示为空。 + 这样做的目的是保护作为用户映射选项存储的密码信息。 + + + + +
+
+ + + <literal>user_mappings</literal> + + + 视图user_mappings包含定义在当前数据库中的所有用户映射。只有当前用户能够访问其对应外部服务器(作为拥有者或具有某些权限)的用户映射才会被显示。 + + + + <literal>user_mappings</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + authorization_identifier + sql_identifier + + 被映射的用户名称,如果映射是公共的则为PUBLIC + + + + + foreign_server_catalog + sql_identifier + + 这个映射所使用的外部服务器所在的数据库名称(总是当前数据库) + + + + + foreign_server_name + sql_identifier + + 这个映射所使用的外部服务器的名称 + + + + +
+
+ + + <literal>view_column_usage</literal> + + + 视图view_column_usage标识视图查询表达式(即定义该视图的 SELECT 语句)中使用的所有列。只有当包含该列的表归当前已启用角色所有时,该列才会出现在这个视图中。 + + + + + 系统表列不被包括。在某个时候这应该会被修复。 + + + + + <literal>view_column_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + view_catalog + sql_identifier + + 包含该视图的数据库名称(总是当前数据库) + + + + + view_schema + sql_identifier + + 包含该视图的模式名称 + + + + + view_name + sql_identifier + + 视图的名称 + + + + + table_catalog + sql_identifier + + 被视图所使用的列所属表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 被视图所使用的列所属表的模式名称 + + + + + table_name + sql_identifier + + 被视图所使用的列所属表的名称 + + + + + column_name + sql_identifier + + 被该视图所使用的列名称 + + + + +
+
+ + + <literal>view_routine_usage</literal> + + + 视图view_routine_usage标识视图查询表达式(即定义该视图的 SELECT 语句)中使用的所有例程(函数和过程)。只有归当前已启用角色所有的例程才会出现在这个视图中。 + + + + <literal>view_routine_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含该视图的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该视图的模式名称 + + + + + table_name + sql_identifier + + 视图的名称 + + + + + specific_catalog + sql_identifier + + 包含该函数的数据库名称(总是当前数据库) + + + + + specific_schema + sql_identifier + + 包含函数的模式的名称 + + + + + specific_name + sql_identifier + + 函数的特定名称。详见。 + + + + +
+
+ + + <literal>view_table_usage</literal> + + + 视图view_table_usage标识视图查询表达式(即定义该视图的 SELECT 语句)中使用的所有表。只有归当前已启用角色所有的表才会出现在这个视图中。 + + + + + 系统表没有被包括。这应当会在某个时候被修复。 + + + + + <literal>view_table_usage</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + view_catalog + sql_identifier + + 包含该视图的数据库名称(总是当前数据库) + + + + + view_schema + sql_identifier + + 包含该视图的模式名称 + + + + + view_name + sql_identifier + + 视图的名称 + + + + + table_catalog + sql_identifier + + 包含被该视图所使用的表的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含被该视图所使用的表的模式名称 + + + + + table_name + sql_identifier + + 包含被该视图所使用的表的名称 + + + + +
+
+ + + <literal>views</literal> + + + 视图views包含定义在当前数据库中的所有视图。只有当前用户能够访问(作为拥有者或具有某些权限)的视图才会被显示。 + + + + <literal>views</literal> 列 + + + + + 名称 + 数据类型 + + 描述 + + + + + + + table_catalog + sql_identifier + + 包含该视图的数据库名称(总是当前数据库) + + + + + table_schema + sql_identifier + + 包含该视图的模式名称 + + + + + table_name + sql_identifier + + 视图的名称 + + + + + view_definition + character_data + + 定义视图的查询表达式(如果该视图不被当前已启用角色拥有则为空) + + + + + check_option + character_data + + 如果视图上定义了 CHECK OPTION,则为 CASCADEDLOCAL;如果没有定义,则为 NONE。 + + + + + is_updatable + yes_or_no + + 如果视图可更新(允许UPDATEDELETE),则为YES,否则为NO + + + + + is_insertable_into + yes_or_no + + 如果视图可插入(允许INSERT),则为YES,否则为NO + + + + + is_trigger_updatable + yes_or_no + + 如果该视图上定义了 INSTEAD OF UPDATE 触发器,则为YES,否则为NO + + + + + is_trigger_deletable + yes_or_no + + 如果该视图上定义了 INSTEAD OF DELETE 触发器,则为YES,否则为NO + + + + + is_trigger_insertable_into + yes_or_no + + 如果该视图上定义了 INSTEAD OF INSERT 触发器,则为YES,否则为NO + + + + +
+
+ +
diff --git a/zh/9.6/install-windows.sgml b/zh/9.6/install-windows.sgml new file mode 100644 index 00000000..333b38d1 --- /dev/null +++ b/zh/9.6/install-windows.sgml @@ -0,0 +1,390 @@ + + + + 在 <productname>Windows</productname> 上从源代码安装 + + + 安装 + 在 Windows 上 + + + + 建议大多数用户下载适用于 Windows 的二进制发行版,它可以从PostgreSQL网站获得图形化安装程序包。从源码构建仅面向开发PostgreSQL或扩展的人员。 + + + + 在Windows上构建 PostgreSQL 有几种不同的方法。使用 Microsoft 工具进行构建的最简单方式,是安装Visual Studio 2019并使用其自带的编译器。也可以使用完整的Microsoft Visual C++ 2005 to 2019进行构建。在某些情况下,除编译器之外还需要安装Windows SDK。 + + + + 也可以使用MinGW提供的 GNU 编译器工具构建 PostgreSQL,或者在较旧版本的Windows上使用Cygwin。 + + + + 最后,客户端访问库(libpq)可以使用 + Visual C++ 7.1Borland C++构建, + 以便与使用这些工具静态链接构建的应用保持兼容。 + + + + 使用MinGWCygwin构建时,采用的是常规构建系统,见以及中的专门说明。要在这些环境中生成原生 64 位二进制文件,请使用MinGW-w64工具。这些工具也可用于在其他主机(例如LinuxDarwin)上为 32 位和 64 位Windows目标进行交叉编译。不建议使用Cygwin运行生产服务器,它只应用于原生构建无法工作的较旧版本Windows,例如Windows 98。官方二进制文件是使用Visual Studio构建的。 + + + + 原生构建的psql不支持命令行编辑。Cygwin构建支持命令行编辑,因此如果需要在Windows上交互式使用 psql,应当使用它。 + + + + 使用 <productname>Visual C++</productname> 或 <productname>Microsoft Windows SDK</productname> 构建 + + + PostgreSQL 可以使用 Microsoft 的 Visual C++ 编译器套件构建。这些编译器既可以来自Visual StudioVisual Studio Express,也可以来自某些版本的Microsoft Windows SDK。如果你还没有配置好Visual Studio环境,最简单的办法是使用Visual Studio 2019中的编译器,或者Windows SDK 10中的编译器,这两者都可从 Microsoft 免费下载。 + + + 使用 Microsoft 编译器套件既可以构建 32 位版本,也可以构建 64 位版本。32 位 PostgreSQL 可以使用 Visual Studio 2005Visual Studio 2019 构建,也可以使用独立发布的 Windows SDK 6.0 到 10 版本。64 位 PostgreSQL 支持使用 6.0a 到 10 版本的 Microsoft Windows SDKVisual Studio 2008 及以上版本构建。使用 Visual Studio 2005Visual Studio 2013 构建时,编译最低支持到 Windows XPWindows Server 2003。使用 Visual Studio 2015 构建时,最低支持到 Windows VistaWindows Server 2008。使用 Visual Studio 2017Visual Studio 2019 构建时,最低支持到 Windows 7 SP1Windows Server 2008 R2 SP1 + + 使用Visual C++Platform SDK构建所需的工具位于src/tools/msvc目录中。构建时,请确保系统 PATH 中没有来自MinGWCygwin的工具。同时,也要确保所需的 Visual C++ 工具都已在 PATH 中可用。在Visual Studio中,启动Visual Studio Command Prompt。如果你想构建 64 位版本,就必须使用 64 位版本的命令提示符,反之亦然。在 Microsoft Windows SDK 中,启动开始菜单中 SDK 项下列出的 CMD shell。在较新的 SDK 版本中,可以使用 setenv 命令更改目标 CPU 架构、构建类型以及目标操作系统,例如使用 setenv /x86 /release /xp 来面向 Windows XP 或更高版本进行 32 位发行版构建。setenv 的其他选项见 /?。所有命令都应在src\tools\msvc目录中运行。 + + + 在构建之前,你可能需要编辑config.pl文件,以反映你想更改的任何配置选项,或要使用的任何第三方库的路径。完整配置是先读取并解析config_default.pl文件,再应用来自config.pl的任何更改而得到的。例如,要指定Python安装位置,可在config.pl中写入以下内容: + +$config->{python} = 'c:\python26'; + + 你只需要指定那些与config_default.pl中不同的参数。 + + + + 如果你需要设置其他环境变量,请创建一个名为buildenv.pl的文件,并将所需命令放入其中。例如,如果 bison 不在 PATH 中,可以创建一个包含以下内容的文件,以将其路径加入 PATH: + +$ENV{PATH}=$ENV{PATH} . ';c:\some\where\bison\bin'; + + + + + + 需求 + + 构建PostgreSQL需要以下附加产品。请使用config.pl文件指定这些库所在的目录。 + + + + Microsoft Windows SDK + + 如果你的构建环境未附带受支持版本的Microsoft Windows SDK,建议升级到最新版本(目前为版本 10),可从下载。 + + + 你必须始终包含 SDK 中的Windows Headers and Libraries部分。如果你安装的Windows SDK包含Visual C++ Compilers,则无需Visual Studio即可构建。请注意,从 8.0a 版本起,Windows SDK 不再附带完整的命令行构建环境。 + + + + + ActiveState Perl + 运行构建生成脚本需要 ActiveState Perl。MinGW 或 Cygwin Perl 都无法工作。它还必须出现在 PATH 中。二进制文件可从 下载(注意:需要 5.8.3 或更高版本,免费的标准发行版即可)。 + + + + + + 以下附加产品并非入门所必需,但若要构建完整的软件包则需要它们。请使用config.pl文件指定这些库所在的目录。 + + + + ActiveState TCL + 构建 PL/TCL 需要它(注意:需要 8.4 版本,免费的标准发行版即可)。 + + + + BisonFlex + + + 从 Git 构建需要BisonFlex,但从发布文件构建则不需要。只有 Bison 1.875 或 2.2 及以上版本可以工作。Flex必须为 2.5.31 或更高版本。 + + + + BisonFlex都包含在msys工具套件中,可从获取,它是MinGW编译器套件的一部分。 + + + + 除非它们已经在 PATH 中,否则你需要把包含flex.exebison.exe的目录加入buildenv.pl中的 PATH 环境变量。对于 MinGW,该目录是你的 MinGW 安装目录下的\msys\1.0\bin子目录。 + + + + + 来自 GnuWin32 的 Bison 发行版似乎存在一个 bug:如果安装在名称中含有空格的目录中,Bison 就会工作异常,例如英文安装中的默认位置C:\Program Files\GnuWin32。请考虑安装到C:\GnuWin32,或者在 PATH 环境设置中使用指向 GnuWin32 的 NTFS 短文件名路径(例如C:\PROGRA~1\GnuWin32)。 + + + + + PostgreSQL FTP 站点提供、且旧版文档提及的过时 winflex 二进制文件,在 64 位 Windows 主机上会报错 flex: fatal internal error, exec failed。请改用 MSYS 中的 Flex。 + + + + + + Diff + + 运行回归测试需要 Diff,可从下载。 + + + + + Gettext + + 构建带有 NLS 支持的版本需要 Gettext,可从下载。请注意,二进制文件、依赖项以及开发文件都是必需的。 + + + + + MIT Kerberos + + 支持 GSSAPI 认证需要 MIT Kerberos。MIT Kerberos 可从下载。 + + + + + libxml2libxslt + + XML 支持需要它们。二进制文件可从下载,源代码可从获取。请注意,libxml2 需要 iconv,而它可从相同的下载位置获得。 + + + + + openssl + SSL 支持需要它。二进制文件可从 下载,源码可从 下载。 + + + + ossp-uuid + + 支持 UUID-OSSP 需要它(仅 contrib)。源代码可从下载。 + + + + + Python + + 构建PL/Python需要它。二进制文件可从下载。 + + + + + zlib + + pg_dumppg_restore中的压缩支持需要它。二进制文件可从下载。 + + + + + + + + + 64 位 Windows 的特殊注意事项 + + + PostgreSQL 在 64 位 Windows 上只会为 x64 架构构建,不支持 Itanium 处理器。 + + + + 不支持在同一个构建树中混用 32 位和 64 位版本。构建系统会自动检测自己是在 32 位还是 64 位环境中运行,并据此构建 PostgreSQL。因此,在开始构建之前启动正确的命令提示符非常重要。 + + + + 若要使用服务器端第三方库,例如pythonopenssl,该库也必须是 64 位的。不支持在 64 位服务器中加载 32 位库。PostgreSQL 支持的若干第三方库可能仅提供 32 位版本,在这种情况下,它们不能与 64 位 PostgreSQL 一起使用。 + + + + + 构建 + + + 要以发布配置(默认值)构建 PostgreSQL 的全部内容,请运行以下命令: + +build + + 要以调试配置构建 PostgreSQL 的全部内容,请运行以下命令: + +build DEBUG + + 若只构建单个项目,例如 psql,请运行以下命令: + +build psql +build DEBUG psql + + 若要将默认构建配置改为调试模式,请在buildenv.pl文件中加入以下内容: + +$ENV{CONFIG}="Debug"; + + + + + 也可以在 Visual Studio 图形界面中进行构建。在这种情况下,你需要先从命令提示符运行: + +perl mkvcbuild.pl + + 然后在 Visual Studio 中打开生成的pgsql.sln(位于源码树根目录)。 + + + + + 清理和安装 + + + 大多数时候,Visual Studio 的自动依赖跟踪都会处理好变更过的文件。但如果变更较大,你可能需要清理安装。要执行此操作,只需运行clean.bat命令,它会自动清除所有生成的文件。你也可以带上dist参数运行它,此时它的行为类似于make distclean,并且也会删除 flex/bison 的输出文件。 + + + + 默认情况下,所有文件都会写入debugrelease目录下的某个子目录。若要按标准布局安装这些文件,并同时生成初始化和使用数据库所需的文件,请运行以下命令: + +install c:\destination\directory + + + + + 如果你只想安装客户端应用程序和接口库,则可以使用以下命令: + +install c:\destination\directory client + + + + + + 运行回归测试 + + 要运行回归测试,请先确保已完成所有必需部分的构建。此外,还要确保加载系统各部分所需的 DLL(例如过程语言所用的 Perl 和 Python DLL)都能在系统路径中找到。如果不能,请通过 buildenv.pl 文件设置路径。要运行测试,请在 src\tools\msvc 目录中执行以下命令之一: +vcregress check +vcregress installcheck +vcregress plcheck +vcregress contribcheck +vcregress modulescheck +vcregress ecpgcheck +vcregress isolationcheck +vcregress bincheck +vcregress recoverycheck +vcregress upgradecheck +要更改所用的测试调度方式(默认为 parallel),请将其追加到命令行,例如: +vcregress check serial +有关回归测试的更多信息,参见 。 + + + 要对客户端程序运行回归测试,可使用 vcregress bincheck;要运行恢复测试,可使用 vcregress recoverycheck。两者都需要安装额外的 Perl 模块: + + IPC::Run + 撰写本文时,IPC::Run 既未包含在 ActiveState Perl 安装中,也未包含在 ActiveState Perl 软件包管理器(PPM)的库中。要安装它,请下载以下文件:IPC-Run-<version>.tar.gz,这是 CPAN 提供的源码归档,地址为 ,然后将其解压。编辑 buildenv.pl 文件,并添加 PERL5LIB 变量,使其指向解压后归档中的 lib 子目录。例如: +$ENV{PERL5LIB}=$ENV{PERL5LIB} . ';c:\IPC-Run-0.94\lib'; + + + + + + + + + 构建文档 + + 将 PostgreSQL 文档构建为 HTML 格式,需要若干工具和文件。请为这些文件创建一个根目录,并将它们存放在下列子目录中。 + + OpenJade 1.3.1-2 + 下载,并解压到子目录 openjade-1.3.1 中。 + + + + DocBook DTD 4.2 + 下载,并解压到子目录 docbook 中。 + + + + DocBook DSSSL 1.79 + 下载,并解压到子目录 docbook-dsssl-1.79 中。 + + + + ISO 字符实体 + 下载,并解压到子目录 docbook 中。 + + 编辑 buildenv.pl 文件,并添加一个变量来指定根目录的位置,例如: +$ENV{DOCROOT}='c:\docbook'; +要构建文档,请运行命令 builddoc.bat。注意,为了生成索引,实际上会构建两次。生成的 HTML 文件位于 doc\src\sgml。 + + + + + + + 使用<productname>Visual C++</productname>或<productname>Borland C++</productname>构建<application>libpq</application> + + + 只有当你需要带有不同调试/发布标志的版本,或需要静态库以链接到应用中时,才建议使用Visual C++ 7.1-9.0Borland C++构建 libpq。一般用途推荐使用MinGWVisual StudioWindows SDK方法。 + + + + 要使用Visual Studio 7.1 或更高版本构建libpq客户端库,请切换到src目录并输入命令: + +nmake /f win32.mak + + + + 要使用Visual Studio 8.0 或更高版本构建 64 位版本的libpq客户端库,请切换到src目录并输入命令: + +nmake /f win32.mak CPU=AMD64 + + 关于所支持变量的更多细节,参见win32.mak文件。 + + + + 要使用Borland C++构建libpq客户端库,请切换到src目录并输入命令: + +make -N -DCFG=Release /f bcc32.mak + + + + + 生成的文件 + + 将构建以下文件: + + + + interfaces\libpq\Release\libpq.dll + + + 动态可链接的前端库 + + + + + + interfaces\libpq\Release\libpqdll.lib + + + 用于把你的程序链接到libpq.dll的导入库 + + + + + + interfaces\libpq\Release\libpq.lib + + + 前端库的静态版本 + + + + + + + + + 通常不需要安装任何客户端文件。你应当把libpq.dll文件放在与应用程序可执行文件相同的目录中。除非绝对必要,不要把libpq.dll安装到你的WindowsSystemSystem32目录。如果使用安装程序安装该文件,则应利用文件中包含的VERSIONINFO资源进行带版本检查的安装,以确保不会覆盖较新版本的库。 + + + + 如果你计划在这台机器上使用libpq进行开发,则需要把源码树的src\includesrc\interfaces\libpq子目录加入编译器设置中的包含路径。 + + + + 要使用该库,你必须把libpqdll.lib文件加入你的项目。(在 Visual C++ 中,只需右键单击项目并选择添加它。) + + + + diff --git a/zh/9.6/installation.sgml b/zh/9.6/installation.sgml new file mode 100644 index 00000000..fb8b42b6 --- /dev/null +++ b/zh/9.6/installation.sgml @@ -0,0 +1,1948 @@ + + + + + <![%standalone-include[<productname>PostgreSQL</productname>]]> 从源代码安装 + + + 安装 + + + 介绍如何使用源代码发布包安装 PostgreSQL。(如果你安装的是预打包的发行版,例如 RPM 或 Debian 软件包,请忽略本,改为阅读打包者提供的说明。) + + + 简要说明 + + + +./configure +make +su +make install +adduser postgres +mkdir /usr/local/pgsql/data +chown postgres /usr/local/pgsql/data +su - postgres +/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data +/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data >logfile 2>&1 & +/usr/local/pgsql/bin/createdb test +/usr/local/pgsql/bin/psql test +详细说明见本 + + + + + 需求 + + 一般来说,现代的 Unix 兼容平台应该能够运行 PostgreSQL。发布时经过具体测试的平台列于下方 。发行包的 doc 子目录中有若干平台特定的 FAQ 文档,遇到问题时可以查阅。 + + + 构建 PostgreSQL 需要下列软件包: + + + + + + make + + + 需要 GNU make 3.80 或更高版本; + 其他 make 程序或较旧版本的 + GNU make 都 + 无法工作。(GNU + make 有时会以 gmake + 这个名字安装。)要检查是否为 GNU + make,请输入: + +make --version + + + + + + 你需要一个 ISO/ANSI C 编译器(至少符合 C89)。推荐使用较新的 GCC 版本,不过已知 PostgreSQL 也可以使用来自不同厂商的多种编译器构建。 + + + + + 需要 tar 来解开源代码发布包,此外还需要 + gzipbzip2 + 之一。 + + + + + + + readline + + + libedit + + + 默认使用 GNU Readline 库。 + 它可以让 psql(PostgreSQL 命令行 SQL 解释器) + 记住你输入的每一条命令,并允许你使用方向键回忆和编辑此前的命令。 + 这非常有用,强烈推荐。如果你不想使用它,则必须给 + configure 指定 + 选项。作为替代方案,你通常也可以使用 + BSD 许可的 libedit 库,它最初是在 + NetBSD 上开发的。 + libedit 库与 GNU Readline + 兼容;如果找不到 libreadline,或者在 + configure 中使用了 + 选项,就会使用它。如果你使用的是 + 基于软件包的 Linux 发行版,请注意,如果你的发行版把它们拆成单独的软件包, + 那么需要同时安装 readline 和 + readline-devel。 + + + + + + + zlib + + + 默认使用 zlib 压缩库。如果你不想使用它, + 则必须给 configure 指定 + 选项。使用这个选项将禁用 + pg_dumppg_restore + 对压缩归档的支持。 + + + + + + + 下列软件包是可选的。默认配置不需要它们,但在启用某些构建选项时会用到, + 如下所述: + + + + + 要构建服务器端编程语言 PL/Perl, + 你需要一个完整的 Perl 安装, + 包括 libperl 库和头文件。 + 最低要求版本是 Perl 5.8.3。 + 由于 PL/Perl 是一个共享库,在大多数平台上, + libperl + libperl 也必须是共享库。这在较新的 + Perl 版本中似乎是默认行为,但在更早版本中并非如此; + 无论如何,这取决于你所在环境中安装 Perl 的人是如何选择的。 + 如果选择构建 PL/Perl,但 + configure 找不到共享的 + libperl,则配置将失败。在这种情况下, + 你必须手动重新构建并安装 Perl, + 才能构建 PL/Perl。在配置 + Perl 时,请要求生成共享库。 + + + + 如果你打算不仅仅是偶尔使用 PL/Perl, + 应确保 Perl 安装是在启用了 + usemultiplicity 选项的情况下构建的 + (perl -V 会显示这一点)。 + + + + + 要构建 PL/Python 服务器端编程语言,你需要带有头文件以及 distutils 模块的 Python 安装。最低要求版本是 Python 2.3。(要处理numeric类型的函数参数,2.3.x 安装还必须包含单独提供的cdecimal模块;注意,如果缺少该模块,PL/Python回归测试将无法通过。)支持 3.1 及更高版本的 Python 3;但使用 Python 3 时请参见 PL/Python 文档]]>]]>。 + + + 由于 PL/Python 是一个共享库,在大多数平台上, + libpython + libpython 也必须是共享库。默认情况下, + 从源代码构建的 Python 安装并非如此, + 但很多操作系统发行版提供了共享库。如果选择构建 + PL/Python,但 + configure 找不到共享的 + libpython,则配置将失败。这可能意味着你要么需要安装 + 额外的软件包,要么需要重新构建(部分)Python + 安装,以提供这个共享库。从源代码构建时,请在运行 + Python 的 configure 时加上 + --enable-shared 标志。 + + + + + + 要构建 PL/Tcl 过程语言,你当然需要安装 + Tcl。最低要求版本是 + Tcl 8.4。 + + + + + 要启用本地语言支持(NLS),也就是以非英语语言显示程序消息的能力,你需要一个 Gettext API 的实现。某些操作系统已内置此功能(例如 LinuxNetBSDSolaris);对于其他系统,你可以从 下载附加软件包。如果你使用的是 GNU C 库中的 Gettext 实现,那么还需要 GNU Gettext 软件包来提供某些实用程序。如果使用其他实现,则不需要它。 + + + + 如果你希望支持使用这些服务进行认证或加密,就需要 KerberosOpenSSLOpenLDAP 和/或 PAM + + + + 构建 PostgreSQL 文档另有一组要求;参见 。]]> + + + + + 如果你从 Git 树构建,而不是使用已发布的源码包,或者你要进行服务器开发,还需要以下软件包: + + flex lex bison yacc 从 Git 检出构建,或者修改了扫描器和解析器的实际定义文件时,需要 GNU FlexBison。如果需要它们,请确保使用 Flex 2.5.31 或更高版本,以及 Bison 1.875 或更高版本。不能使用其他 lexyacc 程序。 + + + perl 从 Git 检出构建,或者修改了使用 Perl 脚本的任何构建步骤的输入文件时,需要 Perl 5.8.3 或更高版本。在 Windows 上构建时,无论如何都需要 Perl。运行某些测试套件也需要 Perl + + + + + 如果需要获取 GNU 软件包,可以从本地的 GNU 镜像站点获取(站点列表见 ),或者从 获取。 + + 还要检查磁盘空间是否充足。编译期间,源码树需要约 100 MB,安装目录需要约 20 MB。空数据库集簇约占 35 MB;数据库所需空间大约是存储相同数据的纯文本文件的五倍。如果要运行回归测试,还会临时额外需要最多 150 MB。使用 df 命令检查可用磁盘空间。 + + + + 获取源代码 + + 要获取 PostgreSQL &version; 的源码,请访问网站的下载区:。你应该取得一个名为 postgresql-&version;.tar.gzpostgresql-&version;.tar.bz2 的文件。取得文件后,将其解包: +gunzip postgresql-&version;.tar.gz +tar xf postgresql-&version;.tar +(可使用 bunzip2 代替 gunzip 来解压 .bz2 文件。)这会在当前目录下创建目录 postgresql-&version;,其中包含 PostgreSQL 源码。切换到该目录,继续完成其余安装过程。 + + 你也可以直接从版本控制仓库获取源码,参见 + +]]> + + + 安装过程 + + + + + 配置 + + + configure + + + 安装过程的第一步是为你的系统配置源码树,并选择所需选项。这通过运行以下脚本完成:configure。对于默认安装,只需输入: +./configure +此脚本会运行一系列测试,确定各个依赖系统的变量的值,并检测操作系统的特殊之处,最后在构建树中创建若干文件来记录结果。你也可以运行 configure 时使用源码树之外的目录,以将构建目录单独存放。这种过程也称为 VPATHVPATH 构建。方法如下: +mkdir build_dir +cd build_dir +/path/to/source/tree/configure [options go here] +make + + + + + 默认配置将构建服务器和实用程序,以及所有只需要 C 编译器的客户端应用程序和接口。 + 默认情况下,所有文件都会安装到 /usr/local/pgsql 之下。 + + + 你可以通过提供以下一个或多个命令行选项来定制构建和安装过程,这些选项传给 configure: + + + + + + + 把所有文件安装到目录 PREFIX 下, + 而不是 /usr/local/pgsql 下。实际文件会安装到 + 各个子目录中;不会有任何文件直接安装到 PREFIX + 目录中。 + + + 如果有特殊需求,还可以通过以下选项分别定制各个子目录。不过,如果保留它们的默认值,安装就可以重定位,也就是说,安装完成后可以移动目录。(mandoc 的位置不受此影响。) + + 对于可重定位的安装,可以考虑使用 configure--disable-rpath 选项。此外,还需要告知操作系统如何查找共享库。 + + + + + + + + 你可以把与体系结构相关的文件安装到与 PREFIX + 不同的前缀 EXEC-PREFIX 下。 + 这对于在多台主机之间共享与体系结构无关的文件很有用。 + 如果省略该选项,那么 EXEC-PREFIX + 会被设为与 PREFIX 相同, + 与体系结构相关和无关的文件都会安装到同一棵目录树下, + 这通常正是你想要的。 + + + + + + + + + 指定可执行程序的目录。默认是 + EXEC-PREFIX/bin, + 通常也就是 /usr/local/pgsql/bin。 + + + + + + + + + 设置各种配置文件的目录,默认是 + PREFIX/etc。 + + + + + + + + + 设置安装库和动态可加载模块的位置。默认是 + EXEC-PREFIX/lib。 + + + + + + + + + 设置安装 C 和 C++ 头文件的目录。默认是 + PREFIX/include。 + + + + + + + + + 设置多种只读数据文件的根目录。这只会为后续某些选项设置默认值。 + 默认是 PREFIX/share。 + + + + + + + + + 设置已安装程序使用的只读数据文件目录。默认是 + DATAROOTDIR。 + 注意,这与数据库文件实际存放的位置无关。 + + + + + + + + + 设置安装区域设置数据的目录,尤其是消息翻译目录文件。 + 默认是 DATAROOTDIR/locale。 + + + + + + + + + PostgreSQL 附带的手册页将安装到这个目录下 + 各自对应的 manx + 子目录中。默认是 + DATAROOTDIR/man。 + + + + + + + + + 设置安装文档文件(不包括 man 页)的根目录。 + 这只会为后续选项设置默认值。该选项的默认值是 + DATAROOTDIR/doc/postgresql。 + + + + + + + + + PostgreSQL 的 HTML 格式文档将安装到这个目录。 + 默认是 DATAROOTDIR。 + + + + + + + + 为了能把 PostgreSQL 安装到共享安装位置 + (例如 /usr/local/include),同时又不干扰系统其他部分的 + 名字空间,我们做了特别处理。首先,除非完整展开后的目录名已经包含字符串 + postgres 或 + pgsql,否则会自动把字符串 + /postgresql 追加到 + datadirsysconfdir 和 + docdir 上。例如,如果你选择 + /usr/local 作为前缀,那么文档会安装到 + /usr/local/doc/postgresql;但如果前缀是 + /opt/postgres,那么它会安装到 + /opt/postgres/doc。客户端接口的公共 C 头文件 + 会安装到 includedir 中,并且不会污染名字空间。 + 内部头文件和服务器头文件则会安装到 includedir + 下的私有目录中。关于如何访问这些头文件,请参见各接口自己的文档。 + 最后,如果有需要,也会在 libdir 下创建私有子目录, + 用于存放动态可加载模块。 + + + + + + + + + + 在 PostgreSQL 版本号后追加 STRING。例如,可以用它为从未发布的 Git 快照构建或包含自定义补丁的二进制文件添加额外版本字符串,如 git describe 标识符或发行版软件包的发布编号。 + + + + + + + + DIRECTORIES 是一个以冒号分隔的目录列表, + 这些目录会被加入编译器搜索头文件的路径中。 + 如果你把可选软件包(如 GNU Readline) + 安装在非标准位置,就必须使用此选项,并且通常还要使用对应的 + 选项。 + + + 例如:--with-includes=/opt/gnu/include:/usr/sup/include。 + + + + + + + + + DIRECTORIES 是一个以冒号分隔的目录列表, + 用于搜索库文件。如果你把软件包安装在非标准位置, + 很可能需要使用这个选项(以及对应的 )。 + + + 例如:--with-libraries=/opt/gnu/lib:/usr/sup/lib。 + + + + + + + + + 启用本地语言支持(NLS),也就是以非英语语言显示程序消息的能力。 + LANGUAGES 是可选的、以空格分隔的语言代码列表, + 例如 --enable-nls='de fr'。 + (你的列表与实际提供的翻译集合之间的交集会自动计算。) + 如果你不指定列表,则会安装所有可用的翻译。 + + + 要使用此选项,需要一个 Gettext API 实现;参见上文。 + + + + + + + + 将 NUMBER 设为服务器和客户端的默认端口号。 + 默认值是 5432。端口号以后始终都可以修改,但如果在这里指定, + 那么服务器和客户端都会编译进同一个默认值,这可能很方便。 + 通常选择非默认值的唯一合理原因,是你打算在同一台机器上运行多个 + PostgreSQL 服务器。 + + + + + + + + + 构建 PL/Perl 服务器端语言。 + + + + + + + + + 构建 PL/Python 服务器端语言。 + + + + + + + + + 构建 PL/Tcl 服务器端语言。 + + + + + + + + Tcl 会安装文件 tclConfig.sh,其中包含构建与 Tcl 交互的模块所需的配置信息。通常会在已知位置自动找到此文件,但如果要使用其他版本的 Tcl,可以指定查找它的目录。 + + + + + + + 构建时支持 GSSAPI 认证。在许多系统上,GSSAPI 系统(通常是 Kerberos 安装的一部分)并不安装在默认搜索的位置(如 /usr/include/usr/lib),因此除了此选项,还必须使用 configure 会检查所需的头文件和库,确保 GSSAPI 安装满足要求,然后才会继续。 + + + + + + + GSSAPI 使用的 Kerberos 服务主体的默认名称。默认值为 postgres。通常没有理由更改,除非使用 Windows 环境,此时必须将其设为大写的 POSTGRES + + + + + + OpenSSL SSL + + 构建时支持 SSL(加密)连接。这要求已安装 OpenSSL 软件包。configure 会检查所需的头文件和库,确保 OpenSSL 安装满足要求,然后才会继续。 + + + + + + + + 构建时支持 + PAMPAM + (可插拔认证模块)。 + + + + + + + + + 构建时支持 BSD 认证。 + (BSD 认证框架目前仅在 OpenBSD 上可用。) + + + + + + + + 构建时支持 LDAPLDAP 认证和连接参数查找(更多信息参见 ]]>)。在 Unix 上,这要求已安装 OpenLDAP 软件包。在 Windows 上,使用默认的 WinLDAP 库。configure 会检查所需的头文件和库,确保 OpenLDAP 安装满足要求,然后才会继续。 + + + + + + + 构建时支持 systemdsystemd 服务通知。如果服务器二进制文件由 systemd 启动,这可以改善集成;其他情况下没有影响]]>。要使用此选项,需要安装 libsystemd 及相关头文件。 + + + + + + + 禁止使用 Readline 库(也禁止 libedit)。此选项会禁用 psql 中的命令行编辑和历史记录,因此不建议使用。 + + + + + + + + 优先使用 BSD 许可的 libedit 库,而不是 GPL + 许可的 Readline。只有在两个库都已安装时, + 该选项才有意义;在那种情况下,默认行为是使用 + Readline。 + + + + + + + + 构建时支持 Bonjour。这要求操作系统支持 Bonjour。建议在 OS X 上启用。 + + + + + + + 使用指定的 UUID 库构建 ]]> 模块(提供生成 UUID 的函数)。UUID LIBRARY 必须为以下值之一: + + + + ,使用 FreeBSD、NetBSD 以及其他某些 BSD 衍生系统中的 UUID 函数 + + + + + ,使用 e2fsprogs 项目创建的 UUID 库; + 该库存在于大多数 Linux 系统和 OS X 中,也可用于其他平台 + + + + + ,使用 OSSP UUID 库 + + + + + + + + + + + 这是 --with-uuid=ossp 的过时等价写法。 + + + + + + + + + 构建时支持 libxml(以启用 SQL/XML 支持)。 + 这一特性要求 libxml 2.6.23 或更高版本。 + + + + libxml 会安装一个 xml2-config 程序,可用于检测所需的编译器和链接器选项。如果找到该程序,PostgreSQL 会自动使用它。要指定位于不常见位置的 libxml 安装,可以把环境变量 XML2_CONFIG 设为指向该安装对应的 xml2-config 程序,或使用 选项。 + + + + + + + + 构建 ]]> 模块时使用 libxslt。xml2 依赖此库对 XML 执行 XSL 转换。 + + + + + + + + 禁用对时间戳和时间间隔的 64 位整数存储支持,改为以浮点数存储日期时间值。浮点日期时间存储是 8.4 之前PostgreSQL发行版的默认方式,但现在已被弃用,因为它无法在完整timestamp取值范围内支持微秒精度。不过,基于整数的日期时间存储需要 64 位整数类型。因此,在没有这种类型的平台上,或为了与为早期PostgreSQL版本编写的应用保持兼容,可以使用此选项。更多信息参见 ]]>。 + + + + + + + + 禁用 float4 值的按值传递,改为按引用传递。此选项会降低性能,但为了兼容用 C 编写并使用版本 0 调用约定的旧用户定义函数,可能需要使用它。更好的长期解决办法是更新这些函数,使其使用版本 1 调用约定。 + + + + + + + 禁用 float8 值的按值传递,改为按引用传递。此选项会降低性能,但为了兼容用 C 编写并使用版本 0 调用约定的旧用户定义函数,可能需要使用它。更好的长期解决办法是更新这些函数,使其使用版本 1 调用约定。注意,此选项不仅影响 float8,还影响 int8 和 timestamp 等相关类型。在 32 位平台上,默认使用 ,且不允许选择 + + + + + + + 设置段大小,单位为 GB。大型表会拆分为多个操作系统文件,每个文件的大小等于段大小。这避免了许多平台上的文件大小限制问题。默认段大小为 1 GB,在所有支持的平台上都是安全的。如果操作系统支持大文件(如今大多数系统都支持),可以使用更大的段大小。这有助于减少处理超大表时消耗的文件描述符数量。但应注意,不要选择超出平台和拟用文件系统支持范围的值。你可能要使用的其他工具(如 tar)也可能限制可用的文件大小。建议将此值设为 2 的幂,但这并非硬性要求。注意,更改此值需要执行 initdb。 + + + + + + + 设置块大小,单位为 KB。这是表内存储和 I/O 的单位。默认值 8 KB 适用于大多数情况;在特殊情况下,其他值可能有用。该值必须是 1 到 32(KB)之间的 2 的幂。注意,更改此值需要执行 initdb。 + + + + + + + 设置 WAL 段大小,单位为 MB。这是 WAL 日志中每个文件的大小。调整此大小可能有助于控制 WAL 日志传送的粒度。默认大小为 16 MB。该值必须是 1 到 64(MB)之间的 2 的幂。注意,更改此值需要执行 initdb。 + + + + + + + 设置 WAL 块大小,单位为 KB。这是 WAL 日志内存储和 I/O 的单位。默认值 8 KB 适用于大多数情况;在特殊情况下,其他值可能有用。该值必须是 1 到 64(KB)之间的 2 的幂。注意,更改此值需要执行 initdb。 + + + + + + + 即使 PostgreSQL 不支持该平台的 CPU 自旋锁,也允许构建成功。缺少自旋锁支持会导致性能不佳;因此,仅应在构建中止并告知平台缺少自旋锁支持时使用此选项。如果在你的平台上构建 PostgreSQL 必须使用此选项,请向 PostgreSQL 开发者报告该问题。 + + + + + + + + 禁用客户端库的线程安全性。这会使 libpqECPG 程序中的并发线程无法安全地控制各自私有的连接句柄。 + + + + + + + 时区数据 + + + + + PostgreSQL 自带了日期和时间操作所需的时区数据库。 + 这个时区数据库实际上与很多操作系统(如 FreeBSD、Linux 和 Solaris) + 提供的 IANA 时区数据库兼容,因此再次安装它是多余的。 + 使用此选项时,将使用位于 DIRECTORY 的 + 系统提供时区数据库,而不是 PostgreSQL 源码发布中自带的那一份。 + DIRECTORY 必须是绝对路径。 + 在某些操作系统上,/usr/share/zoneinfo + 是一个可能的目录。请注意,安装过程不会检测时区数据是否不匹配或有误。 + 如果你使用该选项,建议运行回归测试,以验证你指定的时区数据能够与 + PostgreSQL 正常配合工作。 + + + 交叉编译 + + + 这个选项主要面向那些非常了解其目标操作系统的二进制包发布者。 + 使用该选项的主要优点是,当本地众多夏令时规则中的任何一条发生变化时, + PostgreSQL 软件包都无需升级。另一个优点是,如果安装时不需要构建 + 时区数据库文件,那么 PostgreSQL 的交叉编译也会更直接。 + + + + + + + + zlib 禁止使用 Zlib 库。这会禁用 pg_dumppg_restore 对压缩归档的支持。此选项仅用于没有该库的罕见系统。 + + + + + + + + 将所有程序和库编译为带调试符号的版本。这意味着你可以在调试器中运行程序, + 以分析问题。这会显著增大安装后的可执行文件大小,而且在非 GCC 编译器上, + 通常还会禁用编译器优化,从而导致变慢。不过,保留这些符号对于处理可能出现的 + 各种问题极其有帮助。目前,只有在你使用 GCC 的情况下,才建议在生产安装中使用 + 该选项。但如果你在做开发工作或运行测试版,就应始终启用它。 + + + + + + + + 如果使用 GCC,所有程序和库都会在编译时插入代码覆盖率测试所需的检测代码。运行时,它们会在构建目录中生成包含代码覆盖率指标的文件。。]]>此选项仅用于使用 GCC 进行开发工作时。 + + + + + + + 如果使用 GCC,所有程序和库都会编译为可进行性能分析的形式。后端退出时,会创建一个子目录,其中包含用于性能分析的 gmon.out 文件。此选项仅用于使用 GCC 进行开发工作时。 + + + + + + + + 在服务器中启用 断言 检查,用于测试许多 + 不可能发生的条件。这对代码开发非常有价值,但这些测试可能会显著拖慢 + 服务器速度。此外,启用这些测试并不一定会增强服务器稳定性! + 断言检查并未按严重程度分类,因此即使某个 bug 相对无害,只要触发了断言失败, + 仍会导致服务器重启。该选项不建议用于生产环境,但如果你在做开发工作 + 或运行测试版,就应当启用它。 + + + + + + + + + 启用自动依赖跟踪。启用后,makefile 会在任何头文件被修改时, + 重新构建所有受影响的目标文件。如果你在做开发工作,这很有用; + 但如果你只是打算编译一次并安装,这只是额外的开销。 + 目前该选项只在 GCC 下有效。 + + + + + + + + DTrace 编译 PostgreSQL 时支持动态跟踪工具 DTrace。。]]> + + 可以设置环境变量 DTRACE 来指定 dtrace 程序。这通常是必要的,因为 dtrace 一般安装在 /usr/sbin 下,而该目录可能不在搜索路径中。 + + 对于 dtrace 程序,可在以下环境变量中指定额外命令行选项:DTRACEFLAGS。在 Solaris 上,要在 64 位二进制文件中包含 DTrace 支持,必须指定 DTRACEFLAGS="-64" 给 configure。例如,使用 GCC 编译器时: +./configure CC='gcc -m64' --enable-dtrace DTRACEFLAGS='-64' ... +使用 Sun 的编译器时: +./configure CC='/opt/SUNWspro/bin/cc -xtarget=native64' --enable-dtrace DTRACEFLAGS='-64' ... + + + + + + + + + 启用使用 Perl TAP 工具的测试。这要求已安装 Perl 和 Perl 模块 IPC::Run。]]> + + + + + + 如果你希望使用不同于 configure 所选的 C 编译器,可以将环境变量 CC 设为所选程序。默认情况下,configure 会在 gcc 可用时选择它,否则选择平台默认编译器(通常为 cc)。类似地,如有需要,可以使用 CFLAGS 变量覆盖默认编译器标志。 + + 可以在 configure 命令行上指定环境变量,例如: +./configure CC=/opt/bin/gcc CFLAGS='-O2 -pipe' + + + + + 下面列出可以按这种方式设置的重要变量: + + + + BISON + + + Bison 程序 + + + + + + CC + + + C 编译器 + + + + + + CFLAGS + + + 传给 C 编译器的选项 + + + + + + CPP + + + C 预处理器 + + + + + + CPPFLAGS + + + 传给 C 预处理器的选项 + + + + + + DTRACE + + + dtrace 程序的位置 + + + + + + DTRACEFLAGS + + + 传给 dtrace 程序的选项 + + + + + + FLEX + + + Flex 程序 + + + + + + LDFLAGS + + + 用于链接可执行文件或共享库的选项 + + + + + + LDFLAGS_EX + + + 仅用于链接可执行文件的附加选项 + + + + + + LDFLAGS_SL + + + 仅用于链接共享库的附加选项 + + + + + + MSGFMT + + + 用于本地语言支持的 msgfmt 程序 + + + + + + PERL + + + Perl 解释器的完整路径名。它将用于确定构建 PL/Perl 所需的依赖。 + + + + + + PYTHON + + Python 解释器的完整路径名。它将用于确定构建 PL/Python 所需的依赖。此外,这里指定(或以其他方式隐式选择)Python 2 还是 3,决定了可用的 PL/Python 语言变体。更多信息参见 PL/Python 文档]]>]]>。如果未设置,则会按以下顺序探测:python python3 python2 + + + + + TCLSH + + Tcl 解释器的完整路径名。它将用于确定构建 PL/Tcl 所需的依赖,并会被代入 Tcl 脚本中。 + + + + + XML2_CONFIG + + + 用于定位 libxml 安装的 xml2-config 程序。 + + + + + + + 有时,事后补充编译器标志会很有用;原有标志由 configure 选定。一个重要的例子是 gcc 选项不能包含在以下变量中:CFLAGS(该变量会传给 configure),因为它会破坏 configure 的许多内置测试。要添加此类标志,请将其放入 COPT 环境变量,再运行 makeCOPT 的内容会被加入以下两组选项:CFLAGSLDFLAGS;这两组选项原本由以下程序设置:configure。例如,你可以执行: +make COPT='-Werror' +或者: +export COPT='-Werror' +make + + + + + 开发服务器内部代码时,建议使用 configure 选项 (开启许多运行时错误检查)和 (提高调试工具的实用性)。 + + + 如果使用 GCC,最好使用至少 的优化级别进行构建, + 因为不使用优化()会关闭一些重要的编译器警告 + (例如未初始化变量的使用)。不过,非零优化级别会让调试变得更复杂, + 因为单步执行编译后的代码时,通常无法与源代码行一一对应。 + 如果你在调试优化后的代码时感到困惑,可以把感兴趣的特定文件用 + 重新编译。一个简单方法是向 + make 传递选项: + make PROFILE=-O0 file.o。 + + + + COPTPROFILE 环境变量实际上会被 + PostgreSQL 的 makefile 以完全相同的方式处理。 + 用哪个只是个人偏好问题,但开发者常见的习惯是把 PROFILE + 用于一次性的标志调整,而 COPT 则可能长期保持设置。 + + + + + + 构建 + + 要开始构建,请输入: +make +(记得使用 GNU make。)构建需要几分钟,具体取决于硬件。显示的最后一行应该是: +All of PostgreSQL successfully made. Ready to install. + + + + 如果要构建所有可构建的内容,包括文档(HTML 和手册页)及附加模块(contrib),请改为输入: +make world +显示的最后一行应该是: +PostgreSQL, contrib, and documentation successfully made. Ready to install. + + + + + 如果你希望构建所有可构建的内容,包括附加模块 + (contrib),但不包括文档,则改为输入: + +make world-bin + + + + + + 回归测试 + + + 回归测试 + + + 如果希望在安装前测试刚构建的服务器,可以在此时运行回归测试。回归测试是一套测试程序,用于验证 PostgreSQL 是否在你的机器上按开发者预期的方式运行。输入: +make check +(不能以 root 身份运行;请使用非特权用户。)src/test/regress/README 及文档]]>]]>包含解读测试结果的详细信息。以后任何时候都可以通过执行同一命令来重复此测试。 + + + + 安装文件 + + + 如果正在升级现有系统,请务必阅读 ]]>其中包含升级数据库集簇的说明。 + + + + 要安装 PostgreSQL,请输入: + +make install + + 这会把文件安装到 中指定的目录。 + 请确保你有权限写入该区域。通常这一步需要以 root 身份执行。 + 另一种做法是预先创建目标目录,并安排授予适当权限。 + + + + 要安装文档(HTML 和手册页),请输入: + +make install-docs + + + + + 如果你上面执行的是 world 构建,则改为输入: + +make install-world + + 这也会安装文档。 + + + + 如果你上面执行的是不含文档的 world 构建,则改为输入: + +make install-world-bin + + + + + 你可以用 make install-strip 代替 + make install,在安装时剥离可执行文件和库中的符号。 + 这会节省一些空间。如果你在构建时启用了调试支持,那么剥离操作实际上会移除调试支持, + 因此只有在不再需要调试时才应该这样做。install-strip + 会尽量合理地节省空间,但它并不知道如何从每个可执行文件中去掉所有不需要的字节, + 所以如果你希望尽可能节省所有磁盘空间,仍需要手工处理。 + + + 标准安装会提供客户端应用开发以及服务器端程序开发所需的全部头文件,例如用 C 编写的自定义函数或数据类型。(在 PostgreSQL 8.0 之前,后者需要单独执行 make install-all-headers 命令,但此步骤现已并入标准安装。) + + + 仅客户端安装: + + 如果你只想安装客户端应用程序和接口库,可以使用以下命令: + +make -C src/bin install +make -C src/include install +make -C src/interfaces install +make -C doc install + + src/bin 中有少量仅供服务器使用的二进制文件, + 不过它们都很小。 + + + + + + + 卸载: + + 要撤销安装,请使用命令 make uninstall。 + 不过,这不会删除任何已创建的目录。 + + + + + 清理: + + + 安装完成后,你可以使用命令 make clean + 从源码树中删除构建产生的文件,以释放磁盘空间。 + 这会保留 configure 程序生成的文件, + 以便你之后可以用 make 重新构建全部内容。 + 要把源码树重置到发布时的状态,请使用 make distclean。 + 如果你打算在同一个源码树中为多个平台构建,就必须为每个平台执行这一步并重新配置。 + (另一种做法是为每个平台使用单独的构建树,这样源码树就不会被修改。) + + + + + 如果你已经执行了一次构建,随后发现 configure 选项有误, + 或者修改了任何会被 configure 检查的内容 + (例如软件升级),那么在重新配置和重新构建之前先执行 + make distclean 是一个好主意。否则,你对配置选项所做的更改 + 可能不会传播到所有需要它们的地方。 + + + + + 安装后设置 + + + 共享库 + + + 共享库 + + + + 在某些使用共享库的系统上,你需要告诉系统如何找到新安装的共享库。 + 需要这样做的系统包括 + FreeBSD、 + HP-UX、 + Linux、 + NetBSD、 + OpenBSD 和 + Solaris。 + + + 设置共享库搜索路径的方法因平台而异,但最常见的方法是设置环境变量 LD_LIBRARY_PATH,方法如下。在 Bourne shell(shkshbashzsh): + +LD_LIBRARY_PATH=/usr/local/pgsql/lib +export LD_LIBRARY_PATH +或者在 cshtcsh: + +setenv LD_LIBRARY_PATH /usr/local/pgsql/lib +请将 /usr/local/pgsql/lib 替换为你为 指定的值,设置过程见 。应将这些命令放入 shell 启动文件,例如 /etc/profile~/.bash_profile。关于此方法相关注意事项的一些有用信息,参见 。 + + + + 在某些系统上,更好的做法可能是在构建 + 之前 设置环境变量 LD_RUN_PATH。 + + + + 在 Cygwin 上,请把库目录加入 + PATH,或者把 .dll 文件移到 + bin 目录中。 + + + + 如果不确定,请参考你系统的手册页(可能是 ld.so + 或 rld)。如果你随后收到类似下面这样的消息: + +psql: error in loading shared libraries +libpq.so.2.1: cannot open shared object file: No such file or directory + + 那就说明这一步确实是必须的。到那时再处理即可。 + + + + + ldconfig + + 如果你使用的是 Linux 且拥有 root 权限, + 可以在安装后运行: + +/sbin/ldconfig /usr/local/pgsql/lib + + (或者等效目录),这样可以让运行时链接器更快地找到共享库。 + 更多信息请参见 ldconfig 的手册页。 + 在 FreeBSD、 + NetBSD 和 + OpenBSD 上,相应命令是: + +/sbin/ldconfig -m /usr/local/pgsql/lib + + 其他系统尚不清楚是否有等效命令。 + + + + + 环境变量 + + + PATH + + + + 如果你安装到了 /usr/local/pgsql,或者安装到了其他默认 + 不会搜索程序的目录,那么应该把 /usr/local/pgsql/bin + (或者你在 中通过 + 设置的值)加入你的 + PATH。严格来说,这不是必需的,但它会让 + PostgreSQL 的使用方便得多。 + + + + 为此,请把下面内容加到你的 shell 启动文件中,例如 + ~/.bash_profile(如果你希望影响所有用户,则用 + /etc/profile): + +PATH=/usr/local/pgsql/bin:$PATH +export PATH + + 如果你使用的是 cshtcsh, + 则使用这条命令: + +set path = ( /usr/local/pgsql/bin $path ) + + + + + + MANPATH + + 要让系统能够找到 man 文档,除非你安装到了默认会搜索 + 的位置,否则需要把类似下面的内容加到 shell 启动文件中: + +MANPATH=/usr/local/pgsql/share/man:$MANPATH +export MANPATH + + + + + 环境变量 PGHOSTPGPORT 会告诉客户端应用 + 数据库服务器的主机和端口,从而覆盖编译时内置的默认值。 + 如果你准备从远端运行客户端应用,那么让每个打算使用数据库的用户都设置 + PGHOST 会比较方便。不过这不是必需的;对于大多数客户端程序, + 也可以通过命令行选项传递这些设置。 + + + + + + + 开始使用 + + 以下简要介绍安装后如何启动并运行 PostgreSQL。主文档包含更多信息。 + + + + PostgreSQL 服务器创建用户账户,服务器将以该用户身份运行。用于生产环境时,应创建单独的非特权账户(通常使用 postgres)。如果没有 root 权限,或者只是想试用,使用自己的用户账户即可;但以 root 身份运行服务器存在安全风险,而且不允许运行。 +adduser postgres + + + + + + 要创建数据库安装,请使用 initdb 命令。要运行 initdb,必须登录 PostgreSQL 服务器账户。不能以 root 身份运行。 +root# mkdir /usr/local/pgsql/data +root# chown postgres /usr/local/pgsql/data +root# su - postgres +postgres$ /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data + + + + 选项指定数据存放位置。可以使用任何所需路径,不必位于安装目录下。只需像这里展示的那样,在启动 initdb 之前,确保服务器账户能够写入该目录(如果目录尚不存在,则能够创建它)。 + + + + 此时,如果未使用 initdb-A 选项,可以在启动服务器之前修改 pg_hba.conf,控制对服务器的本地访问。默认会信任所有本地用户。 + + + + 前面的 initdb 步骤应该已经告诉你如何启动数据库服务器。现在就启动它。命令应该类似于: +/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data +这会在前台启动服务器。要让服务器在后台运行,请使用类似以下命令: +nohup /usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data \ + </dev/null >>server.log 2>&1 </dev/null & + + + + 要停止在后台运行的服务器,可以输入: +kill `cat /usr/local/pgsql/data/postmaster.pid` + + + + + + 创建数据库: +createdb testdb +然后输入: +psql testdb +以连接到该数据库。在提示符下,可以输入 SQL 命令并开始试用。 + + + + + + 接下来做什么? + + + + + PostgreSQL 发行包包含一套完整文档,你应该找时间阅读。安装后,除非更改了安装目录,否则可以在浏览器中打开 /usr/local/pgsql/doc/html/index.html 来访问文档。 + + 主文档的前几章是教程。如果你完全不熟悉 SQL 数据库,应先阅读这些章节。如果熟悉数据库概念,可以接着阅读服务器管理部分,其中介绍了如何设置数据库服务器、数据库用户和认证。 + + + + 通常,你会希望修改计算机配置,使其在每次启动时自动启动数据库服务器。文档中对此提供了一些建议。 + + + + 对已安装的服务器运行回归测试(使用 make installcheck)。如果安装前没有运行测试,现在一定应该运行。文档中也对此作了说明。 + + + + 默认情况下,PostgreSQL 配置为可以在最低限度的硬件上运行。这使它几乎可以在任何硬件配置下启动。不过,默认配置并不是为了获得最佳性能而设计的。要达到最佳性能,必须调整若干服务器参数,最常见的两个是 shared_bufferswork_mem。文档提到的其他参数也会影响性能。 + + + + +]]> + + + + 受支持的平台 + + + 如果代码中包含了使某个平台(也就是一种 CPU 架构和操作系统的组合)工作的相关处理, + 并且最近已经在该平台上验证过能够成功构建并通过回归测试, + 那么 PostgreSQL 开发社区就认为该平台受支持。 + 目前,大多数平台兼容性测试都是由 + PostgreSQL 构建农场 + 中的测试机器自动完成的。如果你有兴趣在某个平台上使用 + PostgreSQL,而该平台尚未出现在构建农场中, + 但代码已经能够工作或可以被修改得可工作,我们强烈鼓励你设置一个构建农场 + 成员机器,以便持续确保兼容性。 + + + 一般来说,可以预期 PostgreSQL 能在以下 CPU 架构上工作:x86、x86_64、IA64、PowerPC、PowerPC 64、S/390、S/390x、Sparc、Sparc 64、ARM、MIPS、MIPSEL、M68K 和 PA-RISC。代码中包含对 M32R 和 VAX 的支持,但尚不清楚这些架构近期是否经过测试。对于不受支持的 CPU 类型,通常可以通过配置 来构建,但性能会很差。 + + 可以预期 PostgreSQL 能在以下操作系统上工作:Linux(所有近期发行版)、Windows(Win2000 SP4 及更高版本)、FreeBSD、OpenBSD、NetBSD、OS X、AIX、HP/UX、Solaris 和 UnixWare。其他类 Unix 系统也可能可用,但目前未在测试。大多数情况下,某个操作系统所支持的全部 CPU 架构也都可以工作。尤其是在使用较旧系统时,请查看下面的,看看是否有针对你的操作系统的特别说明。 + + + 如果你在某个平台上遇到安装问题,而根据近期构建农场的结果该平台是受支持的, + 请把问题报告到 pgsql-bugs@lists.postgresql.org。 + 如果你有兴趣把 PostgreSQL 移植到一个新平台, + 那么 pgsql-hackers@lists.postgresql.org 是合适的讨论地点。 + + + + + 平台相关说明 + + 本节记录 PostgreSQL 安装和设置时的一些平台相关附加问题。请务必阅读安装说明,尤其是 。此外,也请参阅 src/test/regress/README 及文档]]>]]> 中关于如何解释回归测试结果的说明。 + + + 这里未列出的平台,目前没有已知的平台特定安装问题。 + + + + AIX + + + AIX + 在其上安装 + + + PostgreSQL 可以在 AIX 上运行,但正确安装可能比较困难。AIX 4.3.3 至 6.1 被视为受支持版本。你可以使用 GCC 或原生 IBM 编译器 xlc。一般来说,使用较新的 AIX 和 PostgreSQL 版本会有所帮助。关于哪些 AIX 版本已知可用的最新信息,请查看构建农场。 + + 对于受支持的 AIX 版本,建议至少达到以下修复级别: + + + + AIX 4.3.3 + 维护级别 11,加上 ML11 后续补丁包 + + + + AIX 5.1 + 维护级别 9,加上 ML9 后续补丁包 + + + + AIX 5.2 + 技术级别 10,服务包 3 + + + + AIX 5.3 + 技术级别 7 + + + + AIX 6.1 + 基础级别 + + + + 要检查当前修复级别,在 AIX 4.3.3 至 AIX 5.2 ML 7 上使用 oslevel -r,在更高版本上使用 oslevel -s + + 如果将 Readline 或 libz 安装在 /usr/local,除了自己的选项外,还应使用以下 configure 标志:--with-includes=/usr/local/include --with-libraries=/usr/local/lib + + + GCC 问题 + + 在 AIX 5.3 上,使用 GCC 编译和运行 PostgreSQL 曾出现一些问题。 + + 应使用晚于 3.3.2 的 GCC 版本,尤其是在使用预打包版本时。我们使用 4.0.1 的效果很好。较早版本的问题似乎更多与 IBM 打包 GCC 的方式有关,而非 GCC 本身的问题,因此如果自行编译 GCC,使用较早版本的 GCC 也很可能成功。 + + + + Unix 域套接字不可用 + + AIX 5.3 存在一个问题:sockaddr_storage 的大小定义不足。在 5.3 版中,IBM 增大了 Unix 域套接字地址结构 sockaddr_un 的大小,却未相应增大 sockaddr_storage。结果是,尝试通过 Unix 域套接字使用 PostgreSQL 时,libpq 会写出该数据结构的边界。TCP/IP 连接正常,但 Unix 域套接字无法正常工作,导致回归测试无法运行。 + + 此问题已报告给 IBM,缺陷报告编号为 PMR29657。升级到维护级别 5300-03 或更高版本即可包含此修复。一种快速解决办法是将 /usr/include/sys/socket.h 中的 _SS_MAXSIZE 改为 1025。无论采用哪种方法,在取得修正后的头文件后,都应重新编译 PostgreSQL。 + + + + 互联网地址问题 + + PostgreSQL 依赖系统的 getaddrinfo 函数解析 listen_addressespg_hba.conf 等位置中的 IP 地址。较旧的 AIX 版本在此函数中存在多种缺陷。如果遇到与这些设置有关的问题,更新到上面所列的适当 AIX 修复级别应该能够解决。 + + + + 一位用户报告: + + 在 AIX 5.3 上部署 PostgreSQL 8.1 时,我们不时遇到统计信息收集器莫名其妙地无法成功启动的问题。这似乎是 IPv6 实现中意外行为所致。看起来在 AIX 5.3 上,PostgreSQL 与 IPv6 配合得不太好。 + + 以下任意操作都能修复此问题。 + + 删除 localhost 的 IPv6 地址: +(as root) +# ifconfig lo0 inet6 ::1/0 delete + + + + + + 从网络服务中移除 IPv6。在 AIX 上,文件 /etc/netsvc.conf 大致相当于 Solaris/Linux 上的 /etc/nsswitch.conf。AIX 上的默认设置如下: +hosts=local,bind +将其替换为: +hosts=local4,bind4 +以停用对 IPv6 地址的搜索。 + + + + + + 这实际上只是针对 IPv6 支持不成熟所引发问题的临时解决办法,而 IPv6 支持在 AIX 5.3 的各次发布中已有明显改进。此方法在 AIX 5.3 上有效,但并不是优雅的解决方案。据报告,在 IPv6 支持更成熟的 AIX 6.1 上,这种做法不仅没有必要,还会导致问题。 + + + + + + 内存管理 + + + AIX 的内存管理方式有些特殊。服务器可能有数 GB 乃至更多的空闲 RAM,但运行应用程序时仍会出现内存不足或地址空间错误。例如,createlang可能因不寻常的错误而失败。以下是以 PostgreSQL 安装所有者身份运行的例子: +-bash-3.00$ createlang plperl template1 +createlang: language installation failed: ERROR: could not load library "/opt/dbs/pgsql748/lib/plperl.so": A memory address is not in the address space for the process. +以 PostgreSQL 安装所属组中非所有者的成员身份运行: +-bash-3.00$ createlang plperl template1 +createlang: language installation failed: ERROR: could not load library "/opt/dbs/pgsql748/lib/plperl.so": Bad address +另一个例子是 PostgreSQL 服务器日志中的内存不足错误,此时每次接近或超过 256 MB 的内存分配都会失败。 + + 这些问题的总体原因是服务器进程使用的默认位数和内存模型。默认情况下,在 AIX 上构建的所有二进制文件都是 32 位的。这与硬件类型或所用内核无关。这些 32 位进程的内存上限为 4 GB,按几种模型之一以 256 MB 的段布局。默认情况下,堆与栈共享一个段,因此堆可用空间不足 256 MB。 + + 对于上面的 createlang 示例,请检查你的 umask 和 PostgreSQL 安装中二进制文件的权限。该示例涉及的二进制文件是 32 位的,安装时使用模式 750 而非 755。由于权限以这种方式设置,只有所有者或所属组的成员可以加载该库。因为它不是所有用户可读,加载器会将该对象放入进程的堆中,而不是通常应放置的共享库段。 + + 这个问题的理想解决办法是使用 64 位构建的 PostgreSQL,但这并非总是可行,因为采用 32 位处理器的系统可以构建 64 位二进制文件,却无法运行它们。 + + 如果需要 32 位二进制文件,请在启动 PostgreSQL 服务器之前,将 LDR_CNTRL 设为 MAXDATA=0xn0000000,其中 1 <= n <= 8,并尝试不同的值及 postgresql.conf 设置,找到能令人满意地工作的配置。这样使用 LDR_CNTRL 会告知 AIX,为服务器的堆预留 MAXDATA 字节,按 256 MB 的段分配。找到可用配置后,可以使用 ldedit 修改二进制文件,使其默认使用所需的堆大小。也可以重新构建 PostgreSQL,传入 configure LDFLAGS="-Wl,-bmaxdata:0xn0000000" 来达到相同效果。 + + 对于 64 位构建,将 OBJECT_MODE 设为 64,并向 configure 传入 CC="gcc -maix64"LDFLAGS="-Wl,-bbigtoc"。(xlc 的选项可能不同。)如果未导出 OBJECT_MODE,构建可能因链接器错误而失败。设置 OBJECT_MODE 后,它会告知 AIX 的 arasld 等构建工具默认处理哪种对象。 + + 默认情况下,可能发生分页空间过量分配。虽然我们尚未见过这种情况,但当内存耗尽且访问了过量分配的空间时,AIX 会终止进程。我们遇到过最接近的情况是,系统认为没有足够内存容纳另一个进程,导致 fork 失败。与 AIX 的许多其他部分一样,如果这成为问题,可以在系统或进程级别配置分页空间分配方式和内存不足时终止进程的行为。 + + + + + Cygwin + + + Cygwin + 在其上安装 + + + 可以使用 Cygwin 这个 Windows 上的类 Linux 环境来构建 PostgreSQL,但这种方式不如原生 Windows 构建)]]>,且如今已不再推荐在 Cygwin 下运行服务器。 + + 从源码构建时,请按照常规安装过程操作(即 ./configure; + make 等),同时注意以下 Cygwin 特有的差异: + + + 请把路径设置成优先使用 Cygwin 的 bin 目录,而不是 Windows 工具目录。 + 这有助于避免编译问题。 + + + + + 不支持 adduser 命令;请使用 Windows NT、2000 或 XP 中相应的用户管理应用。如果不是这些系统,则跳过此步骤。 + + + + 不支持 su 命令;请在 Windows NT、2000 或 XP 上使用 ssh 模拟 su。如果不是这些系统,则跳过此步骤。 + + + + + 不支持 OpenSSL。 + + + + + + 请启动 cygserver 以支持共享内存。 + 为此,请输入命令 /usr/sbin/cygserver + &。每次启动 PostgreSQL 服务器或初始化数据库集簇 + (initdb)时,该程序都必须在运行中。 + 默认的 cygserver 配置可能需要修改 + (例如增大 SEMMNS),以防止 PostgreSQL 因系统资源不足而失败。 + + + + + 在某些使用非 C 区域设置的系统上,构建可能会失败。要修复这一点,请在构建前执行 export LANG=C.utf8 把区域设置改为 C,安装完 PostgreSQL 后再把它恢复为之前的设置。 + + + + 并行回归测试(make check)可能因 listen() 的待处理连接队列溢出而误报回归测试失败;队列溢出会导致连接被拒绝的错误或挂起。可以使用 make 变量 MAX_CONNECTIONS 限制连接数,方法如下: + +make MAX_CONNECTIONS=5 check + +(在某些系统上,并发连接数最高可达约 10 个。) + + + + + + 可以把 cygserver 和 PostgreSQL 服务器安装为 + Windows NT 服务。关于具体做法,请参阅 Cygwin 上 PostgreSQL 二进制包附带的 + README 文档。它安装在 + /usr/share/doc/Cygwin 目录中。 + + + + + HP-UX + + + HP-UX + 在其上安装 + + + 如果系统补丁级别和构建工具合适,PostgreSQL 7.3+ 应能在运行 HP-UX 10.X 或 11.X 的 Series 700/800 PA-RISC 机器上工作。至少有一位开发者经常在 HP-UX 10.20 上测试,我们也收到过在 HP-UX 11.00 和 11.11 上成功安装的报告。 + + 除了 PostgreSQL 源码发行包,还需要 GNU make(HP 的 make 不可用),以及 GCC 或 HP 的完整 ANSI C 编译器。如果打算从 Git 源码而非发行 tar 包构建,还需要 Flex(GNU lex)和 Bison(GNU yacc)。我们也建议确保 HP 补丁相对较新。至少,在 HP-UX 11.11 上构建 64 位二进制文件时,可能需要 PHSS_30966(11.11)或其后续补丁,否则 initdb 可能挂起: +PHSS_30966 s700_800 ld(1) and linker tools cumulative patch +一般而言,应安装最新的 libc 和 ld/dld 补丁;如果使用 HP 的 C 编译器,也应安装最新的编译器补丁。请访问 HP 的支持站点,例如 ,免费获取最新补丁。 + + 如果在 PA-RISC 2.0 机器上构建,并希望使用 GCC 生成 64 位二进制文件,则必须使用 64 位版本的 GCC。HP-UX PA-RISC 和 Itanium 的 GCC 二进制文件可从 获取。别忘了同时获取并安装 binutils。 + + 如果在 PA-RISC 2.0 机器上构建,并希望编译后的二进制文件能在 PA-RISC 1.1 机器上运行,需要在 CFLAGS 中指定 + + 如果在 HP-UX Itanium 机器上构建,需要最新的 HP ANSI C 编译器及其依赖补丁或后续补丁: +PHSS_30848 s700_800 HP C Compiler (A.05.57) +PHSS_30849 s700_800 u2comp/be/plugin library Patch + + + + 如果同时安装了 HP 的 C 编译器和 GCC,可以在运行 configure 时显式选择要使用的编译器: + +./configure CC=cc +这会选择 HP 的 C 编译器;或者执行: +./configure CC=gcc +这会选择 GCC。如果省略此设置,configure 在可以选择时会选用 gcc 作为编译器。 + + 默认安装目标位置为 /usr/local/pgsql,你可能希望将其改为 /opt 下的某个位置。如果是这样,请为 configure 使用 选项。 + + 在回归测试中,几何测试可能存在一些低位数字差异,具体取决于所用编译器和数学库的版本。任何其他错误都值得怀疑。 + + + + macOS + + + macOS + 在其上安装 + + + + 要在 macOS 上从源代码构建 + PostgreSQL,你需要安装 Apple 的命令行开发工具, + 可通过执行下列命令完成: + +xcode-select --install + + (注意,这会弹出一个 GUI 对话框要求确认。) + 你也可以视需要另外安装 Xcode。 + + + + 在较新的 macOS 版本中,需要把 + sysroot 路径嵌入到用于查找某些系统头文件的 include 开关中。 + 这会导致 configure 脚本的输出, + 因为 configure 时所用 SDK 版本不同而变化。 + 在简单场景下这通常不是问题;但如果你要做的是类似于在与服务器代码构建机器不同的 + 另一台机器上构建扩展,就可能需要强制使用不同的 sysroot 路径。 + 要这样做,请设置 PG_SYSROOT,例如: + +make PG_SYSROOT=/desired/path all + + 要找出你机器上的合适路径,请运行: + +xcrun --show-sdk-path + + 请注意,使用与构建核心服务器时不同的 sysroot 版本来构建扩展并不值得推荐; + 最坏情况下,这可能导致难以调试的 ABI 不一致。 + + + + 你也可以在配置时通过向 configure 指定 + PG_SYSROOT,选择非默认的 sysroot 路径: + +./configure ... PG_SYSROOT=/desired/path + + 这主要适用于针对其他 macOS 版本进行交叉编译。 + 不能保证生成的可执行文件能在当前主机上运行。 + + + + 如果要完全禁止 选项,请使用: + +./configure ... PG_SYSROOT=none + + (任何不存在的路径名都可以。) + 如果你希望使用非 Apple 编译器进行构建,这可能会有用,但请注意, + 这种情况并未经过 PostgreSQL 开发者测试,也不受支持。 + + + macOS系统完整性保护(SIP)功能会破坏 make check,因为它会阻止将所需的 DYLD_LIBRARY_PATH 设置传给被测试的可执行文件。可以在 make check 之前先执行 make install 来规避这一问题。不过,大多数 Postgres 开发者会直接关闭 SIP。 + + + + MinGW/原生 Windows + + + MinGW + 在其上安装 + + + Windows 版 PostgreSQL 可以使用 MinGW(用于 Microsoft 操作系统的类 Unix 构建环境)构建,也可以使用 Microsoft 的 Visual C++ 编译器套件构建。MinGW 构建方式使用本章介绍的常规构建系统;Visual C++ 构建方式完全不同,详见 ]]>。后者是完全原生的构建,不使用 MinGW 等附加软件。PostgreSQL 主网站上提供现成的安装程序。 + + 原生 Windows 移植版本要求 32 位或 64 位的 Windows 2000 或更高版本。更早的操作系统没有足够的基础设施(但可以在其上使用 Cygwin)。可以从 下载 MinGW 这一类 Unix 构建工具,以及 MSYS 这一运行 configure 等 shell 脚本所需的 Unix 工具集。运行生成的二进制文件不需要它们;只有创建二进制文件时才需要。 + + 要使用 MinGW 构建 64 位二进制文件,请从 安装 64 位工具集,将其 bin 目录放入 PATH,并使用 --host=x86_64-w64-mingw32 选项运行 configure + + 安装好所有组件后,建议在 CMD.EXE 下运行 psql,因为 MSYS 控制台存在缓冲问题。 + + + 在 Windows 上收集崩溃转储 + + + 如果 PostgreSQL 在 Windows 上崩溃,它能够生成 + minidumps,可用于追踪崩溃原因, + 类似于 Unix 上的核心转储。这些转储可以使用 + Windows Debugger Tools 或 + Visual Studio 读取。 + 要在 Windows 上启用转储生成,请在集簇数据目录中创建一个名为 + crashdumps 的子目录。随后,转储会以唯一名称写入该目录, + 该名称基于崩溃进程的标识符以及崩溃发生时的当前时间。 + + + + + + SCO OpenServer 和 SCO UnixWare + + + SCO + 安装 + + + + UnixWare + 安装 + + + + PostgreSQL 可以在 SCO UnixWare 7 和 SCO OpenServer 5 上构建。 + 在 OpenServer 上,你可以使用 OpenServer Development Kit,也可以使用 + Universal Development Kit。不过,可能需要按下面的说明做一些调整。 + + + + Skunkware + + + 你应当找到你的 SCO Skunkware CD 副本。Skunkware CD 随 UnixWare 7 + 和当前版本的 OpenServer 5 一起提供。Skunkware 包含 Internet 上 + 许多流行程序的即装即用版本。例如,gzip、gunzip、GNU Make、Flex + 和 Bison 都包含在内。对于 UnixWare 7.1,这张 CD 现在标为 + "Open License Software Supplement"。如果你没有这张 CD, + 其上的软件可以从 获取。 + + + + Skunkware 对 UnixWare 和 OpenServer 有不同的版本。请确保为你的 + 操作系统安装正确的版本,下面注明的情况除外。 + + + + 在 UnixWare 7.1.3 及更高版本中,UDK CD 上附带有 GCC 编译器, + GNU Make 也是如此。 + + + + + GNU Make + + + 你需要使用 GNU Make 程序,它在 Skunkware CD 上。默认情况下, + 它会安装为 /usr/local/bin/make。 + + + + 对于 UnixWare 7.1.3 及更高版本,GNU Make 程序位于 UDK CD 的 + OSTK 部分,路径为 /usr/gnu/bin/gmake。 + + + + + Readline + + + Readline 库在 Skunkware CD 上。但 UnixWare 7.1 的 Skunkware CD + 并不包含它。如果你有 UnixWare 7.0.0 或 7.0.1 的 Skunkware CD, + 可以从那里安装。否则,请尝试 + 。 + + + + 默认情况下,Readline 会安装到 /usr/local/lib 和 + /usr/local/include。但是,PostgreSQL 的 + configure 程序在没有帮助的情况下无法在那里找到它。 + 如果你安装了 Readline,请对 configure 使用以下选项: + +./configure --with-libraries=/usr/local/lib --with-includes=/usr/local/include + + + + + + 在OpenServer上使用UDK + + + 如果你在 OpenServer 上使用新的 Universal Development Kit(UDK)编译器, + 需要指定 UDK 库的位置: + +./configure --with-libraries=/udk/usr/lib --with-includes=/udk/usr/include + + 把这些与上面的 Readline 选项结合起来: + +./configure --with-libraries="/udk/usr/lib /usr/local/lib" --with-includes="/udk/usr/include /usr/local/include" + + + + + + 阅读PostgreSQL手册页 + + + 默认情况下,PostgreSQL 的手册页会安装到 + /usr/local/pgsql/share/man。默认情况下,UnixWare + 不会到该目录查找手册页。要能阅读它们,你需要修改 + /etc/default/man 中的 MANPATH + 变量,例如: + +MANPATH=/usr/lib/scohelp/%L/man:/usr/dt/man:/usr/man:/usr/share/man:scohelp:/usr/local/man:/usr/local/pgsql/share/man + + + + + 在 OpenServer 上,要让手册页可用还需要额外花些功夫研究,因为其 + 手册系统与其他平台有些不同。目前,PostgreSQL 完全不会安装它们。 + + + + + 7.1.1b特性补充中的 C99 问题 + + + 对于早于 OpenUNIX 8.0.0(UnixWare 7.1.2)随附编译器的编译器, + 包括 7.1.1b 特性补充,你可能需要在 CFLAGS 或 + CC 环境变量中指定 。 + 其表现是在编译 tuplesort.c 时出现引用内联 + 函数的错误。显然,7.1.2(8.0.0) 及之后的编译器发生了变化。 + + + + + UnixWare上的线程 + + + 对于线程,你必须所有使用 libpq + 的程序上使用 。libpq 使用 + pthread_* 调用,而这些调用只有在使用 + / 标志时才可用。 + + + + + + Solaris + + + Solaris + 在其上安装 + + + PostgreSQL 在 Solaris 上有良好支持。你的操作系统越新,遇到的问题越少;详情见下文。 + + + 所需工具 + + 你可以使用 GCC 或 Sun 编译器套件来构建。为了获得更好的代码优化,在 SPARC 架构上强烈推荐使用 Sun 编译器。我们收到过使用 GCC 2.95.1 时出现问题的报告;建议使用 GCC 2.95.3 或更高版本。如果使用 Sun 编译器,请注意不要选择 /usr/ucb/cc;应使用 /opt/SUNWspro/bin/cc + + 你可以从 下载 Sun Studio。许多 GNU 工具已经集成到 Solaris 10 中,或者包含在 Solaris 配套光盘中。如果你需要适用于较旧 Solaris 版本的软件包,可以到 查找这些工具。如果你更想要源码,请看 + + + + OpenSSL 的问题 + + + 在以 OpenSSL 支持构建 PostgreSQL 时,你可能会在以下文件中遇到编译错误: + + src/backend/libpq/crypt.c + src/backend/libpq/password.c + src/interfaces/libpq/fe-auth.c + src/interfaces/libpq/fe-connect.c + + + 这是因为标准的 /usr/include/crypt.h 头文件与 OpenSSL 提供的头文件之间存在命名空间冲突。 + + + + 将你的 OpenSSL 安装升级到 0.9.6a 版可解决此问题。Solaris 9 及更高版本带有较新版本的 OpenSSL。 + + + + + configure 报告测试程序失败 + + + 如果 configure 报告某个测试程序失败, + 这多半是因为运行时链接器找不到某些库,通常是 libz、libreadline, + 或其他非标准库如 libssl。要把它指向正确位置,请在 + configure 命令行中设置环境变量 LDFLAGS, + 例如: + +configure ... LDFLAGS="-R /usr/sfw/lib:/opt/sfw/lib:/usr/local/lib" + + 更多信息请参见 + ld1 + 手册页。 + + + + + 64 位构建有时会崩溃 + + 在 Solaris 7 及更早版本上,64 位 libc 的 vsnprintf 例程存在缺陷,导致 PostgreSQL 不定期发生核心转储。已知最简单的解决办法是强制 PostgreSQL 使用自带的 vsnprintf,而不是库中的版本。为此,在运行 configure 后,编辑由 configure 生成的文件:在 src/Makefile.global 中,将以下行: +LIBOBJS = +改为: +LIBOBJS = snprintf.o +(此变量中可能已经列有其他文件,顺序无关紧要。)然后照常构建。 + + + + 为获得最佳性能而编译 + + 在 SPARC 架构上,强烈推荐使用 Sun Studio 进行编译。可以尝试使用 优化标志,以生成显著更快的二进制文件。不要使用任何会改变浮点运算行为以及 errno 处理行为的标志(例如 )。这些标志可能导致 PostgreSQL 出现不符合标准的行为,例如在日期和时间计算中。 + + 如果你没有理由在 SPARC 上使用 64 位二进制文件,那么优先选择 32 位版本。64 位运算更慢,而且 64 位二进制文件也比 32 位变体慢。另一方面,在 AMD64 CPU 家族上,32 位代码并不是原生模式,因此 32 位代码在该 CPU 家族上会明显更慢。 + + + + 使用 DTrace 跟踪 PostgreSQL + + 是的,可以使用 DTrace。更多信息见 ]]>。你还可以在这篇文章中找到更多信息: + + 如果发现链接 postgres 可执行文件时中止,并出现类似以下错误消息: +Undefined first referenced + symbol in file +AbortTransaction utils/probes.o +CommitTransaction utils/probes.o +ld: fatal: Symbol referencing errors. No output written to postgres +collect2: ld returned 1 exit status +make: *** [postgres] Error 1 +则说明安装的 DTrace 太旧,无法处理静态函数中的探针。需要 Solaris 10u4 或更高版本。 + + + + + diff --git a/zh/9.6/intagg.sgml b/zh/9.6/intagg.sgml new file mode 100644 index 00000000..c44a95d2 --- /dev/null +++ b/zh/9.6/intagg.sgml @@ -0,0 +1,82 @@ + + + + intagg — 整数聚合器和枚举器 + + + intagg + + + + intagg 模块提供一个整数聚合器和一个枚举器。 + 由于已经有内置函数提供了其能力的超集,所以 intagg 现已过时。 + 不过,该模块仍作为这些内置函数的兼容性包装器提供。 + + + + 函数 + + + int_array_aggregate + + + + array_agg + + + + 聚合器是聚合函数int_array_aggregate(integer), + 它会生成一个整数数组,其中恰好包含输入给它的那些整数。 + 这是对array_agg的包装器,后者对任意数组类型都能做同样的事情。 + + + + int_array_enum + + + + 枚举器是函数int_array_enum(integer[]), + 它返回setof integer。本质上,它是聚合器的逆操作: + 给定一个整数数组,将其展开为一组行。 + 这是对unnest的包装器,后者对任意数组类型都能做同样的事情。 + + + + + + 使用示例 + + 许多数据库系统有一对多表的概念。这种表通常位于两个带索引的表之间,例如: +CREATE TABLE left (id INT PRIMARY KEY, ...); +CREATE TABLE right (id INT PRIMARY KEY, ...); +CREATE TABLE one_to_many(left INT REFERENCES left, right INT REFERENCES right); +它通常按如下方式使用: +SELECT right.* from right JOIN one_to_many ON (right.id = one_to_many.right) + WHERE one_to_many.left = item; +这将返回左侧表中某个条目在右侧表中对应的所有项。这是 SQL 中非常常见的一种构造。 + + 不过,如果one_to_many表中的条目非常多,这种方法就会变得相当繁琐。通常,对于左侧表中的某个特定条目,右侧表中的每个对应项都要执行一次索引扫描并取回一行。如果你的系统非常动态,那就没有太多办法。不过,如果有一部分数据相当静态,可以借助聚合器创建一个汇总表。 +CREATE TABLE summary AS + SELECT left, int_array_aggregate(right) AS right + FROM one_to_many + GROUP BY left; +这样会创建一个表,其中左侧每个条目对应一行,并附带一个由右侧各项组成的数组。不过,如果没有某种使用该数组的方法,它就几乎没有用处;这正是数组枚举器存在的原因。你可以这样做: +SELECT left, int_array_enum(right) FROM summary WHERE left = item; +上面这个使用int_array_enum的查询,产生的结果与下面的查询相同: +SELECT left, right FROM one_to_many WHERE left = item; +区别在于,针对汇总表的查询只需要从表中取出一行,而直接查询one_to_many则必须对每个对应项都进行索引扫描并取回一行。 + + 在某个系统上,EXPLAIN显示,某个查询的代价从 8488 降到了 329。原始查询是一个涉及one_to_many表的连接,后来被替换为: +SELECT right, count(right) FROM + ( SELECT left, int_array_enum(right) AS right + FROM summary JOIN (SELECT left FROM left_table WHERE left = item) AS lefts + ON (summary.left = lefts.left) + ) AS list + GROUP BY right + ORDER BY count DESC; + + + + + + diff --git a/zh/9.6/intarray.sgml b/zh/9.6/intarray.sgml new file mode 100644 index 00000000..78d8f18d --- /dev/null +++ b/zh/9.6/intarray.sgml @@ -0,0 +1,312 @@ + + + + intarray + + + intarray + + + + intarray模块提供了一些有用的函数和操作符,用于操作不含 + NULL 的整数数组。它还支持使用其中某些操作符进行索引搜索。 + + + + 如果所提供的数组中包含任何 NULL 元素,所有这些操作都会抛出错误。 + + + + 这些操作中有许多只对一维数组才有意义。尽管它们也接受更高维度的输入 + 数组,但数据会被视为按存储顺序排列的线性数组。 + + + + <filename>intarray</filename> 函数和操作符 + + + intarray模块提供的函数列在中, + 操作符列在中。 + + + + <filename>intarray</filename> 函数 + + + + + 函数 + 返回类型 + + 描述 + + 示例 + 结果 + + + + + + icount(int[])icount + int + 数组中的元素个数 + icount('{1,2,3}'::int[]) + 3 + + + + sort(int[], text dir)sort + int[] + 对数组排序 — dir必须为ascdesc + sort('{1,2,3}'::int[], 'desc') + {3,2,1} + + + + sort(int[]) + int[] + 按升序排序 + sort(array[11,77,44]) + {11,44,77} + + + + sort_asc(int[])sort_asc + int[] + 按升序排序 + + + + + + sort_desc(int[])sort_desc + int[] + 按降序排序 + + + + + + uniq(int[])uniq + int[] + 删除相邻的重复元素 + uniq(sort('{1,2,3,2,1}'::int[])) + {1,2,3} + + + + idx(int[], int item)idx + int + item匹配的第一个元素的索引(如果没有则为 0) + idx(array[11,22,33,22,11], 22) + 2 + + + + subarray(int[], int start, int len)subarray + int[] + 从位置start开始的数组部分,共 len 个元素 + subarray('{1,2,3,2,1}'::int[], 2, 3) + {2,3,2} + + + + subarray(int[], int start) + int[] + 从位置start开始的数组部分 + subarray('{1,2,3,2,1}'::int[], 2) + {2,3,2,1} + + + + intset(int)intset + int[] + 创建一个只含一个元素的数组 + intset(42) + {42} + + + + +
+ + + <filename>intarray</filename> 操作符 + + + + + 操作符 + 返回值 + + 描述 + + + + + + + int[] && int[] + boolean + 重叠 — 如果数组至少有一个共同元素,则为true + + + int[] @> int[] + boolean + 包含 — 如果左数组包含右数组,则为true + + + int[] <@ int[] + boolean + 包含于 — 如果左数组包含在右数组中,则为true + + + # int[] + int + 数组中的元素个数 + + + int[] # int + int + 索引(与idx函数相同) + + + int[] + int + int[] + 将元素压入数组(添加到数组末尾) + + + int[] + int[] + int[] + 数组连接(右数组添加到左数组末尾) + + + int[] - int + int[] + 从数组中移除与右参数匹配的项 + + + int[] - int[] + int[] + 从左数组中移除右数组中的元素 + + + int[] | int + int[] + 参数的并集 + + + int[] | int[] + int[] + 数组的并集 + + + int[] & int[] + int[] + 数组的交集 + + + int[] @@ query_int + boolean + 如果数组满足查询(见下文),则为true + + + query_int ~~ int[] + boolean + 如果数组满足查询(@@的交换操作符),则为true + + + +
+ + (在 PostgreSQL 8.2 之前,包含操作符 @><@ 分别称为 @~。这些名称仍然可用,但已弃用,最终将被删除。请注意,旧名称与核心几何数据类型以前采用的约定正好相反!) + + + 操作符&&@>和 + <@等价于PostgreSQL内置的 + 同名操作符,不同之处在于它们只适用于不包含 NULL 的整数数组,而内置 + 操作符适用于任何数组类型。这一限制使它们在很多情况下比内置操作符更快。 + + + + @@~~操作符用于测试数组是否满足某个 + 查询,该查询表示为专用数据类型query_int + 的一个值。一个查询由若干整数值组成,这些值会与 + 数组元素进行检查,并且可通过操作符&(AND)、 + |(OR)和!(NOT)组合起来。必要时 + 可以使用括号。例如,查询1&(2|3)可匹配包含 1 + 且还包含 2 或 3 之一的数组。 + +
+ + + 索引支持 + + intarray&&@><@@@操作符以及常规数组相等运算提供索引支持。 + + 提供了两个 GiST 索引操作符类:gist__int_ops(默认使用)适用于小型到中型数据集,而gist__intbig_ops使用更大的签名,更适合为大型数据集建立索引(即包含大量不同数组值的列)。实现使用带有内置有损压缩的 RD 树数据结构。 + + 另有一个非默认的 GIN 操作符类gin__int_ops,支持相同的操作符。 + + + 在 GiST 和 GIN 索引之间如何选择,取决于二者的相对性能特征,相关讨论 + 见其他部分。 + + + + + 示例 + + +-- a message can be in one or more sections +CREATE TABLE message (mid INT PRIMARY KEY, sections INT[], ...); + +-- create specialized index +CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__int_ops); + +-- select messages in section 1 OR 2 - OVERLAP operator +SELECT message.mid FROM message WHERE message.sections && '{1,2}'; + +-- select messages in sections 1 AND 2 - CONTAINS operator +SELECT message.mid FROM message WHERE message.sections @> '{1,2}'; + +-- the same, using QUERY operator +SELECT message.mid FROM message WHERE message.sections @@ '1&2'::query_int; + + + + + 基准测试 + + + 源代码目录contrib/intarray/bench包含一个基准测试 + 套件,可以针对一个已安装的PostgreSQL服务器 + 运行。(还要求已安装DBD::Pg。)运行方式如下: + + + +cd .../contrib/intarray/bench +createdb TEST +psql -c "CREATE EXTENSION intarray" TEST +./create_test.pl | psql TEST +./bench.pl + + + + bench.pl脚本有很多选项,在不带任何参数运行时会显示 + 这些选项。 + + + + + 作者 + + + 所有工作都由 Teodor Sigaev(teodor@sigaev.ru)和 + Oleg Bartunov(oleg@sai.msu.su)完成。更多信息请见 + 。 + Andrey Oktyabrski 在添加新函数和新操作方面也做出了很大贡献。 + + + +
diff --git a/zh/9.6/intro.sgml b/zh/9.6/intro.sgml new file mode 100644 index 00000000..d9529e29 --- /dev/null +++ b/zh/9.6/intro.sgml @@ -0,0 +1,126 @@ + + + + 前言 + + + 本书是PostgreSQL的官方文档。 + 它由PostgreSQL开发人员及其他志愿者在 + PostgreSQL软件开发过程中同步编写。 + 它描述了当前版本的PostgreSQL正式支持的全部功能。 + + + + 为了便于管理有关PostgreSQL的大量信息,本书被组织为若干部分。每一部分都面向不同类型的用户,或者面向处于不同PostgreSQL使用阶段的用户: + + + + + 是面向新用户的非正式介绍。 + + + + + + 记述了SQL查询语言环境,包括数据类型、函数以及用户级性能调优。每个PostgreSQL用户都应阅读这一部分。 + + + + + + 描述了服务器的安装和管理。无论是为自己使用还是为他人提供服务,凡是运行PostgreSQL服务器的人都应阅读这一部分。 + + + + + + 描述PostgreSQL客户端程序的编程接口。 + + + + + + + 包含面向高级用户的服务器可扩展能力相关信息,其中的主题包括用户定义数据类型和函数。 + + + + + + 包含有关 SQL 命令、客户端程序和服务器程序的参考信息。该部分以按命令或程序组织的结构化信息为其他部分提供支持。 + + + + + + 包含可能对PostgreSQL开发人员有用的各类信息。 + + + + + + + 什么是<productname>PostgreSQL</productname>? + + + PostgreSQL是一种基于 + + POSTGRES, Version 4.2 + 的对象关系数据库管理系统(ORDBMS), + 后者由加州大学伯克利分校计算机科学系开发。 + POSTGRES 首创了许多概念,而这些概念直到很久之后才在某些商业数据库系统中出现。 + + + + PostgreSQL是这套原始伯克利代码的开源后继版本。它支持 SQL 标准中的很大一部分,并提供了许多现代特性: + + 复杂查询 + + + 外键 + + + 触发器 + + + 可更新视图 + + + 事务完整性 + + + 多版本并发控制 + + 此外,PostgreSQL还允许用户以多种方式扩展,例如添加新的: + + 数据类型 + + + 函数 + + + 操作符 + + + 聚合函数 + + + 索引方法 + + + 过程语言 + + + + + + 由于采用宽松的许可证,任何人都可以出于任何目的免费使用、修改和分发PostgreSQL,无论是私人、商业还是学术用途。 + + + + &history; + ¬ation; + &info; + &problems; + + diff --git a/zh/9.6/isn.sgml b/zh/9.6/isn.sgml new file mode 100644 index 00000000..1c6924b8 --- /dev/null +++ b/zh/9.6/isn.sgml @@ -0,0 +1,326 @@ + + + + isn + + + isn + + + + isn模块为以下国际产品编号标准提供数据类型:EAN13、UPC、ISBN(图书)、ISMN(音乐)和 ISSN(连续出版物)。输入这些编号时,会依据一份硬编码的前缀列表进行校验;输出时,这份前缀列表也会用于给编号加上连字符。由于新的前缀会不时被分配,这份前缀列表可能已经过时。希望该模块未来版本能从一个或多个表中获取前缀列表,以便用户按需更新;但目前,这份列表只能通过修改源代码并重新编译来更新。或者,该模块未来版本也可能取消前缀校验和连字符支持。 + + + + 数据类型 + + + 显示了isn模块提供的数据类型。 + + + + <filename>isn</filename> 数据类型 + + + + 数据类型 + 描述 + + + + + + EAN13 + + 欧洲商品编号,始终以 EAN13 显示格式显示 + + + + + ISBN13 + + 以新的 EAN13 显示格式显示的国际标准图书编号 + + + + + ISMN13 + + 以新的 EAN13 显示格式显示的国际标准音乐编号 + + + + ISSN13 + + 以新的 EAN13 显示格式显示的国际标准连续出版物编号 + + + + ISBN + + 以旧的短显示格式显示的国际标准图书编号 + + + + ISMN + + 以旧的短显示格式显示的国际标准音乐编号 + + + + ISSN + + 以旧的短显示格式显示的国际标准连续出版物编号 + + + + UPC + + 通用产品代码 + + + + +
+ + + 几点说明: + + + + + ISBN13、ISMN13 和 ISSN13 编号都是 EAN13 编号。 + + + EAN13 编号并不总是 ISBN13、ISMN13 或 ISSN13(不过其中有些确实是)。 + + + 某些 ISBN13 编号可以显示为 ISBN。 + + + 某些 ISMN13 编号可以显示为 ISMN。 + + + 某些 ISSN13 编号可以显示为 ISSN。 + + + UPC 编号是 EAN13 编号的一个子集(它们基本上就是去掉首位 0 的 EAN13 编号)。 + + + 所有 UPC、ISBN、ISMN 和 ISSN 编号都可以表示为 EAN13 编号。 + + + + + 在内部,所有这些类型都使用同一种表示形式(一个 64 位整数),并且彼此可以互换。之所以提供多种类型,是为了控制显示格式,并对原本应表示某一特定类型编号的输入执行更严格的有效性检查。 + + + + 只要可能,ISBNISMNISSN 类型都会显示编号的短版本(ISxN 10);对于不能用短版本表示的编号,则显示为 ISxN 13 格式。EAN13ISBN13ISMN13ISSN13 类型则始终显示 ISxN 的长版本(EAN13)。 + +
+ + + 类型转换 + + + isn模块提供以下几对类型转换: + + + + + + ISBN13 <=> EAN13 + + + + + ISMN13 <=> EAN13 + + + + + ISSN13 <=> EAN13 + + + + + ISBN <=> EAN13 + + + + + ISMN <=> EAN13 + + + + + ISSN <=> EAN13 + + + + + UPC <=> EAN13 + + + + + ISBN <=> ISBN13 + + + + + ISMN <=> ISMN13 + + + + + ISSN <=> ISSN13 + + + + + + 当从EAN13转换到其他类型时,会在运行时检查该值是否落在另一类型的取值范围内;如果不在,就会抛出错误。其他类型转换都只是简单地重新标记,因此总会成功。 + + + + + 函数和操作符 + + + isn模块提供标准比较操作符,以及对所有这些数据类型的 B-树和哈希索引支持。此外,它还提供了一些专用函数,如所示。在该表中,isn表示该模块提供的任意一种数据类型。 + + + + <filename>isn</filename> 函数 + + + + 函数 + 返回值 + 描述 + + + + + + isn_weak(boolean)isn_weak + boolean + 设置弱输入模式(返回新的设置值) + + + isn_weak() + boolean + 获取弱模式的当前状态 + + + make_valid(isn)make_valid + isn + 使无效编号变为有效(清除无效标记) + + + is_valid(isn)is_valid + boolean + 检查是否存在无效标记 + + + +
+ + + 模式用于允许向表中插入无效数据。这里的“无效”指的是校验位错误,而不是缺少数字。 + + + + 为什么会需要使用弱模式呢?例如,手头可能有一大批 ISBN 编号,数量多到难免会有一些编号因为某些奇怪的原因而带有错误的校验位(也许这些编号是从印刷清单扫描得到的,而 OCR 把数字识别错了;也许这些编号是人工录入的……谁知道呢)。总之,可能想把这些混乱情况清理干净,但同时仍希望先把所有编号都装入数据库,并借助外部工具在数据库中定位无效编号,以便核对信息并更容易完成校验;例如,可能会想把表中所有无效编号都查询出来。 + + + + 当在弱模式下向表中插入无效编号时,实际插入的是校验位已更正的编号,但显示时会在末尾附加一个感叹号(!),例如0-11-000322-5!。可以用is_valid函数检查这个无效标记,并用make_valid函数清除它。 + + + 即使未启用弱模式,也可以通过在编号末尾附加!字符来强制插入无效编号。 + + 另一个特殊功能是,在输入时可以用?代替校验位,系统会自动插入正确的校验位。 +
+ + + 示例 + + +--Using the types directly: +SELECT isbn('978-0-393-04002-9'); +SELECT isbn13('0901690546'); +SELECT issn('1436-4522'); + +--Casting types: +-- note that you can only cast from ean13 to another type when the +-- number would be valid in the realm of the target type; +-- thus, the following will NOT work: select isbn(ean13('0220356483481')); +-- but these will: +SELECT upc(ean13('0220356483481')); +SELECT ean13(upc('220356483481')); + +--Create a table with a single column to hold ISBN numbers: +CREATE TABLE test (id isbn); +INSERT INTO test VALUES('9780393040029'); + +--Automatically calculate check digits (observe the '?'): +INSERT INTO test VALUES('220500896?'); +INSERT INTO test VALUES('978055215372?'); + +SELECT issn('3251231?'); +SELECT ismn('979047213542?'); + +--Using the weak mode: +SELECT isn_weak(true); +INSERT INTO test VALUES('978-0-11-000533-4'); +INSERT INTO test VALUES('9780141219307'); +INSERT INTO test VALUES('2-205-00876-X'); +SELECT isn_weak(false); + +SELECT id FROM test WHERE NOT is_valid(id); +UPDATE test SET id = make_valid(id) WHERE id = '2-205-00876-X!'; + +SELECT * FROM test; + +SELECT isbn13(id) FROM test; + + + + + 参考文献 + + + 实现该模块所需的信息收集自若干网站,包括: + + + + + + + + 用于进行连字符分隔的前缀还整理自: + + + + + + + + + 在创建这些算法时已十分谨慎,并依据官方 ISBN、ISMN、ISSN 用户手册中建议的算法进行了细致核验。 + + + + + 作者 + Germán Méndez Bravo (Kronuz), 2004 - 2006 + + + 该模块受到了 Garrett A. Wollman 的isbn_issn代码的启发。 + + + +
diff --git a/zh/9.6/json.sgml b/zh/9.6/json.sgml new file mode 100644 index 00000000..f2a4b761 --- /dev/null +++ b/zh/9.6/json.sgml @@ -0,0 +1,429 @@ + + + + <acronym>JSON</acronym> 类型 + + + JSON + + + + JSONB + + + + JSON 数据类型用于存储 JSON(JavaScript Object Notation)数据,如 + RFC + 7159 所定义。这类数据也可以存储为 text,但 JSON + 数据类型的优势在于会强制每个存储值都符合 JSON 规则。此外,对于存储在 + 这些数据类型中的数据,还提供了各种 JSON 专用的函数和操作符;见 + 。 + + + 有两种 JSON 数据类型:jsonjsonb。它们接受的输入值集合几乎相同。实际使用中的主要区别是效率。json 数据类型保存输入文本的精确副本,处理函数每次执行时都必须重新解析;而 jsonb 数据以分解后的二进制格式存储,额外的转换开销使输入稍慢,但无需重新解析,因此处理速度明显更快。jsonb 还支持索引,这可能带来显著优势。 + + + 由于 json 类型存储的是输入文本的精确副本,因此它会保留标记 + 之间在语义上无关紧要的空白,以及 JSON 对象内部键的顺序。此外,如果值中 + 的某个 JSON 对象包含同一个键多次,所有键/值对都会被保留下来(处理函数会 + 将最后一个值视为生效值)。相比之下,jsonb 不保留空白,不保留 + 对象键的顺序,也不保留重复的对象键。如果输入中指定了重复的键,则只保留 + 最后一个值。 + + + + 一般而言,大多数应用都应优先将 JSON 数据存储为 jsonb, + 除非存在相当特殊的需求,例如遗留系统对对象键顺序的假设。 + + + PostgreSQL 的每个数据库只允许使用一种字符集编码。因此,除非数据库编码为 UTF8,否则 JSON 类型无法严格遵循 JSON 规范。直接包含数据库编码无法表示的字符会失败;反过来,数据库编码能够表示但 UTF8 无法表示的字符则会被允许。 + + RFC 7159 允许 JSON 字符串包含以 \uXXXX 表示的 Unicode 转义序列。json 类型的输入函数允许 Unicode 转义,而不管数据库使用什么编码,并且只检查语法是否正确(即 \u 后面是否有四位十六进制数字)。但 jsonb 的输入函数更严格:除非数据库编码为 UTF8,否则不允许非 ASCII 字符(大于 U+007F 的字符)的 Unicode 转义。jsonb 类型也会拒绝 \u0000(因为它无法在 PostgreSQLtext 类型中表示),并要求正确使用 Unicode 代理对来表示 Unicode 基本多文种平面之外的字符。有效的 Unicode 转义会转换为等价的 ASCII 或 UTF8 字符进行存储,包括将代理对合并为单个字符。 + + + 中描述的许多 JSON 处理函数会将 Unicode 转义转换为普通字符,因此即使输入的类型为 json 而非 jsonb,也会抛出上述相同类型的错误。json 输入函数不做这些检查,可以视为历史遗留行为,不过它确实允许在非 UTF8 数据库编码下简单地存储 JSON Unicode 转义,而不进行处理。一般而言,应尽可能避免将 JSON 中的 Unicode 转义与非 UTF8 数据库编码混用。 + + + + 当把文本形式的 JSON 输入转换为 jsonb 时, + RFC 7159 描述的基本类型会有效映射到原生的 + PostgreSQL 类型上,如 + 所示。因此,什么样的数据构成 + 有效的 jsonb 会有一些额外但较小的限制,这些限制不适用于 + json 类型,也不适用于抽象意义上的 JSON;它们对应于底层 + 数据类型可表示范围的限制。特别地,jsonb 会拒绝超出 + PostgreSQL numeric 数据类型 + 范围的数字,而 json 不会。RFC 7159 + 允许这种由实现定义的限制。不过在实践中,这类问题更可能出现在其他实现中, + 因为通常会把 JSON 的 number 基本类型表示为 IEEE 754 + 双精度浮点数(RFC 7159 明确预见并允许了这一点)。 + 当把 JSON 用作与这类系统交换数据的格式时,应考虑与原先由 + PostgreSQL 存储的数据相比丢失数值精度的风险。 + + + + 另一方面,正如表中所指出的那样,JSON 基本类型的输入格式还有一些轻微限制, + 而对应的 PostgreSQL 类型并没有这些限制。 + + + + JSON 基本类型及其对应的 <productname>PostgreSQL</productname> 类型 + + + + JSON 基本类型 + PostgreSQL 类型 + 说明 + + + + + string + text + 不允许 \u0000;如果数据库编码不是 UTF8,也不允许非 ASCII 字符的 Unicode 转义 + + + number + numeric + 不允许 NaNinfinity + + + boolean + boolean + 只接受小写拼写 truefalse + + + null + (无) + SQL NULL 是不同的概念 + + + +
+ + + JSON 输入和输出语法 + + JSON 数据类型的输入/输出语法遵循 RFC 7159。 + + + 以下都是有效的 json(或 jsonb)表达式: + +-- Simple scalar/primitive value +-- Primitive values can be numbers, quoted strings, true, false, or null +SELECT '5'::json; + +-- Array of zero or more elements (elements need not be of same type) +SELECT '[1, 2, "foo", null]'::json; + +-- Object containing pairs of keys and values +-- Note that object keys must always be quoted strings +SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json; + +-- Arrays and objects can be nested arbitrarily +SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json; + + + + + 如前所述,当一个 JSON 值被输入后又在不进行任何额外处理的情况下输出时, + json 会输出与输入完全相同的文本,而 jsonb + 不会保留诸如空白这类语义上无关紧要的细节。例如,请注意这里的差异: + +SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json; + json +------------------------------------------------- + {"bar": "baz", "balance": 7.77, "active":false} +(1 row) + +SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb; + jsonb +-------------------------------------------------- + {"bar": "baz", "active": false, "balance": 7.77} +(1 row) + + 一个值得注意的语义无关细节是,在 jsonb 中,数字会按照 + 底层 numeric 类型的行为输出。在实践中,这意味着使用 + E 记数法输入的数字在输出时将不再使用这种写法,例如: + +SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb; + json | jsonb +-----------------------+------------------------- + {"reading": 1.230e-5} | {"reading": 0.00001230} +(1 row) + + 不过,正如这个例子所示,jsonb 会保留小数部分末尾的零, + 尽管对于等值检查之类的用途来说,这些零在语义上并不重要。 + + + + + 有效地设计 JSON 文档 + + 以 JSON 形式表示数据,可能比传统的关系数据模型灵活得多,这在需求变化较 + 大的环境中尤其有吸引力。这两种方法完全可能在同一个应用中共存并互为补充。 + 但是,即使对于追求最大灵活性的应用,也仍然建议 JSON 文档拥有某种相对固定 + 的结构。这种结构通常并不受强制约束(尽管也可以用声明式方式强制某些业务规则), + 但具有可预测的结构会让编写查询更容易,从而能够有效地汇总表中一组 + 文档(数据项)。 + + + 当 JSON 数据存储在表中时,它与任何其他数据类型一样,都要面对相同的并发控 + 制考量。虽然存储大型文档是可行的,但要记住,任何更新都会在整行上获取一个 + 行级锁。应考虑将 JSON 文档限制在可管理的大小,以减少更新事务之间的锁争用。 + 理想情况下,每个 JSON 文档都应表示一个原子数据项,按照业务规则,它不应被 + 合理地进一步拆分为更小且可独立修改的数据项。 + + + + + <type>jsonb</type> 包含与存在 + + jsonb + containment + + + jsonb + existence + + + 测试 包含jsonb 的一项重要能力。 + 对于 json 类型,则没有与之对应的一组功能。包含测试用于检查 + 一个 jsonb 文档中是否包含另一个文档。除特别说明外,下面这些 + 示例都返回真: + + +-- Simple scalar/primitive values contain only the identical value: +SELECT '"foo"'::jsonb @> '"foo"'::jsonb; + +-- The array on the right side is contained within the one on the left: +SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb; + +-- Order of array elements is not significant, so this is also true: +SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb; + +-- Duplicate array elements don't matter either: +SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb; + +-- The object with a single pair on the right side is contained +-- within the object on the left side: +SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb": true}'::jsonb @> '{"version": 9.4}'::jsonb; + +-- The array on the right side is not considered contained within the +-- array on the left, even though a similar array is nested within it: +SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- yields false + +-- But with a layer of nesting, it is contained: +SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb; + +-- Similarly, containment is not reported here: +SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- yields false + +-- A top-level key and an empty object is contained: +SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb; + + + + 一般原则是,被包含对象在结构和数据内容上都必须与包含对象匹配;必要时, + 可以从包含对象中丢弃某些不匹配的数组元素或对象键/值对后再进行这种匹配。 + 但要记住,在进行包含匹配时,数组元素的顺序并不重要,重复的数组元素实际 + 上也只会被考虑一次。 + + + + 对于结构必须匹配这一一般原则,有一个特殊例外:数组可以包含一个基本值: + + +-- This array contains the primitive string value: +SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb; + +-- This exception is not reciprocal -- non-containment is reported here: +SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- yields false + + + + jsonb 还有一个 存在操作符,它可看作 + 包含的一种变体:它测试某个字符串(以 text 值给出) + 是否在 jsonb 值的顶层作为对象键或数组元素出现。除特别说明 + 外,下面这些示例都返回真: + + +-- String exists as array element: +SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar'; + +-- String exists as object key: +SELECT '{"foo": "bar"}'::jsonb ? 'foo'; + +-- Object values are not considered: +SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- yields false + +-- As with containment, existence must match at the top level: +SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- yields false + +-- A string is considered to exist if it matches a primitive JSON string: +SELECT '"foo"'::jsonb ? 'foo'; + + + + 当涉及很多键或元素时,JSON 对象比数组更适合用于测试包含或存在,因为对象 + 与数组不同,内部已针对搜索做了优化,不需要进行线性搜索。 + + + + + 由于 JSON 包含是嵌套的,因此适当的查询可以跳过对子对象的显式选择。例如, + 假设我们有一个 doc 列,其顶层是对象,而且大 + 多数对象都带有 tags 字段,该字段中包含子对象数组。下面 + 这个查询会找出那些包含同时带有 "term":"paris" 和 + "term":"food" 的子对象的项,同时忽略 + tags 数组之外的任何此类键: + +SELECT doc->'site_name' FROM websites + WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}'; + + 例如,也可以用下面的写法完成同样的事情: + +SELECT doc->'site_name' FROM websites + WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]'; + + 但这种做法的灵活性较差,而且通常效率也更低。 + + + + 另一方面,JSON 的存在操作符并不是嵌套的:它只会在 JSON 值的顶层查找指定 + 的键或数组元素。 + + + + + 各种包含和存在操作符,以及所有其他 JSON 操作符和函数,均见 + 。 + + + + + <type>jsonb</type> 索引 + + jsonb + indexes on + + + + GIN 索引可用于高效搜索大量 jsonb 文档(数据项)中出现的键 + 或键/值对。提供了两种 GIN 操作符类,它们在性能和灵活性 + 之间提供不同的权衡。 + + 对于jsonb,默认 GIN 操作符类支持使用顶层键存在操作符??&?|以及路径/值存在操作符@>的查询。(这些操作符所实现语义的详情,参见。)使用此操作符类创建索引的示例如下: +CREATE INDEX idxgin ON api USING GIN (jdoc); +非默认的 GIN 操作符类jsonb_path_ops仅支持为@>操作符建立索引。使用此操作符类创建索引的示例如下: +CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops); + + + + + 假设有一个表,用于存储从第三方 Web 服务检索到的 JSON 文档,而且该服务的 + 模式定义已有文档说明。一个典型文档如下: + +{ + "guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a", + "name": "Angela Barton", + "is_active": true, + "company": "Magnafone", + "address": "178 Howard Place, Gulf, Washington, 702", + "registered": "2009-11-07T08:53:22 +08:00", + "latitude": 19.793713, + "longitude": 86.513373, + "tags": [ + "enim", + "aliquip", + "qui" + ] +} + + 我们把这些文档存储在名为 api 的表中,存放于 + 名为 jdocjsonb 列里。 + 如果在该列上创建了 GIN 索引,那么下面这样的查询就可以利用这个索引: + +-- Find documents in which the key "company" has value "Magnafone" +SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}'; + + 但是,类似下面这样的查询就无法使用该索引,因为虽然操作符 + ? 可索引,但它并未直接应用到被索引的列 + jdoc 上: + +-- Find documents in which the key "tags" contains key or array element "qui" +SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui'; + + 不过,只要适当地使用表达式索引,上述查询也可以利用索引。如果经常查询 + "tags" 键中的特定项,那么定义如下索引可能是值得的: + +CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags')); + + 现在,WHERE 子句 + jdoc -> 'tags' ? 'qui' 会被识别为把可索引操作符 + ? 应用于被索引表达式 + jdoc -> 'tags'。(表达式索引的更多信息见 。) + + + 另一种查询方法是利用包含,例如: + +-- Find documents in which the key "tags" contains array element "qui" +SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}'; + + jdoc 列上的简单 GIN 索引可以支持这个查询。 + 但要注意,这样的索引会存储 jdoc 列中每个键和 + 值的副本,而前一个例子中的表达式索引只存储 tags 键下 + 出现的数据。虽然简单索引方法灵活得多(因为它支持对任意键的查询),但有针 + 对性的表达式索引通常会比简单索引更小,搜索起来也更快。 + + + 虽然 jsonb_path_ops 操作符类只支持使用 @> 操作符的查询,但与默认操作符类 jsonb_ops 相比,它具有显著的性能优势。对于相同数据,jsonb_path_ops 索引通常比 jsonb_ops 索引小得多,搜索的针对性也更强,特别是当查询包含数据中频繁出现的键时。因此,搜索操作的性能通常优于默认操作符类。 + + + jsonb_opsjsonb_path_ops + GIN 索引之间的技术差异在于,前者会为数据中的每个键和值分别创建独立的 + 索引项,而后者只会为数据中的每个值创建索引项。 + + + 在这里,术语 也包括数组元素,尽管在 JSON 术语中, + 有时会把数组元素与对象中的值区分开来。 + + + 基本上,每个 jsonb_path_ops 索引项都是该值连同 + 通向该值的键一起计算出的哈希。例如,要索引 + {"foo": {"bar": "baz"}},会创建一个单独的索引项, + 其哈希值中同时纳入 foobar 和 + baz 这三者。因此,查找这一结构的包含查询会得到一次 + 非常精确的索引搜索;但完全没有办法据此找出 foo 是否 + 作为键出现。另一方面,jsonb_ops 索引会分别创建三个 + 索引项来表示 foobar 和 + baz;然后为了执行包含查询,它会查找包含这三个项的行。 + 尽管 GIN 索引可以相当高效地执行这种 AND 搜索,但它仍然会比等效的 + jsonb_path_ops 搜索更不精确、也更慢,尤其是在包含这 + 三个索引项中任意一个的行数非常多时。 + + + + jsonb_path_ops 方法的一个缺点是,它不会为不包含任何值 + 的 JSON 结构产生索引项,例如 {"a": {}}。如果请求查 + 找包含此类结构的文档,就需要执行一次全索引扫描,这会相当慢。因此, + jsonb_path_ops 并不适合经常执行此类搜索的应用。 + + + + jsonb也支持btreehash索引。通常只有在需要检查完整 JSON 文档是否相等时,这些索引才有用。对于btree排序,jsonb数据的顺序很少值得关注,但为求完整,列出如下: +Object > Array > Boolean > Number > String > Null + +Object with n pairs > object with n - 1 pairs + +Array with n elements > array with n - 1 elements +键值对数量相等的对象按以下顺序比较: +键-1, 值-1, 键-2 ... +注意,对象键按其存储顺序比较;尤其是,较短的键存储在较长的键之前,因此可能产生不直观的结果,例如: +{ "aa": 1, "c": 1} > {"b": 1, "d": 1} +类似地,元素数量相等的数组按以下顺序比较: +元素-1, 元素-2 ... +JSON 基本值使用与其底层PostgreSQL数据类型相同的规则进行比较。字符串使用数据库的默认排序规则进行比较。 + +
diff --git a/zh/9.6/keywords.sgml b/zh/9.6/keywords.sgml new file mode 100644 index 00000000..8f2a9652 --- /dev/null +++ b/zh/9.6/keywords.sgml @@ -0,0 +1,5320 @@ + + + + <acronym>SQL</acronym> 关键字 + + + key word + list of + + + + 列出了在 SQL 标准以及PostgreSQL &version;中被视为关键字的所有词元。背景信息可参见。(由于篇幅所限,这里只列出了 SQL 标准最近两个版本的情况,以及用于历史比较的 SQL-92;这些版本与其他中间标准版本之间的差异很小。) + + + + SQL 区分保留关键字和非保留关键字。根据标准,保留关键字才是真正的关键字,它们绝不允许作为标识符使用。非保留关键字只在特定上下文中具有特殊含义,在其他上下文中则可以作为标识符使用。大多数非保留关键字实际上是 SQL 规定的内置表和内置函数名称。非保留关键字这一概念,本质上只是为了声明某个词在某些上下文中附带了预定义含义。 + + + PostgreSQL解析器中,情况要更复杂一些。这里的词元分属若干不同类别,从绝不能用作标识符的词元,到在解析器中完全没有特殊地位、只是被当作普通标识符处理的词元都有。(后一类通常就是 SQL 规定的函数名。)即使是保留关键字,在PostgreSQL中也不是绝对保留的,它们仍然可以用作列标签(例如SELECT 55 AS CHECK,尽管CHECK是一个保留关键字)。 + + 中针对PostgreSQL的那一列里,我们把解析器明确识别、但又允许作为列名或表名的那些关键字归类为非保留。某些原本属于非保留的关键字不能用作函数名或数据类型名,因此会另外加以标记。(这类词大多表示具有特殊语法的内置函数或数据类型。函数或类型本身仍然可用,但用户不能重新定义它们。)不允许作为列名或表名的词元则标为保留。有些保留关键字仍允许作为函数名或数据类型名,这一点也会在表中标出。如果没有这样的标记,保留关键字就只允许作为AS列标签名使用。 + + 一般来说,如果你在某条命令中把这里列出的关键字当作标识符使用,并因此遇到意外的解析器错误,应尝试为该标识符加上引号,看看问题是否消失。 + + + 在研读之前,有一点非常重要:某个关键字在PostgreSQL中不被保留,并不意味着与该词相关的特性尚未实现;反过来,某个关键字的存在也不意味着相应特性一定存在。 + + + + + + + <acronym>SQL</acronym> 关键字 + + + + + 关键字 + PostgreSQL + SQL:2011 + SQL:2008 + SQL-92 + + + + + + A + + 非保留 + 非保留 + + + + ABORT + 非保留 + + + + + + ABS + + 保留 + 保留 + + + + ABSENT + + 非保留 + 非保留 + + + + ABSOLUTE + 非保留 + 非保留 + 非保留 + 保留 + + + ACCESS + 非保留 + + + + + + ACCORDING + + 非保留 + 非保留 + + + + ACTION + 非保留 + 非保留 + 非保留 + 保留 + + + ADA + + 非保留 + 非保留 + 非保留 + + + ADD + 非保留 + 非保留 + 非保留 + 保留 + + + ADMIN + 非保留 + 非保留 + 非保留 + + + + AFTER + 非保留 + 非保留 + 非保留 + + + + AGGREGATE + 非保留 + + + + + + ALL + 保留 + 保留 + 保留 + 保留 + + + ALLOCATE + + 保留 + 保留 + 保留 + + + ALSO + 非保留 + + + + + + ALTER + 非保留 + 保留 + 保留 + 保留 + + + ALWAYS + 非保留 + 非保留 + 非保留 + + + + ANALYSE + 保留 + + + + + + ANALYZE + 保留 + + + + + + AND + 保留 + 保留 + 保留 + 保留 + + + ANY + 保留 + 保留 + 保留 + 保留 + + + ARE + + 保留 + 保留 + 保留 + + + ARRAY + 保留 + 保留 + 保留 + + + + ARRAY_AGG + + 保留 + 保留 + + + + ARRAY_MAX_CARDINALITY + + 保留 + + + + + AS + 保留 + 保留 + 保留 + 保留 + + + ASC + 保留 + 非保留 + 非保留 + 保留 + + + ASENSITIVE + + 保留 + 保留 + + + + ASSERTION + 非保留 + 非保留 + 非保留 + 保留 + + + ASSIGNMENT + 非保留 + 非保留 + 非保留 + + + + ASYMMETRIC + 保留 + 保留 + 保留 + + + + AT + 非保留 + 保留 + 保留 + 保留 + + + ATOMIC + + 保留 + 保留 + + + + ATTRIBUTE + 非保留 + 非保留 + 非保留 + + + + ATTRIBUTES + + 非保留 + 非保留 + + + + AUTHORIZATION + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + AVG + + 保留 + 保留 + 保留 + + + BACKWARD + 非保留 + + + + + + BASE64 + + 非保留 + 非保留 + + + + BEFORE + 非保留 + 非保留 + 非保留 + + + + BEGIN + 非保留 + 保留 + 保留 + 保留 + + + BEGIN_FRAME + + 保留 + + + + + BEGIN_PARTITION + + 保留 + + + + + BERNOULLI + + 非保留 + 非保留 + + + + BETWEEN + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + BIGINT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + BINARY + 保留(可用作函数或类型) + 保留 + 保留 + + + + BIT + 非保留(不能用作函数或类型) + + + 保留 + + + BIT_LENGTH + + + + 保留 + + + BLOB + + 保留 + 保留 + + + + BLOCKED + + 非保留 + 非保留 + + + + BOM + + 非保留 + 非保留 + + + + BOOLEAN + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + BOTH + 保留 + 保留 + 保留 + 保留 + + + BREADTH + + 非保留 + 非保留 + + + + BY + 非保留 + 保留 + 保留 + 保留 + + + C + + 非保留 + 非保留 + 非保留 + + + CACHE + 非保留 + + + + + + CALL + + 保留 + 保留 + + + + CALLED + 非保留 + 保留 + 保留 + + + + CARDINALITY + + 保留 + 保留 + + + + CASCADE + 非保留 + 非保留 + 非保留 + 保留 + + + CASCADED + 非保留 + 保留 + 保留 + 保留 + + + CASE + 保留 + 保留 + 保留 + 保留 + + + CAST + 保留 + 保留 + 保留 + 保留 + + + CATALOG + 非保留 + 非保留 + 非保留 + 保留 + + + CATALOG_NAME + + 非保留 + 非保留 + 非保留 + + + CEIL + + 保留 + 保留 + + + + CEILING + + 保留 + 保留 + + + + CHAIN + 非保留 + 非保留 + 非保留 + + + + CHAR + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + CHARACTER + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + CHARACTERISTICS + 非保留 + 非保留 + 非保留 + + + + CHARACTERS + + 非保留 + 非保留 + + + + CHARACTER_LENGTH + + 保留 + 保留 + 保留 + + + CHARACTER_SET_CATALOG + + 非保留 + 非保留 + 非保留 + + + CHARACTER_SET_NAME + + 非保留 + 非保留 + 非保留 + + + CHARACTER_SET_SCHEMA + + 非保留 + 非保留 + 非保留 + + + CHAR_LENGTH + + 保留 + 保留 + 保留 + + + CHECK + 保留 + 保留 + 保留 + 保留 + + + CHECKPOINT + 非保留 + + + + + + CLASS + 非保留 + + + + + + CLASS_ORIGIN + + 非保留 + 非保留 + 非保留 + + + CLOB + + 保留 + 保留 + + + + CLOSE + 非保留 + 保留 + 保留 + 保留 + + + CLUSTER + 非保留 + + + + + + COALESCE + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + COBOL + + 非保留 + 非保留 + 非保留 + + + COLLATE + 保留 + 保留 + 保留 + 保留 + + + COLLATION + 保留(可用作函数或类型) + 非保留 + 非保留 + 保留 + + + COLLATION_CATALOG + + 非保留 + 非保留 + 非保留 + + + COLLATION_NAME + + 非保留 + 非保留 + 非保留 + + + COLLATION_SCHEMA + + 非保留 + 非保留 + 非保留 + + + COLLECT + + 保留 + 保留 + + + + COLUMN + 保留 + 保留 + 保留 + 保留 + + + COLUMNS + + 非保留 + 非保留 + + + + COLUMN_NAME + + 非保留 + 非保留 + 非保留 + + + COMMAND_FUNCTION + + 非保留 + 非保留 + 非保留 + + + COMMAND_FUNCTION_CODE + + 非保留 + 非保留 + + + + COMMENT + 非保留 + + + + + + COMMENTS + 非保留 + + + + + + COMMIT + 非保留 + 保留 + 保留 + 保留 + + + COMMITTED + 非保留 + 非保留 + 非保留 + 非保留 + + + CONCURRENTLY + 保留(可用作函数或类型) + + + + + + CONDITION + + 保留 + 保留 + + + + CONDITION_NUMBER + + 非保留 + 非保留 + 非保留 + + + CONFIGURATION + 非保留 + + + + + + CONFLICT + 非保留 + + + + + + CONNECT + + 保留 + 保留 + 保留 + + + CONNECTION + 非保留 + 非保留 + 非保留 + 保留 + + + CONNECTION_NAME + + 非保留 + 非保留 + 非保留 + + + CONSTRAINT + 保留 + 保留 + 保留 + 保留 + + + CONSTRAINTS + 非保留 + 非保留 + 非保留 + 保留 + + + CONSTRAINT_CATALOG + + 非保留 + 非保留 + 非保留 + + + CONSTRAINT_NAME + + 非保留 + 非保留 + 非保留 + + + CONSTRAINT_SCHEMA + + 非保留 + 非保留 + 非保留 + + + CONSTRUCTOR + + 非保留 + 非保留 + + + + CONTAINS + + 保留 + 非保留 + + + + CONTENT + 非保留 + 非保留 + 非保留 + + + + CONTINUE + 非保留 + 非保留 + 非保留 + 保留 + + + CONTROL + + 非保留 + 非保留 + + + + CONVERSION + 非保留 + + + + + + CONVERT + + 保留 + 保留 + 保留 + + + COPY + 非保留 + + + + + + CORR + + 保留 + 保留 + + + + CORRESPONDING + + 保留 + 保留 + 保留 + + + COST + 非保留 + + + + + + COUNT + + 保留 + 保留 + 保留 + + + COVAR_POP + + 保留 + 保留 + + + + COVAR_SAMP + + 保留 + 保留 + + + + CREATE + 保留 + 保留 + 保留 + 保留 + + + CROSS + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + CSV + 非保留 + + + + + + CUBE + 非保留 + 保留 + 保留 + + + + CUME_DIST + + 保留 + 保留 + + + + CURRENT + 非保留 + 保留 + 保留 + 保留 + + + CURRENT_CATALOG + 保留 + 保留 + 保留 + + + + CURRENT_DATE + 保留 + 保留 + 保留 + 保留 + + + CURRENT_DEFAULT_TRANSFORM_GROUP + + 保留 + 保留 + + + + CURRENT_PATH + + 保留 + 保留 + + + + CURRENT_ROLE + 保留 + 保留 + 保留 + + + + CURRENT_ROW + + 保留 + + + + + CURRENT_SCHEMA + 保留(可用作函数或类型) + 保留 + 保留 + + + + CURRENT_TIME + 保留 + 保留 + 保留 + 保留 + + + CURRENT_TIMESTAMP + 保留 + 保留 + 保留 + 保留 + + + CURRENT_TRANSFORM_GROUP_FOR_TYPE + + 保留 + 保留 + + + + CURRENT_USER + 保留 + 保留 + 保留 + 保留 + + + CURSOR + 非保留 + 保留 + 保留 + 保留 + + + CURSOR_NAME + + 非保留 + 非保留 + 非保留 + + + CYCLE + 非保留 + 保留 + 保留 + + + + DATA + 非保留 + 非保留 + 非保留 + 非保留 + + + DATABASE + 非保留 + + + + + + DATALINK + + 保留 + 保留 + + + + DATE + + 保留 + 保留 + 保留 + + + DATETIME_INTERVAL_CODE + + 非保留 + 非保留 + 非保留 + + + DATETIME_INTERVAL_PRECISION + + 非保留 + 非保留 + 非保留 + + + DAY + 非保留 + 保留 + 保留 + 保留 + + + DB + + 非保留 + 非保留 + + + + DEALLOCATE + 非保留 + 保留 + 保留 + 保留 + + + DEC + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + DECIMAL + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + DECLARE + 非保留 + 保留 + 保留 + 保留 + + + DEFAULT + 保留 + 保留 + 保留 + 保留 + + + DEFAULTS + 非保留 + 非保留 + 非保留 + + + + DEFERRABLE + 保留 + 非保留 + 非保留 + 保留 + + + DEFERRED + 非保留 + 非保留 + 非保留 + 保留 + + + DEFINED + + 非保留 + 非保留 + + + + DEFINER + 非保留 + 非保留 + 非保留 + + + + DEGREE + + 非保留 + 非保留 + + + + DELETE + 非保留 + 保留 + 保留 + 保留 + + + DELIMITER + 非保留 + + + + + + DELIMITERS + 非保留 + + + + + + DENSE_RANK + + 保留 + 保留 + + + + DEPENDS + 非保留 + + + + + + DEPTH + + 非保留 + 非保留 + + + + DEREF + + 保留 + 保留 + + + + DERIVED + + 非保留 + 非保留 + + + + DESC + 保留 + 非保留 + 非保留 + 保留 + + + DESCRIBE + + 保留 + 保留 + 保留 + + + DESCRIPTOR + + 非保留 + 非保留 + 保留 + + + DETERMINISTIC + + 保留 + 保留 + + + + DIAGNOSTICS + + 非保留 + 非保留 + 保留 + + + DICTIONARY + 非保留 + + + + + + DISABLE + 非保留 + + + + + + DISCARD + 非保留 + + + + + + DISCONNECT + + 保留 + 保留 + 保留 + + + DISPATCH + + 非保留 + 非保留 + + + + DISTINCT + 保留 + 保留 + 保留 + 保留 + + + DLNEWCOPY + + 保留 + 保留 + + + + DLPREVIOUSCOPY + + 保留 + 保留 + + + + DLURLCOMPLETE + + 保留 + 保留 + + + + DLURLCOMPLETEONLY + + 保留 + 保留 + + + + DLURLCOMPLETEWRITE + + 保留 + 保留 + + + + DLURLPATH + + 保留 + 保留 + + + + DLURLPATHONLY + + 保留 + 保留 + + + + DLURLPATHWRITE + + 保留 + 保留 + + + + DLURLSCHEME + + 保留 + 保留 + + + + DLURLSERVER + + 保留 + 保留 + + + + DLVALUE + + 保留 + 保留 + + + + DO + 保留 + + + + + + DOCUMENT + 非保留 + 非保留 + 非保留 + + + + DOMAIN + 非保留 + 非保留 + 非保留 + 保留 + + + DOUBLE + 非保留 + 保留 + 保留 + 保留 + + + DROP + 非保留 + 保留 + 保留 + 保留 + + + DYNAMIC + + 保留 + 保留 + + + + DYNAMIC_FUNCTION + + 非保留 + 非保留 + 非保留 + + + DYNAMIC_FUNCTION_CODE + + 非保留 + 非保留 + + + + EACH + 非保留 + 保留 + 保留 + + + + ELEMENT + + 保留 + 保留 + + + + ELSE + 保留 + 保留 + 保留 + 保留 + + + EMPTY + + 非保留 + 非保留 + + + + ENABLE + 非保留 + + + + + + ENCODING + 非保留 + 非保留 + 非保留 + + + + ENCRYPTED + 非保留 + + + + + + END + 保留 + 保留 + 保留 + 保留 + + + END-EXEC + + 保留 + 保留 + 保留 + + + END_FRAME + + 保留 + + + + + END_PARTITION + + 保留 + + + + + ENFORCED + + 非保留 + + + + + ENUM + 非保留 + + + + + + EQUALS + + 保留 + 非保留 + + + + ESCAPE + 非保留 + 保留 + 保留 + 保留 + + + EVENT + 非保留 + + + + + + EVERY + + 保留 + 保留 + + + + EXCEPT + 保留 + 保留 + 保留 + 保留 + + + EXCEPTION + + + + 保留 + + + EXCLUDE + 非保留 + 非保留 + 非保留 + + + + EXCLUDING + 非保留 + 非保留 + 非保留 + + + + EXCLUSIVE + 非保留 + + + + + + EXEC + + 保留 + 保留 + 保留 + + + EXECUTE + 非保留 + 保留 + 保留 + 保留 + + + EXISTS + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + EXP + + 保留 + 保留 + + + + EXPLAIN + 非保留 + + + + + + EXPRESSION + + 非保留 + + + + + EXTENSION + 非保留 + + + + + + EXTERNAL + 非保留 + 保留 + 保留 + 保留 + + + EXTRACT + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + FALSE + 保留 + 保留 + 保留 + 保留 + + + FAMILY + 非保留 + + + + + + FETCH + 保留 + 保留 + 保留 + 保留 + + + FILE + + 非保留 + 非保留 + + + + FILTER + 非保留 + 保留 + 保留 + + + + FINAL + + 非保留 + 非保留 + + + + FIRST + 非保留 + 非保留 + 非保留 + 保留 + + + FIRST_VALUE + + 保留 + 保留 + + + + FLAG + + 非保留 + 非保留 + + + + FLOAT + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + FLOOR + + 保留 + 保留 + + + + FOLLOWING + 非保留 + 非保留 + 非保留 + + + + FOR + 保留 + 保留 + 保留 + 保留 + + + FORCE + 非保留 + + + + + + FOREIGN + 保留 + 保留 + 保留 + 保留 + + + FORTRAN + + 非保留 + 非保留 + 非保留 + + + FORWARD + 非保留 + + + + + + FOUND + + 非保留 + 非保留 + 保留 + + + FRAME_ROW + + 保留 + + + + + FREE + + 保留 + 保留 + + + + FREEZE + 保留(可用作函数或类型) + + + + + + FROM + 保留 + 保留 + 保留 + 保留 + + + FS + + 非保留 + 非保留 + + + + FULL + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + FUNCTION + 非保留 + 保留 + 保留 + + + + FUNCTIONS + 非保留 + + + + + + FUSION + + 保留 + 保留 + + + + G + + 非保留 + 非保留 + + + + GENERAL + + 非保留 + 非保留 + + + + GENERATED + + 非保留 + 非保留 + + + + GET + + 保留 + 保留 + 保留 + + + GLOBAL + 非保留 + 保留 + 保留 + 保留 + + + GO + + 非保留 + 非保留 + 保留 + + + GOTO + + 非保留 + 非保留 + 保留 + + + GRANT + 保留 + 保留 + 保留 + 保留 + + + GRANTED + 非保留 + 非保留 + 非保留 + + + + GREATEST + 非保留(不能用作函数或类型) + + + + + + GROUP + 保留 + 保留 + 保留 + 保留 + + + GROUPING + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + GROUPS + + 保留 + + + + + HANDLER + 非保留 + + + + + + HAVING + 保留 + 保留 + 保留 + 保留 + + + HEADER + 非保留 + + + + + + HEX + + 非保留 + 非保留 + + + + HIERARCHY + + 非保留 + 非保留 + + + + HOLD + 非保留 + 保留 + 保留 + + + + HOUR + 非保留 + 保留 + 保留 + 保留 + + + ID + + 非保留 + 非保留 + + + + IDENTITY + 非保留 + 保留 + 保留 + 保留 + + + IF + 非保留 + + + + + + IGNORE + + 非保留 + 非保留 + + + + ILIKE + 保留(可用作函数或类型) + + + + + + IMMEDIATE + 非保留 + 非保留 + 非保留 + 保留 + + + IMMEDIATELY + + 非保留 + + + + + IMMUTABLE + 非保留 + + + + + + IMPLEMENTATION + + 非保留 + 非保留 + + + + IMPLICIT + 非保留 + + + + + + IMPORT + 非保留 + 保留 + 保留 + + + + IN + 保留 + 保留 + 保留 + 保留 + + + INCLUDING + 非保留 + 非保留 + 非保留 + + + + INCREMENT + 非保留 + 非保留 + 非保留 + + + + INDENT + + 非保留 + 非保留 + + + + INDEX + 非保留 + + + + + + INDEXES + 非保留 + + + + + + INDICATOR + + 保留 + 保留 + 保留 + + + INHERIT + 非保留 + + + + + + INHERITS + 非保留 + + + + + + INITIALLY + 保留 + 非保留 + 非保留 + 保留 + + + INLINE + 非保留 + + + + + + INNER + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + INOUT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + INPUT + 非保留 + 非保留 + 非保留 + 保留 + + + INSENSITIVE + 非保留 + 保留 + 保留 + 保留 + + + INSERT + 非保留 + 保留 + 保留 + 保留 + + + INSTANCE + + 非保留 + 非保留 + + + + INSTANTIABLE + + 非保留 + 非保留 + + + + INSTEAD + 非保留 + 非保留 + 非保留 + + + + INT + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + INTEGER + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + INTEGRITY + + 非保留 + 非保留 + + + + INTERSECT + 保留 + 保留 + 保留 + 保留 + + + INTERSECTION + + 保留 + 保留 + + + + INTERVAL + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + INTO + 保留 + 保留 + 保留 + 保留 + + + INVOKER + 非保留 + 非保留 + 非保留 + + + + IS + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + ISNULL + 保留(可用作函数或类型) + + + + + + ISOLATION + 非保留 + 非保留 + 非保留 + 保留 + + + JOIN + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + K + + 非保留 + 非保留 + + + + KEY + 非保留 + 非保留 + 非保留 + 保留 + + + KEY_MEMBER + + 非保留 + 非保留 + + + + KEY_TYPE + + 非保留 + 非保留 + + + + LABEL + 非保留 + + + + + + LAG + + 保留 + 保留 + + + + LANGUAGE + 非保留 + 保留 + 保留 + 保留 + + + LARGE + 非保留 + 保留 + 保留 + + + + LAST + 非保留 + 非保留 + 非保留 + 保留 + + + LAST_VALUE + + 保留 + 保留 + + + + LATERAL + 保留 + 保留 + 保留 + + + + LEAD + + 保留 + 保留 + + + + LEADING + 保留 + 保留 + 保留 + 保留 + + + LEAKPROOF + 非保留 + + + + + + LEAST + 非保留(不能用作函数或类型) + + + + + + LEFT + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + LENGTH + + 非保留 + 非保留 + 非保留 + + + LEVEL + 非保留 + 非保留 + 非保留 + 保留 + + + LIBRARY + + 非保留 + 非保留 + + + + LIKE + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + LIKE_REGEX + + 保留 + 保留 + + + + LIMIT + 保留 + 非保留 + 非保留 + + + + LINK + + 非保留 + 非保留 + + + + LISTEN + 非保留 + + + + + + LN + + 保留 + 保留 + + + + LOAD + 非保留 + + + + + + LOCAL + 非保留 + 保留 + 保留 + 保留 + + + LOCALTIME + 保留 + 保留 + 保留 + + + + LOCALTIMESTAMP + 保留 + 保留 + 保留 + + + + LOCATION + 非保留 + 非保留 + 非保留 + + + + LOCATOR + + 非保留 + 非保留 + + + + LOCK + 非保留 + + + + + + LOCKED + 非保留 + + + + + + LOGGED + 非保留 + + + + + + LOWER + + 保留 + 保留 + 保留 + + + M + + 非保留 + 非保留 + + + + MAP + + 非保留 + 非保留 + + + + MAPPING + 非保留 + 非保留 + 非保留 + + + + MATCH + 非保留 + 保留 + 保留 + 保留 + + + MATCHED + + 非保留 + 非保留 + + + + MATERIALIZED + 非保留 + + + + + + MAX + + 保留 + 保留 + 保留 + + + MAXVALUE + 非保留 + 非保留 + 非保留 + + + + MAX_CARDINALITY + + + 保留 + + + + MEMBER + + 保留 + 保留 + + + + MERGE + + 保留 + 保留 + + + + MESSAGE_LENGTH + + 非保留 + 非保留 + 非保留 + + + MESSAGE_OCTET_LENGTH + + 非保留 + 非保留 + 非保留 + + + MESSAGE_TEXT + + 非保留 + 非保留 + 非保留 + + + METHOD + 非保留 + 保留 + 保留 + + + + MIN + + 保留 + 保留 + 保留 + + + MINUTE + 非保留 + 保留 + 保留 + 保留 + + + MINVALUE + 非保留 + 非保留 + 非保留 + + + + MOD + + 保留 + 保留 + + + + MODE + 非保留 + + + + + + MODIFIES + + 保留 + 保留 + + + + MODULE + + 保留 + 保留 + 保留 + + + MONTH + 非保留 + 保留 + 保留 + 保留 + + + MORE + + 非保留 + 非保留 + 非保留 + + + MOVE + 非保留 + + + + + + MULTISET + + 保留 + 保留 + + + + MUMPS + + 非保留 + 非保留 + 非保留 + + + NAME + 非保留 + 非保留 + 非保留 + 非保留 + + + NAMES + 非保留 + 非保留 + 非保留 + 保留 + + + NAMESPACE + + 非保留 + 非保留 + + + + NATIONAL + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + NATURAL + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + NCHAR + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + NCLOB + + 保留 + 保留 + + + + NESTING + + 非保留 + 非保留 + + + + NEW + + 保留 + 保留 + + + + NEXT + 非保留 + 非保留 + 非保留 + 保留 + + + NFC + + 非保留 + 非保留 + + + + NFD + + 非保留 + 非保留 + + + + NFKC + + 非保留 + 非保留 + + + + NFKD + + 非保留 + 非保留 + + + + NIL + + 非保留 + 非保留 + + + + NO + 非保留 + 保留 + 保留 + 保留 + + + NONE + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + NORMALIZE + + 保留 + 保留 + + + + NORMALIZED + + 非保留 + 非保留 + + + + NOT + 保留 + 保留 + 保留 + 保留 + + + NOTHING + 非保留 + + + + + + NOTIFY + 非保留 + + + + + + NOTNULL + 保留(可用作函数或类型) + + + + + + NOWAIT + 非保留 + + + + + + NTH_VALUE + + 保留 + 保留 + + + + NTILE + + 保留 + 保留 + + + + NULL + 保留 + 保留 + 保留 + 保留 + + + NULLABLE + + 非保留 + 非保留 + 非保留 + + + NULLIF + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + NULLS + 非保留 + 非保留 + 非保留 + + + + NUMBER + + 非保留 + 非保留 + 非保留 + + + NUMERIC + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + OBJECT + 非保留 + 非保留 + 非保留 + + + + OCCURRENCES_REGEX + + 保留 + 保留 + + + + OCTETS + + 非保留 + 非保留 + + + + OCTET_LENGTH + + 保留 + 保留 + 保留 + + + OF + 非保留 + 保留 + 保留 + 保留 + + + OFF + 非保留 + 非保留 + 非保留 + + + + OFFSET + 保留 + 保留 + 保留 + + + + OIDS + 非保留 + + + + + + OLD + + 保留 + 保留 + + + + ON + 保留 + 保留 + 保留 + 保留 + + + ONLY + 保留 + 保留 + 保留 + 保留 + + + OPEN + + 保留 + 保留 + 保留 + + + OPERATOR + 非保留 + + + + + + OPTION + 非保留 + 非保留 + 非保留 + 保留 + + + OPTIONS + 非保留 + 非保留 + 非保留 + + + + OR + 保留 + 保留 + 保留 + 保留 + + + ORDER + 保留 + 保留 + 保留 + 保留 + + + ORDERING + + 非保留 + 非保留 + + + + ORDINALITY + 非保留 + 非保留 + 非保留 + + + + OTHERS + + 非保留 + 非保留 + + + + OUT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + OUTER + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + OUTPUT + + 非保留 + 非保留 + 保留 + + + OVER + 非保留 + 保留 + 保留 + + + + OVERLAPS + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + OVERLAY + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + OVERRIDING + + 非保留 + 非保留 + + + + OWNED + 非保留 + + + + + + OWNER + 非保留 + + + + + + P + + 非保留 + 非保留 + + + + PAD + + 非保留 + 非保留 + 保留 + + + PARALLEL + 非保留 + + + + + + PARAMETER + + 保留 + 保留 + + + + PARAMETER_MODE + + 非保留 + 非保留 + + + + PARAMETER_NAME + + 非保留 + 非保留 + + + + PARAMETER_ORDINAL_POSITION + + 非保留 + 非保留 + + + + PARAMETER_SPECIFIC_CATALOG + + 非保留 + 非保留 + + + + PARAMETER_SPECIFIC_NAME + + 非保留 + 非保留 + + + + PARAMETER_SPECIFIC_SCHEMA + + 非保留 + 非保留 + + + + PARSER + 非保留 + + + + + + PARTIAL + 非保留 + 非保留 + 非保留 + 保留 + + + PARTITION + 非保留 + 保留 + 保留 + + + + PASCAL + + 非保留 + 非保留 + 非保留 + + + PASSING + 非保留 + 非保留 + 非保留 + + + + PASSTHROUGH + + 非保留 + 非保留 + + + + PASSWORD + 非保留 + + + + + + PATH + + 非保留 + 非保留 + + + + PERCENT + + 保留 + + + + + PERCENTILE_CONT + + 保留 + 保留 + + + + PERCENTILE_DISC + + 保留 + 保留 + + + + PERCENT_RANK + + 保留 + 保留 + + + + PERIOD + + 保留 + + + + + PERMISSION + + 非保留 + 非保留 + + + + PLACING + 保留 + 非保留 + 非保留 + + + + PLANS + 非保留 + + + + + + PLI + + 非保留 + 非保留 + 非保留 + + + POLICY + 非保留 + + + + + + PORTION + + 保留 + + + + + POSITION + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + POSITION_REGEX + + 保留 + 保留 + + + + POWER + + 保留 + 保留 + + + + PRECEDES + + 保留 + + + + + PRECEDING + 非保留 + 非保留 + 非保留 + + + + PRECISION + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + PREPARE + 非保留 + 保留 + 保留 + 保留 + + + PREPARED + 非保留 + + + + + + PRESERVE + 非保留 + 非保留 + 非保留 + 保留 + + + PRIMARY + 保留 + 保留 + 保留 + 保留 + + + PRIOR + 非保留 + 非保留 + 非保留 + 保留 + + + PRIVILEGES + 非保留 + 非保留 + 非保留 + 保留 + + + PROCEDURAL + 非保留 + + + + + + PROCEDURE + 非保留 + 保留 + 保留 + 保留 + + + PROGRAM + 非保留 + + + + + + PUBLIC + + 非保留 + 非保留 + 保留 + + + QUOTE + 非保留 + + + + + + RANGE + 非保留 + 保留 + 保留 + + + + RANK + + 保留 + 保留 + + + + READ + 非保留 + 非保留 + 非保留 + 保留 + + + READS + + 保留 + 保留 + + + + REAL + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + REASSIGN + 非保留 + + + + + + RECHECK + 非保留 + + + + + + RECOVERY + + 非保留 + 非保留 + + + + RECURSIVE + 非保留 + 保留 + 保留 + + + + REF + 非保留 + 保留 + 保留 + + + + REFERENCES + 保留 + 保留 + 保留 + 保留 + + + REFERENCING + + 保留 + 保留 + + + + REFRESH + 非保留 + + + + + + REGR_AVGX + + 保留 + 保留 + + + + REGR_AVGY + + 保留 + 保留 + + + + REGR_COUNT + + 保留 + 保留 + + + + REGR_INTERCEPT + + 保留 + 保留 + + + + REGR_R2 + + 保留 + 保留 + + + + REGR_SLOPE + + 保留 + 保留 + + + + REGR_SXX + + 保留 + 保留 + + + + REGR_SXY + + 保留 + 保留 + + + + REGR_SYY + + 保留 + 保留 + + + + REINDEX + 非保留 + + + + + + RELATIVE + 非保留 + 非保留 + 非保留 + 保留 + + + RELEASE + 非保留 + 保留 + 保留 + + + + RENAME + 非保留 + + + + + + REPEATABLE + 非保留 + 非保留 + 非保留 + 非保留 + + + REPLACE + 非保留 + + + + + + REPLICA + 非保留 + + + + + + REQUIRING + + 非保留 + 非保留 + + + + RESET + 非保留 + + + + + + RESPECT + + 非保留 + 非保留 + + + + RESTART + 非保留 + 非保留 + 非保留 + + + + RESTORE + + 非保留 + 非保留 + + + + RESTRICT + 非保留 + 非保留 + 非保留 + 保留 + + + RESULT + + 保留 + 保留 + + + + RETURN + + 保留 + 保留 + + + + RETURNED_CARDINALITY + + 非保留 + 非保留 + + + + RETURNED_LENGTH + + 非保留 + 非保留 + 非保留 + + + RETURNED_OCTET_LENGTH + + 非保留 + 非保留 + 非保留 + + + RETURNED_SQLSTATE + + 非保留 + 非保留 + 非保留 + + + RETURNING + 保留 + 非保留 + 非保留 + + + + RETURNS + 非保留 + 保留 + 保留 + + + + REVOKE + 非保留 + 保留 + 保留 + 保留 + + + RIGHT + 保留(可用作函数或类型) + 保留 + 保留 + 保留 + + + ROLE + 非保留 + 非保留 + 非保留 + + + + ROLLBACK + 非保留 + 保留 + 保留 + 保留 + + + ROLLUP + 非保留 + 保留 + 保留 + + + + ROUTINE + + 非保留 + 非保留 + + + + ROUTINE_CATALOG + + 非保留 + 非保留 + + + + ROUTINE_NAME + + 非保留 + 非保留 + + + + ROUTINE_SCHEMA + + 非保留 + 非保留 + + + + ROW + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + ROWS + 非保留 + 保留 + 保留 + 保留 + + + ROW_COUNT + + 非保留 + 非保留 + 非保留 + + + ROW_NUMBER + + 保留 + 保留 + + + + RULE + 非保留 + + + + + + SAVEPOINT + 非保留 + 保留 + 保留 + + + + SCALE + + 非保留 + 非保留 + 非保留 + + + SCHEMA + 非保留 + 非保留 + 非保留 + 保留 + + + SCHEMA_NAME + + 非保留 + 非保留 + 非保留 + + + SCOPE + + 保留 + 保留 + + + + SCOPE_CATALOG + + 非保留 + 非保留 + + + + SCOPE_NAME + + 非保留 + 非保留 + + + + SCOPE_SCHEMA + + 非保留 + 非保留 + + + + SCROLL + 非保留 + 保留 + 保留 + 保留 + + + SEARCH + 非保留 + 保留 + 保留 + + + + SECOND + 非保留 + 保留 + 保留 + 保留 + + + SECTION + + 非保留 + 非保留 + 保留 + + + SECURITY + 非保留 + 非保留 + 非保留 + + + + SELECT + 保留 + 保留 + 保留 + 保留 + + + SELECTIVE + + 非保留 + 非保留 + + + + SELF + + 非保留 + 非保留 + + + + SENSITIVE + + 保留 + 保留 + + + + SEQUENCE + 非保留 + 非保留 + 非保留 + + + + SEQUENCES + 非保留 + + + + + + SERIALIZABLE + 非保留 + 非保留 + 非保留 + 非保留 + + + SERVER + 非保留 + 非保留 + 非保留 + + + + SERVER_NAME + + 非保留 + 非保留 + 非保留 + + + SESSION + 非保留 + 非保留 + 非保留 + 保留 + + + SESSION_USER + 保留 + 保留 + 保留 + 保留 + + + SET + 非保留 + 保留 + 保留 + 保留 + + + SETOF + 非保留(不能用作函数或类型) + + + + + + SETS + 非保留 + 非保留 + 非保留 + + + + SHARE + 非保留 + + + + + + SHOW + 非保留 + + + + + + SIMILAR + 保留(可用作函数或类型) + 保留 + 保留 + + + + SIMPLE + 非保留 + 非保留 + 非保留 + + + + SIZE + + 非保留 + 非保留 + 保留 + + + SKIP + 非保留 + + + + + + SMALLINT + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + SNAPSHOT + 非保留 + + + + + + SOME + 保留 + 保留 + 保留 + 保留 + + + SOURCE + + 非保留 + 非保留 + + + + SPACE + + 非保留 + 非保留 + 保留 + + + SPECIFIC + + 保留 + 保留 + + + + SPECIFICTYPE + + 保留 + 保留 + + + + SPECIFIC_NAME + + 非保留 + 非保留 + + + + SQL + 非保留 + 保留 + 保留 + 保留 + + + SQLCODE + + + + 保留 + + + SQLERROR + + + + 保留 + + + SQLEXCEPTION + + 保留 + 保留 + + + + SQLSTATE + + 保留 + 保留 + 保留 + + + SQLWARNING + + 保留 + 保留 + + + + SQRT + + 保留 + 保留 + + + + STABLE + 非保留 + + + + + + STANDALONE + 非保留 + 非保留 + 非保留 + + + + START + 非保留 + 保留 + 保留 + + + + STATE + + 非保留 + 非保留 + + + + STATEMENT + 非保留 + 非保留 + 非保留 + + + + STATIC + + 保留 + 保留 + + + + STATISTICS + 非保留 + + + + + + STDDEV_POP + + 保留 + 保留 + + + + STDDEV_SAMP + + 保留 + 保留 + + + + STDIN + 非保留 + + + + + + STDOUT + 非保留 + + + + + + STORAGE + 非保留 + + + + + + STRICT + 非保留 + + + + + + STRIP + 非保留 + 非保留 + 非保留 + + + + STRUCTURE + + 非保留 + 非保留 + + + + STYLE + + 非保留 + 非保留 + + + + SUBCLASS_ORIGIN + + 非保留 + 非保留 + 非保留 + + + SUBMULTISET + + 保留 + 保留 + + + + SUBSTRING + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + SUBSTRING_REGEX + + 保留 + 保留 + + + + SUCCEEDS + + 保留 + + + + + SUM + + 保留 + 保留 + 保留 + + + SYMMETRIC + 保留 + 保留 + 保留 + + + + SYSID + 非保留 + + + + + + SYSTEM + 非保留 + 保留 + 保留 + + + + SYSTEM_TIME + + 保留 + + + + + SYSTEM_USER + + 保留 + 保留 + 保留 + + + T + + 非保留 + 非保留 + + + + TABLE + 保留 + 保留 + 保留 + 保留 + + + TABLES + 非保留 + + + + + + TABLESAMPLE + 保留(可用作函数或类型) + 保留 + 保留 + + + + TABLESPACE + 非保留 + + + + + + TABLE_NAME + + 非保留 + 非保留 + 非保留 + + + TEMP + 非保留 + + + + + + TEMPLATE + 非保留 + + + + + + TEMPORARY + 非保留 + 非保留 + 非保留 + 保留 + + + TEXT + 非保留 + + + + + + THEN + 保留 + 保留 + 保留 + 保留 + + + TIES + + 非保留 + 非保留 + + + + TIME + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + TIMESTAMP + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + TIMEZONE_HOUR + + 保留 + 保留 + 保留 + + + TIMEZONE_MINUTE + + 保留 + 保留 + 保留 + + + TO + 保留 + 保留 + 保留 + 保留 + + + TOKEN + + 非保留 + 非保留 + + + + TOP_LEVEL_COUNT + + 非保留 + 非保留 + + + + TRAILING + 保留 + 保留 + 保留 + 保留 + + + TRANSACTION + 非保留 + 非保留 + 非保留 + 保留 + + + TRANSACTIONS_COMMITTED + + 非保留 + 非保留 + + + + TRANSACTIONS_ROLLED_BACK + + 非保留 + 非保留 + + + + TRANSACTION_ACTIVE + + 非保留 + 非保留 + + + + TRANSFORM + 非保留 + 非保留 + 非保留 + + + + TRANSFORMS + + 非保留 + 非保留 + + + + TRANSLATE + + 保留 + 保留 + 保留 + + + TRANSLATE_REGEX + + 保留 + 保留 + + + + TRANSLATION + + 保留 + 保留 + 保留 + + + TREAT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + TRIGGER + 非保留 + 保留 + 保留 + + + + TRIGGER_CATALOG + + 非保留 + 非保留 + + + + TRIGGER_NAME + + 非保留 + 非保留 + + + + TRIGGER_SCHEMA + + 非保留 + 非保留 + + + + TRIM + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + TRIM_ARRAY + + 保留 + 保留 + + + + TRUE + 保留 + 保留 + 保留 + 保留 + + + TRUNCATE + 非保留 + 保留 + 保留 + + + + TRUSTED + 非保留 + + + + + + TYPE + 非保留 + 非保留 + 非保留 + 非保留 + + + TYPES + 非保留 + + + + + + UESCAPE + + 保留 + 保留 + + + + UNBOUNDED + 非保留 + 非保留 + 非保留 + + + + UNCOMMITTED + 非保留 + 非保留 + 非保留 + 非保留 + + + UNDER + + 非保留 + 非保留 + + + + UNENCRYPTED + 非保留 + + + + + + UNION + 保留 + 保留 + 保留 + 保留 + + + UNIQUE + 保留 + 保留 + 保留 + 保留 + + + UNKNOWN + 非保留 + 保留 + 保留 + 保留 + + + UNLINK + + 非保留 + 非保留 + + + + UNLISTEN + 非保留 + + + + + + UNLOGGED + 非保留 + + + + + + UNNAMED + + 非保留 + 非保留 + 非保留 + + + UNNEST + + 保留 + 保留 + + + + UNTIL + 非保留 + + + + + + UNTYPED + + 非保留 + 非保留 + + + + UPDATE + 非保留 + 保留 + 保留 + 保留 + + + UPPER + + 保留 + 保留 + 保留 + + + URI + + 非保留 + 非保留 + + + + USAGE + + 非保留 + 非保留 + 保留 + + + USER + 保留 + 保留 + 保留 + 保留 + + + USER_DEFINED_TYPE_CATALOG + + 非保留 + 非保留 + + + + USER_DEFINED_TYPE_CODE + + 非保留 + 非保留 + + + + USER_DEFINED_TYPE_NAME + + 非保留 + 非保留 + + + + USER_DEFINED_TYPE_SCHEMA + + 非保留 + 非保留 + + + + USING + 保留 + 保留 + 保留 + 保留 + + + VACUUM + 非保留 + + + + + + VALID + 非保留 + 非保留 + 非保留 + + + + VALIDATE + 非保留 + + + + + + VALIDATOR + 非保留 + + + + + + VALUE + 非保留 + 保留 + 保留 + 保留 + + + VALUES + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + VALUE_OF + + 保留 + + + + + VARBINARY + + 保留 + 保留 + + + + VARCHAR + 非保留(不能用作函数或类型) + 保留 + 保留 + 保留 + + + VARIADIC + 保留 + + + + + + VARYING + 非保留 + 保留 + 保留 + 保留 + + + VAR_POP + + 保留 + 保留 + + + + VAR_SAMP + + 保留 + 保留 + + + + VERBOSE + 保留(可用作函数或类型) + + + + + + VERSION + 非保留 + 非保留 + 非保留 + + + + VERSIONING + + 保留 + + + + + VIEW + 非保留 + 非保留 + 非保留 + 保留 + + + VIEWS + 非保留 + + + + + + VOLATILE + 非保留 + + + + + + WHEN + 保留 + 保留 + 保留 + 保留 + + + WHENEVER + + 保留 + 保留 + 保留 + + + WHERE + 保留 + 保留 + 保留 + 保留 + + + WHITESPACE + 非保留 + 非保留 + 非保留 + + + + WIDTH_BUCKET + + 保留 + 保留 + + + + WINDOW + 保留 + 保留 + 保留 + + + + WITH + 保留 + 保留 + 保留 + 保留 + + + WITHIN + 非保留 + 保留 + 保留 + + + + WITHOUT + 非保留 + 保留 + 保留 + + + + WORK + 非保留 + 非保留 + 非保留 + 保留 + + + WRAPPER + 非保留 + 非保留 + 非保留 + + + + WRITE + 非保留 + 非保留 + 非保留 + 保留 + + + XML + 非保留 + 保留 + 保留 + + + + XMLAGG + + 保留 + 保留 + + + + XMLATTRIBUTES + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLBINARY + + 保留 + 保留 + + + + XMLCAST + + 保留 + 保留 + + + + XMLCOMMENT + + 保留 + 保留 + + + + XMLCONCAT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLDECLARATION + + 非保留 + 非保留 + + + + XMLDOCUMENT + + 保留 + 保留 + + + + XMLELEMENT + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLEXISTS + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLFOREST + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLITERATE + + 保留 + 保留 + + + + XMLNAMESPACES + + 保留 + 保留 + + + + XMLPARSE + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLPI + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLQUERY + + 保留 + 保留 + + + + XMLROOT + 非保留(不能用作函数或类型) + + + + + + XMLSCHEMA + + 非保留 + 非保留 + + + + XMLSERIALIZE + 非保留(不能用作函数或类型) + 保留 + 保留 + + + + XMLTABLE + + 保留 + 保留 + + + + XMLTEXT + + 保留 + 保留 + + + + XMLVALIDATE + + 保留 + 保留 + + + + YEAR + 非保留 + 保留 + 保留 + 保留 + + + YES + 非保留 + 非保留 + 非保留 + + + + ZONE + 非保留 + 非保留 + 非保留 + 保留 + + + +
+ + +
diff --git a/zh/9.6/legal.sgml b/zh/9.6/legal.sgml new file mode 100644 index 00000000..f454d2de --- /dev/null +++ b/zh/9.6/legal.sgml @@ -0,0 +1,35 @@ + + +2021 + + + 1996-2021 + PostgreSQL 全球开发组 + + + + 法律声明 + + PostgreSQL 版权所有 © 1996-2021 PostgreSQL 全球开发组。 + + Postgres95 版权所有 © 1994-5 加利福尼亚大学董事会。 + + + 特此授权,可为任何目的使用、复制、修改和分发本软件及其文档, + 无需付费,也无需书面协议,但条件是所有副本中都必须包含上述版权声明、 + 本段以及后续两段内容。 + + + + 在任何情况下,加利福尼亚大学都不对任何一方承担因使用本软件及其文档 + 而产生的任何直接、间接、特殊、附带或后果性损害赔偿责任, + 包括利润损失,即使加利福尼亚大学已被告知此类损害发生的可能性。 + + + + 加利福尼亚大学明确不作任何担保,包括但不限于适销性和特定用途适用性的默示担保。 + 本协议项下提供的软件按原样提供,加利福尼亚大学没有义务提供维护、支持、 + 更新、增强或修改。 + + + diff --git a/zh/9.6/libpq.sgml b/zh/9.6/libpq.sgml new file mode 100644 index 00000000..c4e943ac --- /dev/null +++ b/zh/9.6/libpq.sgml @@ -0,0 +1,5775 @@ + + + + <application>libpq</application> - C 库 + + + libpq + + + + C + + + + libpq是应用程序员使用PostgreSQLC接口。libpq是一个库函数的集合,它们允许客户端程序传递查询给PostgreSQL后端服务器并且接收这些查询的结果。 + + + + libpq也是很多其他PostgreSQL应用接口的底层引擎,包括为 C++、Perl、Python、Tcl 和 ECPG编写的接口。如果你使用那些包,某些方面的libpq行为将会对你很重要。特别是,描述了任何使用libpq的应用的用户可见的行为。 + + + + 在本章的末尾()包括了一些短程序来展示如何编写使用libpq的应用。在源代码发布的src/test/examples目录中还有一些完整的libpq应用的示例。 + + + + 使用libpq的客户端程序必须包括头文件libpq-fe.hlibpq-fe.h并必须与libpq库链接在一起。 + + + + 数据库连接控制函数 + + 以下函数用于建立到PostgreSQL后端服务器的连接。应用程序可以同时保持多个后端连接。(这样做的原因之一是访问多个数据库。)每个连接由一个PGconnPGconn对象表示,该对象可以通过以下函数获取:PQconnectdb, + PQconnectdbParams,或PQsetdbLogin。注意,这些函数总是返回非空的对象指针,除非内存不足,甚至无法分配PGconn对象。应调用PQstatus函数检查返回值,确认连接成功后,再通过连接对象发送查询。 + + 如果不受信任的用户能够访问一个没有采用模式的安全使用方式的数据库,那么每个会话开始时都应从search_path中移除公开可写的模式。可以把参数关键词options设置为-csearch_path=。也可以在连接后发出PQexec(conn, "SELECT pg_catalog.set_config('search_path', '', false)")。这种考虑并非专门针对libpq;它适用于每一种可执行任意 SQL 命令的接口。 + + + + + + 在 Unix 上,复制一个拥有打开 libpq 连接的进程可能导致不可预料的结果,因为父进程和子进程会共享相同的套接字和操作系统资源。出于这个原因,我们不推荐这样的用法,尽管从子进程执行一个exec来载入新的可执行代码是安全的。 + + + + + + PQconnectdbParamsPQconnectdbParams + + + 开启一个到数据库服务器的新连接。 + + +PGconn *PQconnectdbParams(const char * const *keywords, + const char * const *values, + int expand_dbname); + + + + + 这个函数使用从两个以NULL结尾的数组中取得的参数打开一个新的数据库连接。第一个数组keywords是一个字符串数组,其中每个元素都是一个关键词。第二个数组values给出每个关键词的值。和下面的PQsetdbLogin不同,参数集合可以在不改变函数签名的情况下扩展,因此对于新应用,最好使用这个函数(或者相应的非阻塞函数PQconnectStartParamsPQconnectPoll)。 + + + + 当前能被识别的参数关键词被列举在中。 + + + + 被传递的数组可以为空,这样就会使用所有默认参数。 + 也可以只包含一个或几个参数设置。他们在长度上必须匹配。 + 对于参数数组的处理将会停止于keywords数组中第一个NULL元素。 + 而且,如果与非-NULL keywords条目相关联的values条目为NULL或者空字符串,则忽略该项并继续处理下一对数组项。 + + + + 当expand_dbname为非零时,会检查第一个dbname关键词的值以查看它是否为一个连接字符串。 + 如果是,它被扩展到从字符串中提取的单独的连接参数。 + 该值被认为是一个连接字符串,而不仅是一个数据库名称,如果它包含一个等号(=)或者它以URI模式标志符开头, + (有关连接字符串格式的更多详情可见。) + 只有dbname的第一次出现会按这种方式处理,任何后续dbname值会被当做一个普通数据库名处理。 + + + + 通常,参数数组从开头到结尾进行处理。 + 当关键词有重复时,使用最后一个值(不是 NULL 或空)。 + 此规则特别适用于连接字符串中的关键字与一个出现在keywords数组中的关键字冲突的情况。 + 因此,程序员可以决定数组条目是否能被覆盖或用连接字符串获取的值覆盖。 + 出现在扩展的dbname条目之前的数组条目可以被连接字符串的字段所覆盖,反之,这些字段被dbname之后出现的数组条目所覆盖。(但是,再有,只有在那些条目支持非空值时。) + + + + 在处理完所有数组条目和任何扩展的连接字符串后,所有未设置的连接参数都将使用默认值填充。 + 如果一个未设置参数的相关环境变量(参见 )被设置了,它的值会被使用。 + 如果环境变量未被设置,则使用参数的内置默认值。 + + + + + + + PQconnectdbPQconnectdb + + + 开启一个到数据库服务器的新连接。 + + +PGconn *PQconnectdb(const char *conninfo); + + + + + 这个函数使用从字符串conninfo中得到的参数开启一个新的数据库连接。 + + + + 被传递的字符串可以为空,这样将会使用所有的默认参数。也可以包含由空格分隔的一个或多个参数设置,还可以包含一个URI。详见。 + + + + + + + PQsetdbLoginPQsetdbLogin + + + 开启一个到数据库服务器的新连接。 + +PGconn *PQsetdbLogin(const char *pghost, + const char *pgport, + const char *pgoptions, + const char *pgtty, + const char *dbName, + const char *login, + const char *pwd); + + + + 这是 PQconnectdb 的前身,使用固定的一组参数。除缺失参数始终采用默认值之外,功能相同。对于要使用默认值的任意固定参数,请传入 NULL 或空字符串。 + + 如果 dbName 包含 = 符号,或具有有效的连接 URI 前缀,就会将其当作 conninfo 字符串处理,方式与将其传给 PQconnectdb 完全相同,然后按照 PQconnectdbParams 的规则应用其余参数。 + + + + + PQsetdbPQsetdb + + + 开启一个到数据库服务器的新连接。 + +PGconn *PQsetdb(char *pghost, + char *pgport, + char *pgoptions, + char *pgtty, + char *dbName); + + + + + 这是一个调用PQsetdbLogin的宏,其中为loginpwd参数使用空指针。提供它是为了向后兼容非常老的程序。 + + + + + + PQconnectStartParamsPQconnectStartParams + PQconnectStartPQconnectStart + PQconnectPollPQconnectPoll + + + nonblocking connection + 以非阻塞的方式建立一个到数据库服务器的连接。 + + +PGconn *PQconnectStartParams(const char * const *keywords, + const char * const *values, + int expand_dbname); + +PGconn *PQconnectStart(const char *conninfo); + +PostgresPollingStatusType PQconnectPoll(PGconn *conn); + + + + + 这三个函数被用来开启一个到数据库服务器的连接,这样你的应用的执行线程不会因为远程的I/O而被阻塞。这种方法的要点在于等待 I/O 完成可能在应用的主循环中发生,而不是在PQconnectdbParamsPQconnectdb中,并且因此应用能够把这种操作和其他动作并行处理。 + + + + 在PQconnectStartParams中,数据库连接使用从keywordsvalues数组中取得的参数创建,并且被expand_dbname控制,这和之前描述的PQconnectdbParams相同。 + + + + 在PQconnectStart中,数据库连接使用从字符串conninfo中取得的参数创建,这和之前描述的PQconnectdb相同。 + + + 无论是PQconnectStartParams还是PQconnectStart还是PQconnectPoll都不会阻塞,只要满足以下限制: + + 必须恰当地使用 hostaddrhost 参数,以确保不会进行名称和反向名称查询。详细信息请参见 中这些参数的说明。 + + + + + 如果你调用PQtrace,确保你追踪的该流对象不会阻塞。 + + + + + + 如后文所述,你要确保在调用PQconnectPoll之前,套接字处于合适的状态。 + + + + + + + 注意:PQconnectStartParams的用法与下文展示的PQconnectStart类似。 + + + + 要开始无阻塞的连接请求,可调用conn = PQconnectStart("connection_info_string")。如果conn为空,则libpq无法分配一个新的PGconn结构体。否则,一个有效的PGconn指针会被返回(不过还没有表示一个到数据库的有效连接)。从PQconnectStart返回后,调用status = PQstatus(conn)。如果status等于CONNECTION_BAD,则PQconnectStart失败。 + + + + 如果PQconnectStart成功,下一个阶段是轮询libpq,这样它能够继续进行连接序列。使用PQsocket(conn)来获得该数据库连接底层的套接字描述符。这样循环:如果PQconnectPoll(conn)上一次返回PGRES_POLLING_READING,等到该套接字准备好读取(按照select()poll()或类似的系统函数所指示的)。则再次调用PQconnectPoll(conn)。反之,如果PQconnectPoll(conn)上一次返回PGRES_POLLING_WRITING,等到该套接字准备好写入,则再次调用PQconnectPoll(conn)。如果你还没有调用过PQconnectPoll,即刚刚调用过PQconnectStart之后,行为就像是它上次返回了PGRES_POLLING_WRITING。持续这个循环直到PQconnectPoll(conn)返回PGRES_POLLING_FAILED指示连接过程已经失败,或者返回PGRES_POLLING_OK指示连接已经被成功地建立。 + + + 在连接过程中的任何时刻,都可以通过调用PQstatus来检查连接状态。如果该调用返回CONNECTION_BAD,则连接过程已经失败;如果返回CONNECTION_OK,则连接已就绪。通过以下函数的返回值也同样可以检测这两种状态:PQconnectPoll,该函数已在上文介绍。在异步连接过程中(也仅在此过程中)还可能出现其他状态。它们指明连接过程的当前阶段,例如可以用来向用户提供反馈。这些状态如下: + + CONNECTION_STARTED + + + 等待连接被建立。 + + + + + + CONNECTION_MADE + + + 连接 OK,等待发送。 + + + + + + CONNECTION_AWAITING_RESPONSE + + + 等待来自服务器的一个回应。 + + + + + + CONNECTION_AUTH_OK + + + 收到认证,等待后端启动结束。 + + + + + + CONNECTION_SSL_STARTUP + + + 协商 SSL 加密。 + + + + + + CONNECTION_SETENV + + + 协商环境驱动的参数设置。 + + + + + 注意,虽然这些常量会保留下来以维持兼容性,但应用程序绝不能依赖它们按某种特定顺序出现、必定出现,或状态始终是这些已记录的值之一。应用程序可以采用类似以下的做法: +switch(PQstatus(conn)) +{ + case CONNECTION_STARTED: + feedback = "Connecting..."; + break; + + case CONNECTION_MADE: + feedback = "Connected to server..."; + break; +. +. +. + default: + feedback = "Connecting..."; +} + + + + + 在使用PQconnectPoll时,连接参数connect_timeout会被忽略:判断是否超时是应用的责任。否则,PQconnectStart后面跟着PQconnectPoll循环等效于PQconnectdb。 + + + + 注意如果PQconnectStart返回一个非空的指针,你必须在用完它之后调用PQfinish来处理该结构体和任何相关的内存块。即使连接尝试失败或被放弃时也必须完成这些工作。 + + + + + + PQconndefaultsPQconndefaults + + + 返回默认连接选项。 + +PQconninfoOption *PQconndefaults(void); + +typedef struct +{ + char *keyword; /* 该选项的关键词 */ + char *envvar; /* 依赖的环境变量名 */ + char *compiled; /* 依赖的内置默认值 */ + char *val; /* 选项的当前值,或者 NULL */ + char *label; /* 连接对话框中域的标签 */ + char *dispchar; /* 指示如何在一个连接对话框中显示这个域。值是: + "" 显示输入的值 + "*" 密码域 - 隐藏值 + "D" 调试选项 - 默认不显示 */ + int dispsize; /* 用于对话框的以字符计的域尺寸 */ +} PQconninfoOption; + + + + + 返回一个连接选项数组。这可以用来确定用于连接服务器的所有可能的PQconnectdb选项和它们的当前缺省值。返回值指向一个PQconninfoOption结构体的数组,该数组以一个包含空keyword指针的条目结束。如果无法分配内存,则返回该空指针。注意当前缺省值(val域)将依赖于环境变量和其他上下文。一个缺失或者无效的服务文件将会被无声地忽略掉。调用者必须把连接选项当作只读对待。 + + + + 在处理完选项数组后,把它交给PQconninfoFree释放。如果没有这么做, 每次调用PQconndefaults都会导致一小部分内存泄漏。 + + + + + + + PQconninfoPQconninfo + + + 返回被一个活动连接使用的连接选项。 + +PQconninfoOption *PQconninfo(PGconn *conn); + + + + 返回一个连接选项数组。可以用它确定所有可能的 PQconnectdb 选项,以及实际用于连接服务器的值。返回值指向一个 PQconninfoOption 结构体数组,该数组以 keyword 指针为空的条目结束。上文针对 PQconndefaults 的所有注意事项,也适用于 PQconninfo 的结果。 + + + + + + + PQconninfoParsePQconninfoParse + + + 返回从提供的连接字符串中解析到的连接选项。 + + +PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); + + + + + 解析一个连接字符串并且将结果选项作为一个数组返回,或者在连接字符串有问题时返回NULL。这个函数可以用来抽取所提供的连接字符串中的PQconnectdb选项。返回值指向一个PQconninfoOption结构体的数组,该数组以一个包含空keyword指针的条目结束。 + + + + 所有合法选项将出现在结果数组中,但是任何在连接字符串中没有出现的选项的PQconninfoOptionval会被设置为NULL,默认值不会被插入。 + + + + 如果errmsg不是NULL,那么成功时*errmsg会被设置为NULL, 否则设置为被malloc过的错误字符串以说明该问题(也可以将*errmsg设置为NULL并且函数返回NULL,这表示一种内存耗尽的情况)。 + + + + 在处理完选项数组后,把它交给PQconninfoFree释放。如果没有这么做, 每次调用PQconninfoParse都会导致一小部分内存泄漏。反过来,如果发生一个错误并且errmsg不是NULL,确保使用PQfreemem释放错误字符串。 + + + + + + + PQfinishPQfinish + + + 关闭与服务器的连接。同时释放PGconn对象使用的内存。 + +void PQfinish(PGconn *conn); + + + + + 注意,即使与服务器的连接尝试失败(由PQstatus指示),应用也应当调用PQfinish来释放PGconn对象使用的内存。不能在调用PQfinish之后再使用PGconn指针。 + + + + + + PQresetPQreset + + + 重置与服务器的通讯通道。 + +void PQreset(PGconn *conn); + + + + + 此函数将关闭与服务器的连接,并尝试重新建立到同一服务器的新连接,使用之前使用过的所有参数。 + 这可能有助于在工作连接丢失后的错误恢复。 + + + + + + PQresetStartPQresetStart + PQresetPollPQresetPoll + + + 以非阻塞方式重置与服务器的通讯通道。 + + +int PQresetStart(PGconn *conn); + +PostgresPollingStatusType PQresetPoll(PGconn *conn); + + + + 这些函数会关闭与服务器的连接,并尝试重新建立到同一服务器的新连接,使用之前使用过的所有参数。如果原本可用的连接丢失,这可以用于错误恢复。它们与上文的 PQreset 不同之处在于采用非阻塞方式。它们受到与 PQconnectStartParamsPQconnectStartPQconnectPoll 相同的限制。 + + 要开始重置连接,请调用 PQresetStart。如果它返回 0,则重置失败。如果返回 1,则使用 PQresetPoll 轮询重置过程,其方式与使用 PQconnectPoll 创建连接完全相同。 + + + + + PQpingParamsPQpingParams + + + PQpingParams报告服务器状态。它接受的连接参数与上文介绍的PQconnectdbParams相同。取得服务器状态不需要提供正确的用户名、密码或数据库名;但如果提供的值不正确,服务器会记录一次失败的连接尝试。 +PGPing PQpingParams(const char * const *keywords, + const char * const *values, + int expand_dbname); +该函数返回以下值之一: + + PQPING_OK + + + 服务器正在运行,并且看起来可以接受连接。 + + + + + + PQPING_REJECT + + + 服务器正在运行,但是处于一种不允许连接的状态(启动、关闭或崩溃恢复)。 + + + + + + PQPING_NO_RESPONSE + + + 无法联系到服务器。这可能表示服务器没有运行,或者给定的连接参数中有些错误(例如,错误的端口号),或者有一个网络连接问题(例如,一个防火墙阻断了连接请求)。 + + + + + + PQPING_NO_ATTEMPT + + + 没有尝试联系服务器,因为提供的参数显然不正确,或者有一些客户端问题(例如,内存用完)。 + + + + + + + + + + + + PQpingPQping + + + PQping报告服务器状态。它接受的连接参数与上文介绍的PQconnectdb相同。取得服务器状态不需要提供正确的用户名、密码或数据库名;但如果提供的值不正确,服务器会记录一次失败的连接尝试。 +PGPing PQping(const char *conninfo); + + + + 返回值与 PQpingParams 相同。 + + + + + + + + + 连接字符串 + + + conninfo + + + + URI + + + + 几个libpq函数解析用户指定的字符串以获取连接参数。 + 这些字符串有两种被接受的格式:普通的keyword = value字符串和 + RFC + 3986 URI。 + + + + 关键词/值连接字符串 + + + 在第一种格式中,每一个参数设置的形式都是keyword = value。 + 设置的等号周围的空白是可选的。 + 要写一个空值或一个包含空白的值,将它用单引号包围,例如keyword = 'a value'。 + 值中的单引号和反斜线必须用一个反斜线转义,即\'\\。 + + + + 示例: + +host=localhost port=5432 dbname=mydb connect_timeout=10 + + + + + 能被识别的参数关键词在中列出。 + + + + + 连接 URI + + + 一个连接URI的一般形式是: + +postgresql://[user[:password]@][host][:port][/dbname][?param1=value1&...] + + + + + URI模式标志符可以是postgresql://postgres://。 + 每一个剩下的URI部分都是可选的。 + 下列示例展示了合法的URI语法: + +postgresql:// +postgresql://localhost +postgresql://localhost:5433 +postgresql://localhost/mydb +postgresql://user@localhost +postgresql://user:secret@localhost +postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp + + 通常出现在URI的层次部分的值,也能够以命名参数的方式给出。例如: + +postgresql:///mydb?host=localhost&port=5433 + + 全部的命名参数必须匹配中列出的关键词,除了与JDBC连接URI兼容之外,ssl=true的实例转换到sslmode=require。 + + + 可以在 URI 的任意部分使用百分号编码来包含具有特殊含义的符号。 + + + 主机部分可能是主机名或一个 IP 地址。要指定一个 IPv6 地址,将它封闭在方括号中: + +postgresql://[2001:db8::1234]/database + + + + + 主机组件会被按照参数对应的描述来解释。 + 特别地,如果主机部分是空或看起来像一个绝对路径名称,将使用一个 Unix 域套接字连接,否则将启动一个 TCP/IP 连接。 + 不过要注意,斜线是 URI 层次部分中的一个保留字符。 + 因此,要指定一个非标准的 Unix 域套接字目录,要么忽略 URI 中的主机部分并且指定该主机为一个命名参数,要么在 URI 的主机部分用百分号编码路径: + +postgresql:///dbname?host=/var/lib/postgresql +postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + + + + + + + 参数关键词 + + 当前识别的参数关键字如下: + + host + + 要连接的主机名。主机名如果主机名以斜杠开头,则指定的是 Unix 域通信,而非 TCP/IP 通信;此值是存放套接字文件的目录名。当未指定 host 或其值为空时,默认连接到 /tmp(或构建 PostgreSQL 时指定的套接字目录)中的 Unix 域套接字。Unix 域套接字在没有 Unix 域套接字的机器上,默认连接到 localhost + + + + + hostaddr + + 要连接的主机的数字 IP 地址。应采用标准的 IPv4 地址格式,例如 172.28.40.9。如果机器支持 IPv6,也可以使用 IPv6 地址。只要此参数指定了非空字符串,就始终使用 TCP/IP 通信。 + + 使用hostaddr代替host可以避免主机名查找,这对于有时间限制的应用程序可能很重要。但是,采用 GSSAPI 或 SSPI 认证方式,以及进行verify-fullSSL 证书验证时,都需要主机名。遵循以下规则: + + + 如果指定了host而没有指定hostaddr,则会发生主机名查找。 + + + + + 如果指定了hostaddr而没有指定host, + 则hostaddr的值给出服务器的网络地址。 + 如果认证方法需要主机名,则连接尝试将失败。 + + + + + 如果同时指定了hosthostaddr, + 则hostaddr的值给出服务器的网络地址。 + 除非认证方法需要,否则host的值将被忽略, + 在这种情况下,它将用作主机名。 + + + 注意,以下情况很可能导致认证失败:host不是位于网络地址hostaddr的服务器名称。另外,注意在~/.pgpass中是用host而不是hostaddr来标识连接的(参见)。 + + + 如果既没有主机名也没有主机地址,libpq 会使用本地 Unix 域套接字连接;在没有 Unix 域套接字的机器上,则会尝试连接到 localhost + + + + + port + + + + 连接到服务器主机的端口号,或者Unix域连接的套接字文件名扩展。 + 端口 + + + + + + dbname + + + + 数据库名称。默认为与用户名相同。在某些情况下,该值会被检查是否为扩展格式; + 有关更多详细信息,请参阅。 + + + + + + user + + + + PostgreSQL用户连接的用户名。 + 默认为运行应用程序的操作系统用户名相同。 + + + + + + password + + + + 如果服务器要求密码认证,则使用密码。 + + + + + + + connect_timeout + + + 连接时的最长等待时间,以秒为单位(写成十进制整数字符串)。 + 零或未指定表示无限等待。不建议使用小于2秒的超时时间。 + + + + + client_encoding + + + + 这将为此连接设置client_encoding配置参数。除了对应服务器选项接受的值外, + 您还可以使用auto来从客户端的当前区域设置(Unix系统上的LC_CTYPE环境变量)确定正确的编码。 + + + + + + options + + + + 指定连接开始时发送到服务器的命令行选项。例如,将其设置为-c geqo=off会把会话的geqo参数值设为off。 + 此字符串中的空格被视为分隔命令行参数,除非用反斜杠(\)转义;写\\表示字面上的反斜杠。 + 有关可用选项的详细讨论,请参阅。 + + + + + + application_name + + + + 指定配置参数的值。 + + + + + + fallback_application_name + + + + 指定配置参数的回退值。 + 如果没有通过连接参数或PGAPPNAME环境变量为application_name指定值, + 则将使用此值。在通用实用程序中指定回退名称很有用,该程序希望设置默认应用程序名称, + 但允许用户覆盖它。 + + + + + + keepalives + + + + 控制是否使用客户端TCP保持活动。默认值为1,表示开启,但如果不想要保持活动,可以将其更改为0,表示关闭。 + 对于通过Unix域套接字进行的连接,此参数将被忽略。 + + + + + + keepalives_idle + + + + 控制在多少秒的不活动后,TCP应向服务器发送保持活动消息。值为零使用系统默认值。 + 对通过Unix域套接字进行的连接或禁用保持活动的连接,此参数将被忽略。 + 仅在支持TCP_KEEPIDLE或等效套接字选项的系统以及Windows上支持; + 在其他系统上,它没有任何效果。 + + + + + + keepalives_interval + + + + 控制在服务器未确认的情况下重新传输TCP保持活动消息的秒数。值为零时使用系统默认值。 + 此参数在通过Unix域套接字进行连接或禁用保持活动时将被忽略。 + 仅在支持TCP_KEEPINTVL或等效套接字选项的系统和Windows上支持; + 在其他系统上,此参数无效。 + + + + + + keepalives_count + + + + 控制在客户端与服务器之间连接被视为断开之前可以丢失的TCP keepalive数量。 + 值为零时使用系统默认值。对通过Unix域套接字建立的连接或禁用keepalives的连接,此参数将被忽略。 + 仅在支持TCP_KEEPCNT或等效套接字选项的系统上受支持; + 在其他系统上,此参数无效。 + + + + + + tty + + 忽略此参数(以前用于指定服务器调试输出的发送位置)。 + + + + + sslmode + + + 这个选项确定是否以及以何种优先级与服务器协商安全的SSL TCP/IP连接。有六种模式: + + + + disable + + + 仅尝试非SSL连接 + + + + + + allow + + + 首先尝试非SSL连接;如果失败,则尝试SSL连接 + + + + + + prefer (默认) + + + 首先尝试SSL连接;如果失败,则尝试非SSL连接 + + + + + + require + + + 仅尝试SSL连接。如果存在根CA文件,则验证证书的方式与指定了verify-ca时相同 + + + + + + verify-ca + + + 仅尝试SSL连接,并验证服务器证书是否由受信任的证书颁发机构(CA)颁发 + + + + + + verify-full + + + 仅尝试SSL连接,验证服务器证书是否由受信任的CA颁发,并且请求的服务器主机名与证书中的匹配 + + + + + + 详细了解这些选项如何工作,请参阅。 + + + + sslmode被忽略用于Unix域套接字通信。 + 如果PostgreSQL没有SSL支持编译, + 使用选项requireverify-ca或 + verify-full会导致错误,而选项allowprefer + 将被接受,但libpq实际上不会尝试建立SSL + 连接。SSL使用libpq的SSL + + + + + + requiressl + + + + 此选项已被sslmode设置所取代。 + + + + 如果设置为1,则需要与服务器建立SSL连接(这相当于sslmode + require)。libpq将拒绝连接,如果服务器不接受 + SSL连接。如果设置为0(默认值), + libpq将与服务器协商连接类型(相当于sslmode + prefer)。此选项仅在PostgreSQL编译时启用SSL支持。 + + + + + + sslcompression + + 如果设为 1(默认值),则会压缩通过 SSL 连接发送的数据。如果设为 0,则禁用压缩(这要求 OpenSSL 1.0.0 或更高版本)。如果建立的是非 SSL 连接,或者所用 OpenSSL 版本不支持此功能,则忽略此参数。 + 压缩会消耗 CPU 时间,但在网络成为瓶颈时能够提高吞吐量。如果 CPU 性能是限制因素,禁用压缩能够改善响应时间和吞吐量。 + + + + + sslcert + + + + 这个参数指定客户端SSL证书的文件名,替换默认的 + ~/.postgresql/postgresql.crt。 + 如果没有建立SSL连接,则此参数将被忽略。 + + + + + + sslkey + + + + 这个参数指定了用于客户端证书的密钥的位置。它可以指定一个文件名,该文件名将被用来替代默认的 + ~/.postgresql/postgresql.key,或者它可以指定一个从外部引擎 + (引擎是OpenSSL可加载模块)获取的密钥。外部引擎规范应该包括一个由冒号分隔的引擎名称和 + 一个引擎特定的密钥标识符。如果没有进行SSL连接,则此参数将被忽略。 + + + + + + sslrootcert + + + + 这个参数指定一个包含SSL证书颁发机构(CA)证书的文件名。 + 如果文件存在,服务器的证书将被验证是否由这些机构之一签名。 + 默认值是~/.postgresql/root.crt。 + + + + + + sslcrl + + 此参数指定 SSL 证书吊销列表(CRL)的文件名。如果该文件存在,在验证服务器证书时,会拒绝其中列出的证书。默认值为 ~/.postgresql/root.crl + + + + + requirepeer + + + + 这个参数指定了服务器的操作系统用户名,例如requirepeer=postgres。 + 在建立Unix域套接字连接时,如果设置了这个参数,客户端会在连接开始时检查服务器进程是否在指定的用户下运行; + 如果不是,则连接会因错误而中止。 + 这个参数可用于提供类似于在TCP/IP连接上使用SSL证书的服务器认证。 + (请注意,如果Unix域套接字位于/tmp或其他公共可写位置, + 任何用户都可以在那里启动一个服务器监听。使用这个参数来确保您连接到由受信任用户运行的服务器。) + 此选项仅在实现了peer认证方法的平台上受支持;请参见。 + + + + + + krbsrvname + + + 用于使用GSSAPI进行认证时要使用的Kerberos服务名称。 + 这必须与服务器配置中指定的Kerberos认证服务名称匹配,才能成功进行认证。 + (另请参见。) + + + + + + gsslib + + + + 用于GSSAPI认证的GSS库。 + 目前,除了包含GSSAPI和SSPI支持的Windows构建之外,这将被忽略。 + 在这种情况下,将其设置为gssapi,以使libpq使用GSSAPI库进行认证,而不是默认的SSPI。 + + + + + + service + + + + 用于额外参数的服务名称。它指定了pg_service.conf中保存额外连接参数的服务名称。 + 这允许应用程序只指定一个服务名称,以便可以集中维护连接参数。参见。 + + + + + + + + + + + 连接状态函数 + + + 这些函数可以被用来询问一个已有数据库连接对象的状态。 + + + + + + libpq-fe.h + libpq-int.h + libpq应用程序员应该小心地维护PGconn抽象。使用下面描述的访问函数来理解PGconn的内容。我们不推荐使用libpq-int.h引用内部的PGconn域,因为它们可能在未来改变。 + + + + 以下函数返回建立连接时确定的参数值。这些值在PGconn对象的生命周期内保持不变。 + + + + PQdb PQdb + + + + 返回该连接的数据库名。 + +char *PQdb(const PGconn *conn); + + + + + + + PQuser PQuser + + + + 返回该连接的用户名。 + +char *PQuser(const PGconn *conn); + + + + + + + PQpass PQpass + + + + 返回该连接的密码。 + +char *PQpass(const PGconn *conn); + + + + + + + PQhost PQhost + + + + 返回连接的服务器主机名。可能是主机名、IP 地址或者一个目录路径(如果通过 Unix 套接字连接,路径的情况很容易区分,因为路径总是一个绝对路径,以/开始)。 + +char *PQhost(const PGconn *conn); + + + + + + + PQport PQport + + + + 返回连接的端口。 + + +char *PQport(const PGconn *conn); + + + + + + + PQtty PQtty + + + 返回连接的调试TTY。(此设置已过时,因为服务器不再理会TTY设置,但为保持向后兼容,仍保留了此函数。) +char *PQtty(const PGconn *conn); + + + + + + + PQoptions PQoptions + + + + 返回被传递给连接请求的命令行选项。 + +char *PQoptions(const PGconn *conn); + + + + + + + + 以下函数返回的状态数据可能在执行操作时发生变化,这些操作针对PGconn对象。 + + PQstatus PQstatus + + + + 返回该连接的状态。 + +ConnStatusType PQstatus(const PGconn *conn); + + + + + 该状态可以是一系列值之一。不过,其中只有两个在一个异步连接过程之外可见:CONNECTION_OKCONNECTION_BAD。 + 一个到数据库的完好连接的状态为CONNECTION_OK。一个失败的连接尝试则由状态CONNECTION_BAD表示。 + 通常,一个 OK 状态将一直保持到PQfinish,但是一次通信失败可能导致该状态过早地改变为CONNECTION_BAD。 + 在那种情况下,该应用可以通过调用PQreset尝试恢复。 + + + + 关于其他可能会被返回的状态代码,请见PQconnectStartParamsPQconnectStartPQconnectPoll的条目。 + + + + + + PQtransactionStatus PQtransactionStatus + + + + 返回服务器的当前事务内状态。 + + +PGTransactionStatusType PQtransactionStatus(const PGconn *conn); + + + 该状态可能是PQTRANS_IDLE(当前空闲)、PQTRANS_ACTIVE(一个命令运行中)、PQTRANS_INTRANS(空闲,处于一个合法的事务块中)或者PQTRANS_INERROR(空闲,处于一个失败的事务块中)。如果该连接损坏,将会报告PQTRANS_UNKNOWN。只有当一个查询已经被发送给服务器并且还没有完成时,才会报告PQTRANS_ACTIVE。 + + + + + + PQparameterStatus PQparameterStatus + + + + 查找服务器某个参数的当前设置。 + + +const char *PQparameterStatus(const PGconn *conn, const char *paramName); + + + 服务器会在连接启动时,以及某些参数值发生变化时,自动报告这些参数值。PQparameterStatus可用于查询这些设置。如果已知该参数,则返回其当前值;如果未知,则返回NULL。 + + + + 当前版本报告的参数包括: + server_versionserver_encodingclient_encodingapplication_nameis_superusersession_authorizationDateStyleIntervalStyleTimeZoneinteger_datetimesstandard_conforming_strings。 + (8.0 之前的版本不报告 server_encodingTimeZoneinteger_datetimes;8.1 之前的版本不报告 standard_conforming_strings;8.4 之前的版本不报告 IntervalStyle;9.0 之前的版本不报告 application_name。) + 注意,server_versionserver_encodinginteger_datetimes 在启动后不能改变。 + + + 使用 3.0 之前协议的服务器不报告参数设置,但 libpq 仍包含获取 server_versionclient_encoding 值的逻辑。建议应用程序使用 PQparameterStatus,而不是专门编写代码来确定这些值。(但要注意,在使用 3.0 之前协议的连接上,连接启动后通过 SET 改变 client_encoding,不会反映在 PQparameterStatus 的结果中。)对于 server_version,另请参见 PQserverVersion,它以数值形式返回此信息,更易于比较。 + + + 如果服务器未报告standard_conforming_strings的值,应用程序可以假定其为off,即反斜杠在字符串字面量中被视为转义字符。此外,服务器报告此参数也表明它接受转义字符串语法(E'...')。 + + + + 返回的指针虽然被声明为const,但实际上指向与PGconn结构体关联的可变存储。不能假定该指针在执行其他查询后仍然有效。 + + + + + + PQprotocolVersion PQprotocolVersion + + + 查询正在使用的前端/后端协议。 +int PQprotocolVersion(const PGconn *conn); +应用程序可以使用此函数判断是否支持某些特性。目前可能的值为 2(协议 2.0)、3(协议 3.0)或零(连接无效)。连接启动完成后,协议版本不会改变,但理论上可能在重置连接时改变。与PostgreSQL7.4 或更新版本的服务器通信时,通常使用协议 3.0;7.4 之前的服务器仅支持协议 2.0。(协议 1.0 已过时,且不被以下库支持:libpq。) + + + + + + PQserverVersion PQserverVersion + + + + 返回一个表示后端版本的整数。 + +int PQserverVersion(const PGconn *conn); + + 应用可能会使用这个函数来判断它们连接到的数据库服务器的版本。这个数字是这样形成的:将主版本、次版本和修订版本号分别转换成两位十进制数,然后把它们拼接在一起。例如,版本8.1.5将被返回为80105,而版本8.2将被返回为80200(不显示前导零)。如果连接无效则返回零。 + + + + + + PQerrorMessage PQerrorMessage + + + + 错误消息返回连接上的一个操作最近产生的错误消息。 + + +char *PQerrorMessage(const PGconn *conn); + + + + + + 几乎所有的libpq函数在失败时都会为PQerrorMessage设置一个消息。 + 注意按照libpq习惯,一个非空PQerrorMessage结果可能由多行构成,并且将包括一个尾部新行。 + 调用者不应该直接释放结果。当相关的PGconn句柄被传递给PQfinish时,它将被释放。在PGconn结构体上的多个操作之间,不能指望结果字符串会保持不变。 + + + + + + PQsocketPQsocket + + + 获得到服务器连接套接字的文件描述符号。一个合法的描述符将会大于等于零。结果为 -1 表示当前没有打开服务器连接(在普通操作期间这将不会改变,但是在连接设置或重置期间可能改变)。 + + +int PQsocket(const PGconn *conn); + + + + + + + + PQbackendPIDPQbackendPID + + + 返回处理这个连接的后端进程的进程ID(PID)。 + PID + 确定服务器进程的 PID + in libpq + + + +int PQbackendPID(const PGconn *conn); + + + + + 后端PID有助于调试目的并且可用于与NOTIFY消息(它包括发出提示的后端进程的PID)进行比较。注意PID属于一个在数据库服务器主机上执行的进程,而不是本地主机进程! + + + + + + PQconnectionNeedsPasswordPQconnectionNeedsPassword + + + 如果连接认证方法要求一个密码但没有可用的密码,返回真(1)。否则返回假(0)。 + + +int PQconnectionNeedsPassword(const PGconn *conn); + + + + + 这个函数可以在连接尝试失败后被应用于决定是否向用户提示要求一个密码。 + + + + + + PQconnectionUsedPasswordPQconnectionUsedPassword + + + 如果连接认证方法使用一个密码,返回真(1)。否则返回假(0)。 + + +int PQconnectionUsedPassword(const PGconn *conn); + + + + + 这个函数能在一次连接尝试失败或成功后用于检测该服务器是否要求一个密码。 + + + + + + + 以下函数返回与 SSL 相关的信息。这些信息通常在连接建立后不会改变。 + + PQsslInUsePQsslInUse + + + + 返回true(1)如果连接使用SSL,返回false(0)如果不使用。 + + +int PQsslInUse(const PGconn *conn); + + + + + + + + PQsslAttributePQsslAttribute + + 返回连接的 SSL 相关信息。 +const char *PQsslAttribute(const PGconn *conn, const char *attribute_name); + + + + + 可用属性列表因使用的SSL库和连接类型而异。如果连接不使用SSL或指定的属性名称对于所使用的库未定义,则返回NULL。 + + + 通常可以取得以下属性: + + library + + + 使用的SSL实现的名称。(目前只实现了"OpenSSL") + + + + + protocol + + + 使用的SSL/TLS版本。常见值为"SSLv2""SSLv3""TLSv1""TLSv1.1" + 和"TLSv1.2",但如果使用其他协议,则实现可能返回其他字符串。 + + + + + key_bits + + + 加密算法使用的密钥位数。 + + + + + cipher + + + 使用的密码套件的简称,例如"DHE-RSA-DES-CBC3-SHA"。这些名称特定于每个SSL实现。 + + + + + compression + + + 如果使用了SSL压缩,则返回所使用压缩算法的名称;如果使用了压缩但算法未知,则返回"on"。如果未使用压缩,则返回"off"。 + + + + + + + + + + PQsslAttributeNamesPQsslAttributeNames + + + + 返回可用的SSL属性名称数组。 + 数组以NULL指针结尾。 + +const char * const * PQsslAttributeNames(const PGconn *conn); + + + + + + + PQsslStructPQsslStruct + + + 返回一个指向描述连接的SSL实现特定对象的指针。如果连接未加密或SSL实现不提供连接的请求对象类型,则返回NULL。 + +void *PQsslStruct(const PGconn *conn, const char *struct_name); + + + 可用的结构体取决于所使用的 SSL 实现。对于 OpenSSL,有一个名为 "OpenSSL" 的结构体,取得它时会返回指向 OpenSSLSSL结构体的指针。可以使用类似以下的代码来调用此函数: +#include + +... + + SSL *ssl; + + dbconn = PQconnectdb(...); + ... + + ssl = PQsslStruct(dbconn, "OpenSSL"); + if (ssl) + { + /* 使用OpenSSL函数访问ssl */ + } +]]> + + + 这个结构体可用于验证加密级别,检查服务器证书等。请参考OpenSSL + 文档以获取有关此结构体的信息。 + + + + + + PQgetsslPQgetssl + + + SSL在libpq中 + 返回在连接中使用的SSL结构体,如果未使用SSL,则返回NULL。 + + +void *PQgetssl(const PGconn *conn); + + + + 这个函数等同于PQsslStruct(conn, "OpenSSL")。不应该在新应用程序中使用, 因为返回的结构体特定于OpenSSL,如果使用另一个SSL实现, 则不可用。要检查连接是否使用SSL,请调用PQsslInUse, 要获取有关连接的更多详细信息,请使用PQsslAttribute + + + + + + + + + + 命令执行函数 + + + 一旦到一个数据库服务器的连接被成功建立,这里描述的函数可以被用来执行 SQL 查询和命令。 + + + + 主要函数 + + + + + PQexec PQexec + + + + 提交一个命令给服务器并且等待结果。 + + +PGresult *PQexec(PGconn *conn, const char *command); + + + + + 返回一个PGresult指针或者可能是一个空指针。 + 除了内存不足的情况或者由于严重错误无法将命令发送给服务器之外,一般都会返回一个非空指针。 + PQresultStatus函数应当被调用来检查返回值是否代表错误(包括空指针的值,它会返回PGRES_FATAL_ERROR)。 + 用PQerrorMessage可得到关于那些错误的详细信息。 + + + + 命令字符串可以包含多个 SQL 命令(以分号分隔)。在一次PQexec调用中发送的多个查询会在同一个事务中处理,除非查询字符串中显式包含BEGIN/COMMIT命令将其划分为多个事务。不过要注意,返回的PGresult结构体只描述该字符串中最后执行的命令的结果。如果其中一条命令失败,就会在此处停止处理该字符串,返回的PGresult则描述该错误。 + + + + + PQexecParams PQexecParams + + + + 提交一个命令给服务器并且等待结果,它可以在 SQL 命令文本之外独立地传递参数。 + + +PGresult *PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + + + + PQexecParamsPQexec相似,但是提供了额外的功能:参数值可以与命令字符串分开指定,并且可以以文本或二进制格式请求查询结果。 PQexecParams 仅支持使用协议 3.0 及更高版本的连接;使用协议 2.0 时会失败。 + + + 该函数的参数是: + + + + conn + + + + 要在其中发送命令的连接对象。 + + + + + + command + + + 要执行的 SQL 命令字符串。如果使用了参数,它们在该命令字符串中被引用为$1$2等。 + + + + + + nParams + + + 提供的参数数量。它是数组paramTypes[]paramValues[]paramLengths[]paramFormats[]的长度(当nParams为零时,数组指针可以是NULL)。 + + + + + + paramTypes[] + + + 通过 OID 指定要赋予给参数符号的数据类型。如果paramTypesNULL或者该数组中任何特定元素为零,服务器会用对待未指定类型的字符串字面量的方式为参数符号推测一种数据类型。 + + + + + + paramValues[] + + + 指定参数的实际值。这个数组中的一个空指针表示对应的参数为空,否则该指针指向一个以零终止的文本字符串(用于文本格式)或者以服务器所期待格式的二进制数据(用于二进制格式)。 + + + + + + paramLengths[] + + + 指定二进制格式参数的实际数据长度。它对空参数和文本格式参数被忽略。当没有二进制参数时,该数组指针可以为空。 + + + + + + paramFormats[] + + + 指定参数是否为文本(在参数相应的数组项中放一个零)或二进制(在参数相应的数组项中放一个一)。如果该数组指针为空,那么所有参数都会被假定为文本串。 + + + 以二进制格式传递的值要求后端所期待的内部表示形式的知识。例如,整数必须以网络字节序被传递。传递numeric值要求关于服务器存储格式的知识,正如src/backend/utils/adt/numeric.c::numeric_send()以及src/backend/utils/adt/numeric.c::numeric_recv()中所实现的。 + + + + + + resultFormat + + + 指定零来得到文本格式的结果,或者指定一来得到二进制格式的结果(目前没有规定要求以不同格式得到不同的结果列,尽管在底层协议中这是可以实现的)。 + + + + + + + + + + + PQexecParams 相对于 PQexec 的主要优点是可以将参数值与命令字符串分开,从而避免繁琐且容易出错的加引号和转义工作。 + + + 和PQexec不同,PQexecParams至多允许在给定串中出现一个 SQL 命令(其中可以有分号,但是不能有超过一个非空命令)。这是底层协议的一个限制,但是有助于抵抗 SQL 注入攻击。 + + + + + + 通过 OID 指定参数类型很罗嗦,特别是如果你不愿意将特定的 OID 值硬编码到你的程序中时。不过,即使服务器本身也无法确定参数的类型,你可以避免这样做,或者选择一种与你想要的不同的类型。在 SQL 命令文本中,附加一个显式类型转换给参数符号来表示你将发送什么样的数据类型。例如: + +SELECT * FROM mytable WHERE x = $1::bigint; + + 这强制参数$1被当作bigint,而默认情况下它将被赋予与x相同的类型。当以二进制格式发送参数值时,我们强烈推荐以这种方式或通过指定一个数字类型的 OID 来强制参数类型决定。因为二进制格式比文本格式具有更少的冗余,并且因此服务器将不会有更多机会为你检测一个类型匹配错误。 + + + + + + + PQprepare PQprepare + + + + 提交一个请求用给定参数创建一个预备语句并且等待完成。 + +PGresult *PQprepare(PGconn *conn, + const char *stmtName, + const char *query, + int nParams, + const Oid *paramTypes); + + + + PQprepare 创建一个预备语句,供随后使用 PQexecPrepared 执行。此功能允许重复执行命令,而不必每次都进行解析和规划;详见 PQprepare 仅在使用协议 3.0 及更高版本的连接中受支持,使用协议 2.0 时会失败。 + + + 该函数从query串创建一个名为stmtName的预备语句,该串必须包含一个单一 SQL 命令。 + stmtName可以是""来创建一个未命名语句,在这种情况下任何已存在未命名语句将被自动替换。 + 否则,如果语句名称已经在当前会话中被定义,则是一种错误。如果使用了任何参数,它们在查询中以$1$2等引用。 + nParams是参数的个数,其类型在数组paramTypes[]中被预先指定(当nParams为零时,该数组指针可以是NULL)。 + paramTypes[]通过 OID 指定要赋予给参数符号的数据类型。 + 如果paramTypesNULL或者该数组中任何特定元素为零,服务器会用对待未指定类型的字符串字面量的方式为参数符号推测一种数据类型。 + 还有,查询能够使用编号高于nParams的参数符号,它们的数据类型也会被自动推测(找出推测出的数据类型的方法见PQdescribePrepared)。 + + + + 正如PQexec一样,结果通常是一个PGresult对象,其内容代表服务器端成功或失败。 + 一个空结果表示内存不足或者根本无法发送命令。关于错误的更多信息请见PQerrorMessage。 + + + + 用于PQexecPrepared的预备语句也可以通过执行 SQL语句来创建。此外,虽然没有libpq函数可删除预备语句,但可以使用 SQL语句来完成。 + + + + + PQexecPrepared PQexecPrepared + + + + 发送一个请求来执行一个带有给定参数的预备语句,并等待结果。 + +PGresult *PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + + + + PQexecPrepared类似于PQexecParams, 但要执行的命令是通过指定先前准备好的语句来指定,而不是提供查询字符串。 此功能允许重复使用的命令只被解析和计划一次,而不是每次执行时都要进行。 该语句必须在当前会话中先前准备好。 PQexecPrepared 仅支持使用协议 3.0 及更高版本的连接;使用协议 2.0 时会失败。 + + + 参数与PQexecParams相同,只是给出了预备语句的名称而不是查询字符串, + 并且paramTypes[]参数不存在(因为在创建预备语句时已确定了参数类型)。 + + + + + + PQdescribePrepared PQdescribePrepared + + + + 提交请求以获取有关指定准备好的语句的信息,并等待完成。 + +PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName); + + + + PQdescribePrepared允许应用程序获取关于先前准备的语句的信息。 PQdescribePrepared 仅支持使用协议 3.0 及更高版本的连接;使用协议 2.0 时会失败。 + + + stmtName可以是""NULL来引用 + 未命名的语句,否则必须是现有准备好的语句的名称。成功时,返回一个 + 状态为PGRES_COMMAND_OKPGresult。 + 函数PQnparams和 + PQparamtype可以应用于此 + PGresult以获取有关准备语句参数的信息, + 函数PQnfieldsPQfname、 + PQftype等提供有关语句的结果列(如果有)的信息。 + + + + + + PQdescribePortal PQdescribePortal + + + + 提交请求以获取有关指定门户的信息,并等待完成。 + +PGresult *PQdescribePortal(PGconn *conn, const char *portalName); + + + + PQdescribePortal允许应用程序获取有关先前创建的 portal 的信息。 (libpq不直接提供对 portal 的访问,但你可以使用此函数检查通过DECLARE CURSOR SQL 命令创建的游标的属性。) PQdescribePortal 仅支持使用协议 3.0 及更高版本的连接;使用协议 2.0 时会失败。 + + + portalName可以是""NULL来引用未命名的 portal, + 否则必须是现有 portal 的名称。成功时,将返回一个带有状态PGRES_COMMAND_OKPGresult。 + 函数PQnfieldsPQfnamePQftype等可应用于 + PGresult,以获取有关 portal 的结果列(如果有)的信息。 + + + + + + + PGresultPGresult结构体封装服务器返回的结果。libpq应用程序的开发者应注意保持PGresult的抽象性。请使用下面的访问函数获取PGresult的内容。避免直接引用PGresult结构体的字段,因为这些字段以后可能改变。 + + PQresultStatus PQresultStatus + + + + 返回该命令的结果状态。 + +ExecStatusType PQresultStatus(const PGresult *res); + + + + + PQresultStatus可以返回以下值之一: + + PGRES_EMPTY_QUERY + + + 发送给服务器的字符串为空。 + + + + + + PGRES_COMMAND_OK + + + 一个不返回数据的命令成功完成。 + + + + + + PGRES_TUPLES_OK + + + 一个返回数据的命令(例如SELECT或者SHOW)成功完成。 + + + + + + PGRES_COPY_OUT + + + 从服务器复制出数据的传输开始。 + + + + + + PGRES_COPY_IN + + + 复制数据到服务器的传输开始。 + + + + + + PGRES_BAD_RESPONSE + + + 无法理解服务器的响应。 + + + + + + PGRES_NONFATAL_ERROR + + + 发生了一次非致命错误(一个提示或警告)。 + + + + + + PGRES_FATAL_ERROR + + + 发生了一次致命错误。 + + + + + + PGRES_COPY_BOTH + + + 向服务器复制数据/从服务器复制数据的传输开始。这个特性当前只被用于流复制,因此这个状态应该不会在普通应用中出现。 + + + + + + PGRES_SINGLE_TUPLE + + + PGresult包含来自于当前命令的一个单一结果元组。这个状态只在查询选择了单一行模式时发生(见)。 + + + + 如果结果状态为PGRES_TUPLES_OKPGRES_SINGLE_TUPLE,可以使用下述函数取得查询返回的行。注意,即使SELECT命令恰好返回零行,状态仍为PGRES_TUPLES_OK。 + PGRES_COMMAND_OK用于不可能返回行的命令(例如INSERTUPDATE不带RETURNING子句的命令等)。如果响应为PGRES_EMPTY_QUERY,可能表示客户端软件中存在缺陷。 + + + 一个状态为PGRES_NONFATAL_ERROR的结果将不会被PQexec或者其他查询执行函数直接返回,这类结果将被传递给提示处理器(见 )。 + + + + + + PQresStatus PQresStatus + + + PQresultStatus返回的枚举值转换为描述该状态码的字符串常量。调用者不应释放此结果。 +char *PQresStatus(ExecStatusType status); + + + + + + + PQresultErrorMessage PQresultErrorMessage + + + 返回与命令关联的错误消息;如果没有错误,则返回空字符串。 +char *PQresultErrorMessage(const PGresult *res); +如果发生了错误,返回的字符串会包含末尾换行符。调用者不应直接释放结果。在将关联的PGresult句柄传给以下函数时,会释放该结果:PQclear。 + + + + 紧跟着一个PQexecPQgetResult调用,PQerrorMessage(在连接上)将返回与PQresultErrorMessage相同的字符串(在结果上)。 + 不过,一个PGresult将保持它的错误消息直到被销毁,而连接的错误消息将在后续操作被执行时被更改。 + 当你想要知道与一个特定PGresult相关的状态,使用PQresultErrorMessage。 + 而当你想要知道连接上最后一个操作的状态,使用PQerrorMessage。 + + + + + + PQresultVerboseErrorMessage PQresultVerboseErrorMessage + + + 返回与PGresult对象关联的错误消息的重新格式化版本。 +char *PQresultVerboseErrorMessage(const PGresult *res, + PGVerbosity verbosity, + PGContextVisibility show_context); +某些情况下,客户端可能希望取得之前报告的错误的更详细版本。PQresultVerboseErrorMessage可以满足这一需求:它计算以下函数本应生成的消息:PQresultErrorMessage,假设在生成给定的PGresult时,连接已经采用指定的详细程度设置。如果PGresult不是错误结果,则改为报告PGresult is not an error result。返回的字符串包含末尾换行符。 + + + 和大部分从PGresult中提取数据的其他函数不同,这个函数的结果是一个全新分配的字符串。调用者在不需要这个字符串以后,必须使用PQfreemem()释放它。 + + + + 如果内存不足,可能会返回 NULL。 + + + + + + PQresultErrorFieldPQresultErrorField + + 返回错误报告中的单个字段。 +char *PQresultErrorField(const PGresult *res, int fieldcode); + + fieldcode是错误字段标识符,参见下文列出的符号。NULL会在以下情况下返回:PGresult不是错误或警告结果,或者不包含指定字段。字段值通常不含末尾换行符。调用者不应直接释放结果。在将关联的PGresult句柄传给以下函数时,会释放该结果:PQclear。 + + + 可以使用以下字段代码: + + PG_DIAG_SEVERITY + + + 严重性。域的内容是ERRORFATALPANIC(在一个错误消息中)。或者是WARNINGNOTICEDEBUGINFOLOG(在一个提示消息中)。或者是其中之一的一个本地化翻译。总是存在。 + + + + + + PG_DIAG_SEVERITY_NONLOCALIZED + + + 域的内容是ERRORFATALPANIC(在一个错误消息中)。或者是WARNINGNOTICEDEBUGINFOLOG(在一个提示消息中)。这和PG_DIAG_SEVERITY域相同,不过内容不会被本地化。只存在于PostgreSQL 9.6 版本以后产生的报告中。 + + + + + + PG_DIAG_SQLSTATEerror codeslibpq + + + 用于错误的 SQLSTATE 代码。SQLSTATE 代码标识了已经发生的错误的类型,它可以被前端应用用来执行特定操作(例如错误处理)来响应一个特定数据库错误。一个可能的 SQLSTATE 代码列表可见。这个域无法被本地化,并且总是存在。 + + + + + + PG_DIAG_MESSAGE_PRIMARY + + + 主要的人类可读的错误消息(通常是一行)。总是存在。 + + + + + + PG_DIAG_MESSAGE_DETAIL + + + 细节:一个可选的次级错误消息,它携带了关于问题的更多细节。可能有多行。 + + + + + + PG_DIAG_MESSAGE_HINT + + + 提示:一个关于如何处理该问题的可选建议。它与细节的区别在于它提供了建议(可能不合适)而不是铁的事实。可能有多行。 + + + + + + PG_DIAG_STATEMENT_POSITION + + + 包含一个十进制整数的字符串,它表示一个错误游标位置,该位置是原始语句字符串的索引。第一个字符的索引是 1,位置以字符计算而不是以字节计算。 + + + + + + PG_DIAG_INTERNAL_POSITION + + + 这被定义为与PG_DIAG_STATEMENT_POSITION域相同,但是它被用在游标位置引用一个内部产生的命令而不是客户端提交的命令时。当这个域出现时,PG_DIAG_INTERNAL_QUERY域将总是出现。 + + + + + + PG_DIAG_INTERNAL_QUERY + + + 一个失败的内部产生的命令的文本。例如,这可能是由一个 PL/pgSQL 函数发出的 SQL 查询。 + + + + + + PG_DIAG_CONTEXT + + + 指示错误发生的环境。当前这包括活动过程语言函数的调用栈追踪以及内部生成的查询。追踪是每行一项,最近的排在最前面。 + + + + + + PG_DIAG_SCHEMA_NAME + + + 如果错误与某个特定的数据库对象相关,这里是包含该对象的模式名(如果有)。 + + + + + + PG_DIAG_TABLE_NAME + + + 如果错误与某个特定表相关,这里是该表的名字(该表的模式参考模式名域)。 + + + + + + PG_DIAG_COLUMN_NAME + + + 如果错误与一个特定表列相关,这里是该表列的名字(参考模式和表名域来标识该表)。 + + + + + + PG_DIAG_DATATYPE_NAME + + + 如果错误与一个特定数据类型相关,这里是该数据了行的名字(该数据类型的模式名参考模式名域)。 + + + + + + PG_DIAG_CONSTRAINT_NAME + + + 如果错误与一个特定约束相关,这里是该约束的名字。相关的表或域参考上面列出的域(为了这个目的,索引也被视作约束,即使它们不是用约束语法创建的)。 + + + + + + PG_DIAG_SOURCE_FILE + + + 报告错误的源代码所在的文件名。 + + + + + + PG_DIAG_SOURCE_LINE + + + 报告错误的源代码行号。 + + + + + + PG_DIAG_SOURCE_FUNCTION + + + 报告错误的源代码函数的名字。 + + + + + + + + + 用于模式名、表名、列名、数据类型名和约束名的域只提供给有限的错误类型,见。不要假定任何这些域的存在保证另一个域的存在。核心错误源会遵守上面提到的内在联系,但是用户定义的函数可能以其他方式使用这些域。同样地,不要假定这些域代表当前数据库中同类的对象。 + + + + + 客户端负责格式化显示信息来迎合它的需要,特别是根据需要打断长的行。出现在错误消息域中的新行字符应该被当作分段而不是换行。 + + + libpq 内部产生的错误包含严重性和主要消息,但通常没有其他字段。使用 3.0 之前协议的服务器返回的错误包含严重性和主要消息,有时还包含详细消息,但没有其他字段。 + + + 注意,错误字段只对PGresult对象有效,对PGconn对象无效。没有PQerrorField函数。 + + + + + + PQclearPQclear + + 释放与PGresult关联的存储空间。每个命令结果都应通过PQclear在不再需要结果时将其释放。 +void PQclear(PGresult *res); + + + + + 你可以在需要时一直保留PGresult对象;它不会在你发出新命令时消失,甚至在关闭连接后也不会消失。要销毁它,你必须调用PQclear。否则应用程序会发生内存泄漏。 + + + + + + + + + 检索查询结果信息 + + + 这些函数被用来从一个代表成功查询结果(也就是状态为PGRES_TUPLES_OK或者PGRES_SINGLE_TUPLE)的PGresult对象中抽取信息。它们也可以被用来从一个成功的 Describe 操作中抽取信息:一个 Describe 的结果具有和该查询被实际执行所提供的完全相同的列信息,但是它没有行。对于其他状态值的对象,这些函数会认为结果具有零行和零列。 + + + + + PQntuples PQntuples + + + + + 返回查询结果中的行(元组)数(注意,PGresult对象被限制为不超过INT_MAX行,因此一个int结果就足够了)。 + + +int PQntuples(const PGresult *res); + + + + + + + + PQnfields PQnfields + + + + + 返回查询结果中每一行的列(域)数。 + + +int PQnfields(const PGresult *res); + + + + + + + PQfname PQfname + + + 返回给定列号对应的列名。列号从 0 开始。调用者不应直接释放结果。在将关联的PGresult句柄传给以下函数时,会释放该结果:PQclear。 + +char *PQfname(const PGresult *res, + int column_number); + + + + + 如果列号超出范围,将返回NULL。 + + + + + + PQfnumber PQfnumber + + + + + 返回与给定列名相关联的列号。 + +int PQfnumber(const PGresult *res, + const char *column_name); + + + + + 如果给定的名字不匹配任何列,将返回 -1。 + + + + 给定的名称被视作一个 SQL 命令中的一个标识符,也就是说,除非被双引号引用,它是小写形式的。例如,给定一个 SQL 命令: + +SELECT 1 AS FOO, 2 AS "BAR"; + + 我们将得到结果: + +PQfname(res, 0) foo +PQfname(res, 1) BAR +PQfnumber(res, "FOO") 0 +PQfnumber(res, "foo") 0 +PQfnumber(res, "BAR") -1 +PQfnumber(res, "\"BAR\"") 1 + + + + + + + PQftable PQftable + + + + 返回给定列从中取出的表的 OID。列号从 0 开始。 + +Oid PQftable(const PGresult *res, + int column_number); + + + + 如果列号超出范围、指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回 InvalidOid。可以查询系统表 pg_class,确定所引用的具体表。 + + 包含 libpq 头文件后,将定义类型 Oid 和常量 InvalidOid。它们都属于某种整数类型。 + + + + + PQftablecol PQftablecol + + + + 返回构成指定查询结果列的列(在其表中)的列号。查询结果列号从 0 开始,但是表列具有非零编号。 + +int PQftablecol(const PGresult *res, + int column_number); + + + + 如果列号超出范围、指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回零。 + + + + + PQfformat PQfformat + + + + + 返回指示给定列格式的格式编码。列号从 0 开始。 + +int PQfformat(const PGresult *res, + int column_number); + + + + + 格式代码零指示文本数据表示,而格式代码一表示二进制表示(其他代码被保留用于未来的定义)。 + + + + + + PQftype PQftype + + + + 返回与给定列号相关联的数据类型。被返回的整数是该类型的内部 OID 号。列号从 0 开始。 + +Oid PQftype(const PGresult *res, + int column_number); + + + + 可以查询系统表 pg_type 来获取各种数据类型的名称和属性。内置数据类型的 OID 定义在安装目录中的 include/server/catalog/pg_type.h 文件中。 + + + + + PQfmod PQfmod + + + + + 返回与给定列号相关联的列的修饰符类型。列号从 0 开始。 + +int PQfmod(const PGresult *res, + int column_number); + + + + + 修饰符值的解释是与类型相关的,它们通常指示精度或尺寸限制。值 -1 被用来指示没有信息可用。大部分的数据类型不适用修饰符,在那种情况中值总是 -1。 + + + + + + PQfsize PQfsize + + + + 返回与给定列号相关的列的尺寸(以字节计)。列号从 0 开始。 + +int PQfsize(const PGresult *res, + int column_number); + + + + + PQfsize返回在一个数据库行中为这个列分配的空间,换句话说是服务器对该数据类型的内部表示的尺寸(因此,它对客户端并不是真地非常有用)。一个负值指示该数据类型是变长的。 + + + + + + PQbinaryTuples PQbinaryTuples + + + + 如果PGresult包含二进制数据,返回 1。如果包含的是文本数据,返回 0。 + +int PQbinaryTuples(const PGresult *res); + + + + + 这个函数已经被废弃(除了与COPY一起使用),因为一个单一PGresult可以在某些列中包含文本数据而且在另一些列中包含二进制数据。 + PQfformat要更好。只有结果的所有列是二进制(格式 1)时PQbinaryTuples才返回 1。 + + + + + + PQgetvalue PQgetvalue + + + 返回以下结果中某一行的一个字段值:PGresult。行号和列号都从 0 开始。调用者不应直接释放结果。在将关联的PGresult句柄传给以下函数时,会释放该结果:PQclear。 + +char *PQgetvalue(const PGresult *res, + int row_number, + int column_number); + + + + 对于文本格式的数据,PQgetvalue 返回字段值的字符串表示,以零字节结尾。对于二进制格式的数据,返回值采用该数据类型的 typsendtypreceive 函数所决定的二进制表示。(这种情况下,值后面实际上也有一个零字节,但通常没有用处,因为值本身很可能包含零字节。) + + + 如果该域值为空,则返回一个空串。关于区分空值和空字符串值请见PQgetisnull。 + + + + PQgetvalue返回的指针指向作为PGresult结构体一部分的存储。我们不应该修改它指向的数据,并且如果要在超过PGresult结构体本身的生命期之外使用它,我们必须显式地把该数据拷贝到其他存储中。 + + + + + + PQgetisnullPQgetisnullnull valuein libpq + + + + 测试一个域是否为空值。行号和列号从 0 开始。 + +int PQgetisnull(const PGresult *res, + int row_number, + int column_number); + + + + + 如果该域是空,这个函数返回 1。如果它包含一个非空值,则返回 0(注意PQgetvalue将为一个空域返回一个空串,不是一个空指针)。 + + + + + + PQgetlength PQgetlength + + + + 返回一个域值的真实长度,以字节计。行号和列号从 0 开始。 + +int PQgetlength(const PGresult *res, + int row_number, + int column_number); + + + + + 这是特定数据值的真实数据长度,也就是PQgetvalue指向的对象的尺寸。 + 对于文本数据格式,这和strlen()相同。对于二进制格式这是基本信息。 + 注意我们应该依赖于PQfsize来得到实际的数据长度。 + + + + + + PQnparams PQnparams + + + + 返回一个预备语句的参数数量。 + +int PQnparams(const PGresult *res); + + + + + 只有在查看PQdescribePrepared的结果时,这个函数才有用。对于其他类型的查询,它将返回零。 + + + + + + PQparamtype PQparamtype + + + + 返回所指示的语句参数的数据类型。参数号从 0 开始。 + +Oid PQparamtype(const PGresult *res, int param_number); + + + + + 只有在查看PQdescribePrepared的结果时,这个函数才有用。对于其他类型的查询,它将返回零。 + + + + + + PQprint PQprint + + + + + 将所有的行打印到指定的输出流,以及有选择地将列名打印到指定的输出流。 + +void PQprint(FILE *fout, /* 输出流 */ + const PGresult *res, + const PQprintOpt *po); +typedef struct +{ + pqbool header; /* 打印输出域标题和行计数 */ + pqbool align; /* 填充对齐域 */ + pqbool standard; /* 旧的格式 */ + pqbool html3; /* 输出 HTML 表格 */ + pqbool expanded; /* 扩展表格 */ + pqbool pager; /* 如果必要为输出使用页 */ + char *fieldSep; /* 域分隔符 */ + char *tableOpt; /* 用于 HTML 表格元素的属性 */ + char *caption; /* HTML 表格标题 */ + char **fieldName; /* 替换域名称的空终止数组 */ +} PQprintOpt; + + + + + 这个函数以前被psql用来打印查询结果,但是现在不是这样了。注意它假定所有的数据都是文本格式。 + + + + + + + + 检索其他结果信息 + + + 这些函数被用来从PGresult对象中抽取其他信息。 + + + + + PQcmdStatus PQcmdStatus + + + + 返回来自于产生PGresult的 SQL 命令的命令状态标签。 + +char *PQcmdStatus(PGresult *res); + + + + + 通常这就是该命令的名称,但是它可能包括额外数据,例如已被处理的行数。调用者不应该直接释放该结果。它将在相关的PGresult句柄被传递给PQclear之后被释放。 + + + + + + PQcmdTuples PQcmdTuples + + + + 返回受该 SQL 命令影响的行数。 + +char *PQcmdTuples(PGresult *res); + + + + 此函数返回一个字符串,其中包含产生该 PGresultSQL 语句所影响的行数。此函数只能在执行 SELECTCREATE TABLE ASINSERTUPDATEDELETEMOVEFETCHCOPY 语句之后使用,也可以在对包含 INSERTUPDATEDELETE 语句的预备查询执行 EXECUTE 之后使用。如果产生 PGresult 的是其他命令,PQcmdTuples 将返回空字符串。调用者不应直接释放返回值;将关联的 PGresult 句柄传给 PQclear 时,它会被释放。 + + + + + PQoidValue PQoidValue + + + + + 如果该SQL命令是一个正好将一行插入到具有 OID 的表的INSERT,或者是一个包含合适INSERT语句的预备查询的EXECUTE,这个函数返回被插入行的 OIDOIDin libpq。否则,这个函数返回InvalidOid。如果被INSERT语句影响的表不包含 OID,这个函数也将返回InvalidOid。 + +Oid PQoidValue(const PGresult *res); + + + + + + + PQoidStatus PQoidStatus + + + 此函数已弃用,推荐使用PQoidValue,且此函数不是线程安全的。它返回包含插入行 OID 的字符串,而PQoidValue返回 OID 值。 +char *PQoidStatus(const PGresult *res); + + + + + + + + + + + 用于在 SQL 命令中嵌入字符串的转义 + + + 转义字符串 + in libpq + + + + + PQescapeLiteral PQescapeLiteral + + + + +char *PQescapeLiteral(PGconn *conn, const char *str, size_t length); + + + + + 为了让一个字符串可用于 SQL 命令,PQescapeLiteral会对它进行转义。 + 当在 SQL 命令中把数据值作为字符串字面量插入时,这个函数很有用。一些字符(例如引号和反斜线)必须经过转义,才不会被 SQL 解析器解释成特殊含义。 + PQescapeLiteral执行这种操作。 + + + + PQescapeLiteral返回一个str参数的已被转义版本,该版本被放在用malloc()分配的内存中。 + 当该结果不再被需要时,这个内存应该用PQfreemem()释放。 + 一个终止的零字节不是必须的,并且不应该被计入length(如果在length字节被处理之前找到一个终止字节,PQescapeLiteral会停止在零,该行为更像strncpy)。 + 返回字符串中的所有特殊字符都会被替换,这样它们就能被PostgreSQL字符串字面量解析器正确处理。 + 结果中也会附加一个终止零字节,并包含包围PostgreSQL字符串字面量所需的单引号。 + + + + 发生错误时,PQescapeLiteral返回NULL并且一个合适的消息会被存储在conn对象中。 + + + + + + 在处理来自不可信来源的字符串时,正确转义尤其重要。否则就会有安全风险:你很容易受到SQL 注入攻击,不期望的 SQL 命令可能会被送入数据库。 + + + + + 注意,当一个数据值被作为PQexecParams或其兄弟例程中的一个独立参数传递时,没有必要做转义而且做转义也不正确。 + + + + + + PQescapeIdentifier PQescapeIdentifier + + + + +char *PQescapeIdentifier(PGconn *conn, const char *str, size_t length); + + + + PQescapeIdentifier 对字符串进行转义,使其可用作 SQL 标识符,例如表名、列名或函数名。当用户提供的标识符可能包含未经转义就不会被 SQL 解析器视为标识符一部分的特殊字符,或包含需要保留大小写的大写字符时,此函数很有用。 + + + PQescapeIdentifier返回一个str参数的已被转义为 SQL 标识符的版本,该版本被放在用malloc()分配的内存中。 + 当该结果不再被需要时,这个内存必须用PQfreemem()释放。 + 一个终止的零字节不是必须的,并且不应该被计入length(如果在length字节被处理之前找到一个终止字节,PQescapeIdentifier会停止在零,该行为更像strncpy)。 + 返回串中的所有特殊字符都被替换掉,这样它们能被作为一个 SQL 标识符正确地处理。还会加上一个终止零字节。返回串也将被双引号包围。 + + + + 发生错误时,PQescapeIdentifier返回NULL并且一个合适的消息会被存储在conn对象中。 + + + + + + 与字符串一样,要阻止 SQL 注入攻击,当从一个不可信的来源接收到 SQL 标识符时,它们必须被转义。 + + + + + + + PQescapeStringConn PQescapeStringConn + + + + +size_t PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error); + + + + + PQescapeStringConn转义字符串,它很像PQescapeLiteral。 + 与PQescapeLiteral不一样的是,调用者负责提供一个合适尺寸的缓冲区。 + 此外,PQescapeStringConn不产生必须包围PostgreSQL字符串的单引号。 + 它们应该在结果要插入的 SQL 命令中提供。参数from指向要被转义的串的第一个字符,并且length参数给出了这个串中的字节数。 + 一个终止的零字节不是必须的,并且不应该被计入length(如果在length字节被处理之前找到一个终止字节,PQescapeStringConn会停止在零,该行为更像strncpy)。 + to应当指向一个缓冲区,它至少能容纳length值的两倍再加一个字节,否则该行为是未被定义的。 + 如果tofrom串重叠,行为也是未被定义的。 + + + + 如果error参数不是NULL,那么成功时*error被设置为零,错误时设置为非零。当前唯一可能的错误情况涉及源串中非法的多字节编码。错误时仍然会产生输出串,但是可以预期服务器将认为它是畸形的并且拒绝它。在发生错误时,一个合适的消息被存储在conn对象中,不管error是不是NULL。 + + + + PQescapeStringConn返回写到to的字节数,不包括终止的零字节。 + + + + + + PQescapeString PQescapeString + + + + PQescapeString是以下函数的旧版本,现已弃用:PQescapeStringConn。 + +size_t PQescapeString (char *to, const char *from, size_t length); + + + + PQescapeStringConn 唯一的区别是,PQescapeString 不接受 PGconnerror 参数。因此,它无法根据连接属性(例如字符编码)调整行为,可能给出错误的结果。此外,它也无法报告错误情况。 + + PQescapeString 可以在同一时刻仅使用一个 PostgreSQL 连接的客户端程序中安全使用(在这种情况下,它能够在内部取得所需信息)。在其他情形下,它存在安全隐患,应改用 PQescapeStringConn + + + + + PQescapeByteaConn PQescapeByteaConn + + + 对二进制数据进行转义,使其能够在 SQL 命令中用作以下类型的值:bytea。与PQescapeStringConn一样,这仅用于将数据直接插入 SQL 命令字符串的情况。 +unsigned char *PQescapeByteaConn(PGconn *conn, + const unsigned char *from, + size_t from_length, + size_t *to_length); + + + + + 当某些字节值被用作一个SQL语句中的bytea字面量的一部分时,它们必须被转义。 + PQescapeByteaConn使用十六进制编码或反斜杠转义来转义这些字节。详见。 + + + + from参数指向要被转义的串的第一个字节,并且from_length参数给出这个二进制串中的字节数(一个终止的零字节是不需要的也是不被计算的)。to_length参数指向一个将保持生成的已转义串长度的变量。这个结果串长度包括结果的终止零字节。 + + + PQescapeByteaConn 返回 from 参数所指二进制字符串的转义版本,存放在通过 malloc() 分配的内存中。结果不再需要时,应使用 PQfreemem() 释放该内存。返回字符串中的所有特殊字符都已替换,以便 PostgreSQL 字符串字面量解析器和 bytea 输入函数正确处理。还会添加一个末尾零字节。包围 PostgreSQL 字符串字面量所需的单引号不包含在结果字符串中。 + + + 在发生错误时,将返回一个空指针,并且一个合适的错误消息被存储在conn对象中。当前,唯一可能的错误是没有足够的内存用于结果串。 + + + + + + PQescapeBytea PQescapeBytea + + + + PQescapeBytea是以下函数的旧版本,现已弃用:PQescapeByteaConn。 + +unsigned char *PQescapeBytea(const unsigned char *from, + size_t from_length, + size_t *to_length); + + + + + 与PQescapeByteaConn的唯一区别是PQescapeBytea不用一个PGconn参数。 + 正因为这样,PQescapeBytea只能在一次只使用一个PostgreSQL连接的客户端程序中安全地使用(在这种情况下它可以在内部找出它需要知道的东西)。 + 如果在有多个数据库连接的程序中使用,它可能给出错误的结果(在那种情况下使用PQescapeByteaConn)。 + + + + + + PQunescapeBytea PQunescapeBytea + + + + 将二进制数据的字符串表示转换为二进制数据,这是PQescapeBytea的逆操作。 + 以文本格式取得bytea数据时需要此操作;以二进制格式取得时则不需要。 + + +unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length); + + + + + from参数指向一个字符串,例如对bytea列调用PQgetvalue时返回的字符串。 + PQunescapeBytea将这个字符串表示转换为二进制表示。 + 它返回指向通过malloc()分配的缓冲区的指针,出错时返回NULL,并将缓冲区大小存入to_length。 + 不再需要结果时,必须使用PQfreemem释放它。 + + + + 此转换并不完全是PQescapeBytea的逆操作,因为从PQgetvalue收到的字符串并非经过转义的形式。 + 具体而言,这意味着无需考虑字符串引号,因此也不需要PGconn参数。 + + + + + + + + + + + 异步命令处理 + + + 非阻塞连接 + + + PQexec函数足以满足普通同步应用程序提交命令的需要。不过,它有一些缺点,对某些用户可能很重要: + + + PQexec会等待命令完成。该应用可能有其他的工作要做(例如维护用户界面),这时它将不希望阻塞等待回应。 + + + + + + 因为客户端应用的执行在它等待结果时会被挂起,对于应用来说很难决定要不要尝试取消正在进行的命令(这可以在一个信号处理器中完成,但别无他法)。 + + + + + + PQexec只能返回一个PGresult结构体。 + 如果提交的命令串包含多个SQL命令, 除了最后一个PGresult之外都会被PQexec丢弃。 + + + + + + PQexec总是收集命令的整个结果,把它缓存在一个单一的PGresult中。虽然这简化了应用的错误处理逻辑,它对于包含很多行的结果并不现实。 + + + + + + 如果应用程序不希望受到这些限制,可以改用构成PQexec的底层函数:PQsendQueryPQgetResult。此外,还有PQsendQueryParams, + PQsendPrepare, + PQsendQueryPrepared, + PQsendDescribePrepared,以及PQsendDescribePortal,它们可以与PQgetResult配合使用,分别实现以下函数的功能:PQexecParams, + PQprepare, + PQexecPrepared, + PQdescribePrepared,以及PQdescribePortal + + PQsendQuery PQsendQuery + + + 向服务器提交命令,不等待结果。命令发送成功时返回 1,否则返回 0(此时可以使用PQerrorMessage取得更多失败信息)。 +int PQsendQuery(PGconn *conn, const char *command); +成功调用PQsendQuery之后,应调用PQgetResult一次或多次来取得结果。PQsendQuery在同一连接上不能再次调用,直到PQgetResult返回空指针,表明命令已经完成。 + + + + + PQsendQueryParams PQsendQueryParams + + + 向服务器提交命令及独立指定的参数,不等待结果。 +int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +该函数等价于PQsendQuery,但查询参数可以与查询字符串分开指定。函数参数的处理方式与PQexecParams相同。与PQexecParams一样,它不能用于协议 2.0 的连接,并且查询字符串中只允许包含一条命令。 + + + + + PQsendPrepare PQsendPrepare + + + 发送按给定参数创建预备语句的请求,不等待完成。 +int PQsendPrepare(PGconn *conn, + const char *stmtName, + const char *query, + int nParams, + const Oid *paramTypes); +这是PQprepare的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用PQgetResult,确定服务器是否成功创建了预备语句。函数参数的处理方式与PQprepare相同。与PQprepare一样,它不能用于协议 2.0 的连接。 + + + + + PQsendQueryPrepared PQsendQueryPrepared + + + 发送使用给定参数执行预备语句的请求,不等待结果。 +int PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +该函数类似于PQsendQueryParams,但通过指定先前已准备好的语句的名称来确定要执行的命令,而不是提供查询字符串。函数参数的处理方式与PQexecPrepared相同。与PQexecPrepared一样,它不能用于协议 2.0 的连接。 + + + + + PQsendDescribePrepared PQsendDescribePrepared + + + 提交获取指定预备语句信息的请求,不等待完成。 +int PQsendDescribePrepared(PGconn *conn, const char *stmtName); +这是PQdescribePrepared的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用PQgetResult获取结果。函数参数的处理方式与PQdescribePrepared相同。与PQdescribePrepared一样,它不能用于协议 2.0 的连接。 + + + + + PQsendDescribePortal PQsendDescribePortal + + + 提交获取指定 portal 信息的请求,不等待完成。 +int PQsendDescribePortal(PGconn *conn, const char *portalName); +这是PQdescribePortal的异步版本:请求发送成功时返回 1,否则返回 0。调用成功后,再调用PQgetResult获取结果。函数参数的处理方式与PQdescribePortal相同。与PQdescribePortal一样,它不能用于协议 2.0 的连接。 + + + + + PQgetResult PQgetResult + + + 等待先前的PQsendQuery, + PQsendQueryParams, + PQsendPrepare, + PQsendQueryPrepared, + PQsendDescribePrepared,或PQsendDescribePortal调用产生的下一个结果,并返回该结果。当命令执行完毕且不再有其他结果时,返回空指针。 +PGresult *PQgetResult(PGconn *conn); + + + + 必须反复调用 PQgetResult,直到它返回空指针,表明命令已经完成。(如果当前没有正在执行的命令,调用 PQgetResult 会立即返回空指针。)对于 PQgetResult 返回的每个非空结果,都应使用前文介绍的 PGresult 访问函数处理。使用完毕后,不要忘记调用 PQclear 释放每个结果对象。注意,只有存在正在执行的命令,且所需响应数据尚未被 PQconsumeInput 读取时,PQgetResult 才会阻塞。 + + + + 即使PQresultStatus指示发生了致命错误,也应该调用PQgetResult直到它返回一个空指针, + 以便libpq完全处理错误信息。 + + + + + + + + + 使用PQsendQueryPQgetResult解决了PQexec的一个问题:如果一个命令字符串包含多个SQL命令,这些命令的结果可以被个别地获得(顺便说一句:这样就允许一种简单的重叠处理形式, 客户端可以处理一个命令的结果,而同时服务器可以继续处理同一命令字符串中后面的查询)。 + + + + 可以被PQsendQueryPQgetResult获得的另一种常常想要的特性是一次从大型结果中检索一行。这会在中讨论。 + + + 仅仅调用PQgetResult仍会使客户端阻塞,直到服务器完成下一条SQL命令。可以通过正确使用另外两个函数来避免这种情况: + + PQconsumeInput PQconsumeInput + + + + 如果有来自服务器的输入可用,则使用之。 + +int PQconsumeInput(PGconn *conn); + + + + + PQconsumeInput通常返回 1 表明没有错误,而返回 0 表明有某种麻烦发生(此时可以用PQerrorMessage)。 + 注意该结果并不表明是否真正收集了任何输入数据。在调用PQconsumeInput之后,应用可以检查PQisBusy和/或PQnotifies来看看它们的状态是否改变。 + + + + 即使应用还不准备处理一个结果或通知,PQconsumeInput也可以被调用。 + 这个函数将读取可用的数据并且把它保存在一个缓冲区中,从而导致一个select()的读准备好指示消失。 + 因此应用可以使用PQconsumeInput立即清除select()条件,并且在空闲时再检查结果。 + + + + + + PQisBusy PQisBusy + + + 如果命令仍在忙碌,则返回 1,意味着PQgetResult会阻塞等待输入。返回 0 则表示可以调用PQgetResult,并保证不会阻塞。 +int PQisBusy(PGconn *conn); + + + + + PQisBusy本身将不会尝试从服务器读取数据,因此必须先调用PQconsumeInput,否则繁忙状态将永远不会结束。 + + + + + + + + 一个使用这些函数的典型应用将有一个主循环,在主循环中会使用select()poll()等待所有它必须响应的情况。 + 其中之一将是来自服务器的输入可用,对select()来说意味着PQsocket标识的文件描述符上有可读的数据。 + 当主循环检测到输入准备好时,它将调用PQconsumeInput读取输入。 + 然后它可以调用PQisBusy,如果PQisBusy返回假(0)则接着调用PQgetResult。 + 它还可以调用PQnotifies检测NOTIFY消息(见)。 + + + + 一个使用PQsendQuery/PQgetResult的客户端也可以尝试取消一个正在被服务器处理的命令,见。 + 但是,不管PQcancel的返回值是什么,应用都必须继续使用PQgetResult进行正常的结果读取序列。一次成功的取消只会导致命令比不取消时更快终止。 + + + 使用上述函数可以避免在等待数据库服务器输入时阻塞。不过,应用程序仍可能在等待向服务器发送输出时阻塞。这种情况较少见,但发送很长的 SQL 命令或数据值时可能发生。(如果应用程序通过COPY IN发送数据,发生的可能性则大得多。)为了防止这种情况,实现完全非阻塞的数据库操作,可以使用以下附加函数。 + + PQsetnonblocking PQsetnonblocking + + + + 设置连接的非阻塞状态。 + +int PQsetnonblocking(PGconn *conn, int arg); + + + + + 如果arg为1,则将连接状态设置为非阻塞,如果 + arg为0,则设置为阻塞。如果成功返回0,出错返回-1。 + + + 在非阻塞状态下,对 PQsendQueryPQputlinePQputnbytesPQputCopyDataPQendcopy 的调用不会阻塞;如果需要再次调用,它们会返回错误。 + + + 请注意,PQexec不遵守非阻塞模式;如果调用它,它将以阻塞方式执行。 + + + + + + PQisnonblocking PQisnonblocking + + + + 返回数据库连接的阻塞状态。 + +int PQisnonblocking(const PGconn *conn); + + + + + 如果连接设置为非阻塞模式,则返回1,如果为阻塞,则返回0。 + + + + + + PQflush PQflush + + + + 尝试将任何排队的输出数据刷新到服务器。如果成功(或发送队列为空),则返回0; + 如果由于某种原因失败,则返回-1;如果尚未能够发送发送队列中的所有数据(只有在连接为非阻塞时才会发生此情况), + 则返回1。 + +int PQflush(PGconn *conn); + + + + + + + + + 在一个非阻塞连接上发送任何命令或者数据之后,要调用PQflush。 + 如果它返回 1,就要等待套接字变成读准备好或写准备好。如果它变为写准备好,应再次调用PQflush。 + 如果它变为读准备好,则应先调用PQconsumeInput,然后再调用PQflush。 + 一直重复直到PQflush返回 0(有必要检查读准备好并且用PQconsumeInput耗尽输入,因为服务器可能阻塞给我们发送数据的尝试,例如 NOTICE 消息,并且在我们读它的数据之前它都不会读我们的数据)。 + 一旦PQflush返回 0,应等待套接字变成读准备好并且接着按照上文所述读取响应。 + + + + + + 逐行检索查询结果 + + + libpq + 单行模式 + + + 通常,libpq 会收集一条 SQL 命令的完整结果,并将其作为单个 PGresult 返回给应用程序。对于返回大量行的命令,这种方式可能不可行。在这种情况下,应用程序可以在单行模式下使用 PQsendQueryPQgetResult。此模式在从服务器收到结果行时,每次将一行返回给应用程序。 + + 要进入单行模式,请在成功调用 PQsendQuery(或其同类函数)后,立即调用 PQsetSingleRowMode。此模式选择仅对当前正在执行的查询生效。然后按照 所述,反复调用 PQgetResult,直到返回空指针。如果查询返回了行,每一行都作为独立的 PGresult 对象返回。这些对象看起来与普通查询结果相同,只是状态码为 PGRES_SINGLE_TUPLE,而不是 PGRES_TUPLES_OK。最后一行之后,或者查询返回零行时立即返回一个不含行、状态为 PGRES_TUPLES_OK 的对象,表示不会再有更多行到达。(但注意,仍然必须继续调用 PQgetResult,直到返回空指针。)所有这些 PGresult 对象都包含与该查询普通 PGresult 对象相同的行描述数据(列名、类型等)。每个对象都应像往常一样使用 PQclear 释放。 + + + + + PQsetSingleRowMode PQsetSingleRowMode + + + + 为当前正在执行的查询选择单行模式。 + + +int PQsetSingleRowMode(PGconn *conn); + + + + 此函数只能在调用 PQsendQuery 或其某个同类函数后立即调用,并且必须在对该连接执行任何其他操作之前,例如 PQconsumeInputPQgetResult。在正确时机调用时,此函数会为当前查询启用单行模式并返回 1;否则,模式保持不变,函数返回 0。无论哪种情况,当前查询完成后都会恢复普通模式。 + + + + + + + 处理查询时,服务器可能先返回一些行,然后遇到错误,导致查询中止。通常,libpq 会丢弃这些行,只报告错误。但在单行模式下,这些行已经返回给了应用程序。因此,应用程序会先看到一些 PGRES_SINGLE_TUPLE PGresult 对象,随后看到一个 PGRES_FATAL_ERROR 对象。为了保证正确的事务行为,如果查询最终失败,应用程序必须能够丢弃或撤销此前对这些行所做的全部操作。 + + + + + + 取消进行中的查询 + + + 取消 + SQL 命令 + + + 客户端应用程序可以使用本节介绍的函数,请求取消服务器仍在处理的命令。 + + PQgetCancel PQgetCancel + + + 创建一个数据结构,其中包含取消通过特定数据库连接发出的命令所需的信息。 +PGcancel *PQgetCancel(PGconn *conn); + + + + + PQgetCancel基于一个PGconn连接对象创建PGcancelPGcancel对象。如果给定的connNULL或无效连接,则返回NULLPGcancel是不透明结构体,不应由应用程序直接访问;它只能传给PQcancelPQfreeCancel。 + + + + + + PQfreeCancel PQfreeCancel + + + 释放由以下函数创建的数据结构:PQgetCancel。 + +void PQfreeCancel(PGcancel *cancel); + + + + PQfreeCancel 释放之前由 PQgetCancel 创建的数据对象。 + + + + + PQcancel PQcancel + + + 请求服务器停止处理当前命令。 +int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize); + + + + + 返回值为 1 表示取消请求成功发送,为 0 表示未成功发送。如果未成功发送,errbuf将填充解释性错误消息。errbuf必须是大小为errbufsize的字符数组(推荐大小为 256 字节)。 + + + 不过,成功发送请求并不保证请求会产生效果。如果取消生效,当前命令会提前终止并返回错误结果。如果取消失败(例如服务器已经处理完该命令),则不会产生任何可见结果。 + + + 如果errbuf是信号处理程序中的局部变量,则PQcancel可以安全地从信号处理程序中调用。对于PQcancel而言,PGcancel对象是只读的,因此也可以从与操作PGconn对象的线程不同的线程中调用。 + + + + + + + + PQrequestCancel PQrequestCancel + + + + PQrequestCancel是以下函数的已弃用变体:PQcancel。 + +int PQrequestCancel(PGconn *conn); + + + + + 请求服务器放弃当前命令的处理。它直接作用于PGconn对象,失败时会把错误消息存储到PGconn对象中(可通过PQerrorMessage获取)。虽然功能相同,但这种方法在多线程程序或信号处理程序中并不安全,因为它可能覆盖PGconn中的错误消息,从而破坏当前连接上正在进行的操作。 + + + + + + + + + + 快速路径接口 + + + fast path + + + + PostgreSQL提供一种快速路径接口来向服务器发送简单的函数调用。 + + + + 此接口已显得有些过时,因为可以通过建立定义函数调用的预备语句,获得相近的性能和更多功能。随后使用二进制格式传输参数和结果来执行该语句,就可以替代快速路径函数调用。 + + + + 函数PQfnPQfn请求通过快速路径接口执行服务器函数。 + +PGresult *PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs); + +typedef struct +{ + int len; + int isint; + union + { + int *ptr; + int integer; + } u; +} PQArgBlock; + + + + + fnid参数是要被执行的函数的 OID。argsnargs定义了要传递给函数的参数;它们必须匹配已声明的函数参数列表。当一个参数结构体的isint域为真时,u.integer值被以指定长度(必须是 2 或 4 字节)整数的形式发送给服务器;这时候会发生恰当的字节交换。当isint为假时,*u.ptr中指定数量的字节将不做任何处理被发送出去;这些数据必须是服务器 预期的用于该函数参数数据类型的二进制传输的格式(由于历史原因u.ptr被声明为类型int *,其实把它考虑成void *会更好)。result_buf是放置该函数返回值的缓冲区。调用者必须已经分配了足够的空间来存储返回值(这里没有检查!)。实际的结果长度将被放在result_len指向的整数中返回。如果预期结果是 2 或 4 字节整数,把result_is_int设为 1;否则设为 0。把result_is_int设为 1 导致libpq在必要时对值进行交换字节,这样它就作为对客户端机器正确的int值被传输,注意对任一种允许的结果大小都会传递一个 4 字节整数到*result_buf。当result_is_int是 0 时,服务器发送的二进制格式字节将不做修改直接返回(在这种情况下,把result_buf考虑为类型void *更好)。 + + + + PQfn总是返回一个有效的PGresult指针,成功时状态为PGRES_COMMAND_OK,遇到问题时为PGRES_FATAL_ERROR。 + 在使用结果之前应该检查结果状态。 + 当结果不再使用后,调用者有义务使用PQclear释放PGresult。 + + + + 要传递NULL参数到函数,将参数结构体的len字段设置为-1isintu 字段就不相关了。(但这仅适用于使用协议 3.0 及更高版本的连接。) + + 如果函数返回 NULL,则将 *result_len 设为 -1,而不修改 *result_buf。(这仅适用于使用协议 3.0 及更高版本的连接;在协议 2.0 中,既不修改 *result_len,也不修改 *result_buf。) + + 注意,使用此接口时无法处理集合值结果。此外,函数必须是普通函数,不能是聚合函数或窗口函数。 + + + + + 异步通知 + + + NOTIFY + in libpq + + + + PostgreSQL通过LISTENNOTIFY命令提供异步通知。客户端会话可使用LISTEN命令注册其感兴趣的特定通知通道(也可以用UNLISTEN命令停止监听)。当任何会话执行带有该通道名的NOTIFY命令时,所有监听该通道的会话都会异步收到通知。还可以传递一个载荷字符串,向监听者传递附加数据。 + + + + libpq应用把LISTENUNLISTENNOTIFY命令作为普通 SQL 命令提交。 + 随后通过调用PQnotifies.PQnotifies来检测NOTIFY消息的到达。 + + + 函数PQnotifies从已收到但尚未处理的服务器通知消息列表中返回下一条通知。如果没有待处理的通知,则返回空指针。一旦通知由PQnotifies返回,就被视为已处理,并从通知列表中移除。 +PGnotify *PQnotifies(PGconn *conn); + +typedef struct pgNotify +{ + char *relname; /* notification channel name */ + int be_pid; /* process ID of notifying server process */ + char *extra; /* notification payload string */ +} PGnotify; +处理完一个PGnotify对象(由PQnotifies返回)后,一定要用PQfreemem释放它。只需释放PGnotify指针;relnameextra字段并非独立分配。(这些字段名称是历史遗留的;尤其是,通道名称与关系名称不必有任何关联。) + + + 给出了一个示例程序展示异步通知的使用。 + + + + PQnotifies实际上并不从服务器读取数据;它只是返回之前已被其他libpq函数吸收的消息。 + 在较早版本的libpq中,及时收到NOTIFY消息的唯一方法是不断提交命令,哪怕是空命令,然后在每次PQexec后检查PQnotifies。 + 虽然这种方法仍然有效,但由于效率过低,现已废弃。 + + + + 当你没有可用的命令提交时,一种更好的检查NOTIFY消息的方法是调用PQconsumeInput,然后检查PQnotifies。 + 你可以使用select()等待服务器数据到达,这样在无事可做时就不会浪费CPU资源(参见PQsocket以获得可传给select()的文件描述符)。 + 注意不管是用PQsendQuery/PQgetResult提交命令还是简单地使用PQexec,这种方法都能正常工作。 + 不过,你应该记住在每次PQgetResultPQexec之后检查PQnotifies,看看在命令的处理过程中是否有通知到达。 + + + + + + <command>COPY</command>命令相关的函数 + + + COPY + with libpq + + + + PostgreSQL中的COPY命令有用于libpq的对网络连接读出或者写入的选项。这一节描述的函数允许应用通过提供或者消耗已拷贝的数据来充分利用这个功能。 + + + + 整个处理是应用首先通过PQexec或者一个等效的函数发出 SQL COPY命令。 + 对这个命令的响应(如果命令无误)将是一个状态代码是PGRES_COPY_OUT或 者PGRES_COPY_IN(取决于指定的拷贝方向)的PGresult对象。 + 应用然后就应该使用这一节的函数接收或者传送数据行。在数据传输结束之后,另外一个PGresult对象会被返回以表明传输的成功或者失败。 + 它的状态将是:PGRES_COMMAND_OK表示成功,PGRES_FATAL_ERROR表示发生了一些问题。 + 此时我们可以通过PQexec发出进一步的 SQL 命令(在COPY操作的处理过程中,不能用同一个连接执行其它 SQL 命令)。 + + + + 如果一个COPY命令是通过PQexec在一个可能包含额外命令的字符串中发出的,那么应用在完成COPY序列之后必须继续用PQgetResult取得结果。 + 只有在PQgetResult返回NULL时,我们才能确信PQexec的命令字符串已经处理完毕, 并且可以安全地发出更多命令。 + + + + 这一节的函数应该只在从PQexecPQgetResult获得了PGRES_COPY_OUTPGRES_COPY_IN结果状态后执行。 + + + 一个PGresult对象若带有上述某个状态值,还会携带关于即将开始的COPY操作的附加数据。这些数据可以通过下列函数取得,这些函数也用于查询结果: + + PQnfieldsPQnfieldswith COPY + + + + 返回要复制的列(字段)的数量。 + + + + + + PQbinaryTuplesPQbinaryTupleswith COPY + + + + 0表示整体复制格式为文本(行由换行符分隔,列由分隔符分隔等)。1表示整体复制格式为二进制。 + 有关更多信息,请参见。 + + + + + + PQfformatPQfformatwith COPY + + + + 返回与复制操作的每列关联的格式代码(0表示文本,1表示二进制)。 + 当整体复制格式为文本时,每列的格式代码始终为零,但二进制格式可以支持文本和二进制列。 + (但是,截至当前COPY的实现,只有二进制列出现在二进制复制中; + 因此,每列格式目前始终与整体格式匹配。) + + + + + + + + 这些附加数据值仅在使用协议 3.0 时可用。使用协议 2.0 时,这些函数都返回 0。 + + + + 用于发送<command>COPY</command>数据的函数 + + + 这些函数用于在COPY FROM STDIN期间发送数据。如果在连接不是COPY_IN状态,调用它们会失败。 + + + + + PQputCopyData PQputCopyData + + + + 在COPY_IN状态中向服务器发送数据。 + +int PQputCopyData(PGconn *conn, + const char *buffer, + int nbytes); + + + + + 传输指定buffer中长度为nbytesCOPY数据到服务器。 + 如果数据被放在队列中,结果是 1;如果因为缓冲区满而无法被放在队列中(只可能发生在连接是非阻塞模式时),那么结果是零;如果发生错误,结果为 -1(如果返回值为 -1,那么使用PQerrorMessage检索细节。如果值是零,那么等待写准备好然后重试)。 + + + + 应用可以把COPY数据流划分成任意方便的大小放到缓冲区中。在发送时,缓冲区载荷的边界没有什么语意。数据流的内容必须匹配COPY命令预期的数据格式;详见。 + + + + + + PQputCopyEnd PQputCopyEnd + + + + 在COPY_IN状态中向服务器发送数据结束的指示。 + +int PQputCopyEnd(PGconn *conn, + const char *errormsg); + + + + 如果 errormsgNULL,则成功结束 COPY_IN 操作。如果 errormsg 不为 NULL,则强制 COPY 失败,并将 errormsg 指向的字符串用作错误消息。(但不应假定服务器一定会返回这条完全相同的错误消息,因为服务器可能已经因自身原因使 COPY 失败。还要注意,在使用 3.0 之前协议的连接上,强制失败选项不起作用。) + + 如果终止消息被发送,则结果为 1;在非阻塞模式中,结果为 1 也可能只表示终止消息被成功地放在了发送队列中 (在非阻塞模式中,要确认数据确实被发送出去,你应该接着等待写准备好并且调用PQflush,重复这些直到返回零)。 零表示该函数由于缓冲区满而无法将该终止消息放在队列中,这只会发生在非阻塞模式中(在这种情况下,等待写准备好并且再次尝试PQputCopyEnd调用)。 如果发生系统错误,则返回 -1,可以使用PQerrorMessage检索详情。 + + + 在成功调用PQputCopyEnd之后,调用PQgetResult获取COPY命令的最终结果状态。 + 我们可以用平常的方法来等待这个结果可用。然后返回到正常的操作。 + + + + + + + + + 用于接收<command>COPY</command>数据的函数 + + + 这些函数用于在COPY TO STDOUT的过程中接收数据。如果连接不在COPY_OUT状态,那么调用它们将会失败。 + + + + + PQgetCopyData PQgetCopyData + + + + 在COPY_OUT状态下从服务器接收数据。 + +int PQgetCopyData(PGconn *conn, + char **buffer, + int async); + + + + + 在一个COPY期间尝试从服务器获取另外一行数据。数据总是以每次一个数据行的方式被返回;如果只有一个部分行可用,那么它不会被返回。 + 成功返回一个数据行涉及到分配一块内存来保存该数据。buffer参数必须为非NULL。 + *buffer被设置为指向分配到的内存的指针,或者是在没有返回缓冲区的情况下指向NULL。 + 一个非NULL的结果缓冲区在不需要时必须用PQfreemem释放。 + + + + 在成功返回一行之后,返回的值就是该数据行里数据的字节数(将是大于零)。 + 被返回的字符串总是以零字节结尾,虽然这可能只是对文本COPY有用。 + 一个零结果表示该COPY仍然在处理中,但是还没有可用的行(只在async为真时才可能)。 + 一个 -1 结果表示COPY已经完成。-2 结果表示发生了错误(参考PQerrorMessage获取原因)。 + + + async 为真(非零)时,PQgetCopyData 不会阻塞等待输入;如果 COPY 仍在进行,但没有完整的行可用,则返回零。(这种情况下,应等待读就绪,随后先调用 PQconsumeInput,再调用 PQgetCopyData。)当 async 为假(零)时,PQgetCopyData 会阻塞,直到有数据可用或操作完成。 + + + 在PQgetCopyData返回 -1 之后,调用PQgetResult获取COPY命令的最后结果状态。 + 我们可以用平常的方法来等待这个结果可用。然后返回到正常的操作。 + + + + + + + + + 用于<command>COPY</command>的废弃函数 + + + 这些函数代表了以前的处理COPY的方法。尽管它们还能用,但是现在已经被废弃,因为它们的错误处理很糟糕、检测结束数据的方法也不方便,并且缺少对二进制或非阻塞传输的支持。 + + + + + PQgetline PQgetline + + + + 读取一个以新行终止的字符行到(由服务器传输) 到一个长度为length的字符串缓冲区。 + +int PQgetline(PGconn *conn, + char *buffer, + int length); + + + + + 这个函数拷贝最多length-1 个字符到该缓冲区中,并且把终止的新行转换成一个零字节。 + PQgetline在输入结束时返回EOF,如果整行都被读取则返回 0,如果缓冲区填满了而还没有遇到结束的新行则返回 1。 + + + 注意,应用必须检查是否一个新行包含两个字符\.,这表明服务器 已经完成了COPY命令的结果发送。如果应用可能收到超过length-1 字符长的行, 我们就应该确保正确识别\.行(例如,不要把一个长数据行的结束当作一个终止行)。 + + + + + + PQgetlineAsync PQgetlineAsync + + + + 不阻塞地读取一行COPY数据(由服务器传输)到一个缓冲区中。 + +int PQgetlineAsync(PGconn *conn, + char *buffer, + int bufsize); + + + + + 这个函数类似于PQgetline,但是可以被用于那些必须异步读取COPY数据的应用, 也就是不阻塞的应用。 + 在发出了COPY命令并得到了PGRES_COPY_OUT响应之后, + 应用应该调用PQconsumeInputPQgetlineAsync直到检测到结束数据的信号。 + + + 不像PQgetline,这个函数负责检测结束数据。 + + + + 在每次调用时,如果libpq的输入缓冲区中有一个完整的数据行可用,PQgetlineAsync都将返回数据。 + 否则,在剩余行到达之前不会返回数据。如果识别到拷贝数据结束的标志,此函数返回 -1;如果没有可用数据则返回 0; + 或者返回一个正数,表示返回的数据字节数。如果返回 -1,调用者下一步必须调用PQendcopy,然后回到正常处理。 + + + + 返回的数据将不超过一个数据行的范围。如果可能,每次将返回一个完整行。但如果调用者提供的缓冲区太小不足以容下服务器发送的行,那么将返回部分行。对于文本数据,这可以通过测试返回的最后一个字节是否\n来检测(在二进制COPY中, 需要对COPY数据格式进行实际的分析,以便做相同的判断)。被返回的字符串不是空结尾的(如果你想增加一个终止空,确保传递一个比实际可用空间少一字节的bufsize)。 + + + + + + PQputline PQputline + + + + 向服务器发送一个空终止的字符串。如果 OK 则返回 0;如果不能发送字符串则返回EOF。 + +int PQputline(PGconn *conn, + const char *string); + + + + + 一系列PQputline调用发送的COPY数据流和PQgetlineAsync返回的数据具有相同的格式, + 只是应用不需要每次PQputline调用中发送刚好一个数据行;在每次调用中发送多行或者部分行都是可以的。 + + + + + 在PostgreSQL协议 3.0 之前,应用必须显式地发送两个字符\.作为最后一行来告知服务器应用程序已完成发送COPY数据。 + 虽然这么做仍然有效,但是它已经被废弃并且\.的特殊含义可能在将来的版本中删除。 + 在发送完实际数据之后, 调用PQendcopy就足够了。 + + + + + + + PQputnbytes PQputnbytes + + + + 向服务器发送一个非空终止的字符串。如果 OK 则返回 0,如果不能发送字符串则返回EOF。 + +int PQputnbytes(PGconn *conn, + const char *buffer, + int nbytes); + + + + + 这个函数类似PQputline,除了数据缓冲区不需要以零字节结尾,因为要发送的字节数是直接指定的。在发送二进制数据时使用这个函数。 + + + + + + PQendcopy PQendcopy + + + 与服务器同步。 +int PQendcopy(PGconn *conn); +此函数会等待服务器完成复制。调用时机应为:使用PQputline向服务器发送最后一个字符串后,或者使用PQgetline从服务器收到最后一个字符串后。必须调用此函数,否则服务器与客户端将会不同步。此函数返回后,服务器便准备好接收下一条 SQL 命令。成功完成时返回 0,否则返回非零值。(若返回非零值,可使用PQerrorMessage取得详细信息。) + + + 在使用PQgetResult时,应用应该通过反复调用PQgetline并且在看到终止行后调用PQendcopy来响应PGRES_COPY_OUT结果。 + 然后它应该返回到PQgetResult循环直到PQgetResult返回一个空指针。 + 类似地,PGRES_COPY_IN结果会用一系列PQputline加上之后的PQendcopy来处理,然后返回到PQgetResult循环。 + 这样的安排将保证嵌入到一系列SQL命令中的COPY命令将被正确执行。 + + + + 旧的应用很可能会通过PQexec提交一个COPY命令并且假定事务在PQendcopy之后完成。 + 只有在COPY是命令字符串中唯一的SQL命令时才能正确工作。 + + + + + + + + + + + 控制函数 + + + 这些函数控制libpq行为各种各样的细节。 + + + + + PQclientEncoding PQclientEncoding + + + + + 返回客户端编码。 + +int PQclientEncoding(const PGconn *conn); + + + 请注意,它返回的是编码 ID,而不是一个符号串字符串,如EUC_JP。如果不成功,它会返回 -1。要把一个编码 ID 转换为为一个编码名称,可以用: + + +char *pg_encoding_to_char(int encoding_id); + + + + + + + PQsetClientEncoding PQsetClientEncoding + + + 设置客户端编码。 +int PQsetClientEncoding(PGconn *conn, const char *encoding); + + + conn是到服务器的连接,而encoding是要使用的编码。如果成功设置编码,函数返回 0,否则返回 -1。此连接的当前编码可以通过以下函数确定:PQclientEncoding。 + + + + + + PQsetErrorVerbosity PQsetErrorVerbosity + + + 设置以下函数所返回消息的详细程度:PQerrorMessagePQresultErrorMessage。 + +typedef enum +{ + PQERRORS_TERSE, + PQERRORS_DEFAULT, + PQERRORS_VERBOSE +} PGVerbosity; + +PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); + + + PQsetErrorVerbosity设置详细程度模式,并返回该连接先前的设置。在TERSE模式下,返回的消息只包括严重性、主要文本和位置,通常一行就能容纳。默认模式生成的消息除上述字段外,还包括详细信息、提示或上下文字段(这些内容可能跨越多行)。VERBOSE模式包括所有可用字段。改变详细程度不会影响已有PGresult对象中的消息,只影响随后创建的对象。(如果要以不同的详细程度打印先前的错误,请参见PQresultVerboseErrorMessage。) + + + + + PQsetErrorContextVisibility PQsetErrorContextVisibility + + + 确定对CONTEXT字段的处理方式,这些字段位于以下函数返回的消息中:PQerrorMessagePQresultErrorMessage。 + +typedef enum +{ + PQSHOW_CONTEXT_NEVER, + PQSHOW_CONTEXT_ERRORS, + PQSHOW_CONTEXT_ALWAYS +} PGContextVisibility; + +PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, PGContextVisibility show_context); + + + PQsetErrorContextVisibility设置上下文显示模式,并返回连接的先前设置。此模式控制消息中是否包含CONTEXT字段(除非详细程度设置为TERSE,此时CONTEXT始终不会显示)。NEVER模式从不包含CONTEXT,而ALWAYS在该字段可用时总是包含它。在ERRORS模式(默认)下,CONTEXT字段只包含在错误消息中,不包含在通知和警告中。改变此模式不会影响已有PGresult对象中的消息,只影响随后创建的对象。(如果要以不同的显示模式打印先前的错误,请参见PQresultVerboseErrorMessage。) + + + + + PQtrace PQtrace + + + + 启用对客户端/服务器通讯的跟踪,把跟踪信息输出到一个调试文件流中。 + +void PQtrace(PGconn *conn, FILE *stream); + + + + + + + 在 Windows上,如果libpq库和应用使用了不同的标志编译,那么这个函数调用会导致应用崩溃,因为FILE指针的内部表达是不一样的。特别是多线程/单线程、发布/调试 以及静态/动态标志应该是库和所有使用库的应用都一致。 + + + + + + + + PQuntrace PQuntrace + + + 禁用以下函数启动的跟踪:PQtrace。 + +void PQuntrace(PGconn *conn); + + + + + + + + + + 杂项函数 + + + 一如往常,总有一些函数不适合放在任何其他地方。 + + + + + PQfreemem PQfreemem + + + + 释放libpq分配的内存。 + +void PQfreemem(void *ptr); + + + + + 释放libpq分配的内存,尤其是PQescapeByteaConn,PQescapeBytea,PQunescapeBytea,和PQnotifies分配的内存。 + 特别重要的是,在微软 Windows 上使用这个函数,而不是free()。 + 这是因为只有 当 DLL 和应用的多线程/单线程、发布/调试以及静态/动态标志相同时,才能在一个 DLL 中分配内存并且在应用中释放它。 + 在非微软 Windows 平台上,这个函数与标准库函数free()相同。 + + + + + + PQconninfoFree PQconninfoFree + + + 释放以下函数分配的数据结构:PQconndefaultsPQconninfoParse。 + +void PQconninfoFree(PQconninfoOption *connOptions); + + + + 仅调用 PQfreemem 不足以完成此项释放,因为数组还包含指向附属字符串的引用。 + + + + + + PQencryptPassword + + PQencryptPassword + + + + + + 准备一个PostgreSQL密码的加密形式。 + +char * PQencryptPassword(const char *passwd, const char *user); + + 这个函数旨在用于那些希望发送类似于ALTER USER joe PASSWORD 'pwd'命令的客户端应用。不在这样一个命令中发送原始的明文密码是一个好习惯,因为它可能被暴露在命令日志、活动显示等等中。相反,在发送之前使用这个函数可以将密码转换为加密的形式。参数是明文密码和该密码所属用户的 SQL 名称。返回值是由malloc分配的字符串,如果内存不足则为NULL。调用者可以假定该字符串不包含任何需要转义的特殊字符。用完之后用PQfreemem释放结果。 + + + + + + PQmakeEmptyPGresult PQmakeEmptyPGresult + + + + 用给定的状态,构造一个空PGresult对象。 + +PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); + + + + + 这是libpq内部用于分配并初始化一个空PGresult对象的函数。 + 如果无法分配内存,此函数返回NULL。 + 将它导出供外部调用,是因为一些应用需要自行生成结果对象,特别是带有错误状态的对象。 + 如果conn非空,并且status表示错误,指定连接的当前错误消息会被复制到PGresult中。 + 此外,如果conn非空,连接中注册的所有事件过程也会被复制到PGresult中。 + (这些过程不会收到PGEVT_RESULTCREATE调用,但可参见PQfireResultCreateEvents。) + 注意,最终应对该对象调用PQclear,就像处理libpq自身返回的PGresult一样。 + + + + + + PQfireResultCreateEvents PQfireResultCreateEvents + + + 为每一个在PGresult对象中注册的事件过程触发一个PGEVT_RESULTCREATE事件(见)。成功时返回非 0,如果任何事件过程失败则返回 0。 + + +int PQfireResultCreateEvents(PGconn *conn, PGresult *res); + + + + + conn参数被传送给事件过程,但不会被直接使用。如果事件过程不使用它,则会返回NULL。 + + + + 已经接收到这个对象的PGEVT_RESULTCREATEPGEVT_RESULTCOPY事件的事件过程不会被再次触发。 + + + + 这个函数与PQmakeEmptyPGresult分开的主要原因是在调用事件过程之前创建一个PGresult并且填充它常常是合适的。 + + + + + + PQcopyResult PQcopyResult + + + 创建一个PGresult对象的副本。副本与源结果没有任何关联,并且PQclear必须在不再需要该副本时调用。如果函数失败,会返回NULL +PGresult *PQcopyResult(const PGresult *src, int flags); + + + + 这不是为了制作一个精确的副本。返回的结果总是放在PGRES_TUPLES_OK状态中,并且不复制源中的任何错误消息。 (但是会复制命令状态字符串。)flags参数确定要复制的其他内容。它是几个标志的按位或。 PG_COPYRES_ATTRS指定复制源结果的属性(列定义)。 PG_COPYRES_TUPLES指定复制源结果的元组。(这也意味着复制属性。) PG_COPYRES_NOTICEHOOKS指定复制源结果的通知钩子。 PG_COPYRES_EVENTS指定复制源结果的事件。(但不复制与源相关的任何实例数据。) + + + + + PQsetResultAttrs PQsetResultAttrs + + + + + 设置PGresult对象的属性。 + +int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs); + + + + + 提供的attDescs被复制到结果中。如果attDescs指针为NULLnumAttributes小于1,那么请求将被忽略并且函数成功。如果res已经包含属性,那么函数会失败。如果函数失败,返回值是 0。如果函数成功,返回值是非 0。 + + + + + + PQsetvalue PQsetvalue + + + + 设置一个PGresult对象的一个元组域值。 + +int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len); + + + + + 这个函数将自动按需增加结果的内部元组数组。但是,tup_num参数必须小于等于PQntuples,意味着这个函数对元组数组一次只能增加一个元组。 + 但已存在的任意元组中的任意域可以以任意顺序进行调整。如果field_num的一个值已经存在,它会被覆盖。 + 如果len是 -1,或valueNULL, 该域值会被设置为一个 SQL 空值。 + value会被复制到结果的私有存储中,因此函数返回后就不再需要了。如果函数失败,返回值是 0。如果函数成功,返回值会是非 0。 + + + + + + PQresultAlloc PQresultAlloc + + + + + 为一个PGresult对象分配附属存储。 + +void *PQresultAlloc(PGresult *res, size_t nBytes); + + + + + 当res被清除时,这个函数分配的内存也会被释放掉。如果函数失败,返回值是NULL。结果被保证为按照数据的任意类型充分地对齐,正如malloc所作的。 + + + + + + PQlibVersion PQlibVersion PQserverVersion + + + + 返回所使用的libpq版本。 + +int PQlibVersion(void); + + + + + 在运行时,这个函数的结果可以被用来判断在当前已载入的 libpq 版本中特定的功能是否可用。 + 例如,这个函数可以被用来判断PQconnectdb可以使用哪些连接选项,或者是否支持PostgreSQL 9.0 中加入的byteahex输出格式。 + + + + 这个数字是这样形成的:将主版本、次版本和修订版本号分别转换成两位十进制数,然后把它们拼接在一起。例如,版本9.1将被返回为90100,而版本9.1.2将被返回为90102(不显示前导零)。 + + + + + + 这个函数出现于PostgreSQL版本 9.1,因此它不能被用来在早期的版本中检测所需的功能,因为链接到它将会创建一个对版本9.1的链接依赖。 + + + + + + + + + + + 通知处理 + + + notice processing + in libpq + + + + 服务器产生的通知和警告消息不会被查询执行函数返回,因为它们不代表查询失败。它们可以被传递给一个通知处理函数,并且在处理者返回后执行会继续正常进行。默认的处理函数会把消息打印在stderr上,但是应用可以通过提供它自己的处理函数来重载这种行为。 + + + + 由于历史原因,通知处理有两个级别,称为通知接收器和通知处理器。通知接收器的默认行为是格式化通知并且将一个字符串传递给通知处理器来打印。不过,如果一个应用选择提供自己的通知接收器,它通常会忽略通知处理器层并且在通知接收器中完成所有工作。 + + + + 函数PQsetNoticeReceiver + notice receiver + PQsetNoticeReceiver为一个连接对象设置或者检查当前的通知接收器。 + 相似地,PQsetNoticeProcessor + notice processor + PQsetNoticeProcessor设置或检查当前的通知处理器。 + + +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); + +PQnoticeReceiver +PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); + +typedef void (*PQnoticeProcessor) (void *arg, const char *message); + +PQnoticeProcessor +PQsetNoticeProcessor(PGconn *conn, + PQnoticeProcessor proc, + void *arg); + + + 这些函数中的每一个会返回之前的通知接收器或处理器函数指针,并且设置新值。如果你提供了一个空函数指针,将不会采取任何动作,只会返回当前指针。 + + + + 当接收到一个服务器产生的或者libpq内部产生的通知或警告消息,通知接收器函数会被调用。 + 该函数会以一种PGRES_NONFATAL_ERROR PGresult的形式接收该消息 + (这允许接收器使用PQresultErrorField抽取个别的域,或者使用PQresultErrorMessage或者PQresultVerboseErrorMessage得到一个完整的预格式化的消息)。 + 被传递给PQsetNoticeReceiver的同一个 void 指针也被传递(必要时,这个指针可以被用来访问应用相关的状态)。 + + + + 默认的通知接收器会简单地抽取消息(使用PQresultErrorMessage)并且将它传递给通知处理器。 + + + + 通知处理器负责处理一个以文本形式给出的通知或警告消息。该消息的字符串文本(包括一个收尾的新行)被传递给通知处理器,外加一个同时被传递给PQsetNoticeProcessor的空指针(必要时,这个指针可以被用来访问应用相关的状态)。 + + + + 默认的通知处理器很简单: + +static void +defaultNoticeProcessor(void *arg, const char *message) +{ + fprintf(stderr, "%s", message); +} + + + + + 一旦你设定了一个通知接收器或处理器,你应该期待只要PGconn对象或者从它构造出的PGresult对象存在,该函数就可能被调用。 + 在一个PGresult创建时,PGconn的当前通知处理指针被复制到PGresult中,以备类似PQgetvalue的函数使用。 + + + + + + 事件系统 + + + libpq的事件系统被设计为通知已注册的事件处理器它感兴趣的libpq事件,例如PGconn以及PGresult对象的创建和毁灭。一种主要的使用情况是这允许应用将自己的数据与一个PGconn或者PGresult关联在一起,并且确保那些数据在适当的时候被释放。 + + + 每个注册的事件处理程序都与两个数据相关联,libpq仅将其视为不透明的void *指针。 有一个透传指针,当事件处理程序与PGconn注册时,应用程序提供。 透传指针在PGconn及其生成的所有PGresult的生命周期内永远不会更改; 因此,如果使用,它必须指向长期存在的数据。 此外,还有一个实例数据指针,在每个PGconnPGresult中一开始都是NULL。 可以使用PQinstanceDataPQsetInstanceDataPQresultInstanceDataPQsetResultInstanceData函数来操作此指针。 请注意,与透传指针不同,PGconn的实例数据不会自动继承到从中创建的PGresultlibpq不知道透传和实例数据指针指向的内容(如果有的话),也永远不会尝试释放它们 —— 这是事件处理程序的责任。 + + + 事件类型 + + + 枚举PGEventId命名了事件系统处理的事件类型。它的所有值的名称都以PGEVT开始。对于每一种事件类型,都有一个相应的事件信息结构体用来承载传递给事件处理器的参数。事件类型是: + + + + + PGEVT_REGISTER + + 注册事件在PQregisterEventProc被调用时触发。此时最适合初始化事件处理函数可能需要的instanceData。每个连接中的每个事件处理函数只会触发一次注册事件。如果事件处理函数失败,则中止注册。 +typedef struct +{ + PGconn *conn; +} PGEventRegister; +收到PGEVT_REGISTER事件时,应将evtInfo指针强制转换为PGEventRegister *。此结构体包含一个PGconn,它应处于CONNECTION_OK状态;如果调用PQregisterEventProc紧接在取得正常的PGconn之后,就能保证这一点。返回失败代码时,必须完成全部清理工作,因为不会发送PGEVT_CONNDESTROY事件。 + + + + + PGEVT_CONNRESET + + 连接重置事件会在完成以下调用时触发:PQresetPQresetPoll。在这两种情况下,只有重置成功才会触发该事件。如果事件处理函数失败,整个连接重置就会失败;PGconn会被置于CONNECTION_BAD状态,并且PQresetPoll将返回PGRES_POLLING_FAILED。 + + +typedef struct +{ + PGconn *conn; +} PGEventConnReset; +收到PGEVT_CONNRESET事件时,应将evtInfo指针强制转换为PGEventConnReset *。虽然其中的PGconn刚刚被重置,但所有事件数据都保持不变。应利用此事件重置、重新加载或重新查询相关联的instanceData。注意,即使事件处理函数未能处理PGEVT_CONNRESET,它仍会在连接关闭时收到PGEVT_CONNDESTROY事件。 + + + + + PGEVT_CONNDESTROY + + 连接销毁事件由以下调用触发:PQfinish。事件处理函数负责正确清理其事件数据,因为 libpq 无法管理这部分内存。如果不清理,就会造成内存泄漏。 +typedef struct +{ + PGconn *conn; +} PGEventConnDestroy; +收到PGEVT_CONNDESTROY事件时,应将evtInfo指针强制转换为PGEventConnDestroy *。该事件触发于以下函数执行任何其他清理工作之前:PQfinish。事件处理函数的返回值会被忽略,因为无法通过以下函数报告失败:PQfinish。此外,事件处理函数失败不应中止清理不再使用的内存的过程。 + + + + + PGEVT_RESULTCREATE + + + 任何生成结果的查询执行函数都会触发结果创建事件,其中包括PQgetResult。 + 只有成功创建结果后才会触发该事件。 + + +typedef struct +{ + PGconn *conn; + PGresult *result; +} PGEventResultCreate; + + + 收到PGEVT_RESULTCREATE事件时,应将evtInfo指针转换为PGEventResultCreate *。 + 其中,conn是用于生成结果的连接。 + 这是初始化需要与结果关联的instanceData的理想位置。 + 如果事件过程失败,结果会被清除,失败也会向外传递。 + 事件过程不得自行调用PQclear来清除结果对象。 + 返回失败代码时,必须完成所有清理工作,因为不会发送PGEVT_RESULTDESTROY事件。 + + + + + + PGEVT_RESULTCOPY + + 结果复制事件会在调用PQcopyResult时触发。只有复制完成后才会触发该事件。只有为源结果成功处理过PGEVT_RESULTCREATEPGEVT_RESULTCOPY事件的事件处理函数,才会收到PGEVT_RESULTCOPY事件。 +typedef struct +{ + const PGresult *src; + PGresult *dest; +} PGEventResultCopy; +收到PGEVT_RESULTCOPY事件时,应将evtInfo指针强制转换为PGEventResultCopy *。其中,src结果是复制源,而dest结果是复制目标。可以利用此事件对instanceData进行深复制,因为PQcopyResult无法完成这项工作。如果事件处理函数失败,整个复制操作就会失败,并且dest结果将被清除。返回失败代码时,必须完成所有清理工作,因为不会为目标结果发送PGEVT_RESULTDESTROY事件。 + + + + + PGEVT_RESULTDESTROY + + 结果销毁事件由以下调用触发:PQclear。事件处理函数负责正确清理其事件数据,因为 libpq 无法管理这部分内存。如果不清理,就会造成内存泄漏。 +typedef struct +{ + PGresult *result; +} PGEventResultDestroy; +收到PGEVT_RESULTDESTROY事件时,应将evtInfo指针强制转换为PGEventResultDestroy *。该事件触发于以下函数执行任何其他清理工作之前:PQclear。事件处理函数的返回值会被忽略,因为无法通过以下函数报告失败:PQclear。此外,事件处理函数失败不应中止清理不再使用的内存的过程。 + + + + + + + 事件回调函数 + + + + PGEventProc PGEventProc + + + + PGEventProc是通过 typedef 定义的事件处理函数指针类型,也就是接收 libpq 事件的用户回调函数的指针类型。事件处理函数的签名必须为 +int eventproc(PGEventId evtId, void *evtInfo, void *passThrough) +其中,evtId参数指示发生了哪一种PGEVT事件。必须将evtInfo指针强制转换为适当的结构体类型,以获取关于该事件的更多信息。passThrough参数是在注册事件处理函数时传给以下函数的指针:PQregisterEventProc。函数应在成功时返回非零值,在失败时返回零。 + + + 在任何一个PGconn中,一个特定事件过程只能被注册一次。这是因为该过程的地址被用作查找键来标识相关的实例数据。 + + + + + + 在 Windows 上,函数能够有两个不同的地址:一个对 DLL 之外可见而另一个对 DLL 之内可见。我们应当小心只有其中之一会被用于libpq的事件过程函数,否则将会产生混淆。编写代码的最简单规则是将所有的事件过程声明为static。如果过程的地址必须对它自己的源代码文件之外可见,提供一个单独的函数来返回该地址。 + + + + + + + + + 事件支持函数 + + + + PQregisterEventProc PQregisterEventProc + + + + + 为 libpq 注册一个事件回调过程。 + + +int PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough); + + + + + 在每一个你想要接收事件的PGconn上必须注册一个事件过程。和内存不同,没有限制说一个连接上能注册多少个事件过程。如果该函数成功,它会返回一个非零值。如果它失败,则会返回零。 + + + + 当一个 libpq 事件被触发时,proc参数将被调用。它的内存地址也被用来查找instanceDataname参数被用来在错误消息中引用该事件过程。这个值不能是NULL或一个零长度串。名字串被复制到PGconn中,因此传递进来的东西不需要长期存在。当一个事件发生时,passThrough指针被传递给proc。这个参数可以是NULL。 + + + + + + PQsetInstanceData PQsetInstanceData + + + + 设置连接conn的用于过程procinstanceDatadata。它在成功时返回非零值,失败时返回零(只有proc没有被正确地注册在conn中,才可能会失败)。 + + +int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data); + + + + + + + PQinstanceData PQinstanceData + + + + 返回连接conn的与过程proc相关的instanceData,如果没有则返回NULL。 + + +void *PQinstanceData(const PGconn *conn, PGEventProc proc); + + + + + + + PQresultSetInstanceData PQresultSetInstanceData + + 将结果的instanceData(针对proc)设置为data。成功时返回非零,失败时返回零。(只有当proc未在结果中正确注册时,才可能失败。) +int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data); + + + + + + + PQresultInstanceData PQresultInstanceData + + + + 返回结果的与过程proc相关的instanceData,如果没有则返回NULL。 + + +void *PQresultInstanceData(const PGresult *res, PGEventProc proc); + + + + + + + + + 事件实例 + + + 这里是一个管理与 libpq 连接和结果相关的私有数据的示例的框架。 + + + + + +/* The instanceData */ +typedef struct +{ + int n; + char *str; +} mydata; + +/* PGEventProc */ +static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough); + +int +main(void) +{ + mydata *data; + PGresult *res; + PGconn *conn = + PQconnectdb("dbname=postgres options=-csearch_path="); + + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(conn)); + PQfinish(conn); + return 1; + } + + /* called once on any connection that should receive events. + * Sends a PGEVT_REGISTER to myEventProc. + */ + if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL)) + { + fprintf(stderr, "Cannot register PGEventProc\n"); + PQfinish(conn); + return 1; + } + + /* conn instanceData is available */ + data = PQinstanceData(conn, myEventProc); + + /* Sends a PGEVT_RESULTCREATE to myEventProc */ + res = PQexec(conn, "SELECT 1 + 1"); + + /* result instanceData is available */ + data = PQresultInstanceData(res, myEventProc); + + /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */ + res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS); + + /* result instanceData is available if PG_COPYRES_EVENTS was + * used during the PQcopyResult call. + */ + data = PQresultInstanceData(res_copy, myEventProc); + + /* Both clears send a PGEVT_RESULTDESTROY to myEventProc */ + PQclear(res); + PQclear(res_copy); + + /* Sends a PGEVT_CONNDESTROY to myEventProc */ + PQfinish(conn); + + return 0; +} + +static int +myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) +{ + switch (evtId) + { + case PGEVT_REGISTER: + { + PGEventRegister *e = (PGEventRegister *)evtInfo; + mydata *data = get_mydata(e->conn); + + /* associate app specific data with connection */ + PQsetInstanceData(e->conn, myEventProc, data); + break; + } + + case PGEVT_CONNRESET: + { + PGEventConnReset *e = (PGEventConnReset *)evtInfo; + mydata *data = PQinstanceData(e->conn, myEventProc); + + if (data) + memset(data, 0, sizeof(mydata)); + break; + } + + case PGEVT_CONNDESTROY: + { + PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo; + mydata *data = PQinstanceData(e->conn, myEventProc); + + /* free instance data because the conn is being destroyed */ + if (data) + free_mydata(data); + break; + } + + case PGEVT_RESULTCREATE: + { + PGEventResultCreate *e = (PGEventResultCreate *)evtInfo; + mydata *conn_data = PQinstanceData(e->conn, myEventProc); + mydata *res_data = dup_mydata(conn_data); + + /* associate app specific data with result (copy it from conn) */ + PQsetResultInstanceData(e->result, myEventProc, res_data); + break; + } + + case PGEVT_RESULTCOPY: + { + PGEventResultCopy *e = (PGEventResultCopy *)evtInfo; + mydata *src_data = PQresultInstanceData(e->src, myEventProc); + mydata *dest_data = dup_mydata(src_data); + + /* associate app specific data with result (copy it from a result) */ + PQsetResultInstanceData(e->dest, myEventProc, dest_data); + break; + } + + case PGEVT_RESULTDESTROY: + { + PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo; + mydata *data = PQresultInstanceData(e->result, myEventProc); + + /* free instance data because the result is being destroyed */ + if (data) + free_mydata(data); + break; + } + + /* unknown event ID, just return TRUE. */ + default: + break; + } + + return TRUE; /* event processing succeeded */ +} +]]> + + + + + + 环境变量 + + + 环境变量 + + + 以下环境变量可用于选择连接参数的默认值,供以下函数使用:PQconnectdbPQsetdbLoginPQsetdb,前提是调用代码没有直接指定这些参数的值。例如,这样可以避免在简单的客户端应用程序中硬编码数据库连接信息。 + + + + + PGHOST + + PGHOST的行为与连接参数相同。 + + + + + + + + PGHOSTADDR + + PGHOSTADDR的行为与连接参数相同。 + 这可以替代或者与PGHOST一起设置,以避免DNS查找开销。 + + + + + + + + PGPORT + + PGPORT的行为与连接参数相同。 + + + + + + + + PGDATABASE + + PGDATABASE的行为与连接参数相同。 + + + + + + + + PGUSER + + PGUSER的行为与连接参数相同。 + + + + + + + + PGPASSWORD + + PGPASSWORD的行为与连接参数相同。 + 出于安全原因,不建议使用此环境变量,因为一些操作系统允许非root用户通过ps查看进程环境变量; + 而应考虑使用~/.pgpass文件(参见)。 + + + + + + + + PGPASSFILE + + PGPASSFILE指定用于查找的密码文件名。如果未设置,默认为~/.pgpass(参见)。 + + + + + + + + PGSERVICE + + PGSERVICE的行为与连接参数相同。 + + + + + + + + PGSERVICEFILE + + PGSERVICEFILE指定每个用户的连接服务文件的名称。如果未设置,默认为~/.pg_service.conf(参见)。 + + + + + + + + PGOPTIONS + + PGOPTIONS的行为与连接参数相同。 + + + + + + + + PGAPPNAME + + PGAPPNAME的行为与连接参数相同。 + + + + + + + + PGSSLMODE + + PGSSLMODE的行为与连接参数相同。 + + + + + + + + PGREQUIRESSL + + PGREQUIRESSL的行为与连接参数相同。 + 这个环境变量已被弃用,推荐使用PGSSLMODE变量;设置这两个变量会抑制这个变量的效果。 + + + + + + + + PGSSLCOMPRESSION + + PGSSLCOMPRESSION的行为与连接参数相同。 + + + + + + + + PGSSLCERT + + PGSSLCERT的行为与连接参数相同。 + + + + + + + + PGSSLKEY + + PGSSLKEY的行为与连接参数相同。 + + + + + + + + PGSSLROOTCERT + + PGSSLROOTCERT的行为与连接参数相同。 + + + + + + + + PGSSLCRL + + PGSSLCRL的行为与连接参数相同。 + + + + + + + + PGREQUIREPEER + + PGREQUIREPEER的行为与连接参数相同。 + + + + + + + + PGKRBSRVNAME + + PGKRBSRVNAME的行为与连接参数相同。 + + + + + + + + PGGSSLIB + + PGGSSLIB的行为与连接参数相同。 + + + + + + + + PGCONNECT_TIMEOUT + + PGCONNECT_TIMEOUT的行为与连接参数相同。 + + + + + + + + PGCLIENTENCODING + + PGCLIENTENCODING的行为与连接参数相同。 + + + + + + 以下环境变量可用于指定每个PostgreSQL会话的默认行为。(也可参见命令,了解按用户或按数据库设置默认行为的方法。) + + + + PGDATESTYLE + + PGDATESTYLE设置日期/时间表示的默认风格(等同于SET datestyle TO ...)。 + + + + + + + PGTZ + + PGTZ设置默认的时区(等同于SET timezone TO ...)。 + + + + + + + PGGEQO + + PGGEQO为遗传查询优化器设置默认模式(等同于SET geqo TO ...)。 + + + 有关这些环境变量的正确取值,请参见SQL命令 + + + 下面的环境变量决定libpq的内部行为,它们会覆盖编译在程序中的默认值。 + + + + + + PGSYSCONFDIR + + PGSYSCONFDIR设置包含pg_service.conf文件以及未来版本中可能出现的其他系统范围配置文件的目录。 + + + + + + + PGLOCALEDIR + + PGLOCALEDIR设置包含用于消息本地化的locale文件的目录。 + + + + + + + + + + 密码文件 + + + 密码文件 + + + .pgpass + + + + 用户主目录中的.pgpass文件或PGPASSFILE引用的文件可保存密码,供连接需要密码且尚未通过其他方式指定密码时使用。在 Microsoft Windows 上,文件名为%APPDATA%\postgresql\pgpass.conf(其中%APPDATA%指用户配置文件中的应用数据子目录)。 + + + 该文件中的行应采用以下格式: +hostname:port:database:username:password +(可以复制上面这一行,并在行首加上#,在文件中加入提示注释。)前四个字段中的每一个都可以是字面值,或者是*,后者可以匹配任何内容。将使用与当前连接参数匹配的第一行中的密码字段。(因此,使用通配符时,应将更具体的条目放在前面。)如果条目需要包含:\,请使用\转义该字符。主机名localhost同时匹配来自本地机器的 TCP 连接(主机名localhost)和 Unix 域套接字连接(pghost为空或为默认套接字目录)。在备库中,数据库字段为replication时,匹配连接到主库的流复制连接。除此之外,database字段的用途有限,因为同一用户在同一数据库集簇的所有数据库中使用相同的密码。 + + + 在 Unix 系统上,.pgpass文件上的权限必须不允许所有人或组内访问,可以用chmod 0600 ~/.pgpass这样的命令实现。如果权限没有这么严格,该文件将被忽略。在微软 Windows 上,该文件被假定存储在一个安全的目录中,因此不会进行特别的权限检查。 + + + + + + + 连接服务文件 + + + 连接服务文件 + + + + pg_service.conf + + + + .pg_service.conf + + + + 连接服务文件允许 libpq 连接参数与一个单一服务名称关联。 + 之后在一个 libpq 连接中就可以指定那个服务名称,与其相关的设置将被使用。 + 这允许在不重新编译 libpq 应用的前提下修改连接参数。 + 服务名称也可以使用PGSERVICE环境变量来指定。 + + + + 连接服务文件可以是位于~/.pg_service.conf或由环境变量PGSERVICEFILE指定的 + 位置处的每用户服务文件,也可以是位于`pg_config --sysconfdir`/pg_service.conf或由环境变量 + PGSYSCONFDIR指定的目录中的系统范围文件。如果在用户文件和系统文件中存在同名的服务定义, + 则用户文件优先。 + + + + 该文件使用一种INI 文件格式,其中小节名是服务名并且参数是连接参数。 + 列表见。例如: + +# comment +[mydb] +host=somehost +port=5433 +user=admin + + 在share/pg_service.conf.sample中提供了一个示例文件。 + + + + + + + 连接参数的 LDAP 查找 + + + LDAP 连接参数查找 + + + + 如果libpq已经在编译时打开了 LDAP 支持(configure的选项),就可以通过 LDAP 从一个中央服务器检索hostdbname之类的连接参数。这样做的好处是如果一个数据库的连接参数改变,不需要在所有的客户端机器上更新连接信息。 + + + LDAP 连接参数查询使用连接服务文件pg_service.conf(参见)。在pg_service.conf的配置段中,以ldap://开头的行会被识别为 LDAP URL,并执行 LDAP 查询。结果必须是一个keyword = value键值对列表,用于设置连接选项。URL 必须符合 RFC 1959,格式如下: +ldap://[hostname[:port]]/search_base?attribute?search_scope?filter +其中,hostname默认为localhostport默认为 389。 + + LDAP 查找成功后就会停止处理 pg_service.conf;如果无法联系 LDAP 服务器,则会继续处理。这使后续指向其他 LDAP 服务器的 LDAP URL 行、常规的 keyword = value 对或默认连接选项能够作为后备。如果希望在这种情况下得到错误消息,可以在 LDAP URL 后添加一个语法不正确的行。 + + 例如,使用以下 LDIF 文件创建的 LDAP 条目: +version:1 +dn:cn=mydatabase,dc=mycompany,dc=com +changetype:add +objectclass:top +objectclass:device +cn:mydatabase +description:host=dbserver.mycompany.com +description:port=5439 +description:dbname=mydb +description:user=mydb_user +description:sslmode=require +可以通过以下 LDAP URL 查询: +ldap://ldap.mycompany.com/dc=mycompany,dc=com?description?one?(cn=mydatabase) + + + + + 你也可以将常规的服务文件条目和 LDAP 查找混合。pg_service.conf中一节的完整示例: + +# 只有主机和端口存储在LDAP中,显式指定dbname和user。 +[customerdb] +dbname=customer +user=appuser +ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*) + + + + + + + + SSL 支持 + + + SSL + + + PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见 + + + libpq读取系统范围的OpenSSL配置文件。默认情况下,这个文件被命名为openssl.cnf并且位于openssl version -d所报告的目录中。可以通过设置环境变量OPENSSL_CONF把这个默认值覆盖为想要的配置文件的名称。 + + + + 服务器证书的客户端验证 + + + 默认情况下,PostgreSQL将不会执行服务器证书的任何验证。这意味着可以在不被客户端知晓的情况下伪造服务器身份(例如通过修改一个 DNS 记录或者接管服务器的 IP 地址)。为了阻止哄骗,客户端必须能够通过一条信任链验证服务器的身份。信任链可以这样建立:在一台计算机上放置一个根(自签名的)证书机构(CA)的证书并且在另一台计算机上放置一个由根证书签发的叶子证书。还可以使用一种中间证书,它由根证书签发并且可以签发叶子证书。 + + + 要让客户端验证服务器的身份,请在客户端放置根证书,并在服务器上放置由该根证书签名的叶证书。要让服务器验证客户端的身份,请在服务器上放置根证书,并在客户端放置由该根证书签名的叶证书。也可以使用一个或多个中间证书(通常与叶证书存储在一起),将叶证书链接到根证书。 + + 建立信任链后,客户端可以通过两种方式验证服务器发送的叶证书。如果参数 sslmode 设为 verify-ca,libpq 会沿证书链检查到存储在客户端上的根证书,以验证服务器是否可信。如果 sslmode 设为 verify-full,libpq 还会验证服务器主机名是否与服务器证书中存储的名称匹配。如果无法验证服务器证书,SSL 连接将失败。在大多数对安全敏感的环境中,建议使用 verify-full + + verify-full 模式下,会将主机名与证书的主体替代名称属性匹配;如果不存在类型为 dNSName 的主体替代名称,则与通用名称属性匹配。如果证书的名称属性以星号(*)开头,该星号会被视为通配符,匹配点(.)以外的所有字符。这意味着该证书不会匹配子域。如果使用 IP 地址而不是主机名建立连接,则会匹配该 IP 地址(不执行任何 DNS 查询)。 + + + 要允许服务器证书验证,必须将一个或者更多个根证书放置在用户主目录下的~/.postgresql/root.crt文件中(在Microsoft Windows上该文件名为%APPDATA%\postgresql\root.crt)。如果需要把服务器发来的证书链链接到存储在客户端的根证书,还应该将中间证书加到该文件中。 + + + + 如果文件~/.postgresql/root.crl存在(微软 Windows 上的%APPDATA%\postgresql\root.crl),证书撤销列表(CRL)项也会被检查。 + + + 可以通过设置连接参数 sslrootcertsslcrl,或环境变量 PGSSLROOTCERTPGSSLCRL,更改根证书文件与 CRL 的位置。 + + + + + 为了与 PostgreSQL 的早期版本达到向后兼容,如果存在一个根 CA 文件,sslmode=require的行为将与verify-ca相同,即服务器证书根据 CA 验证。我们鼓励依赖这种行为,并且需要证书验证的应用应该总是使用verify-ca或者verify-full。 + + + + + + 客户端证书 + + + 如果服务器尝试通过请求客户端的叶证书来验证客户端的身份, + libpq将发送存储在文件 + ~/.postgresql/postgresql.crt中的证书,该文件位于用户的主目录中。 + 证书必须链到服务器信任的根证书。匹配的 + 私钥文件~/.postgresql/postgresql.key也必须存在。私钥 + 文件的权限必须不允许任何对世界或组的访问;可以通过命令 + chmod 0600 ~/.postgresql/postgresql.key实现。 + 在Microsoft Windows上,这些文件的名称分别为 + %APPDATA%\postgresql\postgresql.crt和 + %APPDATA%\postgresql\postgresql.key,并且不进行特别的 + 权限检查,因为该目录被假定为安全的。 + 证书和密钥文件的位置可以通过连接参数 + sslcert和sslkey或环境变量PGSSLCERTPGSSLKEY来覆盖。 + + + postgresql.crt 中的第一个证书必须是客户端证书,因为它必须与客户端私钥匹配。可以选择在文件后面追加中间证书,这样就无需在服务器的 root.crt 文件中存储中间证书。 + + + 创建证书的指令请参考。 + + + + + 不同模式中提供的保护 + + sslmode参数选择不同的值可以提供不同程度的保护。SSL 可以防范三类攻击: + + 窃听 + + 如果一个第三方能够检查客户端和服务器之间的网络流量,它能读取连接信息(包括用户名和密码)以及被传递的数据。SSL使用加密来阻止这种攻击。 + + + + + + 中间人(MITM + + 如果一个第三方能对客户端和服务器之间传送的数据进行修改,它就能假装是服务器并且因此能看见并且修改数据,即使这些数据已被加密。然后第三方可以将连接信息和数据转送给原来的服务器,使得它不可能检测到攻击。这样做的通常途径包括 DNS 污染和地址劫持,借此客户端被重定向到一个不同的服务器。还有几种其他的攻击方式能够完成这种攻击。SSL使用证书验证让客户端认证服务器,就可以阻止这种攻击。 + + + + + + 模仿 + + 如果一个第三方能假装是一个授权的客户端,它能够简单地访问它本不能访问的数据。通常这可以由不安全的密码管理所致。SSL使用客户端证书来确保只有持有合法证书的客户端才能访问服务器,这样就能阻止这种攻击。 + + + + + + + + 对于一个已知受 SSL 保护的连接,在连接建立之前,必须在客户端和服务器两端都配置 SSL。如果只在服务器端配置,客户端在得知服务器要求高安全性之前,可能就已经开始发送敏感信息(例如密码)。在 libpq 中,要确保连接安全,可以把sslmode参数设置为verify-fullverify-ca,并为系统提供一个用于验证的根证书。这类似于使用https URL浏览加密网页。 + + + + 一旦服务器已经被认证,客户端可以传递敏感数据。这意味着直到这一点,客户端都不需要知道是否证书将被用于认证,这样只需要在服务器配置中指定就比较安全。 + + + + 所有SSL选项都带来了加密和密钥交换的负荷,因此必须在性能和安全性之间做出平衡。不同sslmode值所保护的风险,以及它们是怎样看待安全性和负荷的。 + + + + SSL 模式描述 + + + + sslmode + 窃听保护 + MITM 防护 + 声明 + + + + + + + disable + + + 我不关心安全性,并且我不想为加密增加负荷。 + + + + + allow + 可能 + + 我不关心安全性,但如果服务器坚持,我将承担加密带来的负荷。 + + + + + prefer + 可能 + + 我不关心安全性,但如果服务器支持,我希望承担加密带来的负荷。 + + + + + require + + + 我想要对数据加密,并且我接受因此带来的负荷。我信任该网络会保证我总是连接到想要连接的服务器。 + + + + + verify-ca + + 取决于 CA策略 + 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器。 + + + + + verify-full + + + 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器,并且就是我指定的那一个。 + + + + + +
+ + + verify-caverify-full之间的区别取决于根CA的策略。如果使用了一个公共CAverify-ca允许连接到那些可能已经被其他人注册到该CA的服务器。在这种情况下,总是应该使用verify-full。如果使用了一个本地CA或者甚至是一个自签名的证书,使用verify-ca常常就可以提供足够的保护。 + + + + sslmode的默认值是prefer。如表中所示,这在安全性的角度来说没有意义,并且它只承诺可能的性能负荷。提供它作为默认值只是为了向后兼容,并且我们不推荐在安全部署中使用它。 + + +
+ + + + SSL 客户端文件使用 + + + 总结了与客户端 SSL 设置相关的文件。 + + + + + libpq/客户端 SSL 文件用法 + + + + + 文件 + 内容 + 效果 + + + + + + + ~/.postgresql/postgresql.crt + 客户端证书 + 发送到服务器 + + + + ~/.postgresql/postgresql.key + 客户端私钥 + 证明客户端证书是由拥有者发送;不代表证书拥有者可信 + + + + ~/.postgresql/root.crt + 可信的证书机构 + 检查服务器证书是由一个可信的证书机构签发 + + + + ~/.postgresql/root.crl + 被证书机构撤销的证书 + 服务器证书不能在这个列表上 + + + + +
+
+ + + SSL 库初始化 + + + 如果您的应用程序初始化libssl和/或libcrypto库,并且libpq + 构建时带有SSL支持,您应该调用PQinitOpenSSL告诉libpq + libssl和/或libcrypto库已被您的应用程序初始化,以便 + libpq不会再初始化这些库。 + + + + + + PQinitOpenSSL PQinitOpenSSL + + + + 允许应用选择要初始化哪个安全性库。 + +void PQinitOpenSSL(int do_ssl, int do_crypto); + + + + + 当do_ssl是非零时,libpq将在第一次打开数据库连接前初始化OpenSSL库。 + 当do_crypto是非零时,libcrypto库将被初始化。 + 默认情况下(如果没有调用PQinitOpenSSL),两个库都会被初始化。 + 当 SSL 支持没有被编译时,这个函数也存在但是什么也不做。 + + + + 如果你的应用使用并且初始化OpenSSL或者它的底层libcrypto库,你必须在第一次打开数据库连接前调用这个函数,并把相应参数设为零。 + 同时要确保在打开一个数据库连接前已经完成了初始化。 + + + + + + PQinitSSL PQinitSSL + + + + 允许应用选择要初始化哪个安全性库。 + +void PQinitSSL(int do_ssl); + + + + + 这个函数等效于PQinitOpenSSL(do_ssl, do_ssl)。 + 这对于要么初始化OpenSSL以及libcrypto要么都不初始化的应用足够用了。 + + + + PQinitSSLPostgreSQL 8.0 就存在了, + 而PQinitOpenSSL直到PostgreSQL 8.4 才被加入,因此PQinitSSL可能对那些需要与旧版本libpq一起工作的应用来说更合适。 + + + + + + + +
+ + + + 在线程化程序中的行为 + + + 线程 + 用于 libpq + + + libpq 默认是可重入且线程安全的。编译应用程序代码时,可能需要使用特殊的编译器命令行选项。有关如何构建支持线程的应用程序,请参阅系统文档,或查看 src/Makefile.global 中的 PTHREAD_CFLAGSPTHREAD_LIBS。以下函数可用于查询 libpq 的线程安全状态: + + + + PQisthreadsafe PQisthreadsafe + + + + 返回libpq库的线程安全状态。 + +int PQisthreadsafe(); + + + + + 如果libpq是线程安全的则返回 1,否则返回 0。 + + + + + + 线程使用的一项限制是:两个线程不能同时操作同一个 PGconn 对象。尤其不能通过同一个连接对象从不同线程并发发出命令。(如果需要并发执行命令,请使用多个连接。) + + + PGresult对象在创建后通常是只读的,因此可以在线程之间自由传递。不过,如果你使用中描述的任何会修改PGresult的函数,则需要自行避免对同一个PGresult执行并发操作。 + + + 已弃用的 PQrequestCancelPQoidStatus 函数不是线程安全的,不应在多线程程序中使用。可以用 PQcancel 替代 PQrequestCancel,用 PQoidValue 替代 PQoidStatus + + + 如果你在应用程序中使用 Kerberos(除了libpq内部之外),则需要在 Kerberos 调用周围加锁,因为 Kerberos 函数不是线程安全的。可参考libpq源代码中的PQregisterThreadLock函数,它提供了一种在libpq与应用程序之间协作加锁的方法。 + + + + + + + 编译 <application>libpq</application> 程序 + + + 编译 + libpq 应用 + + + + 要编译(即编译并且链接)一个使用libpq的程序,你需要做下列所有的事情: + + + + + 包括libpq-fe.h头文件: + +#include <libpq-fe.h> + + 如果你无法这样做,那么你通常会从你的编译器得到像这样的错误消息: + +foo.c: In function `main': +foo.c:34: `PGconn' undeclared (first use in this function) +foo.c:35: `PGresult' undeclared (first use in this function) +foo.c:54: `CONNECTION_BAD' undeclared (first use in this function) +foo.c:68: `PGRES_COMMAND_OK' undeclared (first use in this function) +foo.c:95: `PGRES_TUPLES_OK' undeclared (first use in this function) + + + + + + + 通过为你的编译器提供-Idirectory选项,向你的编译器指出PostgreSQL头文件安装在哪里(在某些情况下编译器默认将查看该目录,因此你可以忽略这个选项)。例如你的编译命令行可能看起来像: + +cc -c -I/usr/local/pgsql/include testprog.c + + 如果你在使用 makefile,那么把该选项加到CPPFLAGS变量中: + +CPPFLAGS += -I/usr/local/pgsql/include + + + + + 如果你的程序可能由其他用户编译,那么你不应该像那样硬编码目录位置。你可以运行工具pg_configpg_configwith libpq在本地系统上找出头文件在哪里: + +$ pg_config --includedir +/usr/local/include + + + + + 如果你安装了pkg-configpkg-configwith + libpq,你可以运行: + +$ pkg-config --cflags libpq +-I/usr/local/include + + 注意这将在路径前面包括。 + + + + 无法为编译器指定正确的选项将导致一个错误消息,例如: + +testlibpq.c:8:22: libpq-fe.h: No such file or directory + + + + + + + 当链接最终的程序时,指定选项-lpq,这样libpq库会被编译进去,也可以用选项-Ldirectory向编译器指出libpq库所在的位置(再次,编译器将默认搜索某些目录)。为了最大的可移植性,将选项放在选项前面。例如: + +cc -o testprog testprog1.o testprog2.o -L/usr/local/pgsql/lib -lpq + + + + + 你也可以使用pg_config找出库目录: + +$ pg_config --libdir +/usr/local/pgsql/lib + + + + + 或者再次使用pkg-config: + +$ pkg-config --libs libpq +-L/usr/local/pgsql/lib -lpq + + 再次提示这会打印出全部的选项,而不仅仅是路径。 + + + + 指出这一部分问题的错误消息可能看起来像: + +testlibpq.o: In function `main': +testlibpq.o(.text+0x60): undefined reference to `PQsetdbLogin' +testlibpq.o(.text+0x71): undefined reference to `PQstatus' +testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage' + + 这意味着你忘了 . + +/usr/bin/ld: cannot find -lpq + + 这意味着你忘记了选项或者没有指定正确的目录。 + + + + + + + + + + 示例程序 + + + 这些示例和其他示例可以在源代码发布的src/test/examples目录中找到。 + + + + <application>libpq</application> 示例程序 1 + + + +#include +#include "libpq-fe.h" + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + int nFields; + int i, + j; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious users can't take control. */ + res = PQexec(conn, + "SELECT pg_catalog.set_config('search_path', '', false)"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SET failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + /* + * Should PQclear PGresult whenever it is no longer needed to avoid memory + * leaks + */ + PQclear(res); + + /* + * Our test case here involves using a cursor, for which we must be inside + * a transaction block. We could do the whole thing with a single + * PQexec() of "select * from pg_database", but that's too trivial to make + * a good example. + */ + + /* Start a transaction block */ + res = PQexec(conn, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* + * Fetch rows from pg_database, the system catalog of databases + */ + res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + res = PQexec(conn, "FETCH ALL in myportal"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + /* first, print out the attribute names */ + nFields = PQnfields(res); + for (i = 0; i < nFields; i++) + printf("%-15s", PQfname(res, i)); + printf("\n\n"); + + /* next, print out the rows */ + for (i = 0; i < PQntuples(res); i++) + { + for (j = 0; j < nFields; j++) + printf("%-15s", PQgetvalue(res, i, j)); + printf("\n"); + } + + PQclear(res); + + /* close the portal ... we don't bother to check for errors ... */ + res = PQexec(conn, "CLOSE myportal"); + PQclear(res); + + /* end the transaction */ + res = PQexec(conn, "END"); + PQclear(res); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} +]]> + + + + + <application>libpq</application>示例程序 2 + + + +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "libpq-fe.h" + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + PGnotify *notify; + int nnotifies; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious users can't take control. */ + res = PQexec(conn, + "SELECT pg_catalog.set_config('search_path', '', false)"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SET failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + /* + * Should PQclear PGresult whenever it is no longer needed to avoid memory + * leaks + */ + PQclear(res); + + /* + * Issue LISTEN command to enable notifications from the rule's NOTIFY. + */ + res = PQexec(conn, "LISTEN TBL2"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* Quit after four notifies are received. */ + nnotifies = 0; + while (nnotifies < 4) + { + /* + * Sleep until something happens on the connection. We use select(2) + * to wait for input, but you could also use poll() or similar + * facilities. + */ + int sock; + fd_set input_mask; + + sock = PQsocket(conn); + + if (sock < 0) + break; /* shouldn't happen */ + + FD_ZERO(&input_mask); + FD_SET(sock, &input_mask); + + if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) + { + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + exit_nicely(conn); + } + + /* Now check for input */ + PQconsumeInput(conn); + while ((notify = PQnotifies(conn)) != NULL) + { + fprintf(stderr, + "ASYNC NOTIFY of '%s' received from backend PID %d\n", + notify->relname, notify->be_pid); + PQfreemem(notify); + nnotifies++; + PQconsumeInput(conn); + } + } + + fprintf(stderr, "Done.\n"); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} +]]> + + + + + <application>libpq</application>示例程序 3 + + + +#endif + +#include +#include +#include +#include +#include +#include "libpq-fe.h" + +/* for ntohl/htonl */ +#include +#include + + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +/* + * This function prints a query result that is a binary-format fetch from + * a table defined as in the comment above. We split it out because the + * main() function uses it twice. + */ +static void +show_binary_results(PGresult *res) +{ + int i, + j; + int i_fnum, + t_fnum, + b_fnum; + + /* Use PQfnumber to avoid assumptions about field order in result */ + i_fnum = PQfnumber(res, "i"); + t_fnum = PQfnumber(res, "t"); + b_fnum = PQfnumber(res, "b"); + + for (i = 0; i < PQntuples(res); i++) + { + char *iptr; + char *tptr; + char *bptr; + int blen; + int ival; + + /* Get the field values (we ignore possibility they are null!) */ + iptr = PQgetvalue(res, i, i_fnum); + tptr = PQgetvalue(res, i, t_fnum); + bptr = PQgetvalue(res, i, b_fnum); + + /* + * The binary representation of INT4 is in network byte order, which + * we'd better coerce to the local byte order. + */ + ival = ntohl(*((uint32_t *) iptr)); + + /* + * The binary representation of TEXT is, well, text, and since libpq + * was nice enough to append a zero byte to it, it'll work just fine + * as a C string. + * + * The binary representation of BYTEA is a bunch of bytes, which could + * include embedded nulls so we have to pay attention to field length. + */ + blen = PQgetlength(res, i, b_fnum); + + printf("tuple %d: got\n", i); + printf(" i = (%d bytes) %d\n", + PQgetlength(res, i, i_fnum), ival); + printf(" t = (%d bytes) '%s'\n", + PQgetlength(res, i, t_fnum), tptr); + printf(" b = (%d bytes) ", blen); + for (j = 0; j < blen; j++) + printf("\\%03o", bptr[j]); + printf("\n\n"); + } +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + const char *paramValues[1]; + int paramLengths[1]; + int paramFormats[1]; + uint32_t binaryIntVal; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious users can't take control. */ + res = PQexec(conn, "SET search_path = testlibpq3"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "SET failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* + * The point of this program is to illustrate use of PQexecParams() with + * out-of-line parameters, as well as binary transmission of data. + * + * This first example transmits the parameters as text, but receives the + * results in binary format. By using out-of-line parameters we can avoid + * a lot of tedious mucking about with quoting and escaping, even though + * the data is text. Notice how we don't have to do anything special with + * the quote mark in the parameter value. + */ + + /* Here is our out-of-line parameter value */ + paramValues[0] = "joe's place"; + + res = PQexecParams(conn, + "SELECT * FROM test1 WHERE t = $1", + 1, /* one param */ + NULL, /* let the backend deduce param type */ + paramValues, + NULL, /* don't need param lengths since text */ + NULL, /* default to all text params */ + 1); /* ask for binary results */ + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + show_binary_results(res); + + PQclear(res); + + /* + * In this second example we transmit an integer parameter in binary form, + * and again retrieve the results in binary form. + * + * Although we tell PQexecParams we are letting the backend deduce + * parameter type, we really force the decision by casting the parameter + * symbol in the query text. This is a good safety measure when sending + * binary parameters. + */ + + /* Convert integer value "2" to network byte order */ + binaryIntVal = htonl((uint32_t) 2); + + /* Set up parameter arrays for PQexecParams */ + paramValues[0] = (char *) &binaryIntVal; + paramLengths[0] = sizeof(binaryIntVal); + paramFormats[0] = 1; /* binary */ + + res = PQexecParams(conn, + "SELECT * FROM test1 WHERE i = $1::int4", + 1, /* one param */ + NULL, /* let the backend deduce param type */ + paramValues, + paramLengths, + paramFormats, + 1); /* ask for binary results */ + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + show_binary_results(res); + + PQclear(res); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} +]]> + + + + +
diff --git a/zh/9.6/lo.sgml b/zh/9.6/lo.sgml new file mode 100644 index 00000000..668d5229 --- /dev/null +++ b/zh/9.6/lo.sgml @@ -0,0 +1,91 @@ + + + + lo — 管理大对象 + + + lo + + + + lo模块为管理大对象(也称为 LO 或 BLOB)提供支持,其中包括数据类型lo和触发器lo_manage。 + + + + 原理 + + + JDBC 驱动的一个问题是(ODBC 驱动也受此影响),其规范假定对 BLOB(二进制大对象)的引用存储在表内,并且如果该表项被更改,相关的 BLOB 就会从数据库中删除。 + + + + 但在目前的PostgreSQL中并非如此。大对象被视为独立对象;表中的一个项可以通过 OID 引用某个大对象,但也可能有多个表项引用同一个大对象 OID,因此系统不会仅因你更改或删除了其中一个表项就删除该大对象。 + + + + 这对于PostgreSQL专用应用没有问题,但使用 JDBC 或 ODBC 的标准代码不会删除这些对象,从而产生孤立对象,也就是不再被任何内容引用、只是占用磁盘空间的对象。 + + + + lo模块允许通过把触发器附加到包含 LO 引用列的表上来解决这个问题。该触发器本质上就是在你删除或修改引用大对象的值时调用lo_unlink。使用这个触发器时,你实际上是假定:凡是出现在受该触发器控制列中的大对象,在数据库中都只有一个引用! + + + + 该模块还提供了数据类型lo,它实际上只是oid类型的一个域。这有助于区分保存大对象引用的数据库列与保存其他对象 OID 的列。使用该触发器并不要求必须使用lo类型,但用它来标识数据库中哪些列表示由该触发器管理的大对象,可能会更方便。还有传言说,如果 BLOB 列不用lo,ODBC 驱动会感到困惑。 + + + + + 如何使用 + + + 下面是一个简单的用法示例: + + + +CREATE TABLE image (title TEXT, raster lo); + +CREATE TRIGGER t_raster BEFORE UPDATE OR DELETE ON image + FOR EACH ROW EXECUTE PROCEDURE lo_manage(raster); + + + + 对于每个将保存指向大对象的唯一引用的列,创建一个BEFORE UPDATE OR DELETE触发器,并将该列名作为唯一的触发器参数。你也可以使用BEFORE UPDATE OF column_name,将该触发器限制为仅在更新该列时执行。如果同一张表中需要多个lo列,就为每一列分别创建一个触发器,并记得为同一张表上的每个触发器指定不同的名称。 + + + + + 限制 + + + + + 删除表时,其中包含的任何对象仍会变成孤立对象,因为这种情况下不会执行触发器。为避免这种情况,可以先执行DELETE FROM table,再执行DROP TABLE。 + + + + TRUNCATE也有同样的风险。 + + + + 如果你已经有,或者怀疑有,孤立的大对象,请参见模块来帮助清理它们。偶尔运行vacuumlo,作为对lo_manage触发器的补充保障,是个不错的主意。 + + + + + + 某些前端可能会创建自己的表,但不会创建相应的触发器。此外,用户也可能不记得(或根本不知道)要创建这些触发器。 + + + + + + + 作者 + + + Peter Mount peter@retep.org.uk + + + + diff --git a/zh/9.6/lobj.sgml b/zh/9.6/lobj.sgml new file mode 100644 index 00000000..839782dc --- /dev/null +++ b/zh/9.6/lobj.sgml @@ -0,0 +1,706 @@ + + + + 大对象 + + large object + BLOBlarge object + + + PostgreSQL提供一种大对象机制,允许以流式方式访问存储在专用大对象结构中的用户数据。在处理大到无法方便地整体操作的数据值时,这种流式访问非常有用。 + + + + 本章介绍PostgreSQL大对象数据的实现,以及相应的编程接口和查询语言接口。本章示例使用libpq C 库,但大多数PostgreSQL原生编程接口都支持等效的功能。其他接口也可能在内部使用大对象接口来为大值提供通用支持,这里不作说明。 + + + + 简介 + + + TOAST + versus large objects + + + + 所有大对象都存储在一个名为pg_largeobject的系统表中。每个大对象在系统表pg_largeobject_metadata中也有一个条目。大对象可以使用一种类似标准文件操作的读写 API 来创建、修改和删除。 + + + + PostgreSQL还支持一种名为TOAST的存储系统,它会自动将大于单个数据库页的值存储到每个表各自的二级存储区域中。这使得大对象机制在一定程度上已经过时。大对象机制仍然保留的一个优势是,它允许的值大小可达 4 TB,而经过TOAST处理的字段最大只能到 1 GB。此外,可以高效地读取和更新大对象的一部分,而对于经过TOAST处理的字段,大多数操作都会把整个值作为一个整体读出或写入。 + + + + + + 实现特性 + + + 大对象实现会把大对象拆分成若干,并将这些块存储在数据库行中。一个 B-树索引保证在执行随机访问读写时,能够快速找到正确的块号。 + + + + 为一个大对象存储的块不必是连续的。例如,如果一个应用打开一个新的大对象,定位到偏移量 1000000,然后在那里写入几个字节,并不会因此分配 1000000 字节的存储空间;只会分配覆盖实际写入数据字节范围的那些块。不过,对于最后一个现有块之前任何未分配的位置,读取操作都会返回零字节。这对应于Unix文件系统中稀疏分配文件的常见行为。 + + + + 自PostgreSQL 9.0 起,大对象具有拥有者和一组访问权限,可用管理。读取大对象需要SELECT权限,写入或截断大对象需要UPDATE权限。只有大对象的拥有者(或者数据库超级用户)才能删除、注释或更改大对象的拥有者。要为兼容先前版本而调整这种行为,请参见运行时参数。 + + + + + 客户端接口 + + + 本节描述PostgreSQLlibpq客户端接口库为访问大对象所提供的功能。PostgreSQL的大对象接口是仿照Unix文件系统接口设计的,提供了与openreadwritelseek等相对应的操作。 + + + + 使用这些函数对大对象进行的所有操作都必须发生在一个 SQL 事务块内,因为大对象文件描述符只在事务持续期间有效。 + + + 执行其中任何一个函数时,如果发生错误,函数会返回一个正常情况下不可能出现的值,通常是 0 或 -1。描述错误的消息保存在连接对象中,可以用 PQerrorMessage 获取。 + + + 使用这些函数的客户端应用应包含头文件libpq/libpq-fs.h并与libpq库链接。 + + + + 创建一个大对象 + + + lo_creat + 函数 + +Oid lo_creat(PGconn *conn, int mode); + + 创建一个新的大对象。返回值是分配给新大对象的 OID,失败时为InvalidOid(零)。 + + 自PostgreSQL 8.1 起,mode未被使用且被忽略;不过,为了与更早版本的向后兼容,最好将其设置为INV_READINV_WRITEINV_READ | INV_WRITE。(这些符号常量定义在头文件libpq/libpq-fs.h中。) + + + + 例如: + +inv_oid = lo_creat(conn, INV_READ|INV_WRITE); + + + + + lo_create + 函数 + +Oid lo_create(PGconn *conn, Oid lobjId); + + 也会创建一个新的大对象。要分配的 OID 可以由lobjId指定;如果指定了它,而该 OID 已经被某个大对象使用,则会失败。如果lobjIdInvalidOid(零),则lo_create会分配一个未使用的 OID(其行为与lo_creat相同)。返回值是分配给新大对象的 OID,失败时为InvalidOid(零)。 + + + + lo_createPostgreSQL 8.1 新增的;如果将该函数用于更早版本的服务器,它会失败并返回InvalidOid。 + + + + 例如: + +inv_oid = lo_create(conn, desired_oid); + + + + + + 导入一个大对象 + + + lo_import + 要把一个操作系统文件导入为大对象,调用 + +Oid lo_import(PGconn *conn, const char *filename); + + filename指定要作为大对象导入的操作系统文件名。返回值是分配给新大对象的 OID,失败时为InvalidOid(零)。注意,该文件是由客户端接口库读取的,而不是由服务器读取的;因此它必须存在于客户端文件系统中,并且对客户端应用可读。 + + + + lo_import_with_oid + 函数 + +Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId); + + 也会导入一个新的大对象。要分配的 OID 可以由lobjId指定;如果指定了它,而该 OID 已经被某个大对象使用,则会失败。如果lobjIdInvalidOid(零),则lo_import_with_oid会分配一个未使用的 OID(其行为与lo_import相同)。返回值是分配给新大对象的 OID,失败时为InvalidOid(零)。 + + + + lo_import_with_oidPostgreSQL 8.4 新增的,它在内部使用了 8.1 新增的lo_create;如果将该函数用于 8.0 或更早版本的服务器,它会失败并返回InvalidOid。 + + + + + 导出一个大对象 + + + lo_export + 要把一个大对象导出到操作系统文件中,调用 + +int lo_export(PGconn *conn, Oid lobjId, const char *filename); + + lobjId参数指定要导出的大对象的 OID,filename参数指定操作系统文件名。注意,该文件是由客户端接口库写入的,而不是由服务器写入的。成功时返回 1,失败时返回 -1。 + + + + + 打开一个现有的大对象 + + + lo_open + 要打开一个现有的大对象以供读取或写入,调用 + +int lo_open(PGconn *conn, Oid lobjId, int mode); + + lobjId参数指定要打开的大对象的 OID。mode位控制该对象是以读取(INV_READ)、写入(INV_WRITE)还是两者兼有的方式打开。(这些符号常量定义在头文件libpq/libpq-fs.h中。)lo_open返回一个(非负的)大对象描述符,供后续在lo_readlo_writelo_lseeklo_lseek64lo_telllo_tell64lo_truncatelo_truncate64以及lo_close中使用。该描述符只在当前事务持续期间有效。失败时返回 -1。 + + + + 服务器当前不区分INV_WRITEINV_READ | INV_WRITE这两种模式:在这两种情况下都允许通过该描述符读取。不过,这两种模式与单独使用INV_READ存在一个重要区别:使用INV_READ时,不能通过该描述符写入,而且从该描述符读取到的数据会反映执行lo_open时活动事务快照中的大对象内容,而不受本事务或其他事务之后写入的影响。对于以INV_WRITE打开的描述符,读取返回的数据会反映其他已提交事务的所有写入以及当前事务的写入。这类似于普通 SQL SELECT命令在REPEATABLE READREAD COMMITTED事务模式下的行为差异。 + + + + 例如: + +inv_fd = lo_open(conn, inv_oid, INV_READ|INV_WRITE); + + + + + +向大对象写入数据 + + + lo_write + 函数 + +int lo_write(PGconn *conn, int fd, const char *buf, size_t len); + + 将buf中的len字节(其大小必须为len)写入大对象描述符fdfd参数必须是先前由lo_open返回的大对象描述符。返回值是实际写入的字节数(在当前实现中,除非出错,否则它总会等于len)。发生错误时,返回值为 -1。 + + + + 虽然len参数被声明为size_t,但该函数会拒绝大于INT_MAX的长度值。实际上,无论如何最好按每块最多几兆字节来传输数据。 + + + + +从大对象读取数据 + + + lo_read + 函数 + +int lo_read(PGconn *conn, int fd, char *buf, size_t len); + + 从大对象描述符fd中读取最多len字节到buf中(其大小必须为len)。fd参数必须是先前由lo_open返回的大对象描述符。返回值是实际读取的字节数;如果先到达大对象末尾,该值就会小于len。发生错误时,返回值为 -1。 + + + + 虽然len参数被声明为size_t,但该函数会拒绝大于INT_MAX的长度值。实际上,无论如何最好按每块最多几兆字节来传输数据。 + + + + +在大对象中定位 + + + lo_lseek + 要改变与大对象描述符关联的当前读或写位置,调用 + +int lo_lseek(PGconn *conn, int fd, int offset, int whence); + + 该函数将由fd标识的大对象描述符的当前位置指针移动到由offset指定的新位置。whence的有效值是SEEK_SET(从对象起始处定位)、SEEK_CUR(从当前位置定位)以及SEEK_END(从对象末尾定位)。返回值是新的位置指针,出错时为 -1。 + + + + lo_lseek64 + 当处理大小可能超过 2 GB 的大对象时,改用 + +pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence); + + 该函数的行为与lo_lseek相同,但它既可以接受大于 2 GB 的offset,也可以返回大于 2 GB 的结果。请注意,如果新位置指针会大于 2 GB,lo_lseek将失败。 + + + + lo_lseek64PostgreSQL 9.3 新增的。如果将该函数用于更早版本的服务器,它会失败并返回 -1。 + + + + + +获取大对象的当前位置 + + + lo_tell + 要取得大对象描述符当前的读或写位置,调用 + +int lo_tell(PGconn *conn, int fd); + + 如果发生错误,返回值是 -1。 + + + + lo_tell64 + 当处理大小可能超过 2 GB 的大对象时,改用 + +pg_int64 lo_tell64(PGconn *conn, int fd); + + 该函数的行为与lo_tell相同,但它可以返回大于 2 GB 的结果。请注意,如果当前读/写位置大于 2 GB,lo_tell将失败。 + + + + lo_tell64PostgreSQL 9.3 新增的。如果将该函数用于更早版本的服务器,它会失败并返回 -1。 + + + + +截断一个大对象 + + + lo_truncate + 要把一个大对象截断为给定长度,调用 + +int lo_truncate(PGconn *conn, int fd, size_t len); + + 该函数把大对象描述符fd截断为长度lenfd参数必须是先前由lo_open返回的大对象描述符。如果len大于大对象当前的长度,则会用空字节('\0')把该大对象扩展到指定长度。成功时,lo_truncate返回零;出错时返回值为 -1。 + + + + 与描述符fd关联的读/写位置不会改变。 + + + + 虽然len参数被声明为size_t,但lo_truncate会拒绝大于INT_MAX的长度值。 + + + + lo_truncate64 + 当处理大小可能超过 2 GB 的大对象时,改用 + +int lo_truncate64(PGconn *conn, int fd, pg_int64 len); + + 该函数的行为与lo_truncate相同,但它可以接受大于 2 GB 的len值。 + + + + lo_truncatePostgreSQL 8.3 新增的;如果将该函数用于更早版本的服务器,它会失败并返回 -1。 + + + + lo_truncate64PostgreSQL 9.3 新增的;如果将该函数用于更早版本的服务器,它会失败并返回 -1。 + + + + +关闭一个大对象描述符 + + + lo_close + 可以通过调用 + +int lo_close(PGconn *conn, int fd); + + 来关闭一个大对象描述符,其中fd是由lo_open返回的大对象描述符。成功时,lo_close返回零;出错时返回值为 -1。 + + + + 任何在事务结束时仍保持打开的大对象描述符都会被自动关闭。 + + + + + 移除一个大对象 + + + lo_unlink + 要从数据库中移除一个大对象,调用 + +int lo_unlink(PGconn *conn, Oid lobjId); + + lobjId参数指定要移除的大对象的 OID。成功时返回 1,失败时返回 -1。 + + + + + + +服务器端函数 + + + 专为通过 SQL 操纵大对象而设计的服务器端函数列在中。 + + + + 面向 SQL 的大对象函数 + + + + 函数 + 返回类型 + + 描述 + + 示例 + 结果 + + + + + + lo_from_bytea lo_from_bytea(loid oid, string bytea) + oid + 创建大对象并在其中存储数据,返回其 OID。传入 0 可让系统选择 OID。 + lo_from_bytea(0, '\xffffff00') + 24528 + + + + lo_put lo_put(loid oid, offset bigint, str bytea) + void + 在给定偏移处写入数据。 + lo_put(24528, 1, '\xaa') + + + + + lo_get lo_get(loid oid , from bigint, for int) + bytea + 提取内容或其中的子串。 + lo_get(24528, 0, 3) + \xffaaff + + + + +
+ + + 前面介绍的每个客户端函数都有对应的服务器端函数;实际上,大多数客户端函数都只是等价服务器端函数的接口。通过 SQL 命令调用起来同样方便的有lo_creatlo_creatlo_createlo_unlinklo_unlinklo_importlo_import以及lo_exportlo_export。下面是它们的使用示例: + + +CREATE TABLE image ( + name text, + raster oid +); + +SELECT lo_creat(-1); -- returns OID of new, empty large object + +SELECT lo_create(43213); -- attempts to create large object with OID 43213 + +SELECT lo_unlink(173454); -- deletes large object with OID 173454 + +INSERT INTO image (name, raster) + VALUES ('beautiful image', lo_import('/etc/motd')); + +INSERT INTO image (name, raster) -- same as above, but specify OID to use + VALUES ('beautiful image', lo_import('/etc/motd', 68583)); + +SELECT lo_export(image.raster, '/tmp/motd') FROM image + WHERE name = 'beautiful image'; + + + + 服务器端 lo_importlo_export 函数的行为与客户端对应函数有很大不同。这两个函数使用数据库拥有者用户的权限,读写服务器文件系统中的文件,因此仅限超级用户使用。相比之下,客户端导入和导出函数使用客户端程序的权限,读写客户端文件系统中的文件。客户端函数不要求超级用户权限。 + + + lo_readlo_write的功能也可以通过服务器端调用获得,但服务器端函数名与客户端接口不同,因为它们不包含下划线。必须将这些函数调用为loreadlowrite。 + + +
+ + +示例程序 + + + 是一个示例程序,它展示了如何使用libpq中的大对象接口。程序中有些部分已经被注释掉,但仍保留在源代码中供读者参考。该程序也可以在源代码发行版的src/test/examples/testlo.c中找到。 + + + + 用<application>libpq</application>操作大对象的示例程序 + +#include + +#include +#include +#include +#include + +#include "libpq-fe.h" +#include "libpq/libpq-fs.h" + +#define BUFSIZE 1024 + +/* + * importFile - + * import file "in_filename" into database as large object "lobjOid" + * + */ +static Oid +importFile(PGconn *conn, char *filename) +{ + Oid lobjId; + int lobj_fd; + char buf[BUFSIZE]; + int nbytes, + tmp; + int fd; + + /* + * open the file to be read in + */ + fd = open(filename, O_RDONLY, 0666); + if (fd < 0) + { /* error */ + fprintf(stderr, "cannot open unix file\"%s\"\n", filename); + } + + /* + * create the large object + */ + lobjId = lo_creat(conn, INV_READ | INV_WRITE); + if (lobjId == 0) + fprintf(stderr, "cannot create large object"); + + lobj_fd = lo_open(conn, lobjId, INV_WRITE); + + /* + * read in from the Unix file and write to the inversion file + */ + while ((nbytes = read(fd, buf, BUFSIZE)) > 0) + { + tmp = lo_write(conn, lobj_fd, buf, nbytes); + if (tmp < nbytes) + fprintf(stderr, "error while reading \"%s\"", filename); + } + + close(fd); + lo_close(conn, lobj_fd); + + return lobjId; +} + +static void +pickout(PGconn *conn, Oid lobjId, int start, int len) +{ + int lobj_fd; + char *buf; + int nbytes; + int nread; + + lobj_fd = lo_open(conn, lobjId, INV_READ); + if (lobj_fd < 0) + fprintf(stderr, "cannot open large object %u", lobjId); + + lo_lseek(conn, lobj_fd, start, SEEK_SET); + buf = malloc(len + 1); + + nread = 0; + while (len - nread > 0) + { + nbytes = lo_read(conn, lobj_fd, buf, len - nread); + buf[nbytes] = '\0'; + fprintf(stderr, ">>> %s", buf); + nread += nbytes; + if (nbytes <= 0) + break; /* no more data? */ + } + free(buf); + fprintf(stderr, "\n"); + lo_close(conn, lobj_fd); +} + +static void +overwrite(PGconn *conn, Oid lobjId, int start, int len) +{ + int lobj_fd; + char *buf; + int nbytes; + int nwritten; + int i; + + lobj_fd = lo_open(conn, lobjId, INV_WRITE); + if (lobj_fd < 0) + fprintf(stderr, "cannot open large object %u", lobjId); + + lo_lseek(conn, lobj_fd, start, SEEK_SET); + buf = malloc(len + 1); + + for (i = 0; i < len; i++) + buf[i] = 'X'; + buf[i] = '\0'; + + nwritten = 0; + while (len - nwritten > 0) + { + nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten); + nwritten += nbytes; + if (nbytes <= 0) + { + fprintf(stderr, "\nWRITE FAILED!\n"); + break; + } + } + free(buf); + fprintf(stderr, "\n"); + lo_close(conn, lobj_fd); +} + + +/* + * exportFile - + * export large object "lobjOid" to file "out_filename" + * + */ +static void +exportFile(PGconn *conn, Oid lobjId, char *filename) +{ + int lobj_fd; + char buf[BUFSIZE]; + int nbytes, + tmp; + int fd; + + /* + * open the large object + */ + lobj_fd = lo_open(conn, lobjId, INV_READ); + if (lobj_fd < 0) + fprintf(stderr, "cannot open large object %u", lobjId); + + /* + * open the file to be written to + */ + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (fd < 0) + { /* error */ + fprintf(stderr, "cannot open unix file\"%s\"", + filename); + } + + /* + * read in from the inversion file and write to the Unix file + */ + while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) + { + tmp = write(fd, buf, nbytes); + if (tmp < nbytes) + { + fprintf(stderr, "error while writing \"%s\"", + filename); + } + } + + lo_close(conn, lobj_fd); + close(fd); + + return; +} + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + char *in_filename, + *out_filename; + char *database; + Oid lobjOid; + PGconn *conn; + PGresult *res; + + if (argc != 4) + { + fprintf(stderr, "Usage: %s database_name in_filename out_filename\n", + argv[0]); + exit(1); + } + + database = argv[1]; + in_filename = argv[2]; + out_filename = argv[3]; + + /* + * set up the connection + */ + conn = PQsetdb(NULL, NULL, NULL, NULL, database); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious users can't take control. */ + res = PQexec(conn, + "SELECT pg_catalog.set_config('search_path', '', false)"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SET failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + res = PQexec(conn, "begin"); + PQclear(res); + printf("importing file \"%s\" ...\n", in_filename); +/* lobjOid = importFile(conn, in_filename); */ + lobjOid = lo_import(conn, in_filename); + if (lobjOid == 0) + fprintf(stderr, "%s\n", PQerrorMessage(conn)); + else + { + printf("\tas large object %u.\n", lobjOid); + + printf("picking out bytes 1000-2000 of the large object\n"); + pickout(conn, lobjOid, 1000, 1000); + + printf("overwriting bytes 1000-2000 of the large object with X's\n"); + overwrite(conn, lobjOid, 1000, 1000); + + printf("exporting large object to file \"%s\" ...\n", out_filename); +/* exportFile(conn, lobjOid, out_filename); */ + if (lo_export(conn, lobjOid, out_filename) < 0) + fprintf(stderr, "%s\n", PQerrorMessage(conn)); + } + + res = PQexec(conn, "end"); + PQclear(res); + PQfinish(conn); + return 0; +} +]]> + + + + +
diff --git a/zh/9.6/logicaldecoding.sgml b/zh/9.6/logicaldecoding.sgml new file mode 100644 index 00000000..3dbea1ae --- /dev/null +++ b/zh/9.6/logicaldecoding.sgml @@ -0,0 +1,608 @@ + + + 逻辑解码 + + 逻辑解码 + + + PostgreSQL 提供了基础设施,可将通过 SQL 执行的修改流式发送给外部消费 + 者。此功能可用于多种目的,包括复制方案和审计。 + + + + 更改通过由逻辑复制槽标识的流发送出去。 + + + + 这些更改以何种格式流式传输,由所使用的输出插件决定。PostgreSQL 发行版提 + 供了一个示例插件。还可以编写额外的插件,在不修改任何核心代码的情况下扩展 + 可用格式的选择。每个输出插件都可以访问由 INSERT 产生 + 的每一个新行,以及由 UPDATE 创建的新行版本。对于 + UPDATEDELETE,旧行版本是否可 + 用取决于所配置的复制标识(见 + )。 + + + + 更改既可以通过流复制协议消费(见 和 + ),也可以通过 SQL 调用函数来消 + 费(见 )。还可以在不修改核心代码的 + 情况下编写其他消费复制槽输出的方法(见 + )。 + + + + 逻辑解码示例 + + + 下面的示例演示如何使用 SQL 接口控制逻辑解码。 + + + + 在使用逻辑解码之前,必须将 设置为 + logical,并将 + 至少设置为 1。然后,应当以超级用户身份连接到目标数据库(下面的示例中为 + postgres)。 + + + +postgres=# -- Create a slot named 'regression_slot' using the output plugin 'test_decoding' +postgres=# SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); + slot_name | xlog_position +-----------------+----------- + regression_slot | 0/16B1970 +(1 row) + +postgres=# SELECT slot_name, plugin, slot_type, database, active, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots; + slot_name | plugin | slot_type | database | active | restart_lsn | confirmed_flush_lsn +-----------------+---------------+-----------+----------+--------+-------------+----------------- + regression_slot | test_decoding | logical | postgres | f | 0/16A4408 | 0/16A4440 +(1 row) + +postgres=# -- There are no changes to see yet +postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL); + lsn | xid | data +-----+-----+------ +(0 rows) + +postgres=# CREATE TABLE data(id serial primary key, data text); +CREATE TABLE + +postgres=# -- DDL isn't replicated, so all you'll see is the transaction +postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL); +-----------+-------+-------------- + location | xid | data +(2 rows) + +postgres=# -- Once changes are read, they're consumed and not emitted +postgres=# -- in a subsequent call: +postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL); + lsn | xid | data +-----+-----+------ +(0 rows) + +postgres=# BEGIN; +postgres=# INSERT INTO data(data) VALUES('1'); +postgres=# INSERT INTO data(data) VALUES('2'); +postgres=# COMMIT; + +postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL); + location | xid | data +-----------+-----+----------------------------------------------- + location | xid | data +(4 rows) + +postgres=# INSERT INTO data(data) VALUES('3'); + +postgres=# -- You can also peek ahead in the change stream without consuming changes +postgres=# SELECT * FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL); + location | xid | data +-----------+-----+----------------------------------------------- + 0/BA5A8E0 | 10299 | BEGIN 10299 + 0/BA5A8E0 | 10299 | table public.data: INSERT: id[integer]:3 data[text]:'3' + 0/BA5A990 | 10299 | COMMIT 10299 +(3 rows) + +postgres=# -- The next call to pg_logical_slot_peek_changes() returns the same changes again +postgres=# SELECT * FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL); + location | xid | data +-----------+-----+----------------------------------------------- + 0/BA5A8E0 | 10299 | BEGIN 10299 + 0/BA5A8E0 | 10299 | table public.data: INSERT: id[integer]:3 data[text]:'3' + 0/BA5A990 | 10299 | COMMIT 10299 +(3 rows) + +postgres=# -- options can be passed to output plugin, to influence the formatting +postgres=# SELECT * FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-timestamp', 'on'); + location | xid | data +-----------+-----+----------------------------------------------- + 0/BA5A8E0 | 10299 | BEGIN 10299 + 0/BA5A8E0 | 10299 | table public.data: INSERT: id[integer]:3 data[text]:'3' + location | xid | data +(3 rows) + +postgres=# -- Remember to destroy a slot you no longer need to stop it consuming +postgres=# -- server resources: +postgres=# SELECT pg_drop_replication_slot('regression_slot'); + pg_drop_replication_slot +----------------------- + +(1 row) + + + + 下面的示例展示了如何通过流复制协议控制逻辑解码,所用程序是 PostgreSQL + 发行版中自带的 。这要求已经配置好客 + 户端认证以允许复制连接(见 + ),并且 + max_wal_senders 的设置足够高,以容纳额外的连接。 + + +$ pg_recvlogical -d postgres --slot test --create-slot +$ pg_recvlogical -d postgres --slot test --start -f - +ControlZ +$ psql -d postgres -c "INSERT INTO data(data) VALUES('4');" +$ fg +BEGIN 693 +table public.data: INSERT: id[integer]:4 data[text]:'4' +COMMIT 693 +ControlC +$ pg_recvlogical -d postgres --slot test --drop-slot + + + + + 逻辑解码概念 + + 逻辑解码 + + + 逻辑解码 + + + + 逻辑解码是将数据库表上的所有持久更改提取为一种连贯、易于理解的格式的过 + 程,这种格式无需详细了解数据库内部状态就能解释。 + + + + 在 PostgreSQL 中,逻辑解码通过解码 + 预写式日志的内容来实现。预写式日志描述的是存 + 储层面的更改,而逻辑解码会将其转换成应用程序特定的形式,例如元组流或 SQL + 语句流。 + + + + + 复制槽 + + + 复制槽 + 逻辑复制 + + + + 在逻辑复制的上下文中,一个槽表示一个更改流,客户端可以按照这些更改在源服 + 务器上发生的顺序来重放它们。每个槽都从单个数据库流式传输一系列更改。 + + + + PostgreSQL 也有流复制槽(见 + ),但它们在那里的使用方式略有不 + 同。 + + + + + 复制槽在整个 PostgreSQL 集簇的所有数据库中都 + 有唯一标识符。槽独立于使用它们的连接而持久存在,并且具备崩溃安全性。 + + + + 逻辑槽在正常运行时会让每个更改只发送一次。每个槽的当前位置只会在检查点 + 时持久化,因此在发生崩溃时,该槽可能会回退到较早的 LSN,这会导致服务器 + 重启后最近的更改再次被发送。逻辑解码客户端有责任避免多次处理同一消息带 + 来的不良影响。客户端可能希望记录解码时见到的最后一个 LSN,并跳过任何重 + 复数据;或者(在使用复制协议时)请求从该 LSN 开始解码,而不是让服务器自 + 行决定起始点。复制进度跟踪功能正是为此设计的,参见 + 复制源。 + + + + 对于同一个数据库,可以存在多个彼此独立的槽。每个槽都有自己的状态,允许不 + 同的消费者从数据库更改流中的不同位置接收更改。对于大多数应用,每个消费 + 者都需要一个单独的槽。 + + + + 逻辑复制槽并不了解接收端的状态。甚至可以让多个不同的接收端在不同时间使用 + 同一个槽;它们只会从前一个接收端停止消费更改的位置之后继续获得更改。但 + 在任意给定时刻,只允许一个接收端从某个槽中消费更改。 + + + + + 复制槽可跨崩溃持久存在,而且不了解其消费者的状态。即使没有连接使用它们, + 它们也会阻止所需资源被移除。这会消耗存储,因为只要复制槽仍然需要, + VACUUM 就不能移除所需的 WAL 和系统目录中的相关行。因此,如果槽已经不再需要,就 + 应当将其删除。 + + + + + + 输出插件 + + 输出插件把预写式日志内部表示的数据转换成复制槽消费者所需的格式。 + + + + + 导出快照 + + 当使用流复制接口创建新的复制槽时, + 会导出 + 一个快照(见 ), + 它准确反映了数据库的这样一种状态:从该状态之后开始,所有更改都会被纳入 + 更改流。这可以通过使用 + SET TRANSACTION + SNAPSHOT 读取创建槽那一刻的数据库状态,据此创建一个新 + 副本。随后,该事务就可以用来转储该时刻的数据库状态,之后再利用该槽的内 + 容进行更新,而不会丢失任何更改。 + + + + + + 流复制协议接口 + + + 命令 + + + CREATE_REPLICATION_SLOT slot_name LOGICAL output_plugin + + + + DROP_REPLICATION_SLOT slot_name + + + + START_REPLICATION SLOT slot_name LOGICAL ... + + + 分别用于创建复制槽、删除复制槽,以及从复制槽流式传输更改。这些命令只在 + 复制连接上可用,不能通过 SQL 使用。关于这些命令的细节,见 + 。 + + + + 命令 可以用来在流复制连接上控制逻 + 辑解码(它在内部使用上述命令)。 + + + + + 逻辑解码 <acronym>SQL</acronym> 接口 + + + 关于与逻辑解码交互的 SQL 层 API 的详细文档,见 + 。 + + + + 同步复制(见 )只支持通过流复 + 制接口使用的复制槽。函数接口以及额外的、非核心的接口都不支持同步复制。 + + + + + 与逻辑解码相关的系统目录 + + + pg_replication_slots + 视图和 + pg_stat_replication + 视图分别提供了有关复制槽和流复制连接当前状态的信息。这些视图同时适用于 + 物理复制和逻辑复制。 + + + + + 逻辑解码输出插件 + + PostgreSQL 源码树中的 + + contrib/test_decoding + + 子目录里有一个输出插件示例。 + + + 初始化函数 + + _PG_output_plugin_init + + 加载输出插件的方式是动态加载共享库,并以输出插件的名称作为库的基本名称。定位该库时使用常规库搜索路径。为了提供所需的输出插件回调,并表明这个库确实是输出插件,库必须提供一个名为_PG_output_plugin_init的函数。此函数会接收一个结构体,需要在其中填写各项操作的回调函数指针。 +typedef struct OutputPluginCallbacks +{ + LogicalDecodeStartupCB startup_cb; + LogicalDecodeBeginCB begin_cb; + LogicalDecodeChangeCB change_cb; + LogicalDecodeCommitCB commit_cb; + LogicalDecodeMessageCB message_cb; + LogicalDecodeFilterByOriginCB filter_by_origin_cb; + LogicalDecodeShutdownCB shutdown_cb; +} OutputPluginCallbacks; + +typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb); +其中,begin_cbchange_cbcommit_cb回调是必需的,而startup_cb, + filter_by_origin_cbshutdown_cb是可选的。 + + + + 能力 + + 为了对变更进行解码、格式化和输出,输出插件可以使用后端的大部分常规基础设施,包括调用输出函数。可以对关系进行只读访问,但仅限于以下两种关系:由initdb创建在pg_catalog模式中的关系,或者使用以下方式标记为用户提供的系统目录表的关系: +ALTER TABLE user_catalog_table SET (user_catalog_table = true); +CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true); +禁止执行任何会导致分配事务 ID 的操作。这包括向表写入数据、执行 DDL 更改以及调用txid_current()。 + + + + + 输出模式 + + + 输出插件回调几乎可以用任意格式向消费者传递数据。对于某些用例,例如通过 + SQL 查看更改,把数据返回为能够容纳任意数据的数据类型(例如 + bytea)会很笨拙。如果输出插件只输出服务器编码中的文本数 + 据,它可以把 OutputPluginOptions.output_type 设置为 + OUTPUT_PLUGIN_TEXTUAL_OUTPUT 而不是 + OUTPUT_PLUGIN_BINARY_OUTPUT,并在 + 启动回调 + 中声明这一点。在这种情况下,所有数据都必须采用服务器编码,这样才能装入 text + datum。断言开启的构建会检查这一点。 + + + + + 输出插件回调 + + + 输出插件通过它所提供的各类回调获知正在发生的更改。 + + + + 并发事务按提交顺序解码,并且只有属于某个特定事务的更改,才会在 + begincommit 回调之间被解码。 + 显式或隐式回滚的事务永远不会被解码。成功的保存点会按照它们在该事务中执行的顺序,被折叠进包含它们的事务中。 + + + + 只有已经安全刷写到磁盘的事务才会被解码。这可能导致 + COMMIT 在紧随其后的 + pg_logical_slot_get_changes() 调用中不会立即被解 + 码,当 synchronous_commit 被设置为 + off 时尤其如此。 + + + + + 启动回调 + 可选的startup_cb回调会在每次创建复制槽或请求其流式传输变更时调用,无论有多少变更已准备好输出。 +typedef void (*LogicalDecodeStartupCB) (struct LogicalDecodingContext *ctx, + OutputPluginOptions *options, + bool is_init); +其中,is_init参数在创建复制槽时为 true,否则为 false。options指向一个输出插件可以设置的选项结构体: +typedef struct OutputPluginOptions +{ + OutputPluginOutputType output_type; +} OutputPluginOptions; + + output_type必须设为OUTPUT_PLUGIN_TEXTUAL_OUTPUTOUTPUT_PLUGIN_BINARY_OUTPUT。另见。 + + + + 启动回调应当验证 + ctx->output_plugin_options 中的选项。如果输出插 + 件需要保存状态,可以使用 + ctx->output_plugin_private 来存储。 + + + + + 关闭回调 + + + 当一个此前处于活动状态的复制槽不再使用时,就会调用可选的 + shutdown_cb 回调。它可用于释放输出插件私有的资 + 源。此时未必是在删除该槽,也可能只是停止流式传输。 + +typedef void (*LogicalDecodeShutdownCB) (struct LogicalDecodingContext *ctx); + + + + + + 事务开始回调 + + + 只要某个已提交事务的开始被解码,就会调用必需的 + begin_cb 回调。已中止的事务及其内容永远不会被解 + 码。 + +typedef void (*LogicalDecodeBeginCB) (struct LogicalDecodingContext *ctx, + ReorderBufferTXN *txn); + + txn 参数包含该事务的元信息,例如它提交时的时间 + 戳以及它的 XID。 + + + + + 事务结束回调 + + + 只要事务提交被解码,就会调用必需的 commit_cb 回 + 调。如果有被修改的行,那么在此之前,所有已修改行的 + change_cb 回调都已经被调用过。 + +typedef void (*LogicalDecodeCommitCB) (struct LogicalDecodingContext *ctx, + ReorderBufferTXN *txn, + XLogRecPtr commit_lsn); + + + + + + 更改回调 + + 必需的change_cb回调会在事务中的每次行修改时被调用,不论该修改是INSERTUPDATEDELETE。即使原命令一次修改了多行,也会针对每一行单独调用此回调。 +typedef void (*LogicalDecodeChangeCB) (struct LogicalDecodingContext *ctx, + ReorderBufferTXN *txn, + Relation relation, + ReorderBufferChange *change); +参数ctxtxn所含的内容与begin_cbcommit_cb回调中的相同。此外,还会传入关系描述符relation(指向该行所属的关系),以及结构体change(描述该行的修改)。 + + + + 只有用户定义表中既不是不记录 WAL 的(见 + ),也不是临时的(见 + )更改,才能通过逻辑解码提 + 取出来。 + + + + + + 源过滤回调 + + + 可选的 filter_by_origin_cb 回调用于判定,从 + origin_id 重放而来的数据是否为输出插件所关心 + 的数据。 + +typedef bool (*LogicalDecodeFilterByOriginCB) (struct LogicalDecodingContext *ctx, + RepOriginId origin_id); + + ctx 参数的内容与其他回调相同。除了源本身之外,没有 + 其他信息可用。如果要表明来自传入节点的更改并不相关,则返回 true,这会 + 使这些更改被过滤掉;否则返回 false。对于被过滤掉的事务和更改,其他回调 + 都不会被调用。 + + + 在实现级联复制或多向复制方案时,这个回调很有用。按源过滤可以避免在这类 + 配置中同一更改被来回复制。虽然事务和更改本身也带有源信息,但通过这个 + 回调来过滤会明显更高效。 + + + + + 通用消息回调 + + 可选的message_cb回调会在每次解码出一条逻辑解码消息时被调用。 +typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx, + ReorderBufferTXN *txn, + XLogRecPtr message_lsn, + bool transactional, + const char *prefix, + Size message_size, + const char *message); +其中,txn参数包含事务的元信息,例如提交时间戳及其 XID。但请注意,如果消息是非事务性的,并且记录该消息的事务尚未分配 XID,这个参数可能为 NULL。lsn包含消息的 WAL 位置。transactional表示消息是否以事务性方式发送。prefix是任意的、以空字符终止的前缀,可用于识别当前插件关注的消息。最后,message参数保存实际消息,其大小为message_size + + 应特别注意,确保输出插件视为有意义的消息前缀具有唯一性。使用扩展名或输 + 出插件自身的名称通常是一个不错的选择。 + + + + + + + 产生输出的函数 + + + 为了真正产生输出,输出插件可以在 + StringInfo 输出缓冲区 ctx->out + 中写入数据,此时位于 begin_cb、 + commit_cbchange_cb 回调内部。 + 写入输出缓冲区之前,必须先调 + 用 OutputPluginPrepareWrite(ctx, last_write);写完 + 缓冲区之后,必须调用 + OutputPluginWrite(ctx, last_write) 来执行写出。 + last_write 指示某次写出是否为该回调的最后一次写 + 出。 + + + + 下面的示例展示了如何把数据输出给输出插件的消费者: + +OutputPluginPrepareWrite(ctx, true); +appendStringInfo(ctx->out, "BEGIN %u", txn->xid); +OutputPluginWrite(ctx, true); + + + + + + + 逻辑解码输出写入器 + + + 逻辑解码还可以增加更多输出方法。详情见 + src/backend/replication/logical/logicalfuncs.c。 + 本质上,需要提供三个函数:一个读取 WAL,一个准备写出输出,另一个执行写 + 出(见 )。 + + + + + 逻辑解码的同步复制支持 + + 概述 + + + 逻辑解码可用于构建 + 同步复制方案,其用户接口 + 与 流复制 的同步复制相同。要做到这一 + 点,必须使用流复制接口(见 + )来流式传出数据。客户端必须像 + 流复制客户端一样发送 Standby status update (F) + 消息(见 )。 + + + + + 通过逻辑解码接收更改的同步副本,只能在单个数据库范围内工作。而与此不同, + synchronous_standby_names 当前是整个服务器范围 + 的,这意味着如果有多个数据库在被活跃使用,这种技术将无法正常工作。 + + + + + + 注意事项 + + 在同步复制配置中,如果事务对[用户]系统目录表加了排他锁,就可能发生死锁。参见了解用户系统目录表。这是因为事务的逻辑解码可能会锁定系统目录表以访问它们。为避免这种情况,用户必须避免对[用户]系统目录表获取排他锁。以下方式可能导致这种情况: + + + 在事务中显式发出 LOCK 命令来锁定 + pg_class。 + + + + + + 在事务中执行 CLUSTER 命令处理 + pg_class。 + + + + + + 在事务中对 [user] 目录表执行 TRUNCATE。 + + + 注意,这些可能造成死锁的命令不仅适用于上面明确指出的系统目录表,也适用于其他任何[用户]系统目录表。 + + + diff --git a/zh/9.6/ltree.sgml b/zh/9.6/ltree.sgml new file mode 100644 index 00000000..54b727b3 --- /dev/null +++ b/zh/9.6/ltree.sgml @@ -0,0 +1,611 @@ + + + + ltree + + + ltree + + + + 该模块实现了数据类型 ltree,用于表示存储在层次化树状结构中的数据标签。 + 它还提供了丰富的标签树搜索能力。 + + + + 定义 + + + 标签是由字母数字字符和下划线组成的序列(例如,在 C 区域设置下,允许的字符为 + A-Za-z0-9_)。标签长度必须少于 256 个字符。 + + + + 示例:42Personal_Services + + + + 标签路径是由点号分隔的零个或多个标签组成的序列,例如 + L1.L2.L3,表示从层次树根节点到某个特定节点的一条路径。 + 标签路径的长度不能超过 65535 个标签。 + + + + 示例:Top.Countries.Europe.Russia + + + + ltree模块提供了几种数据类型: + + + + + + ltree存储一个标签路径。 + + + + + + lquery表示一种用于匹配ltree值的、类似正则表达式的模式。一个简单单词会匹配路径中的相应标签。星号(*)匹配零个或多个标签。例如: +foo 精确匹配标签路径foo +*.foo.* 匹配任何包含标签foo的标签路径 +*.foo 匹配最后一个标签为foo的任意标签路径 + + + + 星号还可以带量词,以限制它们能够匹配的标签数量: +*{n} 精确匹配 n 个标签 +*{n,} 至少匹配 n 个标签 +*{n,m} 至少匹配 n 个、但不超过 m 个标签 +*{,m} 至多匹配 m 个标签 — 与下式相同: *{0,m} + + + + lquery中,有几个修饰符可以放在非星号标签的末尾,使其不只匹配完全相同的标签: +@ 不区分大小写地匹配,例如 a@ 可匹配 A +* 匹配以此前缀开头的任意标签,例如 foo* 可匹配 foobar +% 匹配标签起始处由下划线分隔的单词 +修饰符%的行为稍微复杂一些。它尝试匹配单词,而不是整个标签。例如,foo_bar%可匹配foo_bar_baz,但不能匹配foo_barbaz。如果与*组合使用,则前缀匹配会分别作用于每个单词,例如foo_bar%*可匹配foo1_bar2_baz,但不能匹配foo1_br2_baz。 + + + 此外,还可以写出多个可能带修饰符的非星号项,并用|(OR)分隔,以匹配其中任意一项;也可以在非星号组前加上!(NOT),以匹配不符合这些备选项中任意一项的标签。 + + 下面是一个带注释的示例,使用lquery: + +Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain +a. b. c. d. e. +此查询将匹配满足以下条件的任意标签路径: + + + + 以标签Top开头 + + + + + 接下来,在下一个条件之前有零到两个标签 + + + + + 然后是一个以前缀sport开头的标签,且匹配时不区分大小写 + + + + 接着有一个不匹配footballtennis的标签 + + + + 最后以一个以Russ开头的标签,或精确匹配Spain的标签结束。 + + + + + + + ltxtquery表示一种用于匹配ltree值的、类似全文检索的模式。 + 一个ltxtquery值包含单词,末尾还可以带有修饰符@*%; + 这些修饰符与它们在lquery中的含义相同。单词可以通过&(AND)、 + |(OR)、!(NOT)以及圆括号组合。 + 它与lquery的关键区别在于,ltxtquery匹配单词时不考虑它们在标签路径中的位置。 + + + + 下面是一个ltxtquery示例: + +Europe & Russia*@ & !Transportation + + 它将匹配包含标签Europe以及任意以Russia开头(不区分大小写)的标签的路径, + 但不匹配包含标签Transportation的路径。这些单词在路径中的位置并不重要。 + 另外,当使用%时,该单词可以匹配标签中任意由下划线分隔的单词,而不考虑其位置。 + + + + + + + 注意:ltxtquery允许在符号之间出现空白,而ltreelquery不允许。 + + + + + 操作符和函数 + + + 类型ltree具有常见的比较操作符 + =<>、 + <><=>=。 + 比较时采用树遍历顺序,其中节点的子节点按标签文本排序。此外,还提供了 + 中所示的专用操作符。 + + + + <type>ltree</type> 操作符 + + + + + 操作符 + 返回值 + + 描述 + + + + + + + ltree @> ltree + boolean + 左参数是否为右参数的祖先(或与之相等)? + + + + ltree <@ ltree + boolean + 左参数是否为右参数的后代(或与之相等)? + + + + ltree ~ lquery + boolean + ltree是否匹配lquery + + + + lquery ~ ltree + boolean + ltree是否匹配lquery + + + + ltree ? lquery[] + boolean + ltree是否匹配数组中的任意lquery + + + + lquery[] ? ltree + boolean + ltree是否匹配数组中的任意lquery + + + + ltree @ ltxtquery + boolean + ltree是否匹配ltxtquery + + + + ltxtquery @ ltree + boolean + ltree是否匹配ltxtquery + + + + ltree || ltree + ltree + 连接ltree路径 + + + + ltree || text + ltree + 将文本转换为ltree后再连接 + + + + text || ltree + ltree + 将文本转换为ltree后再连接 + + + + ltree[] @> ltree + boolean + 数组是否包含ltree的某个祖先? + + + + ltree <@ ltree[] + boolean + 数组是否包含ltree的某个祖先? + + + + ltree[] <@ ltree + boolean + 数组是否包含ltree的某个后代? + + + + ltree @> ltree[] + boolean + 数组是否包含ltree的某个后代? + + + + ltree[] ~ lquery + boolean + 数组是否包含匹配lquery的任意路径? + + + + lquery ~ ltree[] + boolean + 数组是否包含匹配lquery的任意路径? + + + + ltree[] ? lquery[] + boolean + ltree数组是否包含匹配任意lquery的路径? + + + + lquery[] ? ltree[] + boolean + ltree数组是否包含匹配任意lquery的路径? + + + + ltree[] @ ltxtquery + boolean + 数组是否包含匹配ltxtquery的任意路径? + + + + ltxtquery @ ltree[] + boolean + 数组是否包含匹配ltxtquery的任意路径? + + + + ltree[] ?@> ltree + ltree + 返回数组中第一个是ltree祖先的项,如果没有则返回 NULL + + + + ltree[] ?<@ ltree + ltree + 返回数组中第一个是ltree后代的项,如果没有则返回 NULL + + + + ltree[] ?~ lquery + ltree + 返回数组中第一个匹配lquery的项,如果没有则返回 NULL + + + + ltree[] ?@ ltxtquery + ltree + 返回数组中第一个匹配ltxtquery的项,如果没有则返回 NULL + + + + +
+ + + 操作符<@@>、 + @~都有对应的 + ^<@^@>^@、 + ^~变体,它们除了不使用索引之外完全相同。这些变体仅对测试有用。 + + + + 可用函数见。 + + + + <type>ltree</type> 函数 + + + + + 函数 + 返回类型 + + 描述 + + 示例 + 结果 + + + + + + subltree(ltree, int start, int end)subltree + ltree + 从位置start到位置end-1 的ltree子路径(从 0 开始计数) + subltree('Top.Child1.Child2',1,2) + Child1 + + + + subpath(ltree, int offset, int len)subpath + ltree + 从位置offset开始、长度为lenltree子路径。如果offset为负,则子路径从距路径末尾 -offset 个标签处开始。如果len为负,则从路径末尾省去那么多个标签。 + subpath('Top.Child1.Child2',0,2) + Top.Child1 + + + + subpath(ltree, int offset) + ltree + 从位置offset开始、一直延伸到路径末尾的ltree子路径。如果offset为负,则子路径从距路径末尾 -offset 个标签处开始。 + subpath('Top.Child1.Child2',1) + Child1.Child2 + + + + nlevel(ltree)nlevel + integer + 路径中的标签数 + nlevel('Top.Child1.Child2') + 3 + + + + index(ltree a, ltree b)index + integer + a 中首次出现 b 的位置;如果未找到则为 -1 + index('0.1.2.3.5.4.5.6.8.5.6.8','5.6') + 6 + + + + index(ltree a, ltree b, int offset) + integer + offset开始搜索时,a 中首次出现 b 的位置;负的offset表示从路径末尾向前-offset个标签处开始 + index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4) + 9 + + + + text2ltree(text)text2ltree + ltree + text转换为ltree + + + + + + ltree2text(ltree)ltree2text + text + ltree转换为text + + + + + + lca(ltree, ltree, ...)lca + ltree + 路径的最长公共祖先(最多支持 8 个参数) + lca('1.2.3','1.2.3.4.5.6') + 1.2 + + + + lca(ltree[]) + ltree + 数组中各路径的最长公共祖先 + lca(array['1.2.3'::ltree,'1.2.3.4']) + 1.2 + + + + +
+
+ + + 索引 + + ltree支持几种能够加速所示操作符的索引类型: + + + + + ltree上的 B-树索引:<<==>=> + + + ltree上的 GiST 索引:<<==>=>@><@@~? + 创建此类索引的示例: + +CREATE INDEX path_gist_idx ON test USING GIST (path); + + + + ltree[]上的 GiST 索引:ltree[] <@ ltreeltree @> ltree[]@~? + 创建此类索引的示例: + +CREATE INDEX path_gist_idx ON test USING GIST (array_path); + + + 注意:这种索引类型是有损的。 + + + + + + + 示例 + + + 本示例使用下列数据(在源代码发行包中的 + contrib/ltree/ltreetest.sql文件里也能找到): + + + +CREATE TABLE test (path ltree); +INSERT INTO test VALUES ('Top'); +INSERT INTO test VALUES ('Top.Science'); +INSERT INTO test VALUES ('Top.Science.Astronomy'); +INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics'); +INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology'); +INSERT INTO test VALUES ('Top.Hobbies'); +INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy'); +INSERT INTO test VALUES ('Top.Collections'); +INSERT INTO test VALUES ('Top.Collections.Pictures'); +INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy'); +INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars'); +INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies'); +INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts'); +CREATE INDEX path_gist_idx ON test USING GIST (path); +CREATE INDEX path_idx ON test USING BTREE (path); + + + + 现在,我们有一个表test,其中的数据描述了下图所示的层次结构: + + + + Top + / | \ + Science Hobbies Collections + / | \ + Astronomy Amateurs_Astronomy Pictures + / \ | +Astrophysics Cosmology Astronomy + / | \ + Galaxies Stars Astronauts + + + + 我们可以做继承查询: + +ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science'; + path +------------------------------------ + Top.Science + Top.Science.Astronomy + Top.Science.Astronomy.Astrophysics + Top.Science.Astronomy.Cosmology +(4 rows) + + + + 下面是一些路径匹配的示例: +ltreetest=> SELECT path FROM test WHERE path ~ '*.Astronomy.*'; + path +----------------------------------------------- + Top.Science.Astronomy + Top.Science.Astronomy.Astrophysics + Top.Science.Astronomy.Cosmology + Top.Collections.Pictures.Astronomy + Top.Collections.Pictures.Astronomy.Stars + Top.Collections.Pictures.Astronomy.Galaxies + Top.Collections.Pictures.Astronomy.Astronauts +(7 rows) + +ltreetest=> SELECT path FROM test WHERE path ~ '*.!pictures@.*.Astronomy.*'; + path +------------------------------------ + Top.Science.Astronomy + Top.Science.Astronomy.Astrophysics + Top.Science.Astronomy.Cosmology +(3 rows) + + + + + 下面是一些全文检索示例: + +ltreetest=> SELECT path FROM test WHERE path @ 'Astro*% & !pictures@'; + path +------------------------------------ + Top.Science.Astronomy + Top.Science.Astronomy.Astrophysics + Top.Science.Astronomy.Cosmology + Top.Hobbies.Amateurs_Astronomy +(4 rows) + +ltreetest=> SELECT path FROM test WHERE path @ 'Astro* & !pictures@'; + path +------------------------------------ + Top.Science.Astronomy + Top.Science.Astronomy.Astrophysics + Top.Science.Astronomy.Cosmology +(3 rows) + + + + + 使用函数构造路径: + +ltreetest=> SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path <@ 'Top.Science.Astronomy'; + ?column? +------------------------------------------ + Top.Science.Space.Astronomy + Top.Science.Space.Astronomy.Astrophysics + Top.Science.Space.Astronomy.Cosmology +(3 rows) + + + + 可以通过创建一个 SQL 函数,在路径的指定位置插入标签来简化这一操作: +CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree + AS 'select subpath($1,0,$2) || $3 || subpath($1,$2);' + LANGUAGE SQL IMMUTABLE; + +ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.Science.Astronomy'; + ins_label +------------------------------------------ + Top.Science.Space.Astronomy + Top.Science.Space.Astronomy.Astrophysics + Top.Science.Space.Astronomy.Cosmology +(3 rows) + + + + + + 转换 + + + 有额外的扩展实现了 PL/Python 的ltree类型转换。这些扩展分别叫做ltree_plpythonultree_plpython2ultree_plpython3u(关于 PL/Python 的命名约定请见)。如果安装了这些转换扩展,并在创建函数时指定它们,则ltree值会映射为 Python 列表。(不过,目前还不支持反向映射。) + + + + + 强烈建议将转换扩展安装在与ltree相同的模式中。否则,如果转换扩展所在模式包含由恶意用户定义的对象,在安装时会存在安全隐患。 + + + + + + 作者 + + + 全部工作均由 Teodor Sigaev(teodor@stack.net)和 + Oleg Bartunov(oleg@sai.msu.su)完成。更多信息见 + 。 + 作者谨感谢 Eugeny Rodichev 的有益讨论。欢迎提出意见和缺陷报告。 + + + +
diff --git a/zh/9.6/maintenance.sgml b/zh/9.6/maintenance.sgml new file mode 100644 index 00000000..aaddf351 --- /dev/null +++ b/zh/9.6/maintenance.sgml @@ -0,0 +1,640 @@ + + + + 日常数据库维护任务 + + + 维护 + + + + 日常维护 + + + + 和任何数据库软件一样,PostgreSQL为了获得最佳性能, + 需要定期执行某些任务。这里讨论的任务是必需的, + 但它们本质上是重复性的,因此可以很容易地用标准工具实现自动化, + 例如 cron 脚本或 Windows 的 + Task Scheduler。建立合适的脚本并检查其是否成功执行, + 是数据库管理员的职责。 + + + + 一个显而易见的维护任务,是按固定计划创建数据的备份副本。没有最近的备份, + 在灾难(磁盘故障、火灾、误删关键表等)发生后就没有恢复的可能。 + PostgreSQL提供的备份和恢复机制在 + 中有详细讨论。 + + + + 另一大类维护任务是定期对数据库进行清理。这一活动在 + 中讨论。与之密切相关的是更新查询规划器 + 将会使用的统计信息,这在中讨论。 + + + + 另一项可能需要定期关注的任务是日志文件管理。这在 + 中讨论。 + + + + check_postgres + 可用于监控数据库健康状况并报告异常情况。 + check_postgres能与 Nagios 和 MRTG 集成, + 但也可以单独运行。 + + + + 和某些其他数据库管理系统相比,PostgreSQL的维护工作量较小。 + 不过,适当地关注这些任务,将大大有助于确保你愉快而高效地使用该系统。 + + + + 日常清理 + + + 清理 + + + + PostgreSQL数据库需要一种称为清理的 + 周期性维护。对于许多安装,让所述的 + 自动清理守护进程执行清理就足够了。为了在你的场景中获得最佳效果, + 你可能需要调整其中描述的自动清理参数。有些数据库管理员希望用手工管理的 + VACUUM命令来补充甚至取代该守护进程的工作,这类命令通常由 + cronTask Scheduler + 脚本按计划执行。要正确设置手工管理的清理,理解下面几小节讨论的问题至关重要。 + 依赖自动清理的管理员也不妨略读这一材料,以帮助理解和调整自动清理。 + + + + 清理基础 + + + PostgreSQL 命令必须定期处理每个表,原因如下: + + 回收或再利用被更新或删除的行所占用的磁盘空间。 + + + + 更新 PostgreSQL 查询规划器使用的数据统计信息。 + + + + 更新可见性映射,以加快 + 仅索引扫描。 + + + + 防止由于事务 ID 回卷或 + 多事务 ID 回卷而丢失非常旧的数据。 + + 针对上述不同目的,需要以不同的频率和范围执行 VACUUM 操作,下面几节会详细说明。 + + + VACUUM有两种变体:标准的 VACUUM 和 + VACUUM FULLVACUUM FULL可以回收更多磁盘空间, + 但执行速度要慢得多。此外,标准形式的 VACUUM 可以与生产数据库操作并行运行。 + (SELECTINSERTUPDATE 以及 + DELETE 等命令会继续正常工作,不过在表被清理时,你将不能使用 + ALTER TABLE 等命令修改该表的定义。) + VACUUM FULL 需要对正在处理的表持有 + ACCESS EXCLUSIVE 锁,因此不能与对该表的其他使用并行进行。 + 一般来说,管理员应尽量使用标准 VACUUM 并避免 + VACUUM FULL。 + + + + VACUUM会产生大量 I/O 流量,这可能导致其他活动会话性能变差。 + 可以调整一些配置参数来降低后台清理对性能的影响,参见 + 。 + + + + + 回收磁盘空间 + + + 磁盘空间 + + + + 在 PostgreSQL 中,对某一行执行 + UPDATEDELETE 时,不会立即移除该行的旧版本。 + 这种方法对于获得多版本并发控制(MVCC,见 + )的好处是必需的:当行版本仍可能对其他事务可见时,就不能删除它。 + 但最终,过时或已删除的行版本将不再是任何事务关心的对象。必须回收它占用的空间, + 以供新行重用,从而避免磁盘空间需求无限增长。这是通过运行 VACUUM + 完成的。 + + + + 标准形式的 VACUUM 会移除表和索引中的死行版本,并将空间标记为可供将来重用。 + 不过,它不会把空间归还给操作系统,除非出现一种特殊情况:表尾的一个或多个页面完全空闲, + 并且能够轻松获得一个排他表锁。相比之下,VACUUM FULL 会主动压实表, + 它会写出一个不含死空间的全新表文件版本。这会将表的大小降到最小,但可能耗时很长。 + 在操作完成之前,它还需要额外的磁盘空间来存放表的新副本。 + + + + 例行清理的通常目标,是足够频繁地执行标准 VACUUM, + 从而避免需要 VACUUM FULL。自动清理守护进程就是按照这种方式工作的, + 事实上它永远不会发出 VACUUM FULL。这种方法的思路不是让表始终保持在最小尺寸, + 而是让磁盘空间的使用维持在稳态:每个表占用的空间相当于其最小尺寸,再加上两次清理之间又被用掉的空间。 + 虽然 VACUUM FULL 可以把表重新缩小到最小尺寸,并把磁盘空间归还给操作系统, + 但如果该表之后还会再次增长,这样做意义并不大。因此,对于维护被频繁更新的表, + 与其不常执行 VACUUM FULL,不如适度频繁地执行标准 VACUUM。 + + + + 有些管理员喜欢自己安排清理,例如在夜间负载较低时完成全部工作。按固定时间表执行清理的难点在于, + 如果某个表的更新活动出现意外高峰,它可能膨胀到确实需要 VACUUM FULL + 才能回收空间的程度。使用自动清理守护进程可以缓解这个问题,因为守护进程会根据更新活动动态安排清理。 + 除非工作负载极其可预测,否则完全禁用守护进程是不明智的。一种可能的折中办法是设置守护进程参数, + 使其只对异常繁重的更新活动作出反应,从而防止情况失控,而在负载正常时,则期望定期调度的 + VACUUM 完成大部分工作。 + + + + 对于不使用自动清理的人,一个典型的做法是在低使用时段每天安排一次面向整个数据库的 + VACUUM,并在必要时更频繁地清理那些更新特别频繁的表。 + (某些更新率极高的安装,甚至会每隔几分钟就清理一次最繁忙的表。) + 如果你在一个集簇中有多个数据库,不要忘记对每一个数据库都执行 + VACUUM;程序 可能会有帮助。 + + + + + 当一个表由于大规模更新或删除活动而包含大量死行版本时,普通的 VACUUM + 可能并不能令人满意。如果你有这样一个表,并且需要回收它所占用的多余磁盘空间, + 就需要使用 VACUUM FULL,或者改用 + , + 又或者使用 + 的某一种重写表变体。这些命令会重写整个表的新副本,并为其构建新的索引。 + 所有这些选项都需要 ACCESS EXCLUSIVE 锁。还要注意, + 它们会临时额外占用大约等于该表大小的磁盘空间,因为在新表和新索引完成之前, + 旧的表和索引副本都不能被释放。 + + + + + + 如果你有一个表,其全部内容会被定期删除,考虑使用 + , + 而不是先 DELETEVACUUM。 + TRUNCATE 会立即移除表的全部内容,而不需要后续再执行 + VACUUMVACUUM FULL 来回收此时未使用的磁盘空间。 + 缺点是它会破坏严格的 MVCC 语义。 + + + + + + 更新规划器统计信息 + + + 统计信息 + 规划器的 + + + + ANALYZE + + + + PostgreSQL 查询规划器依赖于有关表内容的统计信息, + 以便为查询生成良好的计划。这些统计信息由 + 命令收集, + 它既可以单独调用,也可以作为 VACUUM 的一个可选步骤执行。 + 拥有足够准确的统计信息很重要,否则糟糕的计划选择可能会降低数据库性能。 + + + + 如果启用了自动清理守护进程,它会在表内容发生足够大变化时自动发出 + ANALYZE 命令。不过,管理员也可能更愿意依靠手工调度的 + ANALYZE 操作,尤其是在已知某个表上的更新活动不会影响 + 重要列统计信息的情况下。守护进程严格依据插入或更新的行数来安排 + ANALYZE;它并不知道这些变化是否会导致有意义的统计变化。 + + + + 就像为了回收空间而进行的清理一样,频繁更新统计信息对于更新频繁的表比对于很少更新的表更有用。 + 但即便是更新频繁的表,如果数据的统计分布变化不大,也未必需要更新统计信息。 + 一个简单的经验法则是考虑表中各列的最小值和最大值变化了多少。例如, + 一个包含行更新时间的 timestamp 列,会随着行的插入和更新而持续增大其最大值; + 这样的列可能比例如存放网站页面 URL 的列更需要频繁更新统计信息。 + URL 列收到更改的频率可能一样高,但其值的统计分布变化大概相对缓慢。 + + + + 可以对特定表,甚至仅对表中的特定列运行 ANALYZE, + 因此如果你的应用需要,确实可以比其他统计更频繁地更新某些统计信息。 + 然而在实践中,通常最好直接分析整个数据库,因为这是一项很快的操作。 + ANALYZE 使用对表行的统计随机抽样,而不是读取每一行。 + + + + + 虽然按列微调 ANALYZE 的频率未必很有成效,但按列调整 + ANALYZE 收集的统计信息详细程度可能是值得的。 + 在 WHERE 子句中被频繁使用且数据分布高度不规则的列, + 可能需要比其他列更细粒度的数据直方图。参见 ALTER TABLE + SET STATISTICS,或者使用配置参数 更改数据库范围的默认值。 + + + 此外,默认情况下,关于函数选择率的信息很有限。不过,如果创建了使用函数调用的表达式索引,系统就会收集关于该函数的有用统计信息,这可以显著改善使用该表达式索引的查询计划。 + + + + + 自动清理守护进程不会为外部表发出 ANALYZE 命令, + 因为它无法判断多高的频率才有用。如果你的查询需要依赖外部表的统计信息来正确规划, + 一个好办法是按照合适的时间表,在这些表上手工运行 ANALYZE 命令。 + + + + + + + + 更新可见性映射 + + + 清理会为每个表维护一个可见性映射, + 用来跟踪哪些页面只包含已知对所有活动事务都可见的元组 + (并且在该页面再次被修改之前,对所有未来事务也都可见)。这样做有两个目的。 + 第一,下一次清理时可以跳过这样的页面,因为其中没有需要清理的内容。 + + + + 第二,它使 PostgreSQL 能够在不访问底层表的情况下, + 仅使用索引回答某些查询。由于 PostgreSQL 的索引不包含元组可见性信息, + 普通索引扫描会为每个匹配的索引条目取回堆元组,以检查当前事务是否应该看到它。 + 相反,仅索引扫描 + 会先检查可见性映射。如果已知该页面上的所有元组都可见,就可以跳过对堆的访问。 + 这在大型数据集中尤其有用,因为可见性映射能够避免磁盘访问。可见性映射比堆小得多, + 因此即便堆非常大,也很容易缓存。 + + + + + 防止事务 ID 回卷失败 + + + 事务 ID + 回卷 + + + + 回卷 + 事务 ID 的 + + + PostgreSQLMVCC 事务语义依赖于比较事务 ID(XID)的大小:如果某个行版本的插入 XID 大于当前事务的 XID,它就处于未来,不应对当前事务可见。但是,事务 ID 的大小有限(32 位),长期运行的集簇(超过 40 亿个事务)会遇到事务 ID 回卷:XID 计数器回到零,原本处于过去的事务突然看起来处于未来 — 这意味着它们的结果变得不可见。也就是灾难性的数据丢失。(数据其实还在,但如果无法访问,也无济于事。)为避免这种情况,必须至少每经过 20 亿个事务,就清理一次每个数据库中的每个表。 + + 定期清理之所以能解决这一问题,是因为 VACUUM 会将行标记为冻结,表示插入这些行的事务早已提交,足以保证该插入事务的影响对当前及未来的所有事务都可见。普通 XID 使用模 232 算术进行比较。这意味着,对每个普通 XID,都有 20 亿个 XID 比它更老,另有 20 亿个 XID 比它更新;换句话说,普通 XID 空间是一个没有端点的环。因此,用某个普通 XID 创建行版本后,无论该 XID 是多少,在接下来的 20 亿个事务中,该行版本都会被视为处于过去。如果经过超过 20 亿个事务后它仍然存在,就会突然被视为处于未来。为避免这种情况,PostgreSQL 保留了一个特殊 XID:FrozenTransactionId。它不遵循普通 XID 的比较规则,始终被视为比所有普通 XID 都老。冻结行版本的处理方式相当于其插入 XID 是 FrozenTransactionId,因此无论是否发生回卷,对所有普通事务而言,它们都处于过去。所以,这类行版本在被删除之前始终有效,无论存在多久。 + + + + 在 9.4 之前的 PostgreSQL 版本中,冻结是通过实际把一行的插入 XID + 替换为 FrozenTransactionId 实现的,这一点可以在该行的 + xmin 系统列中看到。较新的版本只是设置一个标志位, + 同时保留行原始的 xmin 以供可能的取证用途。不过, + 在从 9.4 之前版本通过 pg_upgrade 升级得到的数据库中, + 仍然可能发现 xmin 等于 + FrozenTransactionId (2) 的行。 + + + 此外,系统目录中可能包含 xmin 等于 + BootstrapTransactionId (1) 的行,这表示它们是在 + initdb 的第一阶段插入的。和 + FrozenTransactionId 一样,这个特殊 XID 也被视为比所有普通 XID 都更老。 + + + + + + 控制某个 XID 值需要达到多老,其对应的行才会被冻结。如果那些本来会被冻结的行很快又会被修改, + 增大这个设置可以避免不必要的工作;而减小这个设置,则会增加在表必须再次被清理之前可以流逝的事务数量。 + + + VACUUM 使用可见性映射来确定必须扫描表的哪些页面。通常,它会跳过没有死行版本的页面,即使其中可能仍有 XID 值很旧的行版本。因此,普通的 VACUUM 不一定会冻结表中的每一个旧行版本。VACUUM 会定期执行激进清理,只跳过既没有死行,也没有任何未冻结 XID 或 MXID 值的页面。 控制 VACUUM 何时执行这种操作:如果距离上一次此类扫描经过的事务数,大于 vacuum_freeze_table_age 减去 vacuum_freeze_min_age,就会扫描全部可见但未全部冻结的页面。将 vacuum_freeze_table_age 设为 0,会强制 VACUUM 对所有扫描都采用这种更激进的策略。 + + 一个表可以不清理的最长时间,是 20 亿个事务减去上次激进清理时的 vacuum_freeze_min_age 值。超过这个期限仍不清理,就可能丢失数据。为确保不会发生这种情况,对于可能包含未冻结行、且其 XID 年龄大于配置参数 所指定年龄的任何表,都会调用自动清理。(即使禁用了自动清理,也会如此。) + + + 这意味着,如果一个表本来不会因为其他原因被清理,大约每经过 + autovacuum_freeze_max_age 减去 + vacuum_freeze_min_age 个事务,就会在该表上触发一次自动清理。 + 对于那些为了回收空间而经常清理的表,这一点并不重要。然而,对于静态表 + (包括只接收插入、但没有更新或删除的表),并不需要为了回收空间而清理, + 因此设法尽可能拉大这些非常大的静态表上强制自动清理之间的间隔,可能会很有用。 + 显然,这可以通过增大 autovacuum_freeze_max_age 或减小 + vacuum_freeze_min_age 来做到。 + + + + vacuum_freeze_table_age 的实际最大值是 0.95 * + autovacuum_freeze_max_age;高于该值的设置会被截断到该最大值。 + 设置一个高于 autovacuum_freeze_max_age 的值没有意义,因为无论如何, + 防回卷自动清理都会在那时被触发,而 0.95 的乘数是为了在那之前留出一些余地以手工运行一次 + VACUUM。经验上,vacuum_freeze_table_age + 应设置为略低于 autovacuum_freeze_max_age 的值,并留出足够空档, + 使一次常规调度的 VACUUM 或由正常删除、更新活动触发的自动清理 + 能在这个窗口内运行。若设置得过于接近,即使该表最近已经为了回收空间而清理过, + 也可能仍会触发防回卷自动清理;而较低的值则会导致更频繁的激进扫描。 + + + 增大 autovacuum_freeze_max_age(以及相应的 vacuum_freeze_table_age)唯一的缺点,是数据库集簇的 pg_clog 子目录会占用更多空间,因为必须保存追溯到 autovacuum_freeze_max_age 视界的所有事务的提交状态。每个事务的提交状态占用两位,因此,如果将 autovacuum_freeze_max_age 设为允许的最大值 20 亿,pg_clog 预计会增长到约 0.5GB。如果这与数据库总大小相比微不足道,建议将 autovacuum_freeze_max_age 设为允许的最大值。否则,应根据愿意为 pg_clog 分配的存储空间来设置。(默认值为 2 亿个事务,对应约 50MB 的 pg_clog 存储。) + + + 减小 vacuum_freeze_min_age 的一个缺点是,它可能导致 + VACUUM 做无用功:如果某个行版本随后不久就被修改 + (因而获得新的 XID),冻结它就是浪费时间。因此,该设置应足够大, + 使得行在不太可能再次发生变化之前不会被冻结。 + + + + 为了跟踪数据库中最老未冻结 XID 的年龄,VACUUM 会在系统表 + pg_classpg_database + 中存储 XID 统计信息。具体来说,某个表在 pg_class 中对应行的 + relfrozenxid 列,记录了上一次对该表执行的激进 + VACUUM 所使用的冻结截止 XID。所有由 XID 早于此截止值的事务 + 插入的行都保证已被冻结。类似地,某个数据库在 + pg_database 中对应行的 datfrozenxid 列, + 是出现在该数据库中的未冻结 XID 的下界,它就是该数据库内各表 + relfrozenxid 值的最小值。查看这些信息的一种方便方式是执行如下查询: + + +SELECT c.oid::regclass as table_name, + greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age +FROM pg_class c +LEFT JOIN pg_class t ON c.reltoastrelid = t.oid +WHERE c.relkind IN ('r', 'm'); + +SELECT datname, age(datfrozenxid) FROM pg_database; + + + age 列度量的是从该截止 XID 到当前事务 XID 之间的事务数。 + + + + VACUUM通常只扫描自上次清理以来被修改过的页面,但只有当表中每一个可能包含未冻结 XID 的页面都被扫描时, + relfrozenxid 才会被推进。当 + relfrozenxid的年龄超过vacuum_freeze_table_age个事务、使用了 VACUUM 的 + FREEZE 选项,或者所有尚未全部冻结的页面碰巧都需要清理以移除死行版本时, + 就会发生这种情况。当 VACUUM 扫描了表中每个尚未全部冻结的页面时, + 它应把 age(relfrozenxid) 设为略高于所用 + vacuum_freeze_min_age 设置的值 + (更高的部分等于自 VACUUM 开始以来已启动的事务数)。如果直到达到 + autovacuum_freeze_max_age 之前,该表都没有执行一次能推进 + relfrozenxidVACUUM, + 那么很快就会被强制执行一次自动清理。 + + + 如果由于某种原因,自动清理未能清除表中的旧 XID,当数据库中最老的 XID 距离回卷点只剩 1100 万个事务时,系统就会开始发出类似以下的警告: +WARNING: database "mydb" must be vacuumed within 10985967 transactions +HINT: To avoid a database shutdown, execute a database-wide VACUUM in that database. +(按照提示,手动执行 VACUUM 应能解决问题;但请注意,VACUUM 必须由超级用户执行,否则无法处理系统目录,也就无法推进数据库的 datfrozenxid。)如果忽略这些警告,一旦距离回卷只剩不到 100 万个事务,系统就会关闭并拒绝启动任何新事务: +ERROR: database is not accepting commands to avoid wraparound data loss in database "mydb" +HINT: Stop the postmaster and vacuum that database in single-user mode. +预留这 100 万个事务的安全余量,是为了让管理员可以手动执行必需的 VACUUM 命令,在不丢失数据的情况下恢复。不过,系统一旦进入安全关闭模式,就不会再执行命令,因此唯一的办法是停止服务器,再以单用户模式启动服务器,执行 VACUUM。单用户模式下不会强制进入关闭模式。有关使用单用户模式的详细信息,参见 参考页面。 + + + 多事务与回卷 + + + MultiXactId + + + + 回卷 + 多事务 ID 的 + + + 多事务 ID 用于支持多个事务对行加锁。由于元组头中存放锁信息的空间有限,当多个事务同时锁定一行时,这些信息会被编码为一个多事务 ID,简称 multixact ID。某个特定 多事务 ID 包含哪些事务 ID 的信息,单独存放在 pg_multixact 子目录中,元组头的 xmax 字段中只保存 多事务 ID。与事务 ID 一样,多事务 ID 由 32 位计数器和相应的存储实现,都需要谨慎处理老化管理、存储清理和回卷。另有单独的存储区保存每个 多事务 的成员列表,它同样使用 32 位计数器,也需要管理。 + + + 每当 VACUUM 扫描表的任何部分时,它都会把遇到的、早于 + + 的任何 多事务 ID 替换为另一个值,该值可能是零值、单个事务 ID,或者更新的 多事务 ID。 + 对于每个表,pg_class.relminmxid + 保存该表任何元组中仍可能出现的最老 多事务 ID。如果这个值早于 + ,就会强制执行一次激进扫描。 + 正如上一节所述,激进扫描意味着只有那些已知为全部冻结的页面才会被跳过。 + 可以对 pg_class.relminmxid + 使用 mxid_age() 来查看其年龄。 + + + + 无论出于何种原因而发生,激进的 VACUUM 扫描都能推进该表的值。最终,随着所有数据库中的所有表都被扫描, + 并推进其最老的 多事务 值,较老 多事务 的磁盘存储就可以被移除。 + + + 作为一项安全措施,对于 多事务 年龄大于 的任何表,都会执行激进清理扫描。如果已使用的成员存储空间超过可寻址存储空间的 50%,还会从 多事务 年龄最大的表开始,逐步对所有表执行激进清理扫描。即使名义上禁用了自动清理,这两类激进扫描也都会发生。 + + + + + 自动清理守护进程 + + + 自动清理 + 一般信息 + + + PostgreSQL 具有一个可选但强烈推荐的特性, + 称为自动清理,其目的是自动执行 + VACUUMANALYZE 命令。 + 启用后,自动清理会检查那些已经积累了大量插入、更新或删除元组的表。 + 这些检查依赖于统计收集功能;因此,除非 被设置为 true, + 否则无法使用自动清理。在默认配置下,自动清理是启用的,并且相关配置参数也设置得比较合适。 + + + + 所谓自动清理守护进程实际上由多个进程组成。其中有一个持久运行的守护进程, + 称为自动清理启动器,负责为所有数据库启动 + 自动清理工作进程。启动器会把工作分散在时间上, + 力图在每个数据库中每隔 秒启动一个工作进程。 + (因此,如果安装中有 N 个数据库,就会每隔 + autovacuum_naptime/N 秒启动一个新的工作进程。) + 同时最多允许运行 个工作进程。 + 如果需要处理的数据库数量多于 autovacuum_max_workers, + 那么一旦第一个工作进程结束,就会处理下一个数据库。每个工作进程会检查其数据库中的每个表, + 并根据需要执行 VACUUM 和/或 ANALYZE。 + 可以设置 来监控自动清理工作进程的活动。 + + + + 如果多个大型表在很短时间内都变得需要清理,那么所有自动清理工作进程都可能会长期忙于清理这些表。 + 这会导致其他表和数据库在某个工作进程空闲下来之前都得不到清理。单个数据库中的工作进程数量没有上限, + 但工作进程会尽量避免重复其他工作进程已经完成的工作。注意,正在运行的工作进程数量不计入 + 或 + 的限制。 + + + 凡是 relfrozenxid 值的年龄超过 个事务的表,始终都会被清理(这也适用于通过存储参数修改了最大冻结年龄的表,见下文)。否则,如果自上次 VACUUM 以来失效的元组数超过清理阈值,就会清理该表。清理阈值定义为: +vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples +其中,清理基础阈值为 ,清理比例因子为 ,元组数为 pg_class.reltuples。失效元组数来自统计收集器;这是一个近似计数,由每次 UPDATEDELETE 操作更新。(之所以只是近似值,是因为高负载下可能丢失部分信息。)如果表的 relfrozenxid 值的年龄超过 vacuum_freeze_table_age 个事务,就会执行激进清理,冻结旧元组并推进 relfrozenxid;否则,只扫描自上次清理以来被修改过的页面。 + + + 对于分析操作,也使用了一个类似的条件:其阈值定义如下: + +analyze threshold = analyze base threshold + analyze scale factor * number of tuples + + 该阈值会与自上次 ANALYZE 以来插入、更新或删除的元组总数进行比较。 + + + + 自动清理无法访问临时表。因此,应通过会话 SQL 命令执行适当的清理和分析操作。 + + + + 默认阈值和尺度因子取自 postgresql.conf,但也可以按表覆盖它们 + (以及许多其他自动清理控制参数);详情见 + 。如果某个设置已经通过表的存储参数修改, + 那么处理该表时就使用那个值;否则使用全局设置。关于全局设置的更多细节,见 + 。 + + + + 当多个工作进程同时运行时,自动清理的代价延迟参数 + (见 )会在所有运行中的工作进程之间 + 平衡,这样无论实际运行了多少个工作进程,系统承受的总 I/O 影响都相同。 + 不过,任何正在处理那些为每表存储参数 + autovacuum_vacuum_cost_delay 或 + autovacuum_vacuum_cost_limit 显式设置过值的表的工作进程, + 都不会被纳入这个均衡算法。 + + + + 自动清理工作进程通常不会阻塞其他命令。如果某个进程试图获取与自动清理持有的 + SHARE UPDATE EXCLUSIVE 锁冲突的锁,那么该加锁操作会中断自动清理。 + 关于冲突锁模式,见 。但是,如果自动清理正在运行, + 其目的是防止事务 ID 回卷(也就是说,在 pg_stat_activity + 视图中,自动清理查询名以 (to prevent wraparound) 结尾), + 则自动清理不会被自动中断。 + + + + + 定期运行需要获取与 SHARE UPDATE EXCLUSIVE 锁冲突的锁的命令 + (例如 ANALYZE),实际上可能会使自动清理永远无法完成。 + + + + + + + + 日常重建索引 + + + 重建索引 + + + + 在某些情况下,值得周期性地使用 命令或一系列单独的重建步骤, + 来重建索引。 + + + + + B-树索引中已经完全变空的索引页会被回收以供重用。不过,仍然可能出现空间利用低效: + 如果某个页面上的索引键除了少数几个之外都被删除,该页面仍会保留分配。因此, + 在一种使用模式下,如果每个键范围中的大多数但不是全部键最终都被删除, + 就会出现糟糕的空间利用。对于这种使用模式,建议定期重建索引。 + + + + 对于非 B-树索引中的膨胀潜力,人们还没有做过充分研究。 + 在使用任何非 B-树索引类型时,定期监控索引的物理大小是个好主意。 + + + + 此外,对 B-树索引而言,新近构建的索引比已经多次更新过的索引在访问时稍快一些, + 因为在新建的索引中,逻辑上相邻的页面通常在物理上也彼此相邻。 + (这一考虑不适用于非 B-树索引。)即便只是为了提高访问速度, + 周期性重建索引也可能是值得的。 + + + 在所有情况下都能安全、方便地使用。但由于该命令需要表的排他锁,通常更适合通过一系列创建和替换步骤来重建索引。对于支持使用 CONCURRENTLY 选项执行 的索引类型,可以改用这种方式重新创建。如果创建成功且生成的索引有效,就可以结合 ,用新建索引替换原索引。如果索引用于强制唯一性或其他约束,可能需要使用 ,将现有约束替换为由新索引强制执行的约束。使用这种多步骤重建方法前,应仔细评估,因为能够用这种方式重建的索引存在限制,而且必须处理错误。 + + + + + 日志文件维护 + + + 服务器日志 + 日志文件维护 + + + + 最好把数据库服务器的日志输出保存到某个地方,而不是简单地把它丢弃到 + /dev/null。诊断问题时,日志输出极其宝贵。但是,日志输出往往十分庞大(尤其是在较高调试级别下),因此你不会希望无限期保存它。 + 你需要对日志文件进行轮转,以便周期性地启用新的日志文件, + 并在合理时间后移除旧文件。 + + + + 如果你只是把 postgresstderr + 重定向到一个文件,那么虽然也能得到日志输出,但截断该日志文件的唯一办法是停止并重新启动服务器。 + 如果你是在开发环境中使用 PostgreSQL,这也许可以接受, + 但几乎没有生产服务器会认为这种行为可以接受。 + + + + 更好的办法是把服务器的 stderr 输出发送给某种日志轮转程序。 + 系统内置了日志轮转功能,你可以在 + postgresql.conf 中将配置参数 logging_collector + 设为 true 来使用它。该程序的控制参数见 + 。你还可以用这种方式以机器可读的 + CSV(逗号分隔值)格式捕获日志数据。 + + + 也可以使用外部日志轮转程序,特别是已经在其他服务器软件上使用了某个此类程序时。例如,rotatelogs 工具包含在 Apache 发行版中,也可用于 PostgreSQL。为此,只需通过管道将服务器的 stderr 输出传递给所需程序。如果使用 pg_ctl 启动服务器,那么 stderr 已经被重定向到 stdout,因此只需一个管道命令,例如: +pg_ctl start | rotatelogs /var/log/pgsql_log 86400 + + + + + 管理日志输出的另一种生产级方法,是把它发送给 syslog, + 并让 syslog 负责文件轮转。为此,在 + postgresql.conf 中把配置参数 log_destination + 设为 syslog(如果只想记录到 syslog)。 + 之后,每当你想强制它开始写入新的日志文件时,都可以向 + syslog 守护进程发送一个 SIGHUP 信号。 + 如果你想自动化日志轮转,也可以把 logrotate + 配置为处理来自 syslog 的日志文件。 + + + + 不过,在很多系统上,syslog 并不十分可靠, + 尤其是在日志消息很大时;它可能恰恰在你最需要消息的时候截断或丢弃它们。 + 此外,在 Linux 上, + syslog 会把每条消息都刷盘,导致性能较差。 + (你可以在 syslog 配置文件的文件名开头使用一个 + - 来禁用同步。) + + + + 请注意,上面描述的所有方案都处理了按可配置间隔启动新日志文件的问题, + 但它们并不处理删除那些已经没有用处的旧日志文件。你很可能还需要设置一个批处理任务, + 定期删除旧日志文件。另一种可能是把轮转程序配置为循环覆盖旧日志文件。 + + + + pgBadger + 是一个进行复杂日志文件分析的外部项目。 + check_postgres + 可以在日志文件中出现重要消息时向 Nagios 发出警报,也能检测许多其他异常情况。 + + + diff --git a/zh/9.6/manage-ag.sgml b/zh/9.6/manage-ag.sgml new file mode 100644 index 00000000..0366a95a --- /dev/null +++ b/zh/9.6/manage-ag.sgml @@ -0,0 +1,299 @@ + + + + 管理数据库 + + database + + + 每个正在运行的PostgreSQL服务器实例都管理着一个或多个数据库。因此,数据库是组织SQL对象(数据库对象)的最高层级。本章描述数据库的属性,以及如何创建、管理和删除数据库。 + + + + 概述 + + + 模式 + + + + 少量对象,例如角色名、数据库名和表空间名,是在集簇级别定义并存储在pg_global表空间中的。集簇内部有多个数据库,它们彼此隔离,但可以访问集簇级对象。每个数据库内部又有多个模式,其中包含表、函数等对象。因此,完整的层次结构是:集簇、数据库、模式、表(或者函数等其他类型的对象)。 + + + + 当连接到数据库服务器时,客户端必须在连接请求中指定数据库名。一个连接无法访问多个数据库。不过,客户端可以对同一个数据库建立多个连接,也可以连接到不同的数据库。数据库级安全由两部分组成:访问控制(见),在连接级管理;授权控制(见),通过 GRANT 系统管理。外部数据包装器(见)允许一个数据库中的对象充当其他数据库或集簇中对象的代理。较早的 dblink 模块(见)也提供了类似能力。默认情况下,所有用户都可以使用所有连接方法连接到所有数据库。 + + + + 如果一个PostgreSQL服务器集簇计划容纳彼此无关、在大多数情况下也不需要感知彼此存在的项目或用户,建议将它们放入不同的数据库,并相应地调整授权和访问控制。如果这些项目或用户彼此相关,因此应当能够使用彼此的资源,那么它们应当位于同一个数据库中,但通常应放入不同的模式;这能提供具有名字空间隔离和授权控制的模块化结构。关于管理模式的更多信息见。 + + + + 虽然可以在单个集簇中创建多个数据库,但仍建议仔细权衡其收益是否超过风险和限制。尤其要考虑共享 WAL(见)对备份和恢复选项的影响。虽然从用户角度看,集簇中的各个数据库彼此隔离,但从数据库管理员的角度看,它们又是紧密绑定的。 + + + + 数据库使用CREATE DATABASE命令创建(见),使用DROP DATABASE命令删除(见)。要确定现有数据库的集合,可以检查系统目录pg_database,例如: + +SELECT datname FROM pg_database; + + 程序的\l元命令和命令行选项也可用于列出现有数据库。 + + + + + SQL标准将数据库称为目录,但在实践中两者没有区别。 + + + + + + 创建数据库 + + CREATE DATABASE + + + 要创建数据库,PostgreSQL服务器必须已经启动并正在运行(见)。 + + + + 数据库用 SQL 命令创建: + +CREATE DATABASE name; + + 其中name遵循SQL标识符的一般规则。当前角色会自动成为新数据库的拥有者。数据库拥有者有权在日后删除该数据库(这也会删除其中的全部对象,即使那些对象具有不同的拥有者)。 + + + + 创建数据库是一项受限操作。关于如何授予该权限,见。 + + + + 由于执行CREATE DATABASE命令时必须先连接到数据库服务器,因此还会有一个问题:某个站点上的第一个数据库是如何创建的?第一个数据库总是由initdb命令在初始化数据存储区时创建的(见)。这个数据库叫作postgrespostgres因此,要创建第一个普通数据库,可以连接到postgres。 + + + + 在初始化数据库集簇时,还会创建第二个数据库template1template1每当在集簇中创建一个新数据库时,新数据库本质上都是通过克隆template1得到的。这意味着你在template1中所做的任何更改,都会传播到后来创建的所有数据库中。因此,除非你希望这些对象出现在每一个新建数据库中,否则应避免在template1中创建对象。更多细节见。 + + + + 为了方便,还可以从 shell 中执行一个程序来创建新数据库,即createdbcreatedb + + +createdb dbname + + + createdb并没有什么特殊之处。它连接到postgres数据库并发出CREATE DATABASE命令,和上面描述的完全一样。关于调用细节见参考页。注意,不带任何参数的createdb会创建一个与当前用户名同名的数据库。 + + + + + 包含关于如何限制谁能连接到给定数据库的信息。 + + + + + 有时你会想为其他人创建数据库,并让他们成为新数据库的拥有者,以便他们自行配置和管理它。要实现这一点,可使用下列命令之一: + +CREATE DATABASE dbname OWNER rolename; + + 这是在 SQL 环境中使用;或者: + +createdb -O rolename dbname + + 这是在 shell 中使用。只有超级用户才允许为其他人(即为一个你不是其成员的角色)创建数据库。 + + + + + 模板数据库 + + + CREATE DATABASE实际上是通过复制一个现有数据库来创建新数据库。默认情况下,它复制名为template1的标准系统数据库。template1因此,该数据库就是创建新数据库时使用的模板。如果你向template1中添加对象,这些对象也会被复制到以后创建的用户数据库中。这种行为允许对数据库中的标准对象集合进行站点本地修改。例如,如果你将过程语言PL/Perl安装到template1中,那么以后创建用户数据库时无需额外操作,它就会自动可用。 + + + + 还有第二个标准系统数据库,名为template0template0该数据库包含与template1初始内容相同的数据,也就是说,只包含你的PostgreSQL版本预定义的标准对象。在数据库集簇初始化之后,template0就不应再被修改。通过指示CREATE DATABASE复制template0而不是template1,你可以创建一个纯净的用户数据库,它不包含template1中的任何站点本地附加对象。这在恢复pg_dump转储时特别方便:转储脚本应当恢复到一个纯净的数据库中,以确保重建出被转储数据库的正确内容,而不会与后来可能加入template1的对象发生冲突。 + + + + 复制template0而不是template1的另一个常见原因是:复制template0时可以指定新的编码和区域设置,而template1的副本则必须使用与它相同的设置。这是因为template1可能包含依赖特定编码或区域设置的数据,而已知template0不会包含这类数据。 + + + + 要通过复制template0创建数据库,可在 SQL 环境中使用: + +CREATE DATABASE dbname TEMPLATE template0; + + 或在 shell 中使用: + +createdb -T template0 dbname + + + + + 可以创建额外的模板数据库,实际上也可以通过将集簇中任意数据库的名称指定为CREATE DATABASE的模板来复制它。不过,必须理解的是,这(至少目前)并不是一个通用的COPY DATABASE功能。最主要的限制是:复制源数据库时,不能有其他会话连接到该数据库。如果CREATE DATABASE开始时存在任何其他连接,该命令就会失败;在复制操作期间,也会阻止到源数据库的新连接。 + + + + 对每个数据库,pg_databasepg_database中都有两个有用的标志列:datistemplatedatallowconndatistemplate可设置为表明某个数据库将作为CREATE DATABASE的模板。如果设置了该标志,任何拥有CREATEDB权限的用户都可以克隆该数据库;如果未设置,则只有超级用户和数据库拥有者才能克隆它。如果datallowconn为假,则不允许建立到该数据库的新连接(但仅仅把该标志设为假并不会终止已有会话)。为了防止被修改,template0数据库通常被标记为datallowconn = falsetemplate0template1始终都应标记为datistemplate = true。 + + + + + template1template0除了template1这个名字是CREATE DATABASE默认源数据库名这一点之外,并无任何特殊地位。例如,可以删除template1,然后再从template0重新创建它,而不会有任何不良后果。如果有人不小心在template1里加入了许多杂项对象,这样做也许是可取的。(要删除template1,必须先令其具有pg_database.datistemplate = false。) + + + + 初始化数据库集簇时也会创建postgres数据库。这个数据库被用作用户和应用连接时的默认数据库。它只是template1的一个副本,因此在必要时也可以删除并重建。 + + + + + + 数据库配置 + + + 正如所述,PostgreSQL服务器提供了大量运行时配置变量。你可以为其中许多设置指定数据库特定的默认值。 + + + + 例如,如果你因为某种原因想在某个数据库中禁用GEQO优化器,通常要么必须对所有数据库都禁用它,要么必须确保每个连入的客户端都会执行SET geqo + TO off。若要让这个设置在某个特定数据库中成为默认值,可以执行如下命令: + +ALTER DATABASE mydb SET geqo TO off; + + 这会保存该设置(但不会立即生效)。以后连接到该数据库时,它看起来就像是在会话开始前刚执行过SET geqo TO off;一样。注意,用户仍然可以在自己的会话中更改该设置;它只会作为默认值。要撤销这样的设置,可使用ALTER DATABASE dbname RESET + varname。 + + + + + 删除数据库 + + + 数据库使用命令删除:DROP DATABASE + +DROP DATABASE name; + + 只有数据库拥有者或超级用户可以删除数据库。删除数据库会移除其中包含的全部对象。数据库的删除无法撤销。 + + + + 当你连接到目标数据库时,不能执行DROP DATABASE命令。不过,你可以连接到任何其他数据库,包括template1数据库。若要删除某个集簇中的最后一个用户数据库,template1将是唯一的选择。 + + + + 为了方便,还有一个 shell 程序可用于删除数据库,即dropdb + +dropdb dbname + + (与createdb不同,默认操作并不是删除与当前用户名同名的数据库。) + + + + + 表空间 + + + 表空间 + + + + PostgreSQL中的表空间允许数据库管理员在文件系统中定义存放表示数据库对象的文件的位置。表空间一旦创建,在创建数据库对象时就可以通过名称引用它。 + + + + 通过使用表空间,管理员可以控制PostgreSQL安装的磁盘布局。这至少有两方面的用途。首先,如果初始化集簇所在的分区或卷空间耗尽且无法扩展,可以在另一个分区上创建表空间并使用它,直到系统能够重新配置。 + + + + 其次,表空间允许管理员利用对数据库对象使用模式的了解来优化性能。例如,一个使用非常频繁的索引可以放在速度非常快且高可用的磁盘上,例如昂贵的固态设备。同时,一个存放归档数据且很少使用或对性能并不关键的表,则可以放在成本更低、速度更慢的磁盘系统上。 + + + + + 即使位于 PostgreSQL 主数据目录之外,表空间仍然是数据库集簇不可分割的一部分,不能被视为一个独立的数据文件集合。它们依赖于主数据目录中的元数据,因此不能附加到不同的数据库集簇,也不能单独备份。同样,如果丢失一个表空间(文件被删除、磁盘故障等),数据库集簇可能会变得不可读或者无法启动。把表空间放在 RAM 磁盘这类临时文件系统上,会危及整个集簇的可靠性。 + + + + + 要定义一个表空间,使用 + 命令,例如:CREATE TABLESPACE + +CREATE TABLESPACE fastspace LOCATION '/ssd1/postgresql/data'; + + 该位置必须是一个已经存在、为空并且由PostgreSQL操作系统用户拥有的目录。随后在这个表空间中创建的所有对象都会存储在该目录下的文件中。该位置不得位于可移动或瞬态存储上,因为如果表空间缺失或丢失,集簇可能无法正常工作。 + + + + + 通常来说,在每个逻辑文件系统上创建多个表空间意义不大,因为你无法控制单个文件在一个逻辑文件系统中的具体位置。不过,PostgreSQL并不强制这种限制,而且它实际上并不能直接感知你的系统中文件系统的边界。它只是在你指定的目录中存储文件。 + + + + + 表空间本身必须由数据库超级用户创建,但之后你可以允许普通数据库用户使用它。为此,需要向他们授予其上的CREATE权限。 + + + + 表、索引以及整个数据库都可以被分配到特定的表空间。为此,对给定表空间拥有CREATE权限的用户必须把表空间名作为参数传给相应命令。例如,下列命令会在表空间space1中创建一个表: + +CREATE TABLE foo(i int) TABLESPACE space1; + + + + + 另一种方式是使用参数: + +SET default_tablespace = space1; +CREATE TABLE foo(i int); + + 当default_tablespace被设置为非空字符串时,它会为未显式指定TABLESPACE子句的CREATE TABLECREATE INDEX命令隐含地补上该子句。 + + + + 还有一个参数,用来决定临时表和索引以及为大数据集排序等用途所使用的临时文件应放置在哪里。它可以是一个表空间名列表,而不只是单个表空间,这样与临时对象相关的负载就可以分散到多个表空间上。每次创建临时对象时,都会从该列表中随机挑选一个成员。 + + + + 与数据库关联的表空间用于存储该数据库的系统目录。此外,如果没有给出TABLESPACE子句,并且default_tablespacetemp_tablespaces(视情况而定)也没有指定其他选择,那么它还是在该数据库中创建的表、索引和临时文件所使用的默认表空间。如果创建数据库时没有为其指定表空间,它将使用其所复制模板数据库的同一个表空间。 + + + + 初始化数据库集簇时会自动创建两个表空间。pg_global表空间只用于共享系统目录。pg_default表空间是template1template0数据库的默认表空间(因此,除非被TABLESPACE子句覆盖,而该子句可在CREATE + DATABASE中指定,否则它也会是其他数据库的默认表空间)。 + + + + 表空间一旦创建,只要请求的用户具有足够权限,就可以从任何数据库中使用它。这意味着,只有当使用该表空间的所有数据库中的所有对象都已被移除之后,才能删除该表空间。 + + + + 要移除一个空表空间,使用 + 命令。 + + + + 要确定现有表空间的集合,可以检查pg_tablespace + 系统目录,例如: + +SELECT spcname FROM pg_tablespace; + + 程序的\db元命令对列出现有表空间也很有用。 + + + + PostgreSQL使用符号链接来简化表空间的实现。这意味着表空间只能在支持符号链接的系统上使用。 + + + + $PGDATA/pg_tblspc目录包含符号链接,它们指向集簇中定义的每一个非内置表空间。虽然不推荐,但也可以通过重新定义这些链接来手工调整表空间布局。无论如何,都不要在服务器运行时执行此操作。注意,在 PostgreSQL 9.1 及更早版本中,你还需要用新位置来更新pg_tablespace目录。(如果不这样做,pg_dump将继续输出旧的表空间位置。) + + + + diff --git a/zh/9.6/mk_feature_tables.pl b/zh/9.6/mk_feature_tables.pl new file mode 100644 index 00000000..45dea798 --- /dev/null +++ b/zh/9.6/mk_feature_tables.pl @@ -0,0 +1,70 @@ +# /usr/bin/perl -w + +# doc/src/sgml/mk_feature_tables.pl + +my $yesno = $ARGV[0]; + +open PACK, $ARGV[1] or die; + +my %feature_packages; + +while () +{ + chomp; + my ($fid, $pname) = split /\t/; + if ($feature_packages{$fid}) + { + $feature_packages{$fid} .= ", $pname"; + } + else + { + $feature_packages{$fid} = $pname; + } +} + +close PACK; + +open FEAT, $ARGV[2] or die; + +print "\n"; + +while () +{ + chomp; + my ($feature_id, $feature_name, $subfeature_id, + $subfeature_name, $is_supported, $comments) = split /\t/; + + $is_supported eq $yesno || next; + + $feature_name =~ s//>/g; + $subfeature_name =~ s//>/g; + + print " \n"; + + if ($subfeature_id) + { + print " $feature_id-$subfeature_id\n"; + } + else + { + print " $feature_id\n"; + } + print " " . $feature_packages{$feature_id} . "\n"; + if ($subfeature_id) + { + print " $subfeature_name\n"; + } + else + { + print " $feature_name\n"; + } + print " $comments\n"; + + print " \n"; +} + +print "\n"; + +close FEAT; diff --git a/zh/9.6/monitoring.sgml b/zh/9.6/monitoring.sgml new file mode 100644 index 00000000..8109c714 --- /dev/null +++ b/zh/9.6/monitoring.sgml @@ -0,0 +1,3068 @@ + + + + 监控数据库活动 + + + 监控 + 数据库活动 + + + + 数据库活动 + 监控 + + + + 数据库管理员常常会问:系统现在正在做什么?本章将讨论如何回答这个问题。 + + + + 有几种工具可用于监控数据库活动和分析性能。本章的大部分内容将用于描述PostgreSQL的统计收集器, + 但也不应忽略常见的 Unix 监控程序,例如pstopiostatvmstat。 + 此外,一旦确定了某个查询性能不佳,可能需要使用PostgreSQL命令进行进一步调查。 + 讨论了EXPLAIN以及理解单个查询行为的其他方法。 + + + + 标准 Unix 工具 + + + ps + 监控活动 + + + 在大多数 Unix 平台上,PostgreSQL会修改由ps报告的命令标题,以便轻松识别各个服务器进程。以下是一个显示示例: +$ ps auxww | grep ^postgres +postgres 15551 0.0 0.1 57536 7132 pts/0 S 18:02 0:00 postgres -i +postgres 15554 0.0 0.0 57536 1184 ? Ss 18:02 0:00 postgres: writer process +postgres 15555 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: checkpointer process +postgres 15556 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: wal writer process +postgres 15557 0.0 0.0 58504 2244 ? Ss 18:02 0:00 postgres: autovacuum launcher process +postgres 15558 0.0 0.0 17512 1068 ? Ss 18:02 0:00 postgres: stats collector process +postgres 15582 0.0 0.0 58772 3080 ? Ss 18:04 0:00 postgres: joe runbug 127.0.0.1 idle +postgres 15606 0.0 0.0 58772 3052 ? Ss 18:07 0:00 postgres: tgl regression [local] SELECT waiting +postgres 15610 0.0 0.0 58772 3056 ? Ss 18:07 0:00 postgres: tgl regression [local] idle in transaction +ps的适当调用方式随平台而异,显示内容的细节也是如此。此示例来自较新的 Linux 系统。)这里列出的第一个进程是主服务器进程。显示的命令参数与启动该进程时使用的参数相同。接下来的五个进程是主进程自动启动的后台工作进程。(如果系统被设置为不启动统计收集器,则不会出现stats collector进程;同样,也可以禁用autovacuum launcher进程。)其余每个进程都是处理一个客户端连接的服务器进程。每个这样的进程都将其命令行显示设置为以下形式: +postgres: user database host activity +在客户端连接的整个生命周期内,用户、数据库和(客户端)主机这几项保持不变,但活动指示符会改变。活动可以是idle(即等待客户端命令)、idle in transaction(在BEGIN块内等待客户端),或者命令类型名称,例如SELECT。此外,服务器进程当前若正在等待另一会话持有的锁,还会附加waiting。在上面的示例中,可以推断进程 15606 正在等待进程 15610 完成事务,从而释放某个锁。(进程 15610 必定是阻塞者,因为不存在其他活动会话。在更复杂的情况下,必须查看pg_locks系统视图,才能确定谁在阻塞谁。) + + 如果已配置,集簇名称也会显示在ps的输出中: +$ psql -c 'SHOW cluster_name' + cluster_name +-------------- + server1 +(1 row) + +$ ps aux|grep server1 +postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: server1: writer process +... + + + + + 如果您关闭了,则活动指示器不会更新;进程标题仅在新进程启动时设置一次。在某些平台上,这可以节省可观的每命令开销;在其他平台上,则微不足道。 + + + + + + Solaris需要特殊处理。您必须使用/usr/ucb/ps,而不是/bin/ps。您还必须使用两个标志,而不仅仅是一个。此外,您对postgres命令的原始调用必须具有比每个服务器进程提供的更短的ps状态显示。如果未满足这三个条件,每个服务器进程的ps输出都会显示原始的postgres命令行。 + + + + + + 统计收集器 + + + 统计信息 + + + + PostgreSQL统计收集器是一个支持收集和报告服务器活动信息的子系统。 + 目前,对表和索引的访问分别以磁盘块和单行计数。每个表中的总行数, + 以及每个表的清理和分析操作的信息也被计数。它还可以统计对用户定义函数的调用 + 和每个函数中花费的总时间。 + + + + PostgreSQL也支持报告关于系统当前正在发生的情况的动态信息, + 例如其他服务器进程当前正在执行的确切命令,以及系统中存在哪些其他连接。此功能独立于收集器进程。 + + + + 统计信息收集配置 + + + 由于收集统计信息会增加查询执行的开销,因此可以配置系统是否收集信息。这由通常在postgresql.conf中设置的配置参数控制(有关设置配置参数的详细信息,请参阅)。 + + + + 参数启用对任意服务器进程当前执行命令的监控。 + + + + 参数控制是否收集关于表和索引访问的统计信息。 + + + + 参数启用对用户定义函数使用的跟踪。 + + + + 参数启用对块读取和写入时间的监控。 + + + + 通常,这些参数会设置在postgresql.conf中,以便它们适用于所有服务器进程,但也可以在单个会话中使用命令打开或关闭它们。(为防止普通用户隐藏其活动不被管理员发现,只有超级用户才能使用SET更改这些参数。) + + + + 统计收集器通过临时文件把收集到的信息传递给其他PostgreSQL进程。 + 这些文件默认存放在由参数指定的目录中,即pg_stat_tmp。 + 为了获得更好的性能,可以把stats_temp_directory指向基于 RAM 的文件系统,以减少物理 I/O 需求。 + 当服务器正常关闭时,统计信息数据的永久副本会存储在pg_stat子目录中,以便在服务器重启后保留这些统计信息。 + 当服务器启动时执行恢复时(例如立即关闭后、服务器崩溃后以及进行时间点恢复时),所有统计计数器都会被重置。 + + + + + + 查看统计信息 + + + 有几个预定义视图列在中,可用于显示系统的当前状态。另有几个视图列在中,可用于显示统计信息收集的结果。或者,也可以像中所述那样,使用底层的统计函数构建自定义视图。 + + + + 当使用统计信息来监视收集到的数据时,重要的是要意识到信息不会立即更新。每个单独的服务器进程会在转为空闲之前把新的统计计数发送给收集器,因此仍在进行中的查询或事务不会影响显示的总计。并且,收集器本身最多每隔PGSTAT_STAT_INTERVAL毫秒才会发出一份新报告一次(除非在构建服务器时做过修改,否则为 500 毫秒)。因此,显示的信息会滞后于实际活动。然而,由track_activities收集的当前查询信息始终是最新的。 + + + + 另一个重要点是,当要求服务器进程显示这些统计信息中的任何一种时,它会先取回收集器进程最近发出的报告,然后在当前事务结束之前,对所有统计视图和函数一直使用这一快照。因此,只要你继续当前事务,统计信息就会显示静态信息。类似地,在事务中第一次请求这类当前查询信息时,就会收集所有会话当前查询的信息,并且在整个事务期间显示的都是同一份信息。这是一个特性,而不是一个错误,因为它允许你对统计信息执行多个查询并对结果做关联,而不必担心数字在你眼前变化。但如果希望每个查询都看到新的结果,请确保在任何事务块之外执行这些查询。或者,也可以调用pg_stat_clear_snapshot(),它会丢弃当前事务的统计快照(如果有)。下一次使用统计信息时,就会获取一个新的快照。 + + + + 事务还可以在视图pg_stat_xact_all_tables、 + pg_stat_xact_sys_tables、 + pg_stat_xact_user_tables和 + pg_stat_xact_user_functions中看到自己的统计信息 + (这些统计信息尚未传送给收集器)。这些数字不像上面所述那样起作用; + 相反,它们在事务期间持续更新。 + + + + 动态统计视图 + + + + + + 视图名称 + 描述 + + + + + + + pg_stat_activity + pg_stat_activity + + 每个服务器进程一行,显示与该进程当前活动有关的信息,例如状态和当前查询。详见 + + + + pg_stat_replicationpg_stat_replication + 每个 WAL 发送进程一行,显示向其所连接的备库进行复制的统计信息。详见 + + + + pg_stat_wal_receiverpg_stat_wal_receiver + 只有一行,显示 WAL 接收进程从其所连接服务器接收数据的统计信息。详见 + + + + + pg_stat_sslpg_stat_ssl + 每个连接(普通连接和复制连接)一行,显示此连接所使用 SSL 的信息。详见 + + + + pg_stat_progress_vacuumpg_stat_progress_vacuum + 每个正在运行VACUUM的后端(包括自动清理工作进程)一行,显示当前进度。请参阅。 + + + + + +
+ + + 已收集统计信息的视图 + + + + + + 视图名称 + 描述 + + + + + + + pg_stat_archiverpg_stat_archiver + 只有一行,显示 WAL 归档进程活动的统计信息。详见 + + + + pg_stat_bgwriterpg_stat_bgwriter + 只有一行,显示后台写入器活动的统计信息。详见 + + + + pg_stat_databasepg_stat_database + 每个数据库一行,显示整个数据库的统计信息。详见 + + + + pg_stat_database_conflictspg_stat_database_conflicts + 每个数据库一行,显示备库上因与恢复冲突而取消查询的数据库范围统计信息。详见 + + + + pg_stat_all_tablespg_stat_all_tables + 当前数据库中的每个表一行,显示访问该表的统计信息。详见 + + + + pg_stat_sys_tablespg_stat_sys_tables + pg_stat_all_tables一样,但只显示系统表。 + + + + pg_stat_user_tablespg_stat_user_tables + pg_stat_all_tables一样,但只显示用户表。 + + + + pg_stat_xact_all_tablespg_stat_xact_all_tables + pg_stat_all_tables 相似,但只统计当前事务中截至目前执行的操作(这些操作尚计入 pg_stat_all_tables 及相关视图)。此视图不包含存活行和死行数量以及清理、分析操作的列。 + + + + pg_stat_xact_sys_tablespg_stat_xact_sys_tables + pg_stat_xact_all_tables一样,但只显示系统表。 + + + + pg_stat_xact_user_tablespg_stat_xact_user_tables + pg_stat_xact_all_tables一样,但只显示用户表。 + + + + pg_stat_all_indexespg_stat_all_indexes + 当前数据库中的每个索引一行,显示访问该索引的统计信息。详见 + + + + pg_stat_sys_indexespg_stat_sys_indexes + pg_stat_all_indexes一样,但只显示系统表上的索引。 + + + + pg_stat_user_indexespg_stat_user_indexes + pg_stat_all_indexes一样,但只显示用户表上的索引。 + + + + pg_statio_all_tablespg_statio_all_tables + 当前数据库中的每个表一行,显示该表的 I/O 统计信息。详见 + + + + pg_statio_sys_tablespg_statio_sys_tables + pg_statio_all_tables一样,但只显示系统表。 + + + + pg_statio_user_tablespg_statio_user_tables + pg_statio_all_tables一样,但只显示用户表。 + + + + pg_statio_all_indexespg_statio_all_indexes + 当前数据库中的每个索引一行,显示该索引的 I/O 统计信息。详见 + + + + pg_statio_sys_indexespg_statio_sys_indexes + pg_statio_all_indexes一样,但只显示系统表上的索引。 + + + + pg_statio_user_indexespg_statio_user_indexes + pg_statio_all_indexes一样,但只显示用户表上的索引。 + + + + pg_statio_all_sequencespg_statio_all_sequences + 当前数据库中的每个序列一行,显示该序列的 I/O 统计信息。详见 + + + + pg_statio_sys_sequencespg_statio_sys_sequences + pg_statio_all_sequences一样,但只显示系统序列(目前没有定义系统序列,因此这个视图总是为空)。 + + + + pg_statio_user_sequencespg_statio_user_sequences + pg_statio_all_sequences一样,但只显示用户序列。 + + + + pg_stat_user_functionspg_stat_user_functions + 每个被跟踪的函数一行,显示该函数执行的统计信息。详见 + + + + pg_stat_xact_user_functionspg_stat_xact_user_functions + pg_stat_user_functions相似,但是只统计在当前事务期间的调用(还没有被包括在pg_stat_user_functions中)。 + + + + +
+ + + 每个索引的统计信息对于判断哪些索引正在被使用以及它们有多有效尤其有用。 + + + + pg_statio_视图主要用于判断缓冲区缓存的有效性。 + 如果实际磁盘读取次数远少于缓冲区命中次数,缓存就能满足大多数读取请求,而无需调用内核。 + 不过,这些统计信息并不能反映全部情况:由于PostgreSQL处理磁盘 I/O 的方式, + 不在PostgreSQL缓冲区缓存中的数据仍可能驻留在内核的 I/O 缓存中, + 因而仍可在不执行物理读取的情况下取得。对于希望更详细地了解PostgreSQL I/O 行为的用户, + 建议将PostgreSQL统计收集器与能够深入查看内核 I/O 处理情况的操作系统工具结合使用。 + + + + + <structname>pg_stat_activity</structname> 视图 + + + + + + 类型 + 描述 + + + + + + datid + oid + + 这个后端连接到的数据库的OID + + + + datname + name + + 这个后端连接到的数据库的名称 + + + + pid + integer + + 这个后端的进程 ID + + + + usesysid + oid + + 登录到这个后端的用户的 OID + + + + usename + name + + 登录到此后端的用户名称 + + + + application_name + text + + 连接到此后端的应用名称 + + + + client_addr + inet + + 连接到这个后端的客户端的 IP 地址。如果这个字段为空值,它表示客户端通过服务器机器上的一个 Unix 套接字连接或者这是一个内部进程,如自动清理。 + + + + client_hostname + text + + 已连接的客户端的主机名,由client_addr的反向 DNS 查找报告。 + 这个字段将只对 IP 连接非空,并且只有 被启用时才会非空。 + + + + client_port + integer + 客户端用于与此后端通信的 TCP 端口号;如果使用 Unix 套接字,则为-1 + + + backend_start + timestamp with time zone + + 这个进程被启动的时间,也就是客户端连接到服务器的时间 + + + + xact_start + timestamp with time zone + + 这个进程的当前事务被启动的时间,如果没有活动事务则为空值。 + 如果当前查询是其事务中的第一个查询,这一列等于query_start列。 + + + + query_start + timestamp with time zone + + 当前活动查询开始的时间;如果state不是active,则为上一个查询的开始时间 + + + + state_change + timestamp with time zone + + state上一次发生改变的时间 + + + + wait_event_type + text + 后端正在等待的事件类型(如果有),否则为空值。可能的值为: + + LWLockNamed:后端正在等待一个特定的具名轻量级锁。每个这样的锁保护共享内存中的某个特定数据结构。wait_event将包含该轻量级锁的名称。 + + + LWLockTranche:后端正在等待一组相关轻量级锁中的某一个。组中的所有锁执行类似的功能;wait_event将标识该组中锁的大致用途。 + + + Lock:后端正在等待重量级锁。重量级锁也称为锁管理器锁,或简称为锁,主要保护 SQL 可见的对象,例如表。不过,它们也用于确保某些内部操作(例如关系扩展)的互斥。wait_event将标识正在等待的锁类型。 + + + BufferPin:服务器进程正在等待访问一个数据缓冲区,访问期间不得有其他进程检查该缓冲区。如果另一个进程持有尚未关闭的游标,且该游标最近读取的数据来自此缓冲区,缓冲区钉住等待就可能持续较长时间。 + + + + + + wait_event + text + 如果后端当前正在等待,则为等待事件名称,否则为空值。详见 + + + state + text + 该后端当前的总体状态。可能的值为: + + active:后端正在执行一个查询。 + + + idle:后端正在等待新的客户端命令。 + + + idle in transaction:后端处于事务中,但当前没有执行查询。 + + + idle in transaction (aborted):该状态与idle in transaction类似,但事务中的某个语句导致了错误。 + + + fastpath function call:后端正在执行一个快速路径函数。 + + + disabled:如果在此后端中禁用了,就会报告此状态。 + + + + + + backend_xid + xid + + 这个后端的顶层事务 ID,如果存在。 + + + + backend_xmin + xid + + 当前后端的xmin视界。 + + + + query + text + + 这个后端最近查询的文本。如果state为active,这个字段显示当前正在执行的查询。 + 在所有其他状态下,它显示上一个被执行的查询。 + + + + +
+ + + pg_stat_activity视图每个服务器进程将有一行,显示与该进程当前活动相关的信息。 + + + + + wait_eventstate列彼此独立。如果某个后端处于active状态,它可能在等待(waiting)某个事件,也可能没有等待。如果状态为activewait_event非空,就意味着某个查询正在执行,但在系统中的某处被阻塞了。 + + + + + <structname>wait_event</structname> 描述 + + + + + 等待事件类型 + 等待事件名称 + 描述 + + + + + LWLockNamed + ShmemIndexLock + 等待在共享内存中找到或分配空间。 + + + OidGenLock + 等待分配或指派 OID。 + + + XidGenLock + 等待分配或指派事务 ID。 + + + ProcArrayLock + 等待获取快照,或在事务结束时清除事务 ID。 + + + SInvalReadLock + 等待从共享失效队列中取出或移除消息。 + + + SInvalWriteLock + 等待向共享失效队列添加消息。 + + + WALBufMappingLock + 等待在WAL缓冲区中替换一个页面。 + + + WALWriteLock + 等待WAL缓冲区写入磁盘。 + + + ControlFileLock + 等待读取或更新控制文件,或创建新的 WAL 文件。 + + + CheckpointLock + 等待执行检查点。 + + + CLogControlLock + 等待读取或更新事务状态。 + + + SubtransControlLock + 等待读取或更新子事务信息。 + + + MultiXactGenLock + 等待读取或更新共享的多事务状态。 + + + MultiXactOffsetControlLock + 等待读取或更新多事务偏移映射。 + + + MultiXactMemberControlLock + 等待读取或更新多事务成员映射。 + + + RelCacheInitLock + 等待读取或写入关系缓存初始化文件。 + + + CheckpointerCommLock + 等待管理fsync请求。 + + + TwoPhaseStateLock + 等待读取或更新预备事务的状态。 + + + TablespaceCreateLock + 等待创建或删除表空间。 + + + BtreeVacuumLock + 等待读取或更新B-树索引的清理相关信息。 + + + AddinShmemInitLock + 等待管理共享内存中的空间分配。 + + + AutovacuumLock + 自动清理工作进程或启动进程正在等待更新或读取自动清理工作进程的当前状态。 + + + AutovacuumScheduleLock + 等待确认已选定要清理的表仍然需要清理。 + + + SyncScanLock + 等待获取同步扫描所用的表扫描起始位置。 + + + RelationMappingLock + 等待更新用于存储系统目录到文件节点映射的关系映射文件。 + + + AsyncCtlLock + 等待读取或更新共享通知状态。 + + + AsyncQueueLock + 等待读取或更新通知消息。 + + + SerializableXactHashLock + 等待获取或存储可串行化事务的信息。 + + + SerializableFinishedListLock + 等待访问已完成的可串行化事务列表。 + + + SerializablePredicateLockListLock + 等待对可串行化事务所持有的锁列表执行操作。 + + + OldSerXidLock + 等待读取或记录相互冲突的可串行化事务。 + + + SyncRepLock + 等待读取或更新同步副本的信息。 + + + BackgroundWorkerLock + 等待读取或更新后台工作进程状态。 + + + DynamicSharedMemoryControlLock + 等待读取或更新动态共享内存状态。 + + + AutoFileLock + 等待更新postgresql.auto.conf文件。 + + + ReplicationSlotAllocationLock + 等待分配或释放复制槽。 + + + ReplicationSlotControlLock + 等待读取或更新复制槽状态。 + + + CommitTsControlLock + 等待读取或更新事务提交时间戳。 + + + CommitTsLock + 等待读取或更新最近设置的事务时间戳值。 + + + ReplicationOriginLock + 等待设置、删除或使用复制源。 + + + MultiXactTruncationLock + 等待读取或截断多事务信息。 + + + OldSnapshotTimeMapLock + 等待读取或更新旧的快照控制信息。 + + + WrapLimitsVacuumLock + 等待更新事务 ID 和多事务消耗量的限制。 + + + NotifyQueueTailLock + 等待更新通知消息存储的限制。 + + + LWLockTranche + clog + 等待 clog(事务状态)缓冲区上的 I/O。 + + + commit_timestamp + 等待提交时间戳缓冲区上的 I/O。 + + + subtrans + 等待子事务缓冲区上的 I/O。 + + + multixact_offset + 等待多事务偏移量缓冲区上的 I/O。 + + + multixact_member + 等待 multixact_member 缓冲区上的 I/O。 + + + async + 等待 async(通知)缓冲区上的 I/O。 + + + oldserxid + 等待 oldserxid 缓冲区上的 I/O。 + + + wal_insert + 等待将 WAL 插入内存缓冲区。 + + + buffer_content + 等待读取或写入内存中的数据页。 + + + buffer_io + 等待数据页上的 I/O。 + + + replication_origin + 等待读取或更新复制进度。 + + + replication_slot_io + 在复制槽上等待I/O。 + + + proc + 等待读取或更新快速路径锁信息。 + + + buffer_mapping + 等待将数据块与缓冲池中的缓冲区关联。 + + + lock_manager + 等待添加或检查后端的锁,或者等待加入或退出锁组(用于并行查询)。 + + + predicate_lock_manager + 等待添加或检查谓词锁信息。 + + + Lock + relation + 等待获取关系上的锁。 + + + extend + 等待扩展一个关系。 + + + frozenid + 等待更新 pg_database.datfrozenxidpg_database.datminmxid + + + page + 等待获取关系页上的锁。 + + + tuple + 等待获取元组上的锁。 + + + transactionid + 等待事务完成。 + + + virtualxid + 等待获取虚拟事务 ID 锁。 + + + speculative token + 等待获取推测插入锁。 + + + object + 等待获取非关系数据库对象上的锁。 + + + userlock + 等待获取用户锁。 + + + advisory + 等待获取用户咨询锁。 + + + BufferPin + BufferPin + 等待获取缓冲区上的钉住。 + + + +
+ + + 对于扩展注册的轻量级锁切片,名称由扩展指定,并显示为wait_event。用户可能只在某个后端中注册了该切片(通过在动态共享内存中分配),此时其他后端没有这项信息,因此在这种情况下显示extension + + + 以下示例展示如何查看等待事件: +SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event is NOT NULL; + pid | wait_event_type | wait_event +------+-----------------+--------------- + 2540 | Lock | relation + 6644 | LWLockNamed | ProcArrayLock +(2 rows) + + + + + <structname>pg_stat_replication</structname> 视图 + + + + + 类型 + 描述 + + + + + + pid + integer + + 一个 WAL 发送进程的进程 ID + + + + usesysid + oid + + 登录到这个 WAL 发送进程的用户的 OID + + + + usename + name + + 登录到这个 WAL 发送进程的用户的名称 + + + + application_name + text + + 连接到这个 WAL 发送进程的应用的名称 + + + + client_addr + inet + + 连接到这个 WAL 发送进程的客户端的 IP 地址。 + 如果这个字段为空值,它表示该客户端通过服务器机器上的一个Unix 套接字连接。 + + + + client_hostname + text + + 已连接的客户端的主机名,由client_addr的反向 DNS 查找报告。 + 这个字段将只对 IP 连接非空,并且只有 被启用时才会非空。 + + + + client_port + integer + + 客户端用来与这个 WAL 发送进程通讯的 TCP 端口号,如果使用 Unix 套接字则为-1 + + + + backend_start + timestamp with time zone + + 这个进程开始的时间,即客户端是何时连接到这个WAL 发送进程的。 + + + + backend_xmin + xid + + 由报告的该备库的xmin视界。 + + + + state + text + 当前 WAL 发送进程的状态 + + + sent_location + pg_lsn + + 在这个连接上发送的最后一个事务日志的位置 + + + + write_location + pg_lsn + + 该备库已写入磁盘的最后一个事务日志位置 + + + + flush_location + pg_lsn + + 该备库已刷入磁盘的最后一个事务日志位置 + + + + replay_location + pg_lsn + + 已在该备库数据库中重放的最后一个事务日志位置 + + + + sync_priority + integer + + 该备库被选为同步备库的优先级。 + + + + sync_state + text + 此备库的同步状态 + + + +
+ + + pg_stat_replication视图为每个 WAL 发送进程包含一行,显示有关复制到该发送进程所连接备库的统计信息。 + 这里只列出直接连接的备库;不包含下游备库的信息。 + + + + + <structname>pg_stat_wal_receiver</structname> 视图 + + + + + 类型 + 描述 + + + + + + pid + integer + + WAL 接收进程的进程 ID + + + + status + text + + WAL 接收进程的活动状态 + + + + receive_start_lsn + pg_lsn + + WAL 接收进程启动时使用的第一个事务日志位置 + + + + receive_start_tli + integer + + WAL 接收进程启动时使用的第一个时间线编号 + + + + received_lsn + pg_lsn + + 已经接收并刷入到磁盘的最后一个事务日志位置,该字段的初始值是启动 WAL 接收进程时使用的第一个日志位置 + + + + received_tli + integer + + 接收并刷入到磁盘的最后一个事务日志位置的时间线编号,该字段的初始值为启动 WAL 接收进程时使用的第一个日志位置的时间线编号 + + + + last_msg_send_time + timestamp with time zone + + 从源 WAL 发送进程收到的最后一条消息的发送时间 + + + + last_msg_receipt_time + timestamp with time zone + + 从源 WAL 发送进程收到的最后一条消息的接收时间 + + + + latest_end_lsn + pg_lsn + + 向源 WAL 发送进程报告的最后一个事务日志位置 + + + + latest_end_time + timestamp with time zone + + 向源 WAL 发送进程报告最后一个预写式日志位置的时间 + + + + slot_name + text + + 这个 WAL 接收进程使用的复制槽的名称 + + + + conninfo + text + + 这个 WAL 接收进程使用的连接字符串,对安全敏感的字段进行了模糊处理。 + + + + +
+ + pg_stat_wal_receiver视图只包含一行,显示 WAL 接收进程从其所连接服务器接收数据的统计信息。 + + + + <structname>pg_stat_ssl</structname> 视图 + + + + + 类型 + 描述 + + + + + + pid + integer + + 后端或 WAL 发送进程的进程 ID + + + + ssl + boolean + + 如果在此连接上使用SSL,则为真 + + + + version + text + + 使用SSL的版本,如果此连接上没有使用SSL则为空值 + + + + cipher + text + + 正在使用的SSL 密码套件的名称,如果此连接上没有使用SSL则为空值 + + + + bits + integer + + 使用的加密算法中的位数,如果此连接上没有使用SSL则为空值 + + + + compression + boolean + 使用 SSL 压缩时为真,否则为假;如果此连接未使用 SSL,则为空值 + + + clientdn + text + + 所用客户端证书中的区别名称(DN,Distinguished Name)字段,如果没有提供客户端证书或在此连接上没有使用SSL,则为空值。 + 如果DN字段长于NAMEDATALEN(标准构建中为64个字符),则该字段将被截断。 + + + + +
+ + + pg_stat_ssl视图将为每一个后端或者 WAL 发送进程包含一行,用来显示这个连接上的 SSL 使用情况。 + 可以把它与pg_stat_activity或者pg_stat_replication通过pid列连接来得到更多有关该连接的细节。 + + + + + <structname>pg_stat_archiver</structname> 视图 + + + + + + 类型 + 描述 + + + + + + archived_count + bigint + + 已成功归档的WAL文件数 + + + + last_archived_wal + text + + 最近成功归档的WAL文件的名称 + + + + last_archived_time + timestamp with time zone + + 最近成功归档操作的时间 + + + + failed_count + bigint + + 记录WAL文件归档失败次数 + + + + last_failed_wal + text + + 最近一次归档操作失败的WAL文件的名称 + + + + last_failed_time + timestamp with time zone + + 最近一次归档操作失败的时间 + + + + stats_reset + timestamp with time zone + 这些统计信息上次被重置的时间 + + + +
+ + + pg_stat_archiver视图总是有一行,其中包含关于集簇的归档进程的数据。 + + + + <structname>pg_stat_bgwriter</structname> 视图 + + + + + + 类型 + 描述 + + + + + + checkpoints_timed + bigint + 已执行的计划检查点数 + + + checkpoints_req + bigint + 已执行的请求检查点数 + + + checkpoint_write_time + double precision + 检查点处理中将文件写入磁盘部分所花费的总时间,单位为毫秒 + + + checkpoint_sync_time + double precision + 检查点处理中将文件同步到磁盘部分所花费的总时间,单位为毫秒 + + + buffers_checkpoint + bigint + 检查点期间写入的缓冲区数 + + + buffers_clean + bigint + + 后台写入器写入的缓冲区数量 + + + + maxwritten_clean + bigint + + 后台写入器因写入了过多缓冲区而停止清理扫描的次数 + + + + buffers_backend + bigint + 后端直接写入的缓冲区数 + + + buffers_backend_fsync + bigint + 后端必须自行执行fsync调用的次数(通常即使后端自行写入,也由后台写入器处理这些调用) + + + buffers_alloc + bigint + + 分配的缓冲区数量 + + + + stats_reset + timestamp with time zone + 这些统计信息上次被重置的时间 + + + +
+ + + pg_stat_bgwriter 视图始终只有一行,包含整个集簇的全局数据。 + + + + <structname>pg_stat_database</structname> 视图 + + + + + 类型 + 描述 + + + + + + + datid + oid + + 数据库的OID + + + + + datname + name + + 数据库的名称 + + + + + numbackends + integer + + 当前连接到此数据库的后端数。 + 这是该视图中唯一返回反映当前状态的值的列;所有其他列返回的都是自上次重置以来累积的值。 + + + + + xact_commit + bigint + + 此数据库中已提交的事务数 + + + + + xact_rollback + bigint + + 该数据库中已回滚的事务数 + + + + + blks_read + bigint + + 在该数据库中读取的磁盘块数 + + + + + blks_hit + bigint + + 在缓冲区缓存中发现磁盘块、因而无需读取的次数(这里只统计 PostgreSQL 缓冲区缓存中的命中,不包括操作系统文件系统缓存中的命中) + + + + + tup_returned + bigint + + 此数据库中的查询返回的行数 + + + + + tup_fetched + bigint + + 此数据库中的查询获取的行数 + + + + + tup_inserted + bigint + + 查询在该数据库中插入的行数 + + + + + tup_updated + bigint + + 这个数据库中查询更新的行数 + + + + + tup_deleted + bigint + + 这个数据库中被查询删除的行数 + + + + + conflicts + bigint + + 由于与此数据库中的恢复冲突而被取消的查询数。(冲突只会发生在备库上;请参阅。) + + + + + temp_files + bigint + + 这个数据库中查询创建的临时文件的数量。所有临时文件都将被计数,而不顾及临时文件为什么被创建(例如,排序或散列),也不考虑设置。 + + + + + temp_bytes + bigint + + 这个数据库中的查询写入临时文件的数据总量。所有临时文件都将被计数,而不考虑临时文件为什么被创建,也不考虑设置。 + + + + + deadlocks + bigint + + 在此数据库中检测到的死锁数 + + + + + blk_read_time + double precision + 此数据库的后端读取数据文件块所花费的时间,单位为毫秒 + + + + blk_write_time + double precision + 此数据库的后端写入数据文件块所花费的时间,单位为毫秒 + + + + stats_reset + timestamp with time zone + + 这些统计数据最后一次重置的时间 + + + + + +
+ + pg_stat_database视图为集簇中的每个数据库包含一行,显示整个数据库的统计信息。 + + + <structname>pg_stat_database_conflicts</structname> 视图 + + + + + 类型 + 描述 + + + + + + datid + oid + + 数据库的OID + + + + datname + name + + 数据库的名称 + + + + confl_tablespace + bigint + + 这个数据库中由于删除表空间而取消的查询的数量 + + + + confl_lock + bigint + + 此数据库中由于锁定超时而被取消的查询数 + + + + confl_snapshot + bigint + + 此数据库中由于旧快照而取消的查询数 + + + + confl_bufferpin + bigint + + 此数据库中由于缓冲区被钉住而被取消的查询数 + + + + confl_deadlock + bigint + + 此数据库中由于死锁而被取消的查询数 + + + + +
+ + pg_stat_database_conflicts视图为每个数据库包含一行,显示备库上因与恢复冲突而取消查询的数据库范围统计信息。由于主库上不会发生这些冲突,该视图只会在备库上包含信息。 + + + <structname>pg_stat_all_tables</structname> 视图 + + + + + 类型 + 描述 + + + + + + relid + oid + + 表的OID + + + + schemaname + name + + 该表所在的模式的名称 + + + + relname + name + + 这个表的名称 + + + + seq_scan + bigint + + 在此表上启动的顺序扫描数 + + + + seq_tup_read + bigint + + 顺序扫描获取的存活行数 + + + + idx_scan + bigint + + 对这个表发起的索引扫描数 + + + + idx_tup_fetch + bigint + + 索引扫描获取的存活行数 + + + + n_tup_ins + bigint + + 插入的行数 + + + + n_tup_upd + bigint + 更新的行数(包括 HOT 更新的行) + + + n_tup_del + bigint + + 删除的行数 + + + + n_tup_hot_upd + bigint + + HOT 更新的行数(即不需要单独更新索引) + + + + n_live_tup + bigint + + 活的行的估计数量 + + + + n_dead_tup + bigint + + 死行的估计数量 + + + + n_mod_since_analyze + bigint + + 自上次分析此表以来修改的行的估计数量 + + + + last_vacuum + timestamp with time zone + + 最后一次手动清理这个表的时间(不包括VACUUM FULL) + + + + last_autovacuum + timestamp with time zone + + 这个表最后一次被自动清理守护进程清理的时间 + + + + last_analyze + timestamp with time zone + + 上一次手动分析这个表的时间 + + + + last_autoanalyze + timestamp with time zone + + 自动清理守护进程最后一次分析这个表的时间 + + + + vacuum_count + bigint + + 这个表被手动清理的次数(VACUUM FULL不计数) + + + + autovacuum_count + bigint + + 这个表被自动清理守护进程清理的次数 + + + + analyze_count + bigint + + 手动分析这个表的次数 + + + + autoanalyze_count + bigint + + 这个表被自动清理守护进程分析的次数 + + + + +
+ + + pg_stat_all_tables视图将为当前数据库中的每一个表(包括 TOAST 表)包含一行,该行显示与对该表的访问相关的统计信息。 + pg_stat_user_tablespg_stat_sys_tables视图包含相同的信息,但是被过滤得分别只显示用户和系统表。 + + + + <structname>pg_stat_all_indexes</structname> 视图 + + + + + 类型 + 描述 + + + + + + relid + oid + + 对于此索引的表的OID + + + + indexrelid + oid + + 这个索引的OID + + + + schemaname + name + + 这个索引所在的模式名称 + + + + relname + name + + 这个索引的表的名称 + + + + indexrelname + name + + 这个索引的名称 + + + + idx_scan + bigint + + 在这个索引上开启的索引扫描的数量 + + + + idx_tup_read + bigint + + 扫描此索引返回的索引项数 + + + + idx_tup_fetch + bigint + + 使用此索引进行简单索引扫描获取的存活表行数 + + + + +
+ + + pg_stat_all_indexes视图将为当前数据库中的每个索引包含一行,该行显示关于对该索引访问的统计信息。pg_stat_user_indexespg_stat_sys_indexes视图包含相同的信息,但是被过滤得只分别显示用户和系统索引。 + + + + 索引可以被简单索引扫描、位图索引扫描以及优化器使用。在一次位图扫描中,多个索引的输出可以被通过 AND 或 OR 规则组合,因此当使用一次位图扫描时难以将取得的个体堆行与特定的索引关联起来。因此,一次位图扫描会增加它使用的索引的pg_stat_all_indexes.idx_tup_read计数,并且为该表增加pg_stat_all_tables.idx_tup_fetch计数,但是它不影响pg_stat_all_indexes.idx_tup_fetch。如果所提供的常量值不在优化器统计信息记录的范围之内,优化器也会访问索引来检查,因为优化器统计信息可能已经过时。 + + + + + + 即使不用位图扫描,idx_tup_readidx_tup_fetch计数也可能不同,因为idx_tup_read统计从该索引取得的索引项而idx_tup_fetch统计从表取得的存活行。如果使用该索引取得了任何死亡行或还未提交的行,或者如果通过一次仅索引扫描的方式避免了任何堆获取,后者将较小。 + + + + + <structname>pg_statio_all_tables</structname> 视图 + + + + + 类型 + 描述 + + + + + + relid + oid + + 表的OID + + + + schemaname + name + + 该表所在的模式的名称 + + + + relname + name + + 这个表的名称 + + + + heap_blks_read + bigint + + 从该表中读取的磁盘块的数量 + + + + heap_blks_hit + bigint + + 该表中的缓冲区命中数 + + + + idx_blks_read + bigint + + 从这个表上所有索引读取的磁盘块数 + + + + idx_blks_hit + bigint + + 这个表上所有索引中的缓冲区命中数 + + + + toast_blks_read + bigint + + 从这个表的TOAST表中读取的磁盘块的数量(如果有的话) + + + + toast_blks_hit + bigint + + 这个表的TOAST表中的缓冲区命中数(如果有的话) + + + + tidx_blks_read + bigint + + 从这个表的TOAST表索引中读取的磁盘块的数量(如果有的话) + + + + tidx_blks_hit + bigint + + 这个表的TOAST表索引中的缓冲区命中数(如果有的话) + + + + +
+ + + pg_statio_all_tables视图将为当前数据库中的每个表(包括 TOAST 表)包含一行,该行显示指定表上有关 I/O 的统计信息。pg_statio_user_tablespg_statio_sys_tables视图包含相同的信息,但是被过滤得分别只显示用户表和系统表。 + + + + <structname>pg_statio_all_indexes</structname> 视图 + + + + + 类型 + 描述 + + + + + + relid + oid + + 对于此索引的表的OID + + + + indexrelid + oid + + 这个索引的OID + + + + schemaname + name + + 这个索引所在的模式名称 + + + + relname + name + + 这个索引的表的名称 + + + + indexrelname + name + + 这个索引的名称 + + + + idx_blks_read + bigint + + 从此索引中读取的磁盘块的数量 + + + + idx_blks_hit + bigint + + 此索引中的缓冲区命中数 + + + + +
+ + + pg_statio_all_indexes视图将为当前数据库中的每个索引包含一行,该行显示指定索引上有关 I/O 的统计信息。 + pg_statio_user_indexespg_statio_sys_indexes视图包含相同的信息,但是被过滤得分别只显示用户索引和系统索引。 + + + + <structname>pg_statio_all_sequences</structname> 视图 + + + + + 类型 + 描述 + + + + + + relid + oid + + 序列的OID + + + + schemaname + name + + 此序列所在的模式的名称 + + + + relname + name + + 此序列的名称 + + + + blks_read + bigint + + 从这个序列中读取的磁盘块的数量 + + + + blks_hit + bigint + + 在此序列中的缓冲区命中数 + + + + +
+ + + pg_statio_all_sequences视图将为当前数据库中的每个序列包含一行,该行显示在指定序列上有关 I/O 的统计信息。 + + + + <structname>pg_stat_user_functions</structname> 视图 + + + + + 类型 + 描述 + + + + + + funcid + oid + + 函数的OID + + + + schemaname + name + + 这个函数所在的模式的名称 + + + + funcname + name + + 这个函数的名称 + + + + calls + bigint + + 这个函数已经被调用的次数 + + + + total_time + double precision + + 在这个函数以及它所调用的其他函数中花费的总时间,以毫秒计 + + + + self_time + double precision + + 在这个函数本身花费的总时间,不包括被它调用的其他函数,以毫秒计 + + + + +
+ + + pg_stat_user_functions视图将为每一个被追踪的函数包含一行,该行显示有关该函数执行的统计信息。 + 参数控制到底哪些函数被跟踪。 + + +
+ + + 统计函数 + + + 其他查看统计信息的方法是直接使用查询,这些查询使用上述标准视图用到的底层统计信息访问函数。 + 如要了解如函数名等细节,可参考标准视图的定义(例如,在psql中你可以发出\d+ pg_stat_activity)。 + 针对每一个数据库统计信息的访问函数把一个数据库 OID 作为参数来标识要报告哪个数据库。而针对每个表和每个索引的函数要求表或索引 OID。 + 针对每个函数统计信息的函数用一个函数 OID。注意只有在当前数据库中的表、索引和函数才能被这些函数看到。 + + + + 与统计收集器相关的其他功能在中列出。 + + + + 附加统计函数 + + + + + 函数 + 返回类型 + 描述 + + + + + + + + pg_backend_pid() + integer + 处理当前会话的服务器进程的进程 ID + + + + pg_stat_get_activity(integer)pg_stat_get_activity + setof record + 返回一条记录,包含指定 PID 的后端的信息;如果指定NULL,则为系统中的每个活动后端返回一条记录。返回字段是pg_stat_activity视图字段的子集。 + + + + pg_stat_get_snapshot_timestamp()pg_stat_get_snapshot_timestamp + timestamp with time zone + + 返回当前统计快照的时间戳。 + + + + + pg_stat_clear_snapshot()pg_stat_clear_snapshot + void + 丢弃当前统计信息快照 + + + + pg_stat_reset()pg_stat_reset + void + 将当前数据库的所有统计计数器重置为零(默认需要超级用户权限,但可以向其他用户授予此函数的 EXECUTE 权限。) + + + + pg_stat_reset_shared(text)pg_stat_reset_shared + void + 根据参数将某些集簇范围的统计计数器重置为零(默认需要超级用户权限,但可以向其他用户授予此函数的 EXECUTE 权限)。调用pg_stat_reset_shared('bgwriter')会将pg_stat_bgwriter视图中显示的所有计数器清零。调用pg_stat_reset_shared('archiver')会将pg_stat_archiver视图中显示的所有计数器清零。 + + + + pg_stat_reset_single_table_counters(oid)pg_stat_reset_single_table_counters + void + 将当前数据库中单个表或索引的统计信息重置为零(默认需要超级用户权限,但可以向其他用户授予此函数的 EXECUTE 权限) + + + + pg_stat_reset_single_function_counters(oid)pg_stat_reset_single_function_counters + void + 将当前数据库中单个函数的统计信息重置为零(默认需要超级用户权限,但可以向其他用户授予此函数的 EXECUTE 权限) + + + +
+ + + + pg_stat_get_activitypg_stat_activity视图的底层函数, + 它返回一个行集合,其中包含有关每个后端进程所有可用的信息。有时只获得该信息的一个子集可能会更方便。 + 在那些情况中,可以使用一组更老的针对每个后端的统计访问函数,这些显示在中。 + 这些访问函数使用一个后端 ID 号,范围从 1 到当前活动后端数目。 + 函数pg_stat_get_backend_idset提供了一种方便的方法为每个活动后端产生一行来调用这些函数。 + 例如,要显示PID以及所有后端当前的查询: + + +SELECT pg_stat_get_backend_pid(s.backendid) AS pid, + pg_stat_get_backend_activity(s.backendid) AS query + FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s; + + + + + 按后端统计函数 + + + + + 函数 + 返回类型 + 描述 + + + + + + + pg_stat_get_backend_idset() + setof integer + 当前活动后端的 ID 编号集合(从 1 到活动后端数) + + + + pg_stat_get_backend_activity(integer) + text + 此后端最近查询的文本 + + + + pg_stat_get_backend_activity_start(integer) + timestamp with time zone + 最近查询的开始时间 + + + + pg_stat_get_backend_client_addr(integer) + inet + 连接到此后端的客户端的 IP 地址 + + + + pg_stat_get_backend_client_port(integer) + integer + 客户端用于通信的 TCP 端口号 + + + + pg_stat_get_backend_dbid(integer) + oid + + 这个后端连接到的数据库的OID + + + + + pg_stat_get_backend_pid(integer) + integer + + 这个后端的进程 ID + + + + + pg_stat_get_backend_start(integer) + timestamp with time zone + 此进程的启动时间 + + + + pg_stat_get_backend_userid(integer) + oid + + 登录到这个后端的用户的 OID + + + + + pg_stat_get_backend_wait_event_type(integer) + text + 如果后端当前正在等待,则为等待事件类型名称,否则为 NULL。详见 + + + + pg_stat_get_backend_wait_event(integer) + text + 如果后端当前正在等待,则为等待事件名称,否则为 NULL。详见 + + + + pg_stat_get_backend_xact_start(integer) + timestamp with time zone + 当前事务的开始时间 + + + + +
+ +
+
+ + + + 查看锁 + + + + 监控 + + + + 监控数据库活动的另外一个有用的工具是pg_locks系统表。这样就允许数据库管理员查看锁管理器中当前存在的锁的信息。例如,这个功能可以被用于: + + + + + 查看当前存在的所有锁、在一个特定数据库中的关系上所有的锁、在一个特定关系上所有的锁,或者由一个特定PostgreSQL会话持有的所有的锁。 + + + + + + 判断当前数据库中带有最多未授予锁的关系(它可能是数据库客户端之间的竞争来源)。 + + + + + + 判断锁竞争给数据库总体性能带来的影响,以及锁竞争随着整个数据库流量的变化范围。 + + + + + pg_locks视图的细节在中。更多有关PostgreSQL的锁和管理并发性的信息,请参考。 + + + + + 进度报告 + + PostgreSQL能够在某些命令执行期间报告其进度。目前,支持进度报告的命令只有VACUUM。未来可能会扩展此功能。 + + + VACUUM 进度报告 + + 只要VACUUM正在运行,pg_stat_progress_vacuum视图就会为每个当前正在清理的后端(包括自动清理工作进程)包含一行。下表描述了将报告的信息,并说明如何解释这些信息。目前不支持报告VACUUM FULL的进度,运行VACUUM FULL的后端不会列在此视图中。 + + + <structname>pg_stat_progress_vacuum</structname> 视图 + + + + + 类型 + 描述 + + + + + + pid + integer + + 后端的进程ID。 + + + + datid + oid + + 后端连接到的数据库的OID。 + + + + datname + name + + 后端连接到的数据库的名称。 + + + + relid + oid + + 正在清理的表的OID。 + + + + phase + text + + 清理的当前处理阶段。参见 。 + + + + heap_blks_total + bigint + + 该表中堆块的总数。这个数字以扫描开始时的数量为准,之后增加的块将不会(并且不需要)被这个VACUUM访问。 + + + + heap_blks_scanned + bigint + + 被扫描的堆块数量。由于可见性映射被用来优化扫描,一些块将被跳过而不做检查, + 被跳过的块会被包括在这个总数中,因此当清理完成时这个数字最终将会等于heap_blks_total。 + 仅当处于scanning heap阶段时这个计数器才会前进。 + + + + heap_blks_vacuumed + bigint + + 被清理的堆块数量。除非表没有索引,这个计数器仅在处于vacuuming heap阶段时才会前进。 + 不包含死亡元组的块会被跳过,因此这个计数器可能有时会向前跳跃一个比较大的增量。 + + + + index_vacuum_count + bigint + + 已完成的索引清理周期数。 + + + + max_dead_tuples + bigint + + 在需要执行索引清理周期之前可存储的死亡元组数量,取决于。 + + + + num_dead_tuples + bigint + + 自上一个索引清理周期以来收集到的死亡元组数量。 + + + + +
+ + + VACUUM 阶段 + + + + + 阶段 + 描述 + + + + + + initializing + + VACUUM正在准备开始扫描堆。这个阶段应该很简短。 + + + + scanning heap + + VACUUM正在扫描堆。如果需要,它将会对每个页面进行剪枝以及碎片整理,并且可能会执行冻结动作。heap_blks_scanned列可以用来监控扫描的进度。 + + + + vacuuming indexes + VACUUM当前正在清理索引。如果表有索引,每次清理都会在堆扫描完成后至少执行一次此阶段。如果(或者,对于自动清理,已设置的)不足以存储找到的死亡元组数量,则每次清理可能多次执行此阶段。 + + + vacuuming heap + + VACUUM当前正在清理堆。清理堆与扫描堆不是同一个概念,清理堆发生在每次索引清理之后。如果heap_blks_scanned小于heap_blks_total,系统将在这个阶段完成之后回去扫描堆;否则,系统将在这个阶段完成后开始索引收尾清理。 + + + + cleaning up indexes + + VACUUM当前正在进行索引收尾清理。这个阶段发生在堆被完全扫描并且对堆和索引的所有清理都已经完成以后。 + + + + truncating heap + + VACUUM正在截断堆,以便把关系尾部的空页面返还给操作系统。这个阶段发生在索引收尾清理完成之后。 + + + + performing final cleanup + + VACUUM正在执行最终清理。在此阶段,VACUUM将清理空闲空间映射, + 更新pg_class中的统计信息,并向统计收集器报告统计信息。当此阶段完成时, + VACUUM将结束。 + + + + +
+ +
+
+ + + 动态追踪 + + + DTrace + + + + PostgreSQL提供了功能来支持数据库服务器的动态追踪。这样就允许在代码中的特 定点上调用外部工具来追踪执行过程。 + + + + 一些探针或追踪点已经被插入在源代码中。这些探针的目的是被数据库开发者和管理员使用。默认情况下,探针不被编译到PostgreSQL中;用户需要显式地告诉配置脚本使得探针可用。 + + + + 目前,在写本文当时DTrace已被支持,它在 Solaris、OS X、FreeBSD、NetBSD 和 Oracle Linux 上可用。 + Linux 的SystemTap项目提供了一种可用的 DTrace 等价物。支持其他动态追踪工具在理论上可以通过改变src/include/utils/probes.h中的宏定义实现。 + + + + 为动态追踪编译 + + + 默认情况下,探针是不可用的,因此你将需要显式地告诉配置脚本让探针在PostgreSQL中可用。要包括 DTrace 支持,在配置时指定。更多信息请见。 + + + + + 内置探针 + + + 如所示,源代码中提供了一些标准探针。显示了在探针中使用的类型。当然,可以增加更多探针来增强PostgreSQL的可观测性。 + + + + 内置 DTrace 探针 + + + + + 名称 + 参数 + 描述 + + + + + + + transaction-start + (LocalTransactionId) + 在一个新事务开始时触发的探针。arg0 是事务 ID。 + + + transaction-commit + (LocalTransactionId) + 在一个事务成功完成时触发的探针。arg0 是事务 ID。 + + + transaction-abort + (LocalTransactionId) + 当一个事务失败结束时触发的探针。arg0 是事务 ID。 + + + query-start + (const char *) + 当一个查询的处理开始时触发的探针。arg0 是查询字符串。 + + + query-done + (const char *) + 当一个查询的处理完成时触发的探针。arg0 是查询字符串。 + + + query-parse-start + (const char *) + 当一个查询的解析开始时触发的探针。arg0 是查询字符串。 + + + query-parse-done + (const char *) + 当一个查询的解析完成时触发的探针。arg0 是查询字符串。 + + + query-rewrite-start + (const char *) + 当一个查询的重写开始时触发的探针。arg0 是查询字符串。 + + + query-rewrite-done + (const char *) + 当一个查询的重写完成时触发的探针。arg0 是查询字符串。 + + + query-plan-start + () + 当一个查询的规划开始时触发的探针。 + + + query-plan-done + () + 当一个查询的规划完成时触发的探针。 + + + query-execute-start + () + 当一个查询的执行开始时触发的探针。 + + + query-execute-done + () + 当一个查询的执行完成时触发的探针。 + + + statement-status + (const char *) + 任何时候当服务器进程更新它的pg_stat_activity.status时触发的探针。arg0 是新的状态字符串。 + + + checkpoint-start + (int) + 当一个检查点开始时触发的探针。arg0 传递位标志来区分不同的检查点类型,例如关闭(shutdown)、立即(immediate)或强制(force)。 + + + checkpoint-done + (int, int, int, int, int) + 当一个检查点完成时触发的探针(下面列出的探针会在检查点处理过程中依次触发)。arg0 是已写入的缓冲区数量。arg1 是缓冲区的总数。arg2、arg3 和 arg4 分别包含了增加、删除和循环回收的 WAL 文件的数量。 + + + clog-checkpoint-start + (bool) + 当一个检查点的 CLOG 部分开始时触发的探针。arg0 为真表示正常检查点,为假表示关闭检查点。 + + + clog-checkpoint-done + (bool) + 当一个检查点的 CLOG 部分完成时触发的探针。arg0 的含义与clog-checkpoint-start中相同。 + + + subtrans-checkpoint-start + (bool) + 当一个检查点的 SUBTRANS 部分开始时触发的探针。arg0 为真表示正常检查点,为假表示关闭检查点。 + + + subtrans-checkpoint-done + (bool) + 当一个检查点的 SUBTRANS 部分完成时触发的探针。arg0 的含义与subtrans-checkpoint-start中相同。 + + + multixact-checkpoint-start + (bool) + 当一个检查点的 MultiXact 部分开始时触发的探针。arg0 为真表示正常检查点,为假表示关闭检查点。 + + + multixact-checkpoint-done + (bool) + 当一个检查点的 MultiXact 部分完成时触发的探针。arg0 的含义与multixact-checkpoint-start中相同。 + + + buffer-checkpoint-start + (int) + 当一个检查点的写缓冲区部分开始时触发的探针。arg0 传递位标志来区分不同的检查点类型,例如关闭(shutdown)、立即(immediate)或强制(force)。 + + + buffer-sync-start + (int, int) + 当我们在检查点期间开始写脏缓冲区时(在标识哪些缓冲区必须被写之后)触发的探针。arg0 是缓冲区总数,arg1 是当前为脏并且需要被写的缓冲区数量。 + + + buffer-sync-written + (int) + 在检查点期间当每个缓冲区被写完之后触发的探针。arg0 是缓冲区的 ID。 + + + buffer-sync-done + (int, int, int) + 当所有脏缓冲区被写之后触发的探针。arg0 是缓冲区总数。arg1 是检查点进程实际写的缓冲区数量。arg2 是期望写的数目(buffer-sync-start的 arg1);arg1 和 arg2 的任何的不同反映在该检查点期间有其他进程刷写了缓冲区。 + + + buffer-checkpoint-sync-start + () + 在脏缓冲区被写入到内核之后并且在开始发出 fsync 请求之前触发的探针。 + + + buffer-checkpoint-done + () + 当同步缓冲区到磁盘完成时触发的探针。 + + + twophase-checkpoint-start + () + 当一个检查点的两阶段部分开始时触发的探针。 + + + twophase-checkpoint-done + () + 当一个检查点的两阶段部分完成时触发的探针。 + + + buffer-read-start + (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool) + 当一次缓冲区读开始时触发的探针。arg0 和 arg1 包含该页的分支号和块号(如果这是一次关系扩展请求,arg1 为 -1)。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是 InvalidBackendId(-1)。arg6 为真表示一次关系扩展请求,为假表示正常读。 + + + buffer-read-done + (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool, bool) + 当一次缓冲区读完成时触发的探针。arg0 和 arg1 包含该页的分支号和块号(如果这是一次关系扩展请求,arg1 现在包含新增加块的块号)。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是 InvalidBackendId(-1)。arg6 为真表示一次关系扩展请求,为假表示正常读。arg7 为真表示在池中找到该缓冲区,为假表示没有找到。 + + + buffer-flush-start + (ForkNumber, BlockNumber, Oid, Oid, Oid) + 在发出对一个共享缓冲区的任意写请求之前触发的探针。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。 + + + buffer-flush-done + (ForkNumber, BlockNumber, Oid, Oid, Oid) + 当一个写请求完成时触发的探针(注意这只反映传递数据给内核的时间,它通常并没有实际地被写入到磁盘)。参数和buffer-flush-start的相同。 + + + buffer-write-dirty-start + (ForkNumber, BlockNumber, Oid, Oid, Oid) + 当一个服务器进程开始写一个脏缓冲区时触发的探针(如果这经常发生,表示太小,或需要调整后台写入器的控制参数)。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。 + + + buffer-write-dirty-done + (ForkNumber, BlockNumber, Oid, Oid, Oid) + 当一次脏缓冲区写完成时触发的探针。参数与buffer-write-dirty-start相同。 + + + wal-buffer-write-dirty-start + () + 当一个服务器进程因为没有可用 WAL 缓冲区空间开始写一个脏 WAL 缓冲区时触发的探针(如果这经常发生,表示太小)。 + + + wal-buffer-write-dirty-done + () + 当一次脏 WAL 缓冲区写入完成时触发的探针。 + + + xlog-insert + (unsigned char, unsigned char) + 当一个 WAL 记录被插入时触发的探针。arg0 是该记录的资源管理器(rmid)。arg1 包含 info 标志。 + + + xlog-switch + () + 当请求一次 WAL 段切换时触发的探针。 + + + smgr-md-read-start + (ForkNumber, BlockNumber, Oid, Oid, Oid, int) + 当开始从一个关系读取一块时触发的探针。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。 + + + smgr-md-read-done + (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) + 当一次块读取完成时触发的探针。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。arg6 是实际读取的字节数,而 arg7 是请求读取的字节数(如果两者不同就意味着麻烦)。 + + + smgr-md-write-start + (ForkNumber, BlockNumber, Oid, Oid, Oid, int) + 当开始向一个关系中写入一个块时触发的探针。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。 + + + smgr-md-write-done + (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) + 当一个块写操作完成时触发的探针。arg0 和 arg1 包含该页的分支号和块号。arg2、arg3和arg4 包含表空间、数据库和关系 OID来标识该关系。对于一个本地缓冲区,arg5 是创建临时关系的后端 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。arg6 是实际写的字节数,而 arg7 是要求写的字节数(如果这两者不同,则意味着麻烦)。 + + + sort-start + (int, bool, int, int, bool) + 当一次排序操作开始时触发的探针。arg0 指示是堆排序、索引排序或 datum 排序。arg1 为真表示强制唯一值。arg2 是键列数。arg3 是允许使用的工作内存大小(以千字节计)。如果要求随机访问排序结果,则 arg4 为真。 + + + sort-done + (bool, long) + 当一次排序完成时触发的探针。arg0 为真表示外部排序,为假表示内排序。arg1 是用于一次外部排序的磁盘块的数目,或用于一次内排序的以千字节计的内存。 + + + lwlock-acquire + (char *, int, LWLockMode) + 当成功获得一个 LWLock 时触发的探针。 + arg0 是该 LWLock 所在的切片(Tranche)。 + arg1 是该 LWLock 在其切片内的偏移量。 + arg2 是所请求的锁模式,是排他或共享。 + + + lwlock-release + (char *, int) + 当一个 LWLock 被释放时(但是注意还没有唤醒任何一个被释放的等待者)触发的探针。 + arg0 是该 LWLock 所在的切片(Tranche)。 + + + lwlock-wait-start + (char *, int, LWLockMode) + 当一个 LWLock不是当即可用并且一个服务器进程因此开始等待该锁变为可用时触发的探针。 + arg0 是该 LWLock 所在的切片(Tranche)。 + arg1 是该 LWLock 在其切片内的偏移量。 + arg2 是所请求的锁模式,是排他或共享。 + + + lwlock-wait-done + (char *, int, LWLockMode) + 当一个进程从对一个 LWLock 的等待中被释放时(它实际还没有得到该锁)时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 + arg1 是该 LWLock 在其切片内的偏移量。 + arg2 是所请求的锁模式,是排他或共享。 + + + lwlock-condacquire + (char *, int, LWLockMode) + 当调用者指定无需等待而成功获得一个 LWLock 时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 + arg1 是该 LWLock 在其切片内的偏移量。 + arg2 是所请求的锁模式,是排他或共享。 + + + lwlock-condacquire-fail + (char *, int, LWLockMode) + 当调用者指定无需等待而没有成功获得一个 LWLock 时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 + arg1 是该 LWLock 在其切片内的偏移量。 + arg2 是所请求的锁模式,是排他或共享。 + + + lock-wait-start + (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) + 当一个重量级锁(lmgr锁)的请求由于锁不可用开始等待时触发的探针。arg0 到 arg3 是标识被锁定对象的标签字段。arg4 指示被锁对象的类型。arg5 表示被请求的锁类型。 + + + lock-wait-done + (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) + 当一个重量级锁(lmgr 锁)的请求结束等待时(即已经得到锁)触发的探针。参数与lock-wait-start一样。 + + + deadlock-found + () + 当死锁检测器发现死锁时触发的探针。 + + + + +
+ + + 探针参数中使用的已定义类型 + + + + + 类型 + 定义 + + + + + + + LocalTransactionId + unsigned int + + + LWLockMode + int + + + LOCKMODE + int + + + BlockNumber + unsigned int + + + Oid + unsigned int + + + ForkNumber + int + + + bool + char + + + + +
+ + +
+ + + + 使用探针 + + + 下面的示例展示了一个分析系统中事务计数的 DTrace 脚本,可以用来代替一次性能测试之前和之后的pg_stat_database快照: + +#!/usr/sbin/dtrace -qs + +postgresql$1:::transaction-start +{ + @start["Start"] = count(); + self->ts = timestamp; +} + +postgresql$1:::transaction-abort +{ + @abort["Abort"] = count(); +} + +postgresql$1:::transaction-commit +/self->ts/ +{ + @commit["Commit"] = count(); + @time["Total time (ns)"] = sum(timestamp - self->ts); + self->ts=0; +} + + 当被执行时,该示例 D 脚本给出这样的输出: + +# ./txn_count.d `pgrep -n postgres` or ./txn_count.d <PID> +^C + +Start 71 +Commit 70 +Total time (ns) 2312105013 + + + + + + + SystemTap 的追踪脚本记法与 DTrace 不同,但底层探针是兼容的。需要注意的一点是,截至本文编写时,SystemTap 脚本必须使用双下划线来代替连字符引用探针名。预计未来的 SystemTap 版本会修复这一点。 + + + + + 你应该记住,DTrace 脚本需要细心地编写和调试,否则被收集的追踪信息可能会毫无意义。在大多数发现问题的情况下,出错的是插桩,而不是底层系统。当讨论使用动态追踪发现的信息时,一定要附上使用的脚本以便其也被检查和讨论。 + + + + + + 定义新探针 + + + 开发者可以在代码中任意位置定义新的探针,当然这要重新编译之后才能生效。下面是插入新探针的步骤: + + + + + + + 决定探针名称以及要通过探针提供的数据 + + + + + + + 把该探针定义加入到src/backend/utils/probes.d + + + + + + + 如果pg_trace.h尚未被包含该探针点的模块引用,则将它包含进来,并且在源代码中期望的位置插入TRACE_POSTGRESQL探针宏 + + + + + + + 重新编译并验证新探针是可用的 + + + + + + + 示例: + + + 这里是一个如何增加一个探针来用事务 ID 追踪所有新事务的示例。 + + + + + + + + 决定探针将被命名为transaction-start并且需要一个LocalTransactionId类型的参数 + + + + + + + 将该探针定义加入到src/backend/utils/probes.d: + +probe transaction__start(LocalTransactionId); + + 注意探针名字中双下划线的使用。在一个使用探针的 DTrace 脚本中,双下划线需要被替换为一个连字符,因此,文档中应向用户说明的名称是transaction-start。 + + + + + + + 在编译时,transaction__start被转换成一个名为TRACE_POSTGRESQL_TRANSACTION_START的宏(注意这里是单下划线),可以通过包含头文件pg_trace.h获得。将宏调用加入到源代码中的合适位置。在这种情况下,看起来类似: + + +TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId); + + + + + + + + 在重新编译和运行新的二进制文件之后,通过运行下面的 DTrace 命令来检查新增的探针是否可用。你应该看到类似下面的输出: + +# dtrace -ln transaction-start + ID PROVIDER MODULE FUNCTION NAME +18705 postgresql49878 postgres StartTransactionCommand transaction-start +18755 postgresql49877 postgres StartTransactionCommand transaction-start +18805 postgresql49876 postgres StartTransactionCommand transaction-start +18855 postgresql49875 postgres StartTransactionCommand transaction-start +18986 postgresql49873 postgres StartTransactionCommand transaction-start + + + + + + + 向C代码中添加追踪宏时,有一些事情需要注意: + + + + + 需要小心的是,为探针参数指定的数据类型要匹配宏中使用的变量的数据类型,否则会发生编译错误。 + + + + + + 在大多数平台上,如果用编译了PostgreSQL,无论何时当控制经过一个追踪宏时,都会对该宏的参数求值,即使没有进行追踪也会这样做。如果只是报告少数局部变量的值,通常无需担心这一点。但是要注意不要将开销大的函数调用放入参数中。如果你需要这样做,考虑通过检查追踪是否真的被启用来保护该宏: + + +if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) + TRACE_POSTGRESQL_TRANSACTION_START(some_function(...)); + + + 每个追踪宏有一个对应的ENABLED宏。 + + + + + + + + +
+ +
diff --git a/zh/9.6/mvcc.sgml b/zh/9.6/mvcc.sgml new file mode 100644 index 00000000..66a717e0 --- /dev/null +++ b/zh/9.6/mvcc.sgml @@ -0,0 +1,1247 @@ + + + + 并发控制 + + + 并发 + + + + 本章描述 PostgreSQL 数据库系统在两个或更多会话试图同时访问同一数据时的行为。在这种情况下,目标是在保持严格数据完整性的同时,为所有会话提供高效访问。每一个数据库应用开发者都应该熟悉本章涉及的主题。 + + + + 介绍 + + + 多版本并发控制 + + + + MVCC + + + + 可串行化快照隔离 + + + + SSI + + + + PostgreSQL 为开发者提供了一组丰富的工具来管理对数据的并发访问。在内部,数据一致性是通过使用多版本模型(多版本并发控制,MVCC)来维护的。这意味着,每条 SQL 语句看到的都是某个较早时刻的数据快照(即一个数据库版本),而不受底层数据当前状态的影响。这样可以防止语句看到由并发事务在同一数据行上执行更新所产生的不一致数据,从而为每个数据库会话提供事务隔离MVCC 摒弃了传统数据库系统的加锁方法,尽量减少锁争用,以便在多用户环境中获得合理的性能。 + + + + 使用 MVCC 并发控制模型而不是加锁的主要优点在于,在 + MVCC 中,为查询(读取)数据而获得的锁不会与为写入数据而获得的锁冲突,因此读取不会阻塞写入,写入也不会阻塞读取。 + PostgreSQL 甚至在通过创新性的可串行化快照隔离 + (SSI)级别提供最严格的事务隔离时,也保持了这一保证。 + + + + PostgreSQL 也提供表级和行级锁设施,供那些通常不需要完整事务隔离、并且倾向于显式管理特定冲突点的应用使用。不过,正确使用 MVCC 通常会比加锁提供更好的性能。此外,由应用定义的咨询锁还提供了一种获取不依附于单个事务的锁的机制。 + + + + + 事务隔离 + + + 事务隔离 + + + + SQL 标准定义了四个事务隔离级别。最严格的是可串行化, + 标准将它定义为:一组可串行化事务的任意并发执行,都保证与按照某种顺序一次只运行一个事务的效果相同。 + 其他三个级别则按现象定义,这些现象源自并发事务之间的相互作用,而在各自级别上这些现象不得发生。 + 标准指出,由于可串行化的定义,这些现象在该级别下都不可能出现。 + (这并不奇怪,如果事务的效果必须与一次只运行一个事务保持一致,又怎么可能看到由相互作用引起的任何现象呢?) + + + + 在各个级别上被禁止的现象有: + + + + + 脏读 + 脏读 + + + + 一个事务读取了由另一个并发未提交事务写入的数据。 + + + + + + + 不可重复读 + 不可重复读 + + + + 一个事务重新读取它之前读过的数据,发现这些数据已经被另一个事务修改(且该事务已在初次读取之后提交)。 + + + + + + + 幻读 + 幻读 + + + + 一个事务重新执行一个返回满足某个搜索条件的一组行的查询,却发现由于另一个最近提交的事务,满足该条件的行集已经发生了变化。 + + + + + + + 串行化异常 + 串行化异常 + + + + 一组事务成功提交后的结果,与将这些事务逐个按某种顺序执行的任何可能结果都不一致。 + + + + + + + + + 事务隔离级别 + + SQL 标准以及 PostgreSQL 实现的事务隔离级别见 。 + + + + 事务隔离级别 + + + + + 隔离级别 + + + 脏读 + + + 不可重复读 + + + 幻读 + + + 串行化异常 + + + + + + + 读未提交 + + + 允许,但 PostgreSQL 中不会发生 + + + 可能 + + + 可能 + + + 可能 + + + + + + 读已提交 + + + 不可能 + + + 可能 + + + 可能 + + + 可能 + + + + + + 可重复读 + + + 不可能 + + + 不可能 + + + 允许,但 PostgreSQL 中不会发生 + + + 可能 + + + + + + 可串行化 + + + 不可能 + + + 不可能 + + + 不可能 + + + 不可能 + + + + +
+ + + 在 PostgreSQL 中,你可以请求四种标准事务隔离级别中的任意一种,但在内部只实现了三种不同的隔离级别;也就是说,PostgreSQL 的读未提交模式与读已提交模式行为相同。这是因为,要把标准隔离级别映射到 PostgreSQL 的多版本并发控制架构上,这是唯一合理的做法。 + + + + 该表还显示,PostgreSQL 的可重复读实现不允许幻读。SQL 标准允许更严格的行为: + 这四个隔离级别只定义哪些现象不允许发生,而不定义哪些现象必须发生。 + 可用隔离级别的行为将在下面各小节中详细说明。 + + + + 要设置事务的事务隔离级别,请使用命令 。 + + + + + 某些 PostgreSQL 数据类型和函数在事务行为方面有特殊规则。特别是,对序列的修改(以及使用 serial 声明的列所对应的计数器)会立即对所有其他事务可见,并且即使执行该修改的事务中止,这些修改也不会被回滚。见 。 + + + + + 读已提交隔离级别 + + + 事务隔离级别 + 读已提交 + + + + 读已提交 + + + + 读已提交PostgreSQL 中默认的隔离级别。 + 当事务使用这一隔离级别时,SELECT 查询(不带 + FOR UPDATE/SHARE 子句)只能看到查询开始之前已经提交的数据; + 它既看不到未提交的数据,也看不到查询执行期间并发事务提交的更改。 + 实际上,SELECT 查询看到的是该查询开始运行瞬间的数据库快照。 + 不过,SELECT 能看到其自身事务中先前执行的更新效果,即使这些更新尚未提交。 + 还要注意,即使两个连续的 SELECT 命令位于同一个事务中, + 如果其他事务在第一个 SELECT 开始之后、第二个 + SELECT 开始之前提交了更改,这两个命令也可能看到不同的数据。 + + + + UPDATEDELETESELECT FOR UPDATE + 和 SELECT FOR SHARE 命令在搜索目标行时的行为与 SELECT 相同: + 它们只会找到在命令开始时已经提交的目标行。不过,当找到这样的目标行时, + 它可能已经被其他并发事务更新、删除或者锁定。在这种情况下,即将执行更新的事务会等待第一个更新事务提交或回滚 + (如果它仍在进行中)。如果第一个更新事务回滚,那么它的作用将被撤销,第二个更新事务就可以继续更新最初找到的行。 + 如果第一个更新事务提交了,那么若该行已被第一个更新者删除,第二个更新事务就会忽略该行; + 否则,第二个更新者将尝试在该行的已更新版本上应用自己的操作。 + 该命令的搜索条件(WHERE 子句)会被重新计算,以判断该行的已更新版本是否仍然符合搜索条件。 + 如果符合,则第二个更新者会基于该行的已更新版本继续执行操作。 + 对于 SELECT FOR UPDATESELECT FOR SHARE, + 这意味着被锁住并返回给客户端的是该行的已更新版本。 + + + + 带有 ON CONFLICT DO UPDATE 子句的 INSERT 的行为类似。 + 在读已提交模式下,每个拟插入的行要么插入成功,要么转而更新;除非出现无关错误,否则这两种结果之一是有保证的。 + 如果冲突来自另一个事务,而其效果对 INSERT 尚不可见, + 则 UPDATE 子句仍将作用于该行,即使从通常意义上说, + 该命令可能看不到该行的任何版本。 + + + + 带有 ON CONFLICT DO NOTHING 子句的 INSERT + 也可能因为另一个事务的结果而不插入某一行,即使该事务的效果对 + INSERT 的快照不可见。同样,这种情况只会出现在读已提交模式下。 + + + + 由于上述规则,更新命令可能看到一个不一致的快照:它能够看到并发更新命令在它试图更新的同一行上的效果, + 却看不到这些命令对数据库中其他行的影响。这种行为使得读已提交模式不适合涉及复杂搜索条件的命令; + 不过,对于较简单的场景它恰到好处。例如,考虑以如下事务更新银行余额: + + +BEGIN; +UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345; +UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534; +COMMIT; + + + 如果两个这样的事务并发地尝试更改账户 12345 的余额,我们显然希望第二个事务从该账户行的已更新版本开始。 + 因为每个命令只影响一个预先确定的行,让它看到该行的已更新版本不会造成任何麻烦的不一致。 + + + + 在读已提交模式下,更复杂的用法可能产生不理想的结果。例如,考虑一个 + DELETE 命令,另一个命令正在修改数据,使某些行开始满足其筛选条件、另一些行不再满足。 + 假设 website 是一个有两行的表,其中 + website.hits 分别等于 9 和 + 10: + + +BEGIN; +UPDATE website SET hits = hits + 1; +-- 从另一个会话运行: DELETE FROM website WHERE hits = 10; +COMMIT; + + + 该 DELETE 将不会产生任何效果,即使在 + UPDATE 之前和之后都存在一行 + website.hits = 10。这是因为更新前值为 + 9 的那一行被跳过了,而当 UPDATE + 完成并且 DELETE 获得锁时,新行值已经不再是 + 10 而是 11,因此不再匹配条件。 + + + + 因为在读已提交模式中,每个命令都从一个新快照开始,而该快照包含截至当时已提交的所有事务, + 所以同一事务中的后续命令无论如何都会看到并发事务已提交的效果。上面真正的问题在于, + 单个命令是否能看到数据库的绝对一致视图。 + + + + 读已提交模式所提供的部分事务隔离对于许多应用来说已经足够,而且这种模式既快速又容易使用。 + 不过,它并不能满足所有场景。执行复杂查询和更新的应用,可能需要比读已提交模式所提供的更严格一致的数据库视图。 + + + + + 可重复读隔离级别 + + + 事务隔离级别 + 可重复读 + + + + 可重复读 + + + + 可重复读隔离级别只能看到事务开始前已提交的数据;它看不到未提交的数据, + 也看不到事务执行期间并发事务提交的更改。(不过,每个查询都能看到其自身事务中先前执行的更新效果, + 即使这些更新尚未提交。)这比 SQL 标准对该隔离级别的要求更强,并且除了串行化异常之外, + 能够防止 中描述的所有现象。正如前面提到的, + 这正是标准明确允许的,因为标准只描述每个隔离级别必须提供的最低保护。 + + + + 这一点与读已提交不同:在可重复读事务中,查询看到的是该事务中第一条非事务控制语句开始时的快照, + 而不是事务中当前语句开始时的快照。因此,在单个事务中的连续 + SELECT 命令会看到相同的数据,也就是说,它们不会看到在自身事务开始后由其他事务提交的更改。 + + + + 使用这一隔离级别的应用必须准备好在发生串行化失败时重试事务。 + + + + UPDATEDELETESELECT FOR UPDATE, + 和SELECT FOR SHARE命令 + 在搜索目标行方面与SELECT相同: + 它们只会找到在事务开始时已提交的目标行。 + 然而,这样的目标行可能在找到时已被另一个并发事务更新(或删除或锁定)。 + 在这种情况下,可重复读事务将等待第一个更新事务提交或回滚(如果仍在进行)。 + 如果第一个更新者回滚,则其效果将被取消,可重复读事务可以继续更新最初找到的行。 + 但如果第一个更新者提交了(并实际更新或删除了行,而不仅仅是锁定了它), + 那么可重复读事务将被回滚,并显示以下消息: + + +ERROR: could not serialize access due to concurrent update + + + 因为可重复读事务不能修改或锁定在其开始之后已被其他事务更改的行。 + + + + 当应用收到这条错误消息时,应当中止当前事务,并从头重试整个事务。 + 第二次执行时,该事务会把先前已提交的更改视为其初始数据库视图的一部分, + 因此以该行的新版本作为新事务更新的起点时,就不存在逻辑冲突。 + + + + 注意,只有更新事务才可能需要重试;只读事务永远不会发生串行化冲突。 + + + + 可重复读模式提供了严格的保证,即每个事务都看到数据库的一个完全稳定的视图。 + 不过,这个视图并不一定总能与同一级别并发事务的某种串行(一次一个)执行保持一致。 + 例如,即使是该级别的只读事务,也可能看到一条表明某批次已完成的控制记录,却看不到逻辑上属于该批次的某条明细记录,因为创建该明细记录的事务读取了控制记录的较早版本。如果不仔细使用显式锁来阻塞冲突事务, + 试图依靠运行在这一隔离级别的事务来强制业务规则,往往无法正确工作。 + + + + 可重复读隔离级别是通过一种技术实现的,这种技术在学术数据库文献中以及某些其他数据库产品中被称为 + 快照隔离。与使用会降低并发性的传统加锁技术的系统相比, + 其行为和性能可能会表现出差异。有些其他系统甚至把可重复读和快照隔离作为两个行为不同的独立隔离级别提供。 + 用来区分这两种技术的允许现象,直到 SQL 标准制定之后才被数据库研究人员正式定义,并且超出了本手册的范围。 + 完整讨论请参阅 。 + + + + + 在 PostgreSQL 9.1 之前,请求可串行化事务隔离级别会得到与这里描述完全相同的行为。 + 若要保留旧式的可串行化行为,现在应请求可重复读。 + + + + + + 可串行化隔离级别 + + + 事务隔离级别 + 可串行化 + + + + 可串行化 + + + + 谓词锁 + + + + 串行化异常 + + + + 可串行化隔离级别提供最严格的事务隔离。 + 该级别为所有已提交事务模拟串行执行;也就是说,效果就像这些事务不是并发执行,而是一个接一个地串行执行一样。 + 不过,与可重复读级别一样,使用该级别的应用必须准备好在发生串行化失败时重试事务。 + 实际上,这个隔离级别的工作方式与可重复读完全相同,只是它还会监视那些可能使一组并发可串行化事务的执行结果, + 与这些事务任何可能的串行(一次一个)执行都不一致的条件。 + 这种监视不会引入超出可重复读已有阻塞之外的任何阻塞,但会带来一定开销; + 一旦检测到可能导致串行化异常的条件,就会触发串行化失败。 + + + 例如,考虑表mytab,它最初包含以下内容: + class | value +-------+------- + 1 | 10 + 1 | 20 + 2 | 100 + 2 | 200 +假设可串行化事务 A 执行以下计算: +SELECT SUM(value) FROM mytab WHERE class = 1; +然后将结果(30)作为新行的value插入,且该行满足class = 2。与此同时,可串行化事务 B 执行以下计算: +SELECT SUM(value) FROM mytab WHERE class = 2; +得到结果 300,并将它插入一个新行,该行满足class = 1。然后两个事务都尝试提交。如果任一事务运行在可重复读隔离级别,两个事务都可以提交;但由于不存在与该结果一致的串行执行顺序,使用可串行化事务时,只会允许一个事务提交,另一个则会被回滚,并收到以下消息: +ERROR: could not serialize access due to read/write dependencies among transactions +这是因为,如果 A 在 B 之前执行,B 算出的总和应该是 330,而不是 300;同样,按另一种顺序执行,A 算出的总和也会不同。 + + + 当依赖可串行化事务来防止异常时,重要的是:从永久用户表读取的任何数据,在读取它的事务成功提交之前, + 都不应视为有效。即使对于只读事务也是如此;唯一的例外是可延迟只读事务中读取的数据, + 它一经读出就可视为有效,因为这种事务会等到能够获取一个保证不存在此类问题的快照后才开始读取数据。 + 在所有其他情况下,应用不能依赖后来被中止事务中读到的结果;相反,应重试事务直到成功。 + + + + 为了保证真正的可串行性,PostgreSQL 使用了谓词锁, + 也就是说,系统会保留一些锁,以便判断某个写操作如果先发生,是否会影响并发事务先前读取的结果。 + 在 PostgreSQL 中,这些锁不会造成任何阻塞,因此会参与形成死锁。 + 它们用于识别并标记并发可串行化事务之间的依赖关系,而这些依赖在某些组合下可能导致串行化异常。 + 相比之下,想要保证数据一致性的读已提交或可重复读事务,可能需要在整张表上加锁, + 这会阻塞其他试图使用该表的用户;或者它可能需要使用 SELECT FOR UPDATE 或 + SELECT FOR SHARE,而这些做法不仅可能阻塞其他事务,还会造成磁盘访问。 + + + + 与大多数其他数据库系统一样,PostgreSQL 中的谓词锁以事务实际访问的数据为基础。 + 这些锁会出现在pg_locks系统视图中, + 其 modeSIReadLock。查询执行期间获取的具体锁取决于查询所使用的计划, + 并且在事务过程中,多个更细粒度的锁(如元组锁)可能会合并成较少的粗粒度锁(如页锁), + 以防耗尽用于跟踪锁的内存。如果某个 READ ONLY 事务检测到不再可能发生会导致串行化异常的冲突, + 它可以在完成前释放其 SIRead 锁。事实上,READ ONLY 事务通常在启动时就能确定这一点, + 从而避免获取任何谓词锁。如果你显式请求一个 SERIALIZABLE READ ONLY DEFERRABLE 事务, + 它会阻塞,直到能够确立这一事实为止。(这是唯一一种可串行化事务会阻塞而可重复读事务不会阻塞的情况。) + 另一方面,SIRead 锁往往需要保留到事务提交之后,直到与之重叠的读写事务完成。 + + + + 统一采用可串行化事务可以简化开发。任何一组成功提交的并发可串行化事务,都保证与把它们一次一个运行的效果相同;这意味着,如果你能够证明某个事务在单独运行时会做正确的事,那么即使不了解其他事务会做什么,也可以相信它在任何可串行化事务的混合执行中也会做正确的事,否则它就不会成功提交。重要的是,采用这种技术的环境应当有一套通用机制来处理串行化失败(其 SQLSTATE 值总是 '40001'),因为很难准确预测究竟哪些事务会对读/写依赖关系有贡献,并因此需要被回滚来防止串行化异常。监视读/写依赖关系会带来开销,被串行化失败中止的事务重新启动也会带来开销;但在把这些开销与显式锁以及 SELECT FOR UPDATESELECT FOR SHARE 所涉及的成本和阻塞进行权衡之后,可串行化事务在某些环境中仍然是性能最佳的选择。 + + + + 虽然 PostgreSQL 的可串行化事务隔离级别只允许那些能够证明存在等价串行执行顺序的并发事务提交, + 但它并不总能阻止某些在真正串行执行中不会出现的错误被报告。特别是,即使在尝试插入某个键之前已经显式检查过该键不存在, + 仍然可能看到由于重叠执行的可串行化事务引起的唯一约束违例。要避免这种问题,必须确保所有 + 插入潜在冲突键的可串行化事务,都先显式检查自己是否可以这样做。例如,设想一个要求用户输入新键的应用, + 它会先尝试查询用户给定的键以检查其是否已经存在,或者通过选取当前最大键再加一来生成新键。 + 如果某些可串行化事务不遵循这一协议而直接插入新键,那么即使在这些并发事务的串行执行中本不会发生唯一约束违例, + 系统仍可能报告唯一约束违例。 + + + + 当依赖可串行化事务进行并发控制时,为了获得最佳性能,应考虑以下问题: + + + + + 在可能时声明事务为READ ONLY。 + + + + + 控制活动连接的数量,并在需要时使用连接池。这始终是重要的性能考量,但在使用可串行化事务的繁忙系统中尤为重要。 + + + + + 单个事务中只包含为保证完整性所必需的内容。 + + + + + 不要让连接不必要地闲置在事务中。配置参数 可用于自动断开这类拖延的会话。 + + + + + 在那些已经由可串行化事务自动提供保护的地方,消除不再需要的显式锁、SELECT FOR UPDATESELECT FOR SHARE。 + + + + + 当系统因谓词锁表内存不足而被迫把多个页级谓词锁合并为单个关系级谓词锁时,串行化失败的比例可能会上升。你可以通过增加 来避免这种情况。 + + + + + 顺序扫描总是需要关系级谓词锁。这可能导致串行化失败的比例上升。通过降低 和/或提高 来鼓励使用索引扫描,可能对此有所帮助。务必在事务回滚和重启次数减少所带来的收益,与查询执行时间整体变化之间进行权衡。 + + + + + + + 可串行化隔离级别是通过一种在学术数据库文献中称为可串行化快照隔离的技术实现的, + 它在快照隔离的基础上增加了对串行化异常的检查。 + 与使用传统加锁技术的其他系统相比,可能会观察到行为和性能方面的一些差异。 + 详细信息请参阅 。 + + +
+ + + 显式锁定 + + + + + + + PostgreSQL 提供了多种锁模式,用于控制对表中数据的并发访问。 + 在 MVCC 无法给出期望行为的场景中,这些模式可用于由应用自行控制的加锁。 + 此外,大多数 PostgreSQL 命令也会自动获取适当模式的锁, + 以确保在命令执行期间,被引用的表不会以不兼容的方式被删除或修改。 + (例如,TRUNCATE 无法安全地与同一张表上的其他操作并发执行, + 因此它会在该表上获取 ACCESS EXCLUSIVE 锁来强制实现这一点。) + + + + 要查看数据库服务器中当前尚未释放的锁列表,可以使用 pg_locks 系统视图。有关监控锁管理器子系统状态的更多信息,请参见 。 + + + + 表级锁 + + + LOCK + + + + 下面的列表给出了可用的锁模式,以及它们在 PostgreSQL 中被自动使用的场景。 + 你也可以通过命令 显式获取其中任意一种锁。 + 请记住,这些锁模式全部都是表级锁,即使名称中包含 row 一词;这些名称只是历史遗留。 + 在某种程度上,这些名称反映了各锁模式的典型用途 — 但它们的语义是完全相同的。 + 一个锁模式与另一个锁模式真正的区别,只在于它与哪些锁模式冲突(见 )。 + 两个事务不能在同一时刻在同一张表上持有相互冲突模式的锁。 + (不过,事务永远不会与自己冲突。例如,它可能先获取 ACCESS EXCLUSIVE 锁, + 随后又在同一张表上获取 ACCESS SHARE 锁。) + 不冲突的锁模式可以被多个事务同时持有。特别要注意,有些锁模式与自身冲突 + (例如,一次只能有一个事务持有 ACCESS EXCLUSIVE 锁), + 而另一些锁模式并不与自身冲突(例如,可以有多个事务持有 ACCESS SHARE 锁)。 + + + + 表级锁模式 + + + ACCESS SHARE + + + + 只与 ACCESS EXCLUSIVE 锁模式冲突。 + + + + SELECT 命令会在被引用的表上获取这种模式的锁。通常,任何只读取表而不修改它的查询都会获取这种锁模式。 + + + + + + + ROW SHARE + + + + 与 EXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。 + + + SELECT FOR UPDATESELECT FOR SHARE 命令会在目标表上获取这种模式的锁(此外,对于引用到但没有用 选取的其他表,还会获取 ACCESS SHARE 锁)。 + + + + + + ROW EXCLUSIVE + + + + 与SHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE锁模式冲突。 + + + + UPDATEDELETEINSERT 命令会在目标表上获取这种锁模式(此外,对任何其他被引用的表还会获取 ACCESS SHARE 锁)。通常,任何修改表中数据的命令都会获取这种锁模式。 + + + + + + + SHARE UPDATE EXCLUSIVE + + + + 与 SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。这种模式用于保护表不受并发模式更改和 VACUUM 运行的影响。 + + + VACUUM(不带 )、ANALYZECREATE INDEX CONCURRENTLYALTER TABLE VALIDATE 和其他 ALTER TABLE 变体获取(完整细节见 )。 + + + + + + SHARE + + + + 与 ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。这种模式用于保护表不受并发数据更改的影响。 + + + + 由 CREATE INDEX(不带 )获取。 + + + + + + + SHARE ROW EXCLUSIVE + + + + 与 ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。这种模式用于保护表不受并发数据更改的影响,并且是自排他的,因此同一时刻只能有一个会话持有它。 + + + CREATE TRIGGER 和多种形式的 ALTER TABLE 获取(见 )。 + + + + + + EXCLUSIVE + + + + 与 ROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 锁模式冲突。这种模式只允许并发的 ACCESS SHARE 锁,也就是说,只有对该表的读操作可以与持有这种锁模式的事务并行执行。 + + + + 由 REFRESH MATERIALIZED VIEW CONCURRENTLY 获取。 + + + + + + + ACCESS EXCLUSIVE + + + + 与所有模式的锁冲突(ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE)。这种模式保证持有者是以任何方式访问该表的唯一事务。 + + + + 由 DROP TABLETRUNCATE、 + REINDEXCLUSTER、 + VACUUM FULL 和不带 的 + REFRESH MATERIALIZED VIEW 命令获取。 + 多种形式的 ALTER TABLE + 也会获取这一层级的锁。这也是未显式指定模式的 LOCK TABLE + 语句的默认锁模式。 + + + + + + + + 只有 ACCESS EXCLUSIVE 锁才会阻塞 SELECT(不带 )语句。 + + + + + 锁一旦被获取,通常会一直持有到事务结束。但是,如果锁是在建立保存点之后才获取的,那么在回滚到该保存点时,这个锁会立即释放。这与 ROLLBACK 会取消保存点之后所有命令效果的原则是一致的。相同的原则也适用于在 PL/pgSQL 异常块中获取的锁:从该块中因错误跳出时,会释放在块中获取的锁。 + + + + + + 冲突的锁模式 + + + + + + + 请求的锁模式 + 当前的锁模式 + + + ACCESS SHARE + ROW SHARE + ROW EXCLUSIVE + SHARE UPDATE EXCLUSIVE + SHARE + SHARE ROW EXCLUSIVE + EXCLUSIVE + ACCESS EXCLUSIVE + + + + + ACCESS SHARE + + + + + + + + X + + + ROW SHARE + + + + + + + X + X + + + ROW EXCLUSIVE + + + + + X + X + X + X + + + SHARE UPDATE EXCLUSIVE + + + + X + X + X + X + X + + + SHARE + + + X + X + + X + X + X + + + SHARE ROW EXCLUSIVE + + + X + X + X + X + X + X + + + EXCLUSIVE + + X + X + X + X + X + X + X + + + ACCESS EXCLUSIVE + X + X + X + X + X + X + X + X + + + +
+
+ + + 行级锁 + + + 除了表级锁之外,还有行级锁。下面列出了这些锁,以及 PostgreSQL 会在哪些场景下自动使用它们。行级锁的完整冲突表见 。请注意,一个事务可以在同一行上持有彼此冲突的锁,甚至可以出现在不同子事务中;除此之外,两个事务不可能在同一行上持有彼此冲突的锁。行级锁不会影响数据查询;它们只会阻塞对同一行的写入者和加锁者。与表级锁一样,行级锁会在事务结束时或回滚到保存点时释放。 + + + + 行级锁模式 + + + FOR UPDATE + + + + FOR UPDATE 会导致 SELECT 语句检索到的行像将要被更新一样被锁定。这会阻止它们在当前事务结束之前被其他事务锁定、修改或删除。也就是说,其他尝试对这些行执行 UPDATEDELETESELECT FOR UPDATESELECT FOR NO KEY UPDATESELECT FOR SHARESELECT FOR KEY SHARE 的事务,都会被阻塞直到当前事务结束。反过来,SELECT FOR UPDATE 也会等待已经在同一行上执行过上述任一命令的并发事务,并随后锁定并返回更新后的那一行(或者不返回任何行,因为该行可能已被删除)。不过,在 REPEATABLE READSERIALIZABLE 事务中,如果要锁定的行自事务开始以来已经发生变化,就会抛出错误。进一步讨论见 。 + + + FOR UPDATE 锁模式也会被对某一行的任何 DELETE 获取, + 以及会修改某些列值的 UPDATE 获取。目前,就 UPDATE 而言, + 这里考虑的列是指其上存在可供外键使用的唯一索引的列 + (因此不考虑部分索引和表达式索引),但这在将来可能会变化。 + + + + + + + FOR NO KEY UPDATE + + + + 其行为类似于 FOR UPDATE,但获取的锁较弱:这种锁不会阻塞试图在同一行上获取锁的 + SELECT FOR KEY SHARE 命令。凡是不会获得 FOR UPDATE + 锁的 UPDATE,都会获得这种锁模式。 + + + + + + + FOR SHARE + + + + 行为与 FOR NO KEY UPDATE 类似,不过它在每个检索到的行上获取的是共享锁而不是排他锁。共享锁会阻塞其他事务在这些行上执行 UPDATEDELETESELECT FOR UPDATESELECT FOR NO KEY UPDATE,但不会阻止它们执行 SELECT FOR SHARESELECT FOR KEY SHARE。 + + + + + + + FOR KEY SHARE + + + + 行为与 FOR SHARE 类似,不过锁更弱:SELECT FOR UPDATE 会被阻塞,但 SELECT FOR NO KEY UPDATE 不会。键共享锁会阻塞其他事务执行 DELETE 或任何会修改键值的 UPDATE,但不会阻塞其他 UPDATE,也不会阻止 SELECT FOR NO KEY UPDATESELECT FOR SHARESELECT FOR KEY SHARE。 + + + + + + + PostgreSQL 不会在内存中保存任何关于已修改行的信息,因此一次加锁的行数没有限制。不过,锁定一行可能会导致一次磁盘写;例如,SELECT FOR UPDATE 会修改被选中的行以标记它们已被锁定,因此会产生磁盘写入。 + + + + 冲突的行级锁 + + + + + + + 请求的锁模式 + 当前的锁模式 + + + FOR KEY SHARE + FOR SHARE + FOR NO KEY UPDATE + FOR UPDATE + + + + + FOR KEY SHARE + + + + X + + + FOR SHARE + + + X + X + + + FOR NO KEY UPDATE + + X + X + X + + + FOR UPDATE + X + X + X + X + + + +
+
+ + + 页级锁 + + + 除了表级锁和行级锁之外,还使用页级共享/排他锁来控制对共享缓冲池中表页面的读写访问。这些锁会在取出或更新某一行后立即释放。应用开发者通常不需要关心页级锁,这里提到它们只是为了完整性。 + + + + + + 死锁 + + + 死锁 + + + + 使用显式锁可能会增加死锁的发生概率。所谓死锁,是指两个(或更多)事务各自持有对方想要的锁。例如,如果事务 1 在表 A 上获得了排他锁,然后试图再获取表 B 上的排他锁,而事务 2 已经持有表 B 上的排他锁,现在又想获取表 A 上的排他锁,那么两者都无法继续进行。PostgreSQL 会自动检测死锁,并通过中止其中一个事务来解决这个问题,使其他事务得以继续完成。(具体会中止哪个事务很难预测,也不应依赖这种预测。) + + + + 还要注意,死锁也可能由于行级锁而发生(因此,即使没有使用显式锁,它们也可能出现)。考虑下面这种情况:两个并发事务都在修改一个表。第一个事务执行: + + +UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111; + + + 这会在指定账号的那一行上获取一个行级锁。然后第二个事务执行: + + +UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222; +UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111; + + + 第一条 UPDATE 语句成功地在指定行上获取了一个行级锁,因此它成功更新了那一行。然而,第二条 UPDATE 语句发现它试图更新的行已经被锁住了,于是它等待持有该锁的事务结束。此时,事务二正在等待事务一结束之后才能继续执行。现在,事务一执行: + + +UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222; + + + 事务一试图在指定行上获取一个行级锁,但它做不到,因为事务二已经持有了这个锁。于是它必须等待事务二完成。这样一来,事务一被事务二阻塞,而事务二又被事务一阻塞:死锁条件形成了。PostgreSQL 会检测到这种情况,并中止其中一个事务。 + + + + 防止死锁的最好办法通常是避免它们出现,也就是确保所有使用同一数据库的应用都以一致的顺序在多个对象上获取锁。在上面的例子中,如果两个事务都是按相同顺序更新这些行,就不会发生死锁。还应确保事务在某个对象上获取的第一个锁,就是该对象所需的最严格锁模式。如果事先无法验证这一点,那么就应通过重试因死锁而中止的事务来动态处理死锁。 + + + + 只要没有检测到死锁,寻求表级锁或行级锁的事务就会无限期等待冲突锁被释放。这意味着让应用长时间保持事务打开并不是好主意(例如等待用户输入时)。 + + + + + 咨询锁 + + + 咨询锁 + + + + + 咨询 + + + + PostgreSQL 提供了一种创建由应用自行定义含义的锁的方法。这类锁被称为咨询锁,因为系统并不强制要求使用它们 — 是否正确使用完全取决于应用。咨询锁对于那些与 MVCC 模型不太契合的加锁策略非常有用。例如,咨询锁的一个常见用途是模拟所谓 平面文件 数据管理系统中典型的悲观锁策略。虽然也可以通过在表中存储一个标志位来达到相同目的,但咨询锁速度更快,可以避免表膨胀,并且会在会话结束时由服务器自动清理。 + + + + 在 PostgreSQL 中获取咨询锁有两种方式:会话级和事务级。 + 会话级咨询锁一旦获取,就会一直保持到显式释放或会话结束。不同于标准锁请求, + 会话级咨询锁请求不遵守事务语义:在随后回滚的事务中获取的锁,回滚后仍会保持; + 同样,即使发出解锁请求的事务后来失败,解锁依然有效。一个进程可以多次获取同一个锁; + 每成功一次加锁请求,都必须有对应的解锁请求,锁才会真正释放。 + 另一方面,事务级锁请求更像普通锁请求:它们会在事务结束时自动释放,而且没有显式解锁操作。 + 对于短期使用咨询锁,这种行为通常比会话级行为更方便。针对同一咨询锁标识符的会话级和事务级锁请求, + 会按预期相互阻塞。如果某个会话已经持有给定的咨询锁,那么它发出的附加请求总会成功, + 即使其他会话正在等待该锁;无论现有锁和新请求属于会话级还是事务级,都是如此。 + + + + 和 PostgreSQL 中的所有锁一样,任何会话当前持有的咨询锁完整列表, + 都可以在pg_locks系统视图中找到。 + + + + 咨询锁和普通锁都存储在一个共享内存池中,其大小由配置变量 定义。必须小心不要耗尽这部分内存,否则服务器将根本无法再授予任何锁。这也为服务器可授予的咨询锁数量设定了上限,具体通常在数万到数十万个之间,取决于服务器的配置。 + + + + 在某些使用咨询锁的方法中,特别是涉及显式排序和 LIMIT 子句的查询, + 必须注意控制由于 SQL 表达式求值顺序而获取的锁。例如: + +SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- 可以 +SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- 危险! +SELECT pg_advisory_lock(q.id) FROM +( + SELECT id FROM foo WHERE id > 12345 LIMIT 100 +) q; -- 可以 + + 在上面的查询中,第二种形式是危险的,因为不能保证在执行加锁函数之前先应用 LIMIT。 + 这可能导致获取了一些应用并未预期的锁,因此应用也不会去释放它们(直到会话结束)。 + 从应用的角度看,这类锁会变成悬而未清的锁,尽管仍然可以在 pg_locks 中看到。 + + + + 用于操作咨询锁的函数见 。 + + + +
+ + + 应用级别的数据一致性检查 + + + 用读已提交事务强制执行有关数据完整性的业务规则非常困难,因为数据视图会随每条语句而变化, + 而且一旦发生写冲突,即使是单条语句也未必局限于该语句自己的快照。 + + + + 虽然可重复读事务在整个执行期间都具有稳定的数据视图,但在使用 MVCC 快照进行数据一致性检查时,还存在一个微妙问题,即所谓的读/写冲突。如果一个事务写入数据,而并发事务试图读取相同的数据(无论是在写入之前还是之后),它都看不到另一个事务的工作。于是,读取者看起来就像是先执行的一方,而不管究竟是谁先启动,也不管谁先提交。如果事情只停留在这一步,就没有问题;但如果读取者还写入了数据,而这些数据又被另一个并发事务读取,那么现在就会有一个事务看起来像是在前面提到的任一事务之前执行。如果看起来最后执行的事务实际上最先提交,就很容易在事务执行顺序图中形成一个环。一旦出现这样的环,不借助额外机制,完整性检查就无法正确工作。 + + + + 正如 中提到的,可串行化事务其实就是在可重复读事务的基础上,增加了对危险读/写冲突模式的非阻塞监控。当检测到某种模式可能在表面执行顺序中形成一个环时,其中一个相关事务就会被回滚,以打破这个环。 + + + + 用可串行化事务强制一致性 + + + 如果对所有写操作,以及所有需要一致数据视图的读操作都使用可串行化事务隔离级别, + 那么不需要额外工作就能确保一致性。从其他环境移植而来、按使用可串行化事务来保证一致性而编写的软件, + 在这方面应当能够在 PostgreSQL正常工作。 + + + + 使用这种技术时,如果应用软件通过某个框架运行,并由该框架自动重试因串行化失败而回滚的事务,就可以避免给应用程序员带来不必要的负担。把 default_transaction_isolation 设置为 serializable 可能是个好主意。通过在触发器中检查事务隔离级别来采取某些措施,以确保不会因为疏忽或为了绕过完整性检查而使用其他事务隔离级别,也是明智的。 + + + + 性能方面的建议见 。 + + + + + 利用可串行化事务提供的这一层完整性保护,尚未扩展到热备模式()。 + 因此,使用热备的用户可能希望在主库上使用可重复读和显式锁定。 + + + + + + 使用显式阻塞锁强制一致性 + + + 当存在非可串行化写入时,要确保某一行当前仍然有效,并保护它不受并发更新影响, + 就必须使用 SELECT FOR UPDATESELECT FOR SHARE + 或适当的 LOCK TABLE 语句。 + (SELECT FOR UPDATESELECT FOR SHARE + 只锁定返回的行以防止并发更新,而 LOCK TABLE 会锁住整张表。) + 从其他环境向 PostgreSQL 迁移应用时,应当考虑这一点。 + + + + 对于从其他环境迁移而来的用户,还需要注意:SELECT FOR UPDATE + 并不能保证并发事务不会更新或删除被选中的行。要在 PostgreSQL + 中做到这一点,必须真正去更新该行,即使没有任何值需要改变。 + SELECT FOR UPDATE 只是临时阻塞其他事务, + 使它们不能获取同样的锁,也不能执行会影响被锁定行的 UPDATE 或 + DELETE;但是一旦持有该锁的事务提交或回滚,被阻塞的事务就会继续执行冲突操作, + 除非在持锁期间已经对该行执行了实际的 UPDATE。 + + + + 在非可串行化的 MVCC 环境下,全局有效性检查需要额外考虑。 + 例如,一个银行应用可能希望检查一个表中的贷方金额总和等于另一个表中的借方金额总和, + 而这两个表都在被活跃更新。在读已提交模式下,比较两个连续的 SELECT sum(...) + 命令的结果并不可靠,因为第二个查询很可能会包含第一个查询没有统计到的事务提交结果。 + 在单个可重复读事务中完成这两次求和,只能准确反映在该可重复读事务开始之前已提交事务的效果 + — 但等到结果交付时,人们完全可能合理地怀疑这个答案是否仍然相关。 + 如果可重复读事务本身在尝试进行一致性检查之前已经应用了某些更改,那么这种检查的意义就更加值得商榷, + 因为此时它包含了事务开始后的一部分而不是全部更改。在这种情况下,谨慎的做法可能是锁定执行检查所需的所有表, + 以获得当前真实状态的无可争议图景。SHARE 模式(或更高)的锁能够保证, + 在被锁定的表中,除了当前事务自身的更改之外,没有其他未提交更改。 + + + + 注意,如果依赖显式锁定来防止并发更改,就应当使用读已提交模式, + 或者在可重复读模式下小心地在执行查询之前先获取锁。 + 可重复读事务获得的锁能够保证没有其他修改该表的事务仍在运行, + 但如果该事务看到的快照早于获取锁的时刻,那么它看到的快照也可能早于表中某些现在已经提交的更改。 + 可重复读事务的快照实际上是在其第一条查询或数据修改命令 + (SELECTINSERTUPDATE、 + DELETE)开始时被冻结的, + 因此可以在快照冻结之前显式获取锁。 + + + + + + 注意事项 + + + 一些 DDL 命令(目前只有 和会重写表的 形式)不是 MVCC 安全的。这意味着在截断或重写提交之后,如果并发事务使用的是在该 DDL 命令提交之前取得的快照,那么该表对它们来说会表现为空表。这只会对那些在 DDL 命令开始前没有访问过相关表的事务造成问题 — 任何在 DDL 命令开始前访问过该表的事务,都会持有至少一个 ACCESS SHARE 表锁,从而阻塞该 DDL 命令直到该事务完成。因此,这些命令不会在针对目标表的连续查询中造成明显的表内容不一致,但它们可能会导致目标表内容与数据库中其他表内容之间出现可见的不一致。 + + + + 对可串行化事务隔离级别的支持尚未添加到热备复制目标(见 )。 + 当前热备模式下支持的最严格隔离级别是可重复读。 + 虽然在主库上把所有永久数据库写操作都放在可串行化事务中执行,能够确保所有备库最终达到一致状态,但在备库上运行的可重复读事务,有时仍可能看到一种瞬态状态,而这种状态与主库上事务的任何串行执行都不一致。 + + + + 对系统目录的内部访问并不是按当前事务的隔离级别执行的。 + 这意味着新创建的数据库对象(例如表)对于并发的可重复读和可串行化事务是可见的, + 即使这些对象中包含的行并不可见。相反,在较高隔离级别下,显式检查系统目录的查询不会看到表示并发创建数据库对象的那些行。 + + + + + 锁定和索引 + + + 索引 + + + + + 尽管 PostgreSQL 为表数据提供了非阻塞的读写访问,但 PostgreSQL 当前实现的索引访问方法并不是每一种都能提供非阻塞的读写访问。各种索引类型的处理方式如下: + + + + + B-树、GiSTSP-GiST 索引 + + + + 读写访问使用短期的页级共享/排他锁。每个索引行在被取出或插入之后,锁都会立即释放。这些索引类型提供最高的并发性,并且不会产生死锁。 + + + + + + + Hash 索引 + + + + 读写访问使用的是 Hash 桶级共享/排他锁。锁会在整个 Hash 桶处理完成后释放。桶级锁比索引级锁具有更好的并发性,但也可能产生死锁,因为它们的持有时间比一次索引操作更长。 + + + + + + + GIN 索引 + + + + 读写访问使用短期的页级共享/排他锁。每个索引行在被取出或插入之后,锁都会立即释放。但要注意,插入一个使用 GIN 索引的值通常会导致每行产生多个索引键插入,因此 GIN 可能为了插入单个值而做大量工作。 + + + + + + + + 目前,B-树索引为并发应用提供了最佳性能;由于它们还比 Hash 索引拥有更多特性,因此对于需要为标量数据建立索引的并发应用,推荐使用 B-树索引类型。处理非标量数据时,B-树就不再适用,此时应改用 GiST、SP-GiST 或 GIN 索引。 + + +
diff --git a/zh/9.6/nls.sgml b/zh/9.6/nls.sgml new file mode 100644 index 00000000..3efcbe01 --- /dev/null +++ b/zh/9.6/nls.sgml @@ -0,0 +1,406 @@ + + + + 本地语言支持 + + + 给翻译者 + + + PostgreSQL + 程序(服务器和客户端)可以用你所偏好的语言发出消息 — + 前提是这些消息已经被翻译。创建和维护翻译后的消息集,需要那些精通自己语言并愿意为 + PostgreSQL 项目作出贡献的人的帮助。做这件事完全不必是程序员。 + 本节说明如何提供帮助。 + + + + 要求 + + + 我们不会评判你的语言能力 — 本节讨论的是软件工具。理论上,你只需要一个文本编辑器。 + 但这只有在你不打算试用自己翻译的消息时才成立,而这种情况不太可能发生。 + 配置你的源代码树时,请务必使用 选项。 + 这还会检查 libintl 库和 msgfmt + 程序,而终端用户无论如何也需要它们。要试用你的工作成果,请遵循安装说明中适用的部分。 + + + + 如果你想开始一项新的翻译工作,或者想执行一次消息目录合并(后文会描述), + 则分别需要 GNU 兼容实现的 xgettext 和 + msgmerge 程序。今后我们会尽量做到:如果你使用打包的源码发布版, + 就不需要 xgettext 了。(如果从 Git 工作,你仍然需要它。) + 目前推荐使用 GNU Gettext 0.10.36 或更高版本。 + + + + 你本地的 gettext 实现应该自带文档。其中有些内容可能与下文重复, + 但要了解更多细节,你应当去查阅那些文档。 + + + + + 概念 + + + 原始(英语)消息及其(可能存在的)译文对应项,以成对形式保存在 + 消息目录中;每个程序以及每种目标语言各有一份消息目录 + (尽管相关程序可以共享同一个消息目录)。消息目录有两种文件格式:第一种是 PO + 文件(Portable Object,可移植对象),它是带有特殊语法的纯文本文件,由翻译者编辑。 + 第二种是 MO 文件(Machine Object,机器对象),它是根据相应的 PO 文件生成的二进制文件, + 在国际化后的程序运行时使用。翻译者不需要处理 MO 文件;事实上几乎没有人需要。 + + + + 消息目录文件的扩展名,毫不意外地是 .po 或 + .mo。基本名则视情况而定,要么是其所对应的程序名, + 要么是该文件所对应的语言。这多少有些令人困惑。例子包括 psql.po + (psql 的 PO 文件)或 fr.mo(法语的 MO 文件)。 + + + + PO 文件的格式示例如下: + +# comment + +msgid "original string" +msgstr "translated string" + +msgid "more original" +msgstr "another translated" +"string can be broken up like this" + +... + + msgid 行是从程序源代码中提取出来的。(并非一定如此,但这是最常见的做法。) + msgstr 行起初为空,由翻译者填入有意义的字符串。字符串可以包含 C 风格的转义字符, + 也可以像上例那样跨多行延续。(下一行必须从行首开始。) + + + + # 字符引入注释。如果 # 字符后面紧跟空白,那么这就是由翻译者维护的注释。 + 也可能存在自动注释,即 # 后紧跟非空白字符的注释。这些注释由各种处理 PO 文件的工具维护, + 目的是帮助翻译者。 + +#. automatic comment +#: filename.c:1023 +#, flags, flags + + #. 风格的注释是从使用该消息的源文件中提取出来的。程序员可能已经为翻译者插入了信息, + 例如期望的对齐方式。#: 注释指出该消息在源代码中使用的精确位置。 + 翻译者不必查看程序源代码,但如果对正确译法有疑问,也可以去看。#, 注释包含以某种方式描述该消息的标志。 + 目前有两种标志:如果某条消息可能因程序源代码的更改而过期,则会设置 fuzzy。 + 翻译者随后可以核实这一点,并在可能时移除 fuzzy 标志。注意,fuzzy 消息不会提供给终端用户。 + 另一个标志是 c-format,它表示该消息是一个 + printf 风格的格式模板。这意味着译文也应该是一个格式字符串, + 并具有相同数量和类型的占位符。有一些工具会据此验证格式字符串,它们就是根据 c-format 标志来检查的。 + + + + + 创建和维护消息目录 + + + 好,那么该如何创建一个 空白 消息目录呢?首先,进入包含你想翻译其消息的程序的目录。 + 如果那里有一个 nls.mk 文件,就说明这个程序已经为翻译做好准备了。 + + + + 如果已经有一些 .po 文件,那么就说明已经有人做过部分翻译工作。 + 这些文件命名为 language.po, + 其中 language 是 + + ISO 639-1 两字母语言代码(小写),例如法语对应 fr.po。 + 如果某种语言确实需要进行多套翻译工作,这些文件也可以命名为 + language_region.po, + 其中 region 是 + + ISO 3166-1 两字母国家代码(大写),例如巴西葡萄牙语对应 + pt_BR.po。如果你找到了自己想要的语言,就可以直接开始编辑那个文件。 + + + 如果你需要开始一项新的翻译工作,先运行以下命令: +make init-po +这会创建一个文件progname.pot。 + (.pot用来与已经投入使用的 PO 文件区分开来;T代表template。)把这个文件复制为language.po并进行编辑。为了让系统知道新语言已经可用,还要编辑文件nls.mk,并在类似下面的行中添加该语言(或语言加国家)代码: +AVAIL_LANGUAGES := de fr +(当然也可能有其他语言。) + + + 随着底层程序或库发生变化,程序员可能会修改或增加消息。这种情况下你不需要从头开始。 + 相反,请运行以下命令: + +make update-po + + 它会创建一个新的空白消息目录文件(也就是你最初使用的那个 pot 文件), + 并将它与现有的 PO 文件合并。如果合并算法对某条消息拿不准, + 就会像上文所述那样把它标记为 fuzzy。新的 PO 文件会以 + .po.new 作为扩展名保存。 + + + + + 编辑 PO 文件 + + PO 文件可以用普通文本编辑器编辑。翻译者只应修改 msgstr 指令后引号之间的内容、添加注释,以及调整 fuzzy 标志。Emacs 里(不出意外地)有一个 PO 模式,相当有用。 + + + PO 文件不必完全填满。如果没有可用译文(或译文为空),软件会自动回退到原始字符串。 + 把不完整的翻译提交到源码树中也没有问题;这样别人就有机会接手你的工作。不过, + 完成一次合并之后,建议优先清除 fuzzy 条目。记住,fuzzy 条目不会被安装; + 它们只用来作为可能正确译文的参考。 + + + + 编辑译文时要记住以下几点: + + + + 务必确保如果原文以换行结束,译文也同样以换行结束。制表符等也是如此。 + + + + + + 如果原文是一个 printf 格式字符串,译文也必须如此。 + 译文还需要按相同顺序保留相同的格式说明符。有时语言自身的规则会让这一点变得不可能, + 或者至少很别扭。在这种情况下,你可以像下面这样修改格式说明符: + +msgstr "Die Datei %2$s hat %1$u Zeichen." + + 这样一来,第一个占位符实际上会使用参数列表中的第二个参数。 + digits$ 必须紧跟在 % 后面, + 位于其他任何格式修饰符之前。(这个特性确实存在于 printf + 函数族中。你以前可能没听说过它,因为在消息国际化之外,它几乎没有用武之地。) + + + + + + 如果原始字符串包含语言错误,就报告它(或者你自己在程序源代码中修正它), + 然后照常翻译。等程序源代码更新之后,修正过的字符串就可以被合并进来。 + 如果原始字符串包含事实性错误,就报告它(或者你自己修正它),并且不要翻译它。 + 相反,你可以在 PO 文件里为该字符串加上一条注释。 + + + + + + 保持原始字符串的风格和语气。特别是,那些不是完整句子的消息 + (cannot open file %s)通常不应以大写字母开头 + (如果你的语言区分大小写),也不应以句号结尾(如果你的语言使用标点符号)。 + 阅读 可能会有帮助。 + + + + + + 如果你不知道某条消息是什么意思,或者它有歧义,就到开发者邮件列表里提问。 + 很可能说英语的终端用户也同样看不懂它,或者会觉得它有歧义,因此最好直接改进那条消息。 + + + + + + + + + + + + 给程序员 + + + 实现机制 + + + 本节描述如何在 PostgreSQL 发行版中的程序或库里实现本地语言支持。 + 目前它只适用于 C 程序。 + + + + 为程序添加 NLS 支持 + + + + 把下面的代码插入到该程序的启动序列中: + +#ifdef ENABLE_NLS +#include <locale.h> +#endif + +... + +#ifdef ENABLE_NLS +setlocale(LC_ALL, ""); +bindtextdomain("progname", LOCALEDIR); +textdomain("progname"); +#endif + + (其中 progname 实际上可以自由选择。) + + + + + + 凡是遇到可能需要翻译的消息,都需要插入对 gettext() 的调用。例如: + +fprintf(stderr, "panic level %d\n", lvl); + + 将改成: + +fprintf(stderr, gettext("panic level %d\n"), lvl); + + (如果没有配置 NLS 支持,gettext 会被定义成一个空操作。) + + + + 这样往往会增加很多杂乱内容。一个常用的简写是: + +#define _(x) gettext(x) + + 如果该程序的大部分通信都是通过一个或少数几个函数完成的,例如后端中的 + ereport(),另一种可行的解决办法是让这个函数在内部对所有输入字符串调用 + gettext。 + + + + + 在程序源代码所在目录中添加一个nls.mk文件。这个文件会作为 makefile 读取。这里需要设置以下变量: + + CATALOG_NAME + + + + 程序名,即 textdomain() 调用中提供的名称。 + + + + + + AVAIL_LANGUAGES + + + 已提供的翻译列表 — 最初为空。 + + + + + GETTEXT_FILES + + + + 包含可翻译字符串的文件列表,也就是那些使用 gettext + 或其他替代方案标记过的文件。最终,这会包含该程序几乎所有的源文件。 + 如果这个列表太长,可以让第一个 文件 是一个 +, + 第二个词则是一个文件名,该文件每行包含一个文件名。 + + + + + + GETTEXT_TRIGGERS + + + + 为翻译者生成消息目录的工具需要知道哪些函数调用包含可翻译字符串。 + 默认只认识 gettext() 调用。如果你使用了 _ + 或其他标识符,就需要在这里列出它们。如果可翻译字符串不是第一个参数, + 条目就需要写成 func:2 这样的形式(表示第二个参数)。 + 如果你有一个支持复数形式消息的函数,条目应写成 func:1,2 + 这样(分别标识单数和复数消息参数)。 + + + + + + + + + + + 构建系统会自动处理消息目录的构建和安装。 + + + + + 消息编写指南 + + + 下面列出一些便于翻译的消息编写指南。 + + + + + 不要像下面这样在运行时拼接句子: + +printf("Files were %s.\n", flag ? "copied" : "removed"); + + 句子中的词序在其他语言里可能完全不同。另外,即使你记得对每个片段都调用 + gettext(),这些片段分开来看也可能难以妥善翻译。 + 最好稍微重复一点代码,让每条待翻译消息都成为一个语义完整的整体。 + 只有数字、文件名之类的运行时变量才应该在运行时插入消息文本中。 + + + + + + 出于类似的原因,下面这样也行不通: + +printf("copied %d file%s", n, n!=1 ? "s" : ""); + + 因为它假定了复数形式的构造方式。如果你以为可以这样解决: + +if (n==1) + printf("copied 1 file"); +else + printf("copied %d files", n): + + 那就会失望了。有些语言的复数形式不止两种,而且规则相当特殊。 + 通常最好从设计上彻底避开这个问题,例如这样: + +printf("number of copied files: %d", n); + + + + + 如果你确实想构造一条正确处理复数的消息,也有相应支持,只是有点别扭。 + 在 ereport() 中生成主错误消息或详细错误消息时, + 可以写成这样: + +errmsg_plural("copied %d file", + "copied %d files", + n, + n) + + 第一个参数是适用于英语单数形式的格式字符串,第二个参数是适用于英语复数形式的格式字符串, + 第三个参数是决定使用哪种复数形式的整数控制值。后续参数会像往常一样按照格式字符串进行格式化。 + (通常,这个复数控制值本身也会是要格式化的值之一,所以必须写两次。) + 在英语中,只需要区分 n 是否为 1; + 而在其他语言中,可能存在许多不同的复数形式。翻译者会把这两个英语形式视为一组, + 并有机会提供多个替代字符串,再根据运行时的 n 值选择合适的那个。 + + + + 如果你需要对一条不是直接用于 errmsg 或 + errdetail 报告的消息做复数处理,就必须使用底层函数 + ngettext。参见 gettext 文档。 + + + + + + 如果你想向翻译者传达某些信息,例如一条消息应如何与其他输出对齐, + 就在该字符串出现之前放置一个以 translator 开头的注释,例如: + +/* translator: This message is not what it seems to be. */ + + 这些注释会被复制到消息目录文件中,这样翻译者就能看到它们。 + + + + + + + + diff --git a/zh/9.6/notation.sgml b/zh/9.6/notation.sgml new file mode 100644 index 00000000..e2785258 --- /dev/null +++ b/zh/9.6/notation.sgml @@ -0,0 +1,26 @@ + + + + 约定 + + + 以下约定用于命令概要: + 方括号([])表示可选部分。(在 Tcl 命令的概要中,改用问号 + (?)表示,这是 Tcl 的惯例。)大括号 + ({})和竖线 + (|)表示必须选择其中一个替代项。 + 点(...)表示前面的元素可以重复。 + + + + 在有助于增强清晰度时,SQL 命令前会加上提示符=>, + shell 命令前会加上提示符$。不过,通常并不显示提示符。 + + + + 一般来说,管理员是负责安装和运行服务器的人。 + 用户则可以是任何正在使用或希望使用 + PostgreSQL系统任一部分的人。 + 不应把这些术语理解得过于狭隘;本书并不对系统管理流程作固定预设。 + + diff --git a/zh/9.6/oid2name.sgml b/zh/9.6/oid2name.sgml new file mode 100644 index 00000000..4b995570 --- /dev/null +++ b/zh/9.6/oid2name.sgml @@ -0,0 +1,305 @@ + + + + + oid2name + + + + oid2name + 1 + 应用程序 + + + + oid2name + 解析 PostgreSQL 数据目录中的 OID 和 filenode + + + + + oid2name + option + + + + + 描述 + + + oid2name 是一个帮助管理员检查 PostgreSQL 所用文件结构的实用程序。要使用它,你需要熟悉数据库文件结构,这在 中有说明。 + + + + + oid2name 这个名称是历史遗留,实际上颇具误导性,因为多数时候你真正关心的是表的 filenode 编号(也就是数据库目录中可见的文件名)。务必理解表 OID 与表 filenode 之间的区别。 + + + + + oid2name 连接到目标数据库,并提取 OID、filenode 和/或表名信息。 + 你也可以让它显示数据库 OID 或表空间 OID。 + + + + + + 选项 + + + oid2name接受以下命令行参数: + + + filenode + 显示文件节点为filenode的表的信息 + + + + + 在列表中包含索引和序列 + + + + oid + 显示 OID 为oid的表的信息 + + + + + 省略标题(适用于脚本) + + + + + 显示表空间 OID + + + + + 包含系统对象(位于模式中的对象) + + + + tablename_pattern + 显示与tablename_pattern匹配的表的信息 + + + + + + + + 输出oid2name版本并退出。 + + + + + + + 显示每个对象的更多信息:表空间名、模式名和 OID + + + + + + + + 显示oid2name命令行参数的帮助并退出。 + + + + + + + + oid2name还接受以下用于连接参数的命令行参数: + + database + 要连接的数据库 + + + + host + 数据库服务器的主机 + + + + port + 数据库服务器的端口 + + + + username + 用于连接的用户名 + + + + password + 密码(已弃用 — 将其放在命令行上会带来安全风险) + + + + + + + 若要显示特定的表,可使用和/或来选择要显示哪些表。 + 接受一个 OID, + 接受一个 filenode, + 而接受一个表名(实际上是一个LIKE模式,因此你可以使用诸如foo%之类的形式)。 + 这些选项可以按需重复使用,最终的列表将包含匹配任意一个选项的所有对象。 + 但请注意,这些选项只能显示由指定的数据库中的对象。 + + + + 如果没有给出,但给出了, + 那么它将列出指定数据库中的所有表。在这种模式下, + 选项控制要列出哪些对象。 + + + + 如果连也没有给出,它将显示数据库 OID 列表。 + 另外,你也可以给出来获取表空间列表。 + + + + + 注解 + + + oid2name要求数据库服务器正在运行,且系统目录未损坏。 + 因此,它对于从数据库灾难性损坏中恢复的帮助非常有限。 + + + + + 示例 + + +$ # what's in this database server, anyway? +$ oid2name +All databases: + Oid Database Name Tablespace +---------------------------------- + 17228 alvherre pg_default + 17255 regression pg_default + 17227 template0 pg_default + 1 template1 pg_default + +$ oid2name -s +All tablespaces: + Oid Tablespace Name +------------------------- + 1663 pg_default + 1664 pg_global + 155151 fastdisk + 155152 bigdisk + +$ # OK, let's look into database alvherre +$ cd $PGDATA/base/17228 + +$ # get top 10 db objects in the default tablespace, ordered by size +$ ls -lS * | head -10 +-rw------- 1 alvherre alvherre 136536064 sep 14 09:51 155173 +-rw------- 1 alvherre alvherre 17965056 sep 14 09:51 1155291 +-rw------- 1 alvherre alvherre 1204224 sep 14 09:51 16717 +-rw------- 1 alvherre alvherre 581632 sep 6 17:51 1255 +-rw------- 1 alvherre alvherre 237568 sep 14 09:50 16674 +-rw------- 1 alvherre alvherre 212992 sep 14 09:51 1249 +-rw------- 1 alvherre alvherre 204800 sep 14 09:51 16684 +-rw------- 1 alvherre alvherre 196608 sep 14 09:50 16700 +-rw------- 1 alvherre alvherre 163840 sep 14 09:50 16699 +-rw------- 1 alvherre alvherre 122880 sep 6 17:51 16751 + +$ # I wonder what file 155173 is ... +$ oid2name -d alvherre -f 155173 +From database "alvherre": + Filenode Table Name +---------------------- + 155173 accounts + +$ # you can ask for more than one object +$ oid2name -d alvherre -f 155173 -f 1155291 +From database "alvherre": + Filenode Table Name +------------------------- + 155173 accounts + 1155291 accounts_pkey + +$ # you can mix the options, and get more details with -x +$ oid2name -d alvherre -t accounts -f 1155291 -x +From database "alvherre": + Filenode Table Name Oid Schema Tablespace +------------------------------------------------------ + 155173 accounts 155173 public pg_default + 1155291 accounts_pkey 1155291 public pg_default + +$ # show disk space for every db object +$ du [0-9]* | +> while read SIZE FILENODE +> do +> echo "$SIZE `oid2name -q -d alvherre -i -f $FILENODE`" +> done +16 1155287 branches_pkey +16 1155289 tellers_pkey +17561 1155291 accounts_pkey +... + +$ # same, but sort by size +$ du [0-9]* | sort -rn | while read SIZE FN +> do +> echo "$SIZE `oid2name -q -d alvherre -f $FN`" +> done +133466 155173 accounts +17561 1155291 accounts_pkey +1177 16717 pg_proc_proname_args_nsp_index +... + +$ # If you want to see what's in tablespaces, use the pg_tblspc directory +$ cd $PGDATA/pg_tblspc +$ oid2name -s +All tablespaces: + Oid Tablespace Name +------------------------- + 1663 pg_default + 1664 pg_global + 155151 fastdisk + 155152 bigdisk + +$ # what databases have objects in tablespace "fastdisk"? +$ ls -d 155151/* +155151/17228/ 155151/PG_VERSION + +$ # Oh, what was database 17228 again? +$ oid2name +All databases: + Oid Database Name Tablespace +---------------------------------- + 17228 alvherre pg_default + 17255 regression pg_default + 17227 template0 pg_default + 1 template1 pg_default + +$ # Let's see what objects does this database have in the tablespace. +$ cd 155151/17228 +$ ls -l +total 0 +-rw------- 1 postgres postgres 0 sep 13 23:20 155156 + +$ # OK, this is a pretty small table ... but which one is it? +$ oid2name -d alvherre -f 155156 +From database "alvherre": + Filenode Table Name +---------------------- + 155156 foo + + + + + 作者 + + + B. Palmer bpalmer@crimelabs.net + + + + diff --git a/zh/9.6/pageinspect.sgml b/zh/9.6/pageinspect.sgml new file mode 100644 index 00000000..9448f513 --- /dev/null +++ b/zh/9.6/pageinspect.sgml @@ -0,0 +1,399 @@ + + + + pageinspect + + + pageinspect + + + + pageinspect模块提供了一组函数,允许你在底层检查数据库页的内容,这对调试很有帮助。所有这些函数都只能由超级用户使用。 + + + + 函数 + + + + + get_raw_page(relname text, fork text, blkno int) returns bytea + + get_raw_page + + + + + + get_raw_page读取指定关系的指定块,并以bytea值的形式返回其副本。这样就能获得该块在单一时刻的一致副本。 + fork应当为'main'(主数据分支)、'fsm'(空闲空间映射)、 + 'vm'(可见性映射)或'init'(初始化分支)。 + + + + + + + get_raw_page(relname text, blkno int) returns bytea + + + + + 这是get_raw_page的简写形式,用于从主分支读取。等价于 + get_raw_page(relname, 'main', blkno) + + + + + + + page_header(page bytea) returns record + + page_header + + + + + + page_header显示PostgreSQL所有堆页和索引页共有的字段。 + + + 应将页面映像(通过get_raw_page获得)作为参数传入。例如: +test=# SELECT * FROM page_header(get_raw_page('pg_class', 0)); + lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid +-----------+----------+--------+-------+-------+---------+----------+---------+----------- + 0/24A1B50 | 1 | 1 | 232 | 368 | 8192 | 8192 | 4 | 0 +返回的列对应于PageHeaderData结构体中的字段。详细信息见src/include/storage/bufpage.h + + + + + + + + fsm_page_contents(page bytea) returns text + + fsm_page_contents + + + + + + fsm_page_contents显示 FSM 页的内部节点结构。例如: +test=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0)); +输出是一个多行字符串,页内二叉树中的每个节点对应一行。只打印非零节点。还会打印所谓的“next”指针,它指向下一个将从该页返回的槽位。 + + 参见src/backend/storage/freespace/README,了解 FSM 页面结构的更多信息。 + + + + + + heap_page_items(page bytea) returns setof record + + heap_page_items + + + + + + heap_page_items显示堆页上的所有行指针。对于正在使用的行指针,还会显示元组头和元组原始数据。无论这些元组在复制原始页时是否对某个 MVCC 快照可见,都会显示出来。 + + 应将堆页面映像(通过get_raw_page获得)作为参数传入。例如: +test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0)); +有关返回字段的说明,参见src/include/storage/itemid.hsrc/include/access/htup_details.h + + + + + + tuple_data_split(rel_oid oid, t_data bytea, t_infomask integer, t_infomask2 integer, t_bits text [, do_detoast bool]) returns bytea[] + + tuple_data_split + + + + + tuple_data_split以与后端内部相同的方式将元组数据拆分为属性。 +test=# SELECT tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2, t_bits) FROM heap_page_items(get_raw_page('pg_class', 0)); +调用该函数时,应使用以下函数返回的属性作为参数:heap_page_items。 + + + 如果do_detoasttrue,则会按需对属性执行去 TOAST 化处理。默认值为false。 + + + + + + + heap_page_item_attrs(page bytea, rel_oid regclass [, do_detoast bool]) returns setof record + + heap_page_item_attrs + + + + + heap_page_item_attrsheap_page_items等效,不同之处在于它将元组原始数据作为属性数组返回,并且可以通过do_detoast选择是否对这些属性执行去 TOAST 化处理;该参数默认为false。 + + 应将堆页面映像(通过get_raw_page获得)作为参数传入。例如: +test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class'::regclass); + + + + + + + bt_metap(relname text) returns record + + bt_metap + + + + + + bt_metap返回 B-树索引元页的信息。例如: +test=# SELECT * FROM bt_metap('pg_cast_oid_index'); +-[ RECORD 1 ]----- +magic | 340322 +version | 2 +root | 1 +level | 0 +fastroot | 1 +fastlevel | 0 + + + + + + + + bt_page_stats(relname text, blkno int) returns record + + bt_page_stats + + + + + + bt_page_stats返回 B-树索引单个页面的概要信息。例如: +test=# SELECT * FROM bt_page_stats('pg_cast_oid_index', 1); +-[ RECORD 1 ]-+----- +blkno | 1 +type | l +live_items | 256 +dead_items | 0 +avg_item_size | 12 +page_size | 8192 +free_size | 4056 +btpo_prev | 0 +btpo_next | 0 +btpo | 0 +btpo_flags | 3 + + + + + + + + bt_page_items(relname text, blkno int) returns setof record + + bt_page_items + + + + + + bt_page_items返回 B-树索引页中所有项的详细信息。例如: +test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1); + itemoffset | ctid | itemlen | nulls | vars | data +------------+---------+---------+-------+------+------------- + 1 | (0,1) | 12 | f | f | 23 27 00 00 + 2 | (0,2) | 12 | f | f | 24 27 00 00 + 3 | (0,3) | 12 | f | f | 25 27 00 00 + 4 | (0,4) | 12 | f | f | 26 27 00 00 + 5 | (0,5) | 12 | f | f | 27 27 00 00 + 6 | (0,6) | 12 | f | f | 28 27 00 00 + 7 | (0,7) | 12 | f | f | 29 27 00 00 + 8 | (0,8) | 12 | f | f | 2a 27 00 00 +在 B-树叶页中,ctid指向一个堆元组。在内部页中,ctid的块号部分指向索引本身的另一个页面,而偏移量部分(第二个数字)被忽略,通常为 1。 + 请注意,任何非最右页(即btpo_next字段值非零的页面)的第一项都是该页的高键,这意味着其data充当该页上所有项的上界,而其ctid字段没有意义。另外,在非叶子页上,第一个真正的数据项(第一个不是高键的项)是一个负无穷项,其data字段中没有实际值。不过,这样的项在ctid字段中确实有一个有效的下行链接。 + + + + + + brin_page_type(page bytea) returns text + + brin_page_type + + + + + + brin_page_type返回给定BRIN索引页的页类型;如果该页不是有效的BRIN页,则抛出错误。例如: + +test=# SELECT brin_page_type(get_raw_page('brinidx', 0)); + brin_page_type +---------------- + meta + + + + + + + + brin_metapage_info(page bytea) returns record + + brin_metapage_info + + + + + + brin_metapage_info返回BRIN索引元页的各类信息。例如: + +test=# SELECT * FROM brin_metapage_info(get_raw_page('brinidx', 0)); + magic | version | pagesperrange | lastrevmappage +------------+---------+---------------+---------------- + 0xA8109CFA | 1 | 4 | 2 + + + + + + + + brin_revmap_data(page bytea) returns setof tid + + brin_revmap_data + + + + + + brin_revmap_data返回BRIN索引范围映射页中的元组标识符列表。例如: + +test=# SELECT * FROM brin_revmap_data(get_raw_page('brinidx', 2)) limit 5; + pages +--------- + (6,137) + (6,138) + (6,139) + (6,140) + (6,141) + + + + + + + + brin_page_items(page bytea, index oid) returns setof record + + brin_page_items + + + + + + brin_page_items返回存储在BRIN数据页中的数据。例如: +test=# SELECT * FROM brin_page_items(get_raw_page('brinidx', 5), + 'brinidx') + ORDER BY blknum, attnum LIMIT 6; + itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | value +------------+--------+--------+----------+----------+-------------+-------------- + 137 | 0 | 1 | t | f | f | + 137 | 0 | 2 | f | f | f | {1 .. 88} + 138 | 4 | 1 | t | f | f | + 138 | 4 | 2 | f | f | f | {89 .. 176} + 139 | 8 | 1 | t | f | f | + 139 | 8 | 2 | f | f | f | {177 .. 264} +返回的列对应于BrinMemTupleBrinValues结构体中的字段。详细信息见src/include/access/brin_tuple.h + + + + + gin_metapage_info(page bytea) returns record + + gin_metapage_info + + + + + + gin_metapage_info返回GIN索引元页的信息。例如: + +test=# SELECT * FROM gin_metapage_info(get_raw_page('gin_index', 0)); +-[ RECORD 1 ]----+----------- +pending_head | 4294967295 +pending_tail | 4294967295 +tail_free_size | 0 +n_pending_pages | 0 +n_pending_tuples | 0 +n_total_pages | 7 +n_entry_pages | 6 +n_data_pages | 0 +n_entries | 693 +version | 2 + + + + + + + + gin_page_opaque_info(page bytea) returns record + + gin_page_opaque_info + + + + + + gin_page_opaque_info返回GIN索引不透明区域的信息,例如页类型。下面是一个示例: + +test=# SELECT * FROM gin_page_opaque_info(get_raw_page('gin_index', 2)); + rightlink | maxoff | flags +-----------+--------+------------------------ + 5 | 0 | {data,leaf,compressed} +(1 row) + + + + + + + + gin_leafpage_items(page bytea) returns setof record + + gin_leafpage_items + + + + + + gin_leafpage_items返回压缩GIN叶页中所存数据的相关信息。例如: + +test=# SELECT first_tid, nbytes, tids[0:5] as some_tids + FROM gin_leafpage_items(get_raw_page('gin_test_idx', 2)); + first_tid | nbytes | some_tids +-----------+--------+---------------------------------------------------------- + (8,41) | 244 | {"(8,41)","(8,43)","(8,44)","(8,45)","(8,46)"} + (10,45) | 248 | {"(10,45)","(10,46)","(10,47)","(10,48)","(10,49)"} + (12,52) | 248 | {"(12,52)","(12,53)","(12,54)","(12,55)","(12,56)"} + (14,59) | 320 | {"(14,59)","(14,60)","(14,61)","(14,62)","(14,63)"} + (167,16) | 376 | {"(167,16)","(167,17)","(167,18)","(167,19)","(167,20)"} + (170,30) | 376 | {"(170,30)","(170,31)","(170,32)","(170,33)","(170,34)"} + (173,44) | 197 | {"(173,44)","(173,45)","(173,46)","(173,47)","(173,48)"} +(7 rows) + + + + + + + + + diff --git a/zh/9.6/parallel.sgml b/zh/9.6/parallel.sgml new file mode 100644 index 00000000..9448f984 --- /dev/null +++ b/zh/9.6/parallel.sgml @@ -0,0 +1,248 @@ + + + + 并行查询 + + + 并行查询 + + + + PostgreSQL 可以制定能够利用多个 CPU、从而更快回答查询的查询计划。这一特性称为并行查询。许多查询无法从并行查询中获益,要么是因为当前实现存在限制,要么是因为根本不存在比串行查询计划更快的可行计划。不过,对于能够从中受益的查询,并行查询带来的加速通常非常明显。许多查询在使用并行查询时可以获得两倍以上的速度提升,有些甚至可以达到四倍或更高。那些访问大量数据但只向用户返回少量行的查询,通常最能从并行查询中获益。本章将解释并行查询的工作方式以及可以在哪些情况下使用它,从而帮助希望利用这一特性的用户了解可以期待的效果。 + + + + 并行查询如何工作 + + 当优化器认定并行查询是某个查询最快的执行策略时,就会创建包含Gather 节点的查询计划。下面是一个简单示例: +EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; + QUERY PLAN +------------------------------------------------------------------------------------- + Gather (cost=1000.00..217018.43 rows=1 width=97) + Workers Planned: 2 + -> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97) + Filter: (filler ~~ '%x%'::text) +(4 rows) + + + + + 无论在哪种情况下,Gather 节点都恰好有一个子计划,也就是将以并行方式执行的那部分计划。如果 Gather 节点位于计划树的最顶端,那么整个查询都会并行执行;如果它位于计划树中的其他位置,那么只有查询的那一部分会并行运行。在上面的示例中,查询只访问一个表,因此除了 Gather 节点本身之外只有一个计划节点;由于该计划节点是 Gather 节点的子节点,所以它会并行运行。 + + + + 通过 使用 EXPLAIN,你可以看到规划器选择的工作进程数量。当查询执行到 Gather 节点时,实现用户会话的进程会请求与规划器所选数量相同的后台工作进程。任意时刻可存在的后台工作进程总数受到 的限制,因此,并行查询实际运行时可能使用比计划更少的工作进程,甚至完全没有工作进程。最优计划可能依赖于可用工作进程的数量,因此这会导致查询性能不佳。如果这种情况经常发生,可以考虑增大 max_worker_processes,让更多工作进程能够同时运行;或者降低 ,使规划器请求更少的工作进程。 + + + + 为某个并行查询成功启动的每个后台工作进程都会执行计划中 Gather 节点的后代那部分。领导者也会执行这部分计划,但它还承担着额外职责:必须读取所有由工作进程生成的元组。当计划的并行部分只生成少量元组时,领导者通常会表现得很像一个额外的工作进程,从而加快查询执行。反过来,当计划的并行部分生成大量元组时,领导者可能几乎完全忙于读取工作进程生成的元组,并执行位于 Gather 节点之上的计划节点所要求的后续处理。在这种情况下,领导者实际参与执行计划并行部分的工作就会很少。 + + + + + + 何时可以使用并行查询? + + + 有几种设置会导致查询规划器在任何情况下都不生成并行查询计划。要让系统能够生成任何并行查询计划,下列设置必须按要求配置。 + + + + + + 必须设置为大于零的值。这只是更一般原则的一个特例,即所使用的工作进程数量不应超过通过 max_parallel_workers_per_gather 配置的数量。 + + + + + 必须设为 none 以外的值。并行查询需要动态共享内存,以便在协作进程之间传递数据。 + + + + + 此外,系统不能运行在单用户模式下。因为在这种情况下整个数据库系统作为单个进程运行,所以没有后台工作进程可用。 + + + + 即使系统通常可以为某个给定查询生成并行查询计划,只要下面任一条件成立,规划器都不会为该查询生成并行查询计划: + + + + + + 查询会写入任何数据或者锁定任何数据库行。如果一个查询在顶层或 CTE 中包含数据修改操作,那么不会为该查询生成并行计划。这是当前实现的限制,未来版本可能会解除这一限制。 + + + + + 查询在执行过程中可能会被挂起。只要系统认为可能发生部分执行或增量执行,就不会生成并行计划。例如,使用 DECLARE CURSOR 创建的游标绝不会使用并行计划。类似地,形如 FOR x IN query LOOP .. END LOOP 的 PL/pgsql 循环也绝不会使用并行计划,因为并行查询系统无法验证在并行查询处于活动状态时执行循环中的代码是否安全。 + + + + + + 查询使用了任何被标记为 PARALLEL UNSAFE 的函数。大多数系统定义的函数都被标记为 PARALLEL SAFE,但用户定义的函数默认被标记为 PARALLEL UNSAFE。参见 中的讨论。 + + + + + + 该查询运行在另一个已经并行执行的查询内部。例如,如果一个由并行查询调用的函数自己又发出一个 SQL 查询,那么该查询将绝不会使用并行计划。这是当前实现的一个限制,但未必值得移除,因为那样可能导致单个查询使用大量进程。 + + + + + 事务隔离级别为可串行化。这是当前实现的限制。 + + + + + 即使某个特定查询已经生成了并行查询计划,在执行时仍然有若干情况会导致该计划无法并行执行。如果发生这种情况,领导者将完全独自执行 Gather 节点以下的那部分计划,几乎就像 Gather 节点根本不存在一样。满足下列任一条件时,就会出现这种情况: + + + + + + 由于后台工作进程总数不能超过 ,因此无法获得后台工作进程。 + + + + + + 客户端发送了带有非零提取计数的 Execute 消息。请参见扩展查询协议中的讨论。由于 libpq 目前没有提供发送此类消息的方法,因此这种情况只会出现在不依赖 libpq 的客户端中。如果这种情况经常发生,那么在可能发生这种情况的会话中将 设置为零可能是个好主意,这样可以避免生成那些在串行运行时可能次优的查询计划。 + + + + + 使用 CREATE TABLE .. AS EXECUTE .. 语句执行预备语句。这种结构将原本的只读操作转换为读写操作,因此不能使用并行查询。 + + + + 事务隔离级别为可串行化。通常不会出现这种情况,因为在事务隔离级别为可串行化时,不会生成并行查询计划。但是,如果在计划生成之后、执行之前将事务隔离级别改为可串行化,就可能出现这种情况。 + + + + + + 并行计划 + + + 由于每个工作进程都会将计划的并行部分执行到底,因此不能简单地拿一个普通查询计划并让多个工作进程同时运行。那样每个工作进程都会生成完整输出结果集的一份副本,所以查询不仅不会比平常更快,反而会产生错误结果。相反,计划的并行部分必须是查询优化器内部所说的部分计划;也就是说,它必须被构造为使执行该计划的每个进程只生成输出行的一个子集,并且保证每一条所需输出行都恰好由某个协作进程生成一次。 + + + + 并行扫描 + + + 目前,唯一经过修改以支持并行查询的扫描类型是顺序扫描。因此,并行计划中的 + 驱动表总是通过Parallel Seq Scan来扫描。关系的块会被分配给 + 各个协作进程。每次只分配一个块,因此对关系的访问仍然是顺序的。每个进程在 + 请求新页之前,会先访问分配给它的页上的所有元组。 + + + + + 并行连接 + + + 驱动表可以通过嵌套循环或哈希连接与一个或多个其他表连接。连接的内侧可以是 + 规划器支持的任何类型的非并行计划,只要它能够安全地在并行工作进程中运行。 + 例如,内侧可以是这样一个索引扫描:它查找取自连接外侧的值。每个工作进程 + 都会完整执行连接的内侧,这对哈希连接来说意味着每个工作进程都要构建一个 + 相同的哈希表。 + + + + + 并行聚合 + + PostgreSQL 通过分两个阶段进行聚合来支持并行聚合。首先,每个参与查询并行部分的进程执行一个聚合步骤,为该进程所见到的每个分组产生一个部分结果。这在计划中体现为一个 Partial Aggregate 节点。然后,部分结果通过 Gather 节点传送给领导者。最后,领导者会把来自所有工作进程的结果再次聚合,以产生最终结果。这在计划中体现为一个 Finalize Aggregate 节点。 + + + + 由于 Finalize Aggregate 节点运行在领导者进程上,因此对于那些相对于输入行数会产生较多分组的查询,查询规划器会认为它不太有利。例如,在最坏情况下,Finalize Aggregate 节点看到的分组数可能与所有工作进程在 Partial Aggregate 阶段看到的输入行数一样多。对于这种情况,使用并行聚合显然不会带来性能收益。查询规划器会在规划过程中考虑这一点,因此在这种场景下不太可能选择并行聚合。 + + + + 并行聚合并非在所有情况下都受支持。每个聚合都必须是并行安全的,并且必须具有合并函数。如果该聚合具有类型为 internal 的转移状态,那么它还必须具有序列化和反序列化函数。更多细节请参见 。如果任何聚合函数调用包含 DISTINCTORDER BY 子句,则不支持并行聚合。对于有序集聚合,或者当查询涉及 GROUPING SETS 时,也不支持并行聚合。只有当查询涉及的所有连接也都属于计划并行部分时,才能使用并行聚合。 + + + + + + 并行计划提示 + + + 如果一个本来预期会生成并行计划的查询却没有生成并行计划,可以尝试降低 。当然,这样得到的计划可能会比规划器原本偏好的串行计划更慢,但并不总是如此。如果即使把这些设置调得很低(例如都设为零)之后仍然得不到并行计划,那么可能存在某些原因使查询规划器无法为该查询生成并行计划。关于可能的原因,请参见 。 + + + + 在执行并行计划时,可以使用 EXPLAIN (ANALYZE, VERBOSE) 显示每个计划节点的逐工作进程统计信息。这有助于判断工作是否在各个计划节点之间均匀分布,以及更全面地理解该计划的性能特征。 + + + + + + + 并行安全性 + + + 规划器会将查询中涉及的操作分类为并行安全并行受限并行不安全。并行安全的操作不会与并行查询的使用产生冲突。并行受限的操作不能在并行工作进程中执行,但可以在启用并行查询时由领导者执行。因此,并行受限的操作绝不能出现在 Gather 节点之下,但可以出现在包含 Gather 节点的计划的其他位置。并行不安全的操作在并行查询启用时完全不能执行,连领导者中也不行。当一个查询包含任何并行不安全的内容时,并行查询对该查询会被完全禁用。 + + + + 下列操作总是并行受限的: + + + + + + 公共表表达式(CTE)的扫描。 + + + + + + 临时表的扫描。 + + + + + + 外部表的扫描,除非外部数据包装器提供了 IsForeignScanParallelSafe API,并明确指出并行执行是安全的。 + + + + + 访问 InitPlanSubPlan + + + + + 为函数和聚合指定并行标签 + + + 规划器无法自动判断一个用户定义的函数或聚合究竟是并行安全、并行受限还是并行不安全,因为这需要预测该函数可能执行的每一种操作。一般来说,这等价于停机问题,因此是不可能做到的。即使对于那些理论上可能判定出来的简单函数,我们也不会尝试,因为那样做代价高昂而且容易出错。相反,所有用户定义的函数都会被假定为并行不安全,除非另有标记。在使用 时,可以通过指定 PARALLEL SAFEPARALLEL RESTRICTEDPARALLEL UNSAFE 来设置标记。在使用 时,可以将 PARALLEL 选项的值指定为 SAFERESTRICTEDUNSAFE。 + + + + 如果函数或聚合会写入数据库、访问序列、修改事务状态(即使只是临时修改,例如 PL/pgsql 函数建立 EXCEPTION 块来捕获错误),或者对设置作出持久更改,那么它们必须标记为 PARALLEL UNSAFE。类似地,如果函数访问临时表、客户端连接状态、游标、预备语句,或者系统无法在工作进程之间同步的各种后端本地状态,那么它必须标记为 PARALLEL RESTRICTED。例如,setseedrandom 就因为最后一个原因而属于并行受限。 + + + + 一般来说,如果某个函数实际上是受限或不安全的,却被标记为安全,或者实际上是不安全的,却被标记为受限,那么在并行查询中使用它时可能会抛出错误,或者产生错误结果。如果 C 语言函数被错误标记,理论上它甚至可能表现出完全未定义的行为,因为系统无法保护自己免受任意 C 代码的影响。不过,在最可能发生的情况下,结果通常也不会比其他任何函数更糟。如果有疑虑,最好还是将函数标记为 UNSAFE。 + + + + 如果在并行工作进程中执行的函数获取了领导者并未持有的锁,例如通过查询该查询中未引用的表,那么这些锁会在工作进程退出时释放,而不是在事务结束时释放。如果你编写了这样一个函数,并且这种行为差异对你很重要,那么应将此类函数标记为 PARALLEL RESTRICTED,以确保它们只在领导者中执行。 + + + + 请注意,查询规划器不会为了得到更优的计划,而考虑推迟计算查询中涉及的并行受限函数或聚合。因此,如果某个应用于特定表的 WHERE 子句是并行受限的,查询规划器就不会考虑把对该表的扫描放在 Gather 节点之下。在某些情况下,把该表扫描纳入查询的并行部分,并将 WHERE 子句的计算推迟到 Gather 节点之上,可能是可行的,甚至更高效。然而,规划器不会这样做。 + + + + + + + diff --git a/zh/9.6/passwordcheck.sgml b/zh/9.6/passwordcheck.sgml new file mode 100644 index 00000000..ed2e2836 --- /dev/null +++ b/zh/9.6/passwordcheck.sgml @@ -0,0 +1,53 @@ + + + + passwordcheck + + + passwordcheck + + + + 每当通过 设置用户密码时, + passwordcheck 模块都会检查密码强度。如果密码被认为过弱,就会被拒绝, + 该命令也会因错误而终止。 + + + + 要启用该模块,请将 '$libdir/passwordcheck' 加入 + postgresql.conf 中的 , + 然后重启服务器。 + + + + 可以通过修改源代码使该模块适应你的需求。例如,可以使用 + CrackLib 来检查密码, + 这只需要在 Makefile 中取消两行注释并重新构建该模块。 + (由于许可原因,我们不能默认包含 CrackLib。) + 如果不使用 CrackLib,该模块会对密码强度施加一些简单规则, + 而这些规则也可以按需修改或扩展。 + + + + + + 为了防止未加密的密码通过网络传输、被写入服务器日志,或以其他方式被数据库管理员窃取, + PostgreSQL允许用户提供预加密密码。 + 很多客户端程序都会利用这一能力,在将密码发送给服务器之前先行加密。 + + + + 这就限制了 passwordcheck 模块的作用,因为在这种情况下, + 它只能尝试猜测密码。因此,如果安全要求很高,并不建议使用 + passwordcheck。与其依赖数据库内部的密码,不如使用 + GSSAPI 之类的外部认证方法(见 ), + 那样更安全。 + + + + 另外,也可以修改 passwordcheck 以拒绝预加密密码, + 但强迫用户以明文设置密码本身也会带来安全风险。 + + + + diff --git a/zh/9.6/perform.sgml b/zh/9.6/perform.sgml new file mode 100644 index 00000000..64cd4dff --- /dev/null +++ b/zh/9.6/perform.sgml @@ -0,0 +1,888 @@ + + + + 性能提示 + + + performance + + + + 查询性能可能受许多因素影响。其中一些因素可以由用户控制,另一些则属于系统底层设计的基本特性。本章提供一些帮助理解和调优PostgreSQL性能的提示。 + + + + 使用<command>EXPLAIN</command> + + + EXPLAIN + + + + query plan + + + + PostgreSQL会为收到的每个查询制定一个查询计划。选择与查询结构和数据特性相匹配的正确计划,对获得良好的性能至关重要,因此系统内置了一个复杂的规划器来尽量选出好的计划。可以使用命令查看规划器为任意查询生成的查询计划。读懂计划是一门需要经验积累的技能,本节将尝试介绍其中的基础知识。 + + + + 本节中的示例取自执行过VACUUM ANALYZE的回归测试数据库,使用的是 9.3 开发版本源码。如果自行尝试这些示例,通常应当能得到相近的结果,但估计代价和行计数可能略有差异,因为ANALYZE生成的统计信息来自随机采样而非精确计数,而且代价本身在某种程度上也依赖于平台。 + + + + 这些示例使用EXPLAIN默认的text输出格式,它紧凑且便于人工阅读。如果希望将EXPLAIN的输出交给程序做进一步分析,则应改用机器可读的输出格式(XML、JSON 或 YAML)。 + + + + <command>EXPLAIN</command>基础 + + + 查询计划的结构是一棵由计划节点组成的树。树的最底层是扫描节点,它们从表中返回原始行。不同的表访问方法对应不同类型的扫描节点,例如顺序扫描、索引扫描和位图索引扫描。也有一些并非表的行来源,例如VALUES子句和FROM中的返回集合函数,它们也各自有对应的扫描节点类型。如果查询需要对原始行执行连接、聚合、排序或其他操作,那么扫描节点之上还会出现额外的节点来完成这些操作。同样,这些操作通常不止一种实现方式,因此这里也会出现不同的节点类型。EXPLAIN会为计划树中的每个节点输出一行,显示基本节点类型以及规划器对该计划节点执行代价的估计值。还可能出现相对节点摘要行缩进的附加行,用来显示该节点的更多属性。第一行,也就是最顶层节点的摘要行,给出了整个计划的估计总执行代价;规划器力图最小化的正是这个数字。 + + + 下面是一个简单的示例,仅用于展示输出的样子: +EXPLAIN SELECT * FROM tenk1; + + QUERY PLAN +------------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244) + + + + + 由于这个查询没有WHERE子句,它必须扫描表中的所有行,因此规划器选择了一个简单的顺序扫描计划。圆括号中的数字从左到右依次表示: + + + + + 估计启动开销。这是输出阶段开始前需要消耗的时间,例如排序节点执行排序所需的时间。 + + + + + + 估计总开销。这里假定该计划节点会运行到结束,也就是取回所有可用的行。实际中某个节点的父节点可能会在尚未读完所有可用行之前提前停止(见后文的LIMIT示例)。 + + + + + + 该计划节点输出行数的估计值。同样,也是假定该节点会运行到结束。 + + + + + + 预计该计划节点输出行的平均宽度,以字节计。 + + + + + + + 这些开销使用由规划器代价参数决定的任意单位来衡量(见)。传统上通常以磁盘页读取作为代价单位;也就是说,惯例上将设为1.0,其他代价参数都相对它来设定。本节中的示例都使用默认代价参数。 + + + + 需要理解的一点是,上层节点的开销包含了其所有子节点的开销。还要注意,这个开销只反映规划器关心的内容。特别是,它没有考虑将输出值转换为文本形式或传输给客户端所消耗的时间,而这些在实际耗时中可能是重要因素;但规划器会忽略这些代价,因为它无法通过改变计划来影响它们。(我们相信每个正确的计划都会输出相同的行集。) + + + + rows值有些容易误解,因为它不是计划节点处理或扫描过的行数,而是该节点输出的行数。由于在该节点上应用的WHERE条件会过滤掉一部分扫描到的行,这个数字通常小于扫描行数。理想情况下,顶层的行数估计应当接近查询实际返回、更新或删除的行数。 + + + 回到我们的示例: +EXPLAIN SELECT * FROM tenk1; + + QUERY PLAN +------------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244) + + + + + 这些数字的来源非常直接。如果执行: + + +SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1'; + + + 可以看到tenk1有 345 个磁盘页和 10000 行。估计代价按如下公式计算:(读取的磁盘页数 * )+(扫描的行数 * )。默认情况下,seq_page_cost为 1.0,cpu_tuple_cost为 0.01,因此估计代价就是 (345 * 1.0) + (10000 * 0.01) = 445。 + + + 现在修改查询,添加一个WHERE条件: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000; + + QUERY PLAN +------------------------------------------------------------ + Seq Scan on tenk1 (cost=0.00..483.00 rows=7001 width=244) + Filter: (unique1 < 7000) +注意,EXPLAIN的输出表明,WHERE子句被作为一个过滤器条件附加到 Seq Scan 计划节点上。这表示计划节点会对扫描到的每一行检查该条件,并且只输出满足条件的行。由于有了WHERE子句,估计输出行数减少了。但扫描仍需访问全部 10000 行,因此代价没有下降;实际上还略有上升(精确地说,增加了 10000 *),以反映检查WHERE条件所消耗的额外 CPU 时间。 + + + 这条查询实际选出的行数是 7000,但估计的rows只是近似值。如果重复这个实验,很可能会得到略有不同的估计值。此外,由于ANALYZE生成的统计信息来自该表的随机采样,这个估计值可能在每次执行ANALYZE之后发生变化。 + + + 现在让条件更严格一些: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100; + + QUERY PLAN +------------------------------------------------------------------------------ + Bitmap Heap Scan on tenk1 (cost=5.07..229.20 rows=101 width=244) + Recheck Cond: (unique1 < 100) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) + Index Cond: (unique1 < 100) +这里规划器决定使用两步计划:子计划节点访问索引,找出匹配索引条件的行的位置,然后上层计划节点再从表本身取出这些行。分别取出各行的代价远高于顺序读取,但由于不必访问表的所有页,这仍然比顺序扫描便宜。(使用两层计划的原因是,上层计划节点在读取行之前,会先把索引确定的行位置按物理顺序排列,以尽量降低逐行读取的代价。节点名称中的位图就是执行这种排序的机制。) + + 现在向WHERE子句添加另一个条件: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx'; + + QUERY PLAN +------------------------------------------------------------------------------ + Bitmap Heap Scan on tenk1 (cost=5.04..229.43 rows=1 width=244) + Recheck Cond: (unique1 < 100) + Filter: (stringu1 = 'xxx'::name) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) + Index Cond: (unique1 < 100) +新增的条件stringu1 = 'xxx'降低了估计输出行数,却没有降低代价,因为仍然必须访问同一组行。注意,stringu1子句不能用作索引条件,因为该索引只建立在unique1列上。它只能作为过滤条件,应用到通过索引取出的行上。因此,代价实际上略有增加,以反映这项额外检查。 + + 在某些情况下,规划器更倾向于一个简单索引扫描计划: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42; + + QUERY PLAN +----------------------------------------------------------------------------- + Index Scan using tenk1_unique1 on tenk1 (cost=0.29..8.30 rows=1 width=244) + Index Cond: (unique1 = 42) +在这种计划中,表行按索引顺序取出,因此读取代价更高,但行数很少,不值得为排序行位置付出额外代价。对于仅取出一行的查询,最常见到这种计划。它也经常用于带有以下条件的查询:ORDER BY与索引顺序匹配,因为这时无需额外排序步骤就能满足ORDER BY。 + + + 如果在WHERE引用的多个列上分别建有索引,规划器可能选择将这些索引做 AND 或 OR 组合: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000; + + QUERY PLAN +------------------------------------------------------------------------------------- + Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244) + Recheck Cond: ((unique1 < 100) AND (unique2 > 9000)) + -> BitmapAnd (cost=25.08..25.08 rows=10 width=0) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) + Index Cond: (unique1 < 100) + -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) + Index Cond: (unique2 > 9000) +但这需要访问两个索引,因此与只使用一个索引、把另一个条件作为过滤条件相比,并不一定更划算。如果调整条件中的范围,就会看到计划相应改变。 + + 下面用一个示例说明LIMIT: + + +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2; + + QUERY PLAN +------------------------------------------------------------------------------------- + Limit (cost=0.29..14.48 rows=2 width=244) + -> Index Scan using tenk1_unique2 on tenk1 (cost=0.29..71.27 rows=10 width=244) + Index Cond: (unique2 > 9000) + Filter: (unique1 < 100) + + + + + 这与上面的查询相同,只是加上了LIMIT,因此不必检索全部行,规划器也就改变了选择。注意,Index Scan 节点的总代价和行计数显示得像是它会运行到结束一样;但 Limit 节点预计在只取到其中五分之一的行后就会停止,因此它的总代价也只有前者的五分之一,这才是该查询真正的估计代价。之所以更偏好这个计划,而不是在前一个计划之上再加一个 Limit 节点,是因为后者仍然无法避免位图扫描的启动代价,那样总代价仍会高于 25。 + + + 使用之前讨论的列,尝试连接两个表: +EXPLAIN SELECT * +FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; + + QUERY PLAN +-------------------------------------------------------------------------------------- + Nested Loop (cost=4.65..118.62 rows=10 width=488) + -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244) + Recheck Cond: (unique1 < 10) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0) + Index Cond: (unique1 < 10) + -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244) + Index Cond: (unique2 = t1.unique2) + + + + + 在这个计划中,有一个嵌套循环连接节点,它的两个输入,也就是两个子节点,都是表扫描。节点摘要行的缩进反映了计划树结构。连接的第一个子节点,也就是外侧子节点,是一个与前面见过的位图扫描类似的节点。它的代价和行计数与SELECT ... WHERE unique1 < 10得到的结果相同,因为WHERE子句unique1 < 10正是在该节点上应用的。t1.unique2 = t2.unique2子句此时还无关,因此不会影响外侧扫描的行计数。嵌套循环连接节点会对从外侧子节点得到的每一行执行一次第二个,也就是内侧子节点。当前外侧行中的列值可以代入内侧扫描;这里外侧行的t1.unique2值可用,因此得到的计划和代价与前面看到的简单SELECT ... WHERE t2.unique2 = constant情形类似。(由于预期在对t2反复执行索引扫描期间会发生缓存命中,估计代价实际上比前面看到的略低一些。)随后,循环节点的代价建立在外侧扫描代价之上,再加上每个外侧行都要执行一次内侧扫描的代价(这里是 10 * 7.91),以及少量连接处理的 CPU 时间。 + + + 本例中,连接的输出行数等于两个扫描行数的乘积,但并非所有情况都如此,因为可能还有额外的WHERE子句同时涉及两个表,因此只能在连接处应用,不能应用到任一输入扫描。下面是一个示例: +EXPLAIN SELECT * +FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 10 AND t2.unique2 < 10 AND t1.hundred < t2.hundred; + + QUERY PLAN +--------------------------------------------------------------------------------------------- + Nested Loop (cost=4.65..49.46 rows=33 width=488) + Join Filter: (t1.hundred < t2.hundred) + -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244) + Recheck Cond: (unique1 < 10) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0) + Index Cond: (unique1 < 10) + -> Materialize (cost=0.29..8.51 rows=10 width=244) + -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..8.46 rows=10 width=244) + Index Cond: (unique2 < 10) +条件t1.hundred < t2.hundred不能在tenk2_unique2索引中检验,因此应用在连接节点上。这会减少连接节点的估计输出行数,但不改变任一输入扫描。 + + + 注意,这里规划器通过在连接的内侧关系之上放置一个 Materialize 计划节点,选择将其物化。这意味着t2索引扫描只会执行一次,尽管嵌套循环连接节点需要读取那份数据十次,也就是外侧关系的每一行都要读取一次。Materialize 节点会在读取数据时将其保存在内存中,并在之后的每次遍历中从内存返回这些数据。 + + + + 在处理外连接时,可能会看到连接计划节点同时带有Join Filter和普通Filter条件。Join Filter 条件来自外连接的ON子句,因此某一行即使未通过 Join Filter,仍可能作为一条补齐空值的行被输出。但普通 Filter 条件是在外连接规则应用之后再执行的,因此会无条件移除行。在内连接中,这两类过滤条件在语义上没有区别。 + + + 如果稍微改变查询的选择率,可能得到完全不同的连接计划: +EXPLAIN SELECT * +FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; + + QUERY PLAN +------------------------------------------------------------------------------------------ + Hash Join (cost=230.47..713.98 rows=101 width=488) + Hash Cond: (t2.unique2 = t1.unique2) + -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244) + -> Hash (cost=229.20..229.20 rows=101 width=244) + -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244) + Recheck Cond: (unique1 < 100) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) + Index Cond: (unique1 < 100) + + + + + 这里规划器选择了哈希连接:先把一个表的行放入内存中的哈希表,然后扫描另一个表,并对其中每一行到哈希表中查找匹配。再次注意缩进如何反映计划结构:tenk1上的位图扫描是 Hash 节点的输入,Hash 节点据此构造哈希表;随后该哈希表被返回给 Hash Join 节点,后者从其外侧子计划读取行,并对每一行在哈希表中进行查找。 + + + 另一种可能的连接类型是归并连接,如下所示: +EXPLAIN SELECT * +FROM tenk1 t1, onek t2 +WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; + + QUERY PLAN +------------------------------------------------------------------------------------------ + Merge Join (cost=198.11..268.19 rows=10 width=488) + Merge Cond: (t1.unique2 = t2.unique2) + -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244) + Filter: (unique1 < 100) + -> Sort (cost=197.83..200.33 rows=1000 width=244) + Sort Key: t2.unique2 + -> Seq Scan on onek t2 (cost=0.00..148.00 rows=1000 width=244) + + + + 归并连接要求输入数据按连接键排序。在这个计划中,tenk1 的数据通过索引扫描按正确顺序访问行来完成排序,而 onek 则更适合顺序扫描后再排序,因为需要访问该表中的更多行。(对大量行进行排序时,顺序扫描加排序往往优于索引扫描,因为索引扫描需要非顺序的磁盘访问。) + + 查看其他候选计划的一种方法,是使用中介绍的启用/禁用标志,强制规划器忽略它认为代价最低的策略。(这是一种粗略但有用的工具。另请参见。)例如,如果不确信顺序扫描加排序是处理上例中onek表的最佳方法,可以尝试: +SET enable_sort = off; + +EXPLAIN SELECT * +FROM tenk1 t1, onek t2 +WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; + + QUERY PLAN +------------------------------------------------------------------------------------------ + Merge Join (cost=0.56..292.65 rows=10 width=488) + Merge Cond: (t1.unique2 = t2.unique2) + -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244) + Filter: (unique1 < 100) + -> Index Scan using onek_unique2 on onek t2 (cost=0.28..224.79 rows=1000 width=244) +这表明规划器认为,通过索引扫描来排序onek,代价比顺序扫描加排序大约高 12%。当然,接下来的问题是它的判断是否正确。可以使用EXPLAIN ANALYZE来调查,下文将详细说明。 + + + + + <command>EXPLAIN ANALYZE</command> + + 可以使用EXPLAINANALYZE选项检查规划器估计的准确性。启用此选项后,EXPLAIN会实际执行查询,然后显示各个计划节点内累计的实际行数和实际运行时间,同时也显示普通EXPLAIN提供的估计值。例如,可能得到如下结果: +EXPLAIN ANALYZE SELECT * +FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; + + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------- + Nested Loop (cost=4.65..118.62 rows=10 width=488) (actual time=0.128..0.377 rows=10 loops=1) + -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244) (actual time=0.057..0.121 rows=10 loops=1) + Recheck Cond: (unique1 < 10) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0) (actual time=0.024..0.024 rows=10 loops=1) + Index Cond: (unique1 < 10) + -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244) (actual time=0.021..0.022 rows=1 loops=10) + Index Cond: (unique2 = t1.unique2) + Planning time: 0.181 ms + Execution time: 0.501 ms +注意,actual time的值以实际时间的毫秒数表示,而cost估计值使用任意单位,因此两者不太可能相等。通常最重要的是检查估计行数是否足够接近实际行数。在本例中,所有估计都完全准确,但实际中很少如此。 + + + 在某些查询计划中,一个子计划节点可能会执行多次。例如,上面那个嵌套循环计划中的内侧索引扫描会对外侧的每一行执行一次。在这种情况下,loops值报告的是该节点的总执行次数,而 actual time 和 rows 显示的是每次执行的平均值。这样做是为了让这些数字更容易与代价估计的展示方式相比较。将它们乘以loops值,就能得到该节点实际消耗的总时间。在上面的示例中,执行tenk2上的索引扫描总共花费了 0.220 毫秒。 + + + 在某些情况下,EXPLAIN ANALYZE除了计划节点的执行时间和行数之外,还会显示额外的执行统计信息。例如,Sort 和 Hash 节点会提供附加信息: +EXPLAIN ANALYZE SELECT * +FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2 ORDER BY t1.fivethous; + + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------- + Sort (cost=717.34..717.59 rows=101 width=488) (actual time=7.761..7.774 rows=100 loops=1) + Sort Key: t1.fivethous + Sort Method: quicksort Memory: 77kB + -> Hash Join (cost=230.47..713.98 rows=101 width=488) (actual time=0.711..7.427 rows=100 loops=1) + Hash Cond: (t2.unique2 = t1.unique2) + -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244) (actual time=0.007..2.583 rows=10000 loops=1) + -> Hash (cost=229.20..229.20 rows=101 width=244) (actual time=0.659..0.659 rows=100 loops=1) + Buckets: 1024 Batches: 1 Memory Usage: 28kB + -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244) (actual time=0.080..0.526 rows=100 loops=1) + Recheck Cond: (unique1 < 100) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.049..0.049 rows=100 loops=1) + Index Cond: (unique1 < 100) + Planning time: 0.194 ms + Execution time: 8.008 ms +Sort 节点显示所用的排序方法(尤其是排序在内存中还是磁盘上进行),以及所需的内存或磁盘空间。Hash 节点显示哈希桶数、批次数,以及哈希表的内存用量峰值。(如果批次数超过一,还会使用磁盘空间,但此处不显示。) + + 另一种附加信息是被过滤条件排除的行数: +EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE ten < 7; + + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..483.00 rows=7000 width=244) (actual time=0.016..5.107 rows=7000 loops=1) + Filter: (ten < 7) + Rows Removed by Filter: 3000 + Planning time: 0.083 ms + Execution time: 5.905 ms +对于应用在连接节点上的过滤条件,这些计数尤其有价值。只有当至少一个扫描行(对于连接节点,则是一个潜在连接行对)被过滤条件排除时,才会出现Rows Removed这一行。 + + 有损索引扫描中,也会出现类似于过滤条件的情况。例如,考虑下面这个查找包含指定点的多边形的查询: +EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)'; + + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Seq Scan on polygon_tbl (cost=0.00..1.05 rows=1 width=32) (actual time=0.044..0.044 rows=0 loops=1) + Filter: (f1 @> '((0.5,2))'::polygon) + Rows Removed by Filter: 4 + Planning time: 0.040 ms + Execution time: 0.083 ms +规划器认为(而且完全正确)这个示例表太小,不值得使用索引扫描,因此得到的是普通顺序扫描,其中所有行都被过滤条件排除。但如果强制使用索引扫描,就会看到: +SET enable_seqscan TO off; + +EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)'; + + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Index Scan using gpolygonind on polygon_tbl (cost=0.13..8.15 rows=1 width=32) (actual time=0.062..0.062 rows=0 loops=1) + Index Cond: (f1 @> '((0.5,2))'::polygon) + Rows Removed by Index Recheck: 1 + Planning time: 0.034 ms + Execution time: 0.144 ms +这里可以看到,索引返回了一个候选行,但随后在重新检查索引条件时被排除。这是因为 GiST 索引对于多边形包含关系测试是有损的:它实际返回的是包含与目标重叠的多边形的行,然后必须对这些行进行精确的包含关系测试。 + + + EXPLAIN有一个BUFFERS选项,可以与ANALYZE一起使用,以获取更多运行时统计信息: +EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000; + + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244) (actual time=0.323..0.342 rows=10 loops=1) + Recheck Cond: ((unique1 < 100) AND (unique2 > 9000)) + Buffers: shared hit=15 + -> BitmapAnd (cost=25.08..25.08 rows=10 width=0) (actual time=0.309..0.309 rows=0 loops=1) + Buffers: shared hit=7 + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1) + Index Cond: (unique1 < 100) + Buffers: shared hit=2 + -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) (actual time=0.227..0.227 rows=999 loops=1) + Index Cond: (unique2 > 9000) + Buffers: shared hit=5 + Planning time: 0.088 ms + Execution time: 0.423 ms +BUFFERS提供的数值有助于找出查询中 I/O 最密集的部分。 + + 请记住,由于EXPLAIN ANALYZE会实际执行查询,任何副作用都会照常发生,即使查询原本输出的结果被丢弃,改为打印EXPLAIN数据。如果想分析会修改数据的查询而不改变表,可以在之后回滚命令,例如: +BEGIN; + +EXPLAIN ANALYZE UPDATE tenk1 SET hundred = hundred + 1 WHERE unique1 < 100; + + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Update on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=14.628..14.628 rows=0 loops=1) + -> Bitmap Heap Scan on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=0.101..0.439 rows=100 loops=1) + Recheck Cond: (unique1 < 100) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1) + Index Cond: (unique1 < 100) + Planning time: 0.079 ms + Execution time: 14.727 ms + +ROLLBACK; + + + + + 如本例所示,当查询是INSERTUPDATEDELETE命令时,真正执行表修改的工作由顶层的 Insert、Update或 Delete 计划节点完成。该节点下面的计划节点负责定位旧行和/或计算新数据。因此,上面看到的是前文已经介绍过的同类位图表扫描,它的输出被送入一个负责存储更新后行的 Update 节点。值得注意的是,虽然数据修改节点可能占用相当可观的运行时间(这里它消耗了大部分时间),但规划器目前不会为这部分工作额外增加任何代价估计。这是因为这项工作对每个正确的查询计划都相同,因此不会影响规划决策。 + + + 当一个UPDATEDELETE命令影响到继承层次结构时,输出可能如下所示: +EXPLAIN UPDATE parent SET f2 = f2 + 1 WHERE f1 = 101; + QUERY PLAN +----------------------------------------------------------------------------------- + Update on parent (cost=0.00..24.53 rows=4 width=14) + Update on parent + Update on child1 + Update on child2 + Update on child3 + -> Seq Scan on parent (cost=0.00..0.00 rows=1 width=14) + Filter: (f1 = 101) + -> Index Scan using child1_f1_key on child1 (cost=0.15..8.17 rows=1 width=14) + Index Cond: (f1 = 101) + -> Index Scan using child2_f1_key on child2 (cost=0.15..8.17 rows=1 width=14) + Index Cond: (f1 = 101) + -> Index Scan using child3_f1_key on child3 (cost=0.15..8.17 rows=1 width=14) + Index Cond: (f1 = 101) +在本例中,Update 节点需要考虑三个子表,以及最初指定的父表。因此有四个输入扫描子计划,每个表一个。为清晰起见,Update 节点标注了将被更新的具体目标表,顺序与相应子计划一致。(这些标注是在PostgreSQL9.5 中新增的;在更早的版本中,读者必须通过检查子计划来推断目标表。) + + + EXPLAIN ANALYZE显示的Planning time,是从已解析的查询生成查询计划并完成优化所花费的时间;其中不包括解析和重写。 + + + + EXPLAIN ANALYZE显示的Execution time包括执行器启动和关闭所需的时间,以及运行所有已触发触发器所花费的时间,但不包括解析、重写和规划时间。若存在BEFORE触发器,其执行时间会计入相关的 Insert、Update 或 Delete 节点;而AFTER触发器的时间不会计入那里,因为AFTER触发器是在整个计划完成之后才触发的。每个触发器,无论是BEFORE还是AFTER,其总耗时也会单独显示。注意,延迟约束触发器要到事务结束时才会执行,因此EXPLAIN ANALYZE完全不会把它们计入其中。 + + + + + + 注意事项 + + + EXPLAIN ANALYZE测得的运行时间,可能以两种重要方式偏离同一查询的正常执行时间。第一,由于不会向客户端传递任何输出行,因此网络传输开销和 I/O 转换开销均不会被计入。第二,EXPLAIN ANALYZE附加的测量开销本身可能相当可观,尤其是在操作系统调用gettimeofday()较慢的机器上。可以使用工具来测量系统上的计时开销。 + + + + 不应将EXPLAIN结果外推到与实际测试场景差异很大的情况。例如,在一个很小的表上得到的结果,不能假定也适用于大型表。规划器的代价估计不是线性的,因此它可能会为更大或更小的表选择不同的计划。一个极端示例是:对于只占用一个磁盘页的表,无论索引是否可用,几乎总会得到顺序扫描计划。规划器认识到,无论如何处理该表都需要一次磁盘页读取,因此再额外读取页面去查看索引并没有价值。(前面的polygon_tbl示例已经展示过这种情况。) + + + 有时实际值与估计值不太一致,但实际上并没有问题。其中一种情况是,计划节点的执行因LIMIT或类似效果而提前停止。例如,在前面使用的LIMIT查询中: +EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2; + + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------- + Limit (cost=0.29..14.71 rows=2 width=244) (actual time=0.177..0.249 rows=2 loops=1) + -> Index Scan using tenk1_unique2 on tenk1 (cost=0.29..72.42 rows=10 width=244) (actual time=0.174..0.244 rows=2 loops=1) + Index Cond: (unique2 > 9000) + Filter: (unique1 < 100) + Rows Removed by Filter: 287 + Planning time: 0.096 ms + Execution time: 0.336 ms +Index Scan 节点的估计代价和行数按该节点执行到完成的情况显示。但实际上,Limit 节点取得两行后就停止请求行,因此实际行数只有 2,运行时间也小于估计代价所暗示的时间。这不是估计错误,只是估计值与实际值的显示方式存在差别。 + + + 归并连接也会产生一些容易误导人的计量现象。如果归并连接已经耗尽其中一个输入,而另一个输入中的下一个键值又大于前一个输入的最后一个键值,那么它就会停止继续读取前者;在这种情况下,不可能再有更多匹配,因此也就不需要扫描另一个输入的剩余部分。这会导致某个子节点没有被完整读取,其结果与前面提到的LIMIT情况类似。此外,如果外侧(第一个)子节点包含带有重复键值的行,内侧(第二个)子节点会被回退并重新扫描,以查找能够匹配该键值的行。EXPLAIN ANALYZE会把这些对同一内侧行的重复输出,统计得像是真实的额外行一样。当外侧存在大量重复值时,内侧子计划节点报告出的实际行计数,可能会明显大于内侧关系中真实存在的行数。 + + + + 由于实现上的限制,BitmapAnd 和 BitmapOr 节点总是报告其实际行计数为零。 + + + + + + + 规划器使用的统计信息 + + + statistics + of the planner + + + + 如上一节所见,查询规划器需要估计查询将检索多少行,才能对查询计划做出良好选择。本节简要介绍系统用于这些估计的统计信息。 + + + 统计信息的一部分是各个表和索引的条目总数,以及各个表和索引占用的磁盘块数。这些信息保存在表pg_classreltuplesrelpages列中。可以使用类似下面的查询来查看: +SELECT relname, relkind, reltuples, relpages +FROM pg_class +WHERE relname LIKE 'tenk1%'; + + relname | relkind | reltuples | relpages +----------------------+---------+-----------+---------- + tenk1 | r | 10000 | 358 + tenk1_hundred | i | 10000 | 30 + tenk1_thous_tenthous | i | 10000 | 30 + tenk1_unique1 | i | 10000 | 30 + tenk1_unique2 | i | 10000 | 30 +(5 rows) +这里可以看到,tenk1包含 10000 行,它的索引也一样,但索引比表小得多(这并不意外)。 + + + 出于效率考虑,reltuplesrelpages不会实时更新,因此它们通常包含有些过时的值。它们会在VACUUMANALYZE以及少数 DDL 命令(如CREATE INDEX)执行时被更新。不扫描全表的VACUUMANALYZE操作(这很常见)会根据其实际扫描到的那一部分增量更新reltuples计数,因此得到的是近似值。无论如何,规划器都会将它在pg_class中找到的值按当前物理表大小进行缩放,从而得到更接近实际情况的近似值。 + + + + pg_statistic + + + + 大多数查询只会检索表中一部分行,因为它们通过WHERE子句限制了需要检查的行。因此,规划器需要估算WHERE子句的选择度,也就是满足WHERE子句中各个条件的行所占比例。完成这项任务所需的信息存储在pg_statistic系统目录中。pg_statistic中的条目由ANALYZEVACUUM ANALYZE命令更新,而且即使刚更新完,也始终只是近似值。 + + + + pg_stats + + + 手动检查统计信息时,与其直接查看pg_statistic,不如查看它的视图pg_statspg_stats旨在让统计信息更易读。此外,pg_stats所有用户都可以读取,而pg_statistic只有超级用户可以读取。(这可以防止没有相应权限的用户通过统计信息获知其他用户表中的内容。pg_stats视图被限制为只显示当前用户有权读取的表的相关行。)例如,可以执行: +SELECT attname, inherited, n_distinct, + array_to_string(most_common_vals, E'\n') as most_common_vals +FROM pg_stats +WHERE tablename = 'road'; + + attname | inherited | n_distinct | most_common_vals +---------+-----------+------------+------------------------------------ + name | f | -0.363388 | I- 580 Ramp+ + | | | I- 880 Ramp+ + | | | Sp Railroad + + | | | I- 580 + + | | | I- 680 Ramp + name | t | -0.284859 | I- 880 Ramp+ + | | | I- 580 Ramp+ + | | | I- 680 Ramp+ + | | | I- 580 + + | | | State Hwy 13 Ramp +(2 rows) +注意,同一列显示了两行,其中一行对应从road表开始的整个继承层次结构(inherited=t),另一行只包含road表本身(inherited=f)。 + + + + ANALYZEpg_statistic中存储的信息量,特别是每列most_common_vals中的最大项数和histogram_bounds数组的大小,可以使用ALTER TABLE SET STATISTICS命令按列设置,也可以通过设置配置变量进行全局设置。目前默认上限是 100 项。提高这一上限可能让规划器做出更准确的估计,尤其是对于数据分布不规则的列;代价则是pg_statistic占用更多空间,并且计算估计值所需时间也会略有增加。相反,对于数据分布较简单的列,较低的上限可能已经足够。 + + + + 更多规划器对统计信息的使用可参阅。 + + + + + + + 用显式<literal>JOIN</literal>子句控制规划器 + + + join + controlling the order + + + + 可以在一定程度上用显式JOIN语法控制查询规划器。要理解这一点为何重要,先需要一些背景知识。 + + + + 在一个简单的连接查询中,例如: + +SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id; + + 规划器可以自由地按任意顺序连接这些表。例如,它可以生成一个查询计划,先利用WHERE条件a.id = b.id将 A 连接到 B,然后再利用另一个WHERE条件把 C 连接到这个结果上。也可以先连接 B 和 C,再把 A 连接到所得结果上。甚至还可以先连接 A 和 C,再与 B 连接,但这样效率会很低,因为必须先形成 A 和 C 的完整笛卡尔积,而WHERE子句中并没有可用于优化这一连接的条件。(PostgreSQL执行器中的所有连接都发生在两个输入表之间,因此结果必须以这些形式之一逐步构造出来。)关键在于,这些不同的连接可能性在语义上是等价的,但执行代价可能相差极大。因此,规划器会探索它们,力图找出最高效的查询计划。 + + + + 当查询只涉及两个或三个表时,需要考虑的连接顺序并不多。但可能的连接顺序数量会随着表数增加而呈指数增长。输入表超过十个左右之后,对所有可能性做穷举搜索实际上就不再可行,甚至六七个表也可能让规划耗时长得令人厌烦。输入表过多时,PostgreSQL规划器会从穷举搜索切换到一种遗传概率搜索,只考虑有限数量的可能性。(切换阈值由运行时参数控制。)遗传搜索耗时更少,但并不一定能找到最优计划。 + + + + 当查询涉及外连接时,规划器比处理普通(内)连接时拥有更小的自由度。例如,考虑: + +SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id); + + 尽管这个查询的约束表面上与前一个非常相似,但它们的语义不同,因为如果 A 中有某一行无法匹配 B 和 C 连接结果中的任何行,该行仍然必须被输出。因此这里规划器对连接顺序没有选择:它必须先连接 B 和 C,再把 A 连接到该结果上。相应地,这个查询比前一个查询需要更少的规划时间。在其他情况下,规划器可能会判断多种连接顺序都是安全的。例如: + +SELECT * FROM a LEFT JOIN b ON (a.bid = b.id) LEFT JOIN c ON (a.cid = c.id); + + 将 A 首先连接到 B 或 C 都是有效的。当前,只有FULL JOIN完全约束连接顺序。大多数涉及LEFT JOINRIGHT JOIN的实际情况都在某种程度上可以被重新排列。 + + + + 显式连接语法(INNER JOINCROSS JOIN或无修饰的JOIN)在语义上和FROM中列出输入关系是一样的, 因此它不约束连接顺序。 + + + + 即使大多数类型的JOIN并不会完全约束连接顺序,仍然可以指示PostgreSQL查询规划器将所有JOIN子句都当作带有连接顺序约束来处理。例如,下面三个查询在逻辑上是等价的: + +SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id; +SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id; +SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id); + + 但如果告诉规划器遵循JOIN顺序,那么第二个和第三个查询在规划上会比第一个花费更少时间。对于只有三个表的连接来说,这种效果微不足道;但当表很多时,它可能非常关键。 + + + + 要强制规划器遵循显式JOIN给出的连接顺序,可以将运行时参数设置为 1。(其他可能值见下文讨论。) + + + + 不必为了缩短搜索时间而完全约束连接顺序,因为可以在普通FROM列表的某一项中使用JOIN操作符。例如: + +SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...; + + 如果设置join_collapse_limit = 1,就会强制规划器先将 A 连接到 B,然后再与其他表连接,但不会进一步约束它的选择。在这个示例中,可能的连接顺序数量减少了 5 倍。 + + + + 以这种方式约束规划器的搜索,是一种既可减少规划时间、又可引导规划器生成更好查询计划的实用技巧。如果规划器默认选择了糟糕的连接顺序,可以通过JOIN语法强制它采用更好的顺序,前提当然是确实知道哪个顺序更好。建议进行实验。 + + + + 与此密切相关、同样会影响规划时间的另一个问题,是将子查询折叠进父查询。例如: + +SELECT * +FROM x, y, + (SELECT * FROM a, b, c WHERE something) AS ss +WHERE somethingelse; + + 这种情况可能出现在使用包含连接的视图时;该视图的SELECT规则会被插入到引用视图的位置,从而得到一个与上面非常相似的查询。通常,规划器会尝试把子查询折叠进父查询,得到: + +SELECT * FROM x, y, a, b, c WHERE something AND somethingelse; + + 这通常会生成比单独规划子查询更好的计划。(例如,外层WHERE条件可能会先把 X 连接到 A,从而消除 A 中的大量行,也就避免了形成子查询完整逻辑输出的需要。)但与此同时,规划时间也增加了;这里把两个彼此独立的三路连接问题,替换成了一个五路连接问题。由于可能性数量呈指数增长,这种差别可能非常大。为了避免陷入巨大的连接搜索问题,如果折叠子查询会使父查询产生超过from_collapse_limitFROM项,规划器就会尝试通过停止提升子查询来规避这一点。可以通过调高或调低这个运行时参数,在规划时间和计划质量之间做权衡。 + + + + 名称相似,因为它们做的几乎是同一件事:一个控制规划器何时将子查询平面化,另一个控制何时将显式连接平面化。通常,要么把join_collapse_limit设置为与from_collapse_limit相同,这样显式连接与子查询的行为类似;要么把join_collapse_limit设置为 1,如果想使用显式连接控制连接顺序。不过,也可以把它们设置为不同的值,以更细致地调节规划时间与运行时间之间的平衡。 + + + + + 填充一个数据库 + + + 初次填充数据库时,可能需要插入大量数据。本节给出一些使这一过程尽可能高效的建议。 + + + + + 禁用自动提交 + + + autocommit + bulk-loading data + + + + 使用多个INSERT时,应关闭自动提交,只在最后提交一次。(在普通 SQL 中,这意味着开始时发出BEGIN,结束时发出COMMIT。某些客户端库可能会替调用方完成这件事,在这种情况下需要确认它们确实会在所需的时候这样做。)如果允许每次插入都单独提交,PostgreSQL就必须为每一行的加入执行大量额外工作。把所有插入都放在一个事务中的另一个好处是:如果其中某一行插入失败,那么此前插入的所有行都会被回滚,这样就不会留下部分装载的数据。 + + + + + 使用<command>COPY</command> + + + 使用在一条命令中装载所有记录,而不是使用一系列INSERT命令。COPY命令针对装载大量行做了优化;它不如INSERT灵活,但在大规模数据装载时开销显著更小。由于COPY是一条单独的命令,因此采用这种方法填充表时无须关闭自动提交。 + + + + 如果不能使用COPY,那么用创建一个预备INSERT语句,再按需多次执行EXECUTE也会有所帮助。这样可以避免重复解析和规划INSERT的开销。不同接口以不同方式提供这一功能,可参阅接口文档中关于预备语句的说明。 + + + + 请注意,在装载大量行时,使用COPY几乎总是比使用INSERT更快,即使已经使用了PREPARE,并把多次插入批量放入同一个事务中也是如此。 + + + + 当COPY与更早的CREATE TABLETRUNCATE命令处于同一事务中时,速度最快。在这种情况下,不需要写 WAL,因为一旦出错,包含新装载数据的文件反正也会被移除。不过,这一点只适用于minimal的非分区表;否则所有命令都必须写 WAL。 + + + + + + + 移除索引 + + + 如果正在装载一个新创建的表,最快的方法是先创建表,用COPY批量装载数据,然后再创建该表所需的索引。在已有数据的表上创建索引,要比在每行装载时对索引做增量更新更快。 + + + + 如果正在向现有表加入大量数据,那么删除索引、装载数据、再重建索引可能是更好的方案。当然,在索引缺失期间,其他数据库用户的性能可能会下降。删除唯一索引之前也必须慎重,因为唯一约束提供的错误检查会在索引缺失期间丧失。 + + + + + + 移除外键约束 + + + 与索引类似,批量检查外键约束比逐行检查更高效。因此,先删除外键约束、装载数据、再重建约束可能很有用。同样,这里也需要在装载速度与约束缺失期间失去错误检查之间做权衡。 + + + + 更重要的是,当在已有外键约束的情况下向表中装载数据时,每一行新数据都需要在服务器待处理的触发器事件列表中占一个条目,因为外键约束检查是通过触发器触发完成的。装载数百万行可能导致触发器事件队列溢出可用内存,造成无法接受的交换,甚至让命令直接失败。因此在装载大量数据时,删除并重新应用外键可能是必须的,而不仅仅是期望如此。如果不能临时移除约束,唯一的替代办法可能就是把装载操作拆分成更小的事务。 + + + + + + 增加<varname>maintenance_work_mem</varname> + + + 在装载大量数据时,临时增大配置变量可以提升性能。这个参数也有助于加速CREATE INDEXALTER TABLE ADD FOREIGN KEY命令。它对COPY本身帮助不大,因此这个建议只有在采用前述一种或两种技巧时才有意义。 + + + + + + 增加<varname>max_wal_size</varname> + + + 临时增大配置变量,也可以让大规模数据装载更快。这是因为向PostgreSQL中装载大量数据,会导致检查点比平常更频繁地发生,而正常频率由checkpoint_timeout配置变量指定。每次发生检查点时,所有脏页都必须刷写到磁盘。通过在批量装载期间临时增大max_wal_size,可以减少所需的检查点次数。 + + + + + 禁用 WAL 归档和流复制 + + + 当在使用 WAL 归档或流复制的安装中载入大量数据时,完成装载后重新做一次基础备份,可能比处理大量增量 WAL 数据更快。为了避免在装载期间记录这些增量 WAL,可以通过将设为minimal、将设为off,并将设为零,来禁用归档和流复制。但请注意,修改这些设置需要重启服务器。 + + 这样做除了省去归档进程或 WAL 发送进程处理 WAL 数据的时间外,还能使某些命令执行得更快,因为这些命令被设计为在wal_levelminimal时完全不写 WAL。(它们可以在结束时执行一次fsync,以比写 WAL 更低的代价保证崩溃安全性。)这适用于以下命令: + + + CREATE TABLE AS SELECT + + + + CREATE INDEX(以及 ALTER TABLE ADD PRIMARY KEY 等变体) + + + + ALTER TABLE SET TABLESPACE + + + + CLUSTER + + + COPY FROM,前提是目标表在同一事务中已被创建或截断 + + + + + + + 事后运行<command>ANALYZE</command> + + + 每当显著改变了表中数据的分布,都强烈建议运行。这也包括向表中批量装载大量数据。运行ANALYZE(或VACUUM ANALYZE)可以确保规划器掌握该表的最新统计信息。如果没有统计信息,或者统计信息已经过时,规划器在生成查询计划时就可能做出糟糕决定,从而导致相关表性能不佳。注意,如果启用了自动清理守护进程,它可能会自动运行ANALYZE;详见。 + + + + + 关于<application>pg_dump</application>的一些注记 + + + 由pg_dump生成的转储脚本会自动应用上面若干条指导原则,但并非全部。 + 若要尽可能快速地还原pg_dump的转储,仍需手动做一些额外操作。 + (注意,这些要点适用于还原转储,而不是创建转储。 + 无论是使用psql加载文本转储,还是使用pg_restorepg_dump归档文件加载,相关要点都是一样的。) + + + + 默认情况下,pg_dump使用COPY;而当它生成完整的模式加数据转储时,也会小心地先装载数据,再创建索引和外键。因此在这种情况下,上述若干指导原则已经被自动处理。剩下需要做的是: + + + + 为maintenance_work_memmax_wal_size设置适当的(即比正常值大的)值。 + + + + + 如果使用 WAL 归档或流复制,可以考虑在恢复期间禁用它们。为此,请在载入转储之前将archive_mode设为off、将wal_level设为minimal,并将max_wal_senders设为零。恢复完成后,再把这些设置改回正确的值,并重新做一次基础备份。 + + + + + 试验pg_dumppg_restore的并行转储与恢复模式,找出最优的并发任务数量。通过选项进行并行转储和恢复,通常会比串行模式获得高得多的性能。 + + + + + 考虑是否应当把整个转储作为单个事务来恢复。要这样做,请把命令行选项传给psqlpg_restore。使用这种模式时,即使是很小的错误也会回滚整个恢复过程,可能丢掉数小时的处理成果。视数据之间的关联程度而定,这种做法未必一定比手工清理更可取。如果使用单个事务并关闭 WAL 归档,COPY命令会运行得最快。 + + + + + 如果在数据库服务器上有多个 CPU 可用,可以考虑使用pg_restore选项。这允许并行数据载入和索引创建。 + + + + + 之后运行ANALYZE。 + + + + + + + 仅包含数据的转储仍然会使用COPY,但它不会删除或重建索引,通常也不会处理外键。 + + + + 可以通过使用选项达到禁用外键的效果 — 但要注意,这样做是取消外键验证,而不仅仅是推迟它。因此如果使用该选项,就有可能插入坏数据。 + + + + 因此,在装载纯数据转储时,如果想采用这些技术,就需要自行负责删除并重建索引与外键。装载数据期间增大max_wal_size仍然有益,但没有必要同时增大maintenance_work_mem;后者更适合留到之后手工重建索引和外键时再调大。完成后也别忘了执行ANALYZE;详见。 + + + + + + 非持久设置 + + + non-durable + + + 持久性是一种数据库特性,保证即使服务器崩溃或断电,已提交的事务也会被记录下来。但持久性会带来显著的数据库开销,因此如果你的应用场景不需要这种保证,可以将PostgreSQL配置为以快得多的速度运行。以下配置更改可以在这些情况下提高性能。除下文特别注明的情况外,数据库软件崩溃时仍然保证持久性;使用这些设置时,只有操作系统突然停止运行才会造成数据丢失或损坏的风险。 + + + 将数据库集簇的数据目录放在一个内存支持的文件系统上(即RAM磁盘)。这消除了所有的数据库磁盘 I/O,但将数据存储限制到可用的内存量(可能有交换区)。 + + + + + + 关闭;不需要将数据刷入磁盘。 + + + + + + 关闭;这样可能无需在每次提交时 + 强制将WAL写入磁盘。这种设置会在数据库崩溃时带来事务丢失的风险(但不会造成数据损坏)。 + + + + + + 关闭;不需要警惕部分页面写入。 + + + + + + 增加; + 这会降低检查点频率,但会增加/pg_wal的存储需求。 + + + + + + 创建不记录 WAL 的表 + 来避免WAL写入,不过这样会使表在崩溃时不再安全。 + + + + + + + + diff --git a/zh/9.6/pgbuffercache.sgml b/zh/9.6/pgbuffercache.sgml new file mode 100644 index 00000000..9c253312 --- /dev/null +++ b/zh/9.6/pgbuffercache.sgml @@ -0,0 +1,191 @@ + + + + pg_buffercache + + + pg_buffercache + + + + pg_buffercache 模块提供了一种手段,可实时检查共享缓冲区缓存中正在发生的情况。 + 它还提供了一种底层方式,可出于测试目的从中逐出数据。 + + + + pg_buffercache_pages + + + 该模块提供一个 C 函数pg_buffercache_pages,它返回一组记录;另外还提供一个视图pg_buffercache,该视图封装此函数以便使用。 + + + 默认情况下,这两个对象的公共访问权限都已被撤销,以防存在潜在的安全问题。 + + + + <structname>pg_buffercache</structname> 视图 + + + 该视图公开的列定义见 。 + + + + <structname>pg_buffercache</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + + bufferid + integer + + + ID,范围为 1..shared_buffers + + + + + relfilenode + oid + pg_class.relfilenode + + 关系的 filenode 编号 + + + + + reltablespace + oid + pg_tablespace.oid + + 关系的表空间 OID + + + + + reldatabase + oid + pg_database.oid + + 关系的数据库 OID + + + + + relforknumber + smallint + + 关系中的分支编号;参见include/common/relpath.h + + + + relblocknumber + bigint + + + 关系中的页号 + + + + + isdirty + boolean + + + 该页是否为脏页? + + + + + usagecount + smallint + + + 时钟扫描访问计数 + + + + + pinning_backends + integer + + + 钉住该缓冲区的后端数量 + + + + + +
+ + + 共享缓存中的每个缓冲区都有一行记录。未使用的缓冲区除了 + bufferid 之外,其余字段均显示为 null。共享系统目录显示为属于数据库 0。 + + + + 由于缓存由所有数据库共享,其中通常会有不属于当前数据库的关系页。 + 这意味着某些行在 pg_class 中可能没有可匹配的行, + 甚至还可能出现错误的连接结果。如果要与 pg_class 进行连接, + 最好将连接限制在 reldatabase 等于当前数据库 OID 或 0 的那些行上。 + + + + 访问 pg_buffercache 视图时,会在足够长的时间内持有内部缓冲区管理器锁, + 以复制该视图将要显示的全部缓冲区状态数据。这确保了该视图产生一致的结果集, + 同时又不会对正常缓冲区活动造成超出必要时长的阻塞。尽管如此,如果频繁读取该视图, + 仍可能对数据库性能产生一定影响。 + +
+ + + 示例输出 + + +regression=# SELECT n.nspname, c.relname, count(*) AS buffers + FROM pg_buffercache b JOIN pg_class c + ON b.relfilenode = pg_relation_filenode(c.oid) AND + b.reldatabase IN (0, (SELECT oid FROM pg_database + WHERE datname = current_database())) + JOIN pg_namespace n ON n.oid = c.relnamespace + GROUP BY n.nspname, c.relname + ORDER BY 3 DESC + LIMIT 10; + + nspname | relname | buffers +------------+------------------------+--------- + public | delete_test_table | 593 + public | delete_test_table_pkey | 494 + pg_catalog | pg_attribute | 472 + public | quad_poly_tbl | 353 + public | tenk2 | 349 + public | tenk1 | 349 + public | gin_test_idx | 306 + pg_catalog | pg_largeobject | 206 + public | gin_test_tbl | 188 + public | spgist_text_tbl | 182 +(10 rows) + + + + + 作者 + + Mark Kirkwood markir@paradise.net.nz + + 设计建议: Neil Conway neilc@samurai.com + + 调试建议: Tom Lane tgl@sss.pgh.pa.us + + +
diff --git a/zh/9.6/pgcrypto.sgml b/zh/9.6/pgcrypto.sgml new file mode 100644 index 00000000..758800f2 --- /dev/null +++ b/zh/9.6/pgcrypto.sgml @@ -0,0 +1,1295 @@ + + + + pgcrypto + + + pgcrypto + + + + encryption + for specific columns + + + + pgcrypto模块为PostgreSQL提供密码学函数。 + + + + 通用哈希函数 + + + <function>digest()</function> + + + digest + + + +digest(data text, type text) returns bytea +digest(data bytea, type text) returns bytea + + + 计算给定data的二进制哈希值。type是要使用的算法。标准算法包括md5sha1sha224sha256sha384sha512。如果pgcrypto是在有 OpenSSL 的情况下构建的,则还有更多算法可用,详见 + + + 如果想把摘要表示为十六进制字符串,可以对结果使用encode()。例如: + +CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ + SELECT encode(digest($1, 'sha1'), 'hex') +$$ LANGUAGE SQL STRICT IMMUTABLE; + + + + + + <function>hmac()</function> + + + hmac + + + +hmac(data text, key text, type text) returns bytea +hmac(data bytea, key bytea, type text) returns bytea + + + + 使用密钥keydata计算基于散列的消息认证码(HMAC)。 + typedigest()中的相同。 + + + + 这与digest()类似,但只有知道密钥时才能重新计算该哈希。 + 这可以防止有人篡改数据后再同时修改哈希使之匹配。 + + + + 如果密钥大于哈希块大小,则会先对其进行哈希,并将结果用作密钥。 + + + + + + 密码哈希函数 + + + 函数crypt()gen_salt()是专门为密码哈希设计的。 + crypt()负责执行哈希,而gen_salt()负责为其准备算法参数。 + + + + crypt()中的算法在以下方面不同于通常的 MD5 或 SHA-1 哈希算法: + + + + + + 它们很慢。由于处理的数据量很小,这是使暴力破解密码变得困难的唯一办法。 + + + + + 它们使用一个称为salt的随机盐值,这样使用相同密码的用户也会得到不同的哈希结果。 + 这也为逆向求解该算法增加了一层额外防护。 + + + + + 它们会在结果中包含算法类型,这样用不同算法哈希的密码就能共存。 + + + + + 其中一些是自适应的 — 这意味着当计算机变快时,你可以把算法调得更慢, + 而不引入与现有密码的不兼容性。 + + + + + + 列出了crypt()函数支持的算法。 + + + + <function>crypt()</function>支持的算法 + + + + 算法 + 最大密码长度 + 是否自适应? + 盐值位数 + 输出长度 + 描述 + + + + + bf + 72 + + 128 + 60 + 基于 Blowfish,2a 变体 + + + md5 + 无限制 + + 48 + 34 + 基于 MD5 的 crypt + + + xdes + 8 + + 24 + 20 + 扩展 DES + + + des + 8 + + 12 + 13 + 原始 UNIX crypt + + + +
+ + + <function>crypt()</function> + + + crypt + + + +crypt(password text, salt text) returns text + + + + 计算password的一个 crypt(3) 风格哈希。 + 存储新密码时,需要使用gen_salt()生成新的salt值。 + 校验密码时,把已存储的哈希值作为salt传入,并测试结果是否与已存储值匹配。 + + + 设置一个新密码的示例: + +UPDATE ... SET pswhash = crypt('new password', gen_salt('md5')); + + + + 身份验证示例: + +SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ; + + 如果输入的密码正确,这会返回true。 + + + + + <function>gen_salt()</function> + + + gen_salt + + + +gen_salt(type text [, iter_count integer ]) returns text + + + + 生成一个供crypt()使用的新随机盐值字符串。 + 该盐值字符串还会告诉crypt()应使用哪种算法。 + + + + type参数指定 hash 算法。 + 接受的类型有:desxdesmd5bf。 + + + + iter_count参数允许用户为支持该参数的算法指定迭代次数。 + 次数越高,对密码进行 hash 所需的时间越长,从而破解它所需时间也越长。 + 不过,如果次数过高,计算一个 hash 可能需要数年时间 — 这显然不切实际。 + 若省略iter_count参数,则使用默认迭代次数。 + 允许的iter_count值取决于算法,如所示。 + + + + <function>crypt()</function>的迭代计数 + + + + 算法 + 默认值 + 最小值 + 最大值 + + + + + xdes + 725 + 1 + 16777215 + + + bf + 6 + 4 + 31 + + + +
+ + + 对xdes算法还有额外的限制:迭代计数必须是一个奇数。 + + + + 为了选择合适的迭代次数,可以考虑原始 DES crypt 在当时硬件上的设计速度是每秒 4 次 hash。 + 低于每秒 4 次 hash 可能会影响可用性,而高于每秒 100 次 hash 则很可能过快。 + + + + 概述了不同 hash 算法之间的相对速度差异。 + 该表展示了在 8 字符密码上尝试所有字符组合所需的时间,假定密码只包含小写字母, + 或者包含大小写字母和数字。在crypt-bf条目中, + 斜杠后的数字是gen_saltiter_count参数值。 + + + + Hash 算法速度 + + + + 算法 + 每秒 hash 次数 + 针对[a-z] + 针对[A-Za-z0-9] + 相对于md5 hash的耗时倍数 + + + + + crypt-bf/8 + 1792 + 4 年 + 3927 年 + 100k + + + crypt-bf/7 + 3648 + 2 年 + 1929 年 + 50k + + + crypt-bf/6 + 7168 + 1 年 + 982 年 + 25k + + + crypt-bf/5 + 13504 + 188 天 + 521 年 + 12.5k + + + crypt-md5 + 171584 + 15 天 + 41 年 + 1k + + + crypt-des + 23221568 + 157.5 分 + 108 天 + 7 + + + sha1 + 37774272 + 90 分 + 68 天 + 4 + + + md5(hash) + 150085504 + 22.5 分 + 17 天 + 1 + + + +
+ + + 注意: + + + + + + 所用机器为 Intel Mobile Core i3。 + + + + + crypt-descrypt-md5算法的数字取自 + John the Ripper v1.6.38 的 -test 输出。 + + + + + md5 hash的数字来自 mdcrack 1.2。 + + + + + sha1的数字来自 lcrack-20031130-beta。 + + + + + crypt-bf的数字是使用一个简单程序测得的,该程序循环处理 1000 个 + 8 字符密码。这样可以展示不同迭代次数下的速度。作为参考: + john -testcrypt-bf/5 给出的结果是 + 13506 次循环/秒。(结果上的极小差异与这样一个事实一致: + crypt-bfpgcrypto中的实现与 + John the Ripper 使用的是同一套实现。) + + + + + + 请注意,尝试所有组合并不是现实中的做法。 + 通常密码破解是借助词典完成的,其中包含常见单词及其各种变体。 + 因此,即使是稍微有点像单词的密码,被破解的速度也可能远快于上表所示; + 而一个 6 字符、不像单词的密码则可能逃过破解,也可能不会。 + +
+
+ + + PGP 加密函数 + + 这里的函数实现 OpenPGP(RFC 4880)标准的加密部分。同时支持对称密钥加密和公钥加密。 + + + 一个加密的 PGP 消息由两个部分,或称两个组成: + + + + + 包含会话密钥的包 — 该会话密钥要么由对称密钥加密,要么由公钥加密。 + + + + + 包含用会话密钥加密的数据的包。 + + + + + + 当使用对称密钥(即密码)加密时: + + + + + 给定密码使用 String2Key (S2K) 算法进行哈希。 + 这与crypt()算法颇为类似 — 故意设计得较慢,并带有随机盐值 — + 但它产生的是一个全长度的二进制密钥。 + + + + + 如果请求单独的会话密钥,则会生成一个新的随机密钥。 + 否则,S2K 密钥将直接用作会话密钥。 + + + + + 如果直接使用 S2K 密钥,那么会话密钥包中只写入 S2K 设置。 + 否则,会话密钥会先用 S2K 密钥加密,再放入会话密钥包。 + + + + + + 当使用公钥加密时: + + + + + 会生成一个新的随机会话密钥。 + + + + + 该密钥会用公钥加密,并放入会话密钥包中。 + + + + + + 无论哪种情况,要加密的数据都会按如下步骤处理: + + + + + 可选的数据处理包括:压缩、转换为 UTF-8 和转换行结束符,三者可任意组合。 + + + + + 数据前面会加上一个随机字节块。这相当于使用随机 IV。 + + + + 追加对随机前缀和数据计算得到的 SHA-1 哈希值。 + + + + 然后用会话密钥加密所有这些内容,并放入数据包。 + + + + + + <function>pgp_sym_encrypt()</function> + + + pgp_sym_encrypt + + + + pgp_sym_encrypt_bytea + + + +pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea +pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea + + + 使用对称 PGP 密码psw加密data。 + options参数可以包含下文所述的选项设置。 + + + + + <function>pgp_sym_decrypt()</function> + + + pgp_sym_decrypt + + + + pgp_sym_decrypt_bytea + + + +pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text +pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea + + + 解密一个经过对称密钥加密的 PGP 消息。 + + + bytea数据不能用pgp_sym_decrypt解密。 + 这是为了避免输出无效字符数据。若原始数据本来是文本, + 则使用pgp_sym_decrypt_bytea解密也没有问题。 + + + options参数可以包含下文所述的选项设置。 + + + + + <function>pgp_pub_encrypt()</function> + + + pgp_pub_encrypt + + + + pgp_pub_encrypt_bytea + + + +pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea +pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea + + + 使用 PGP 公钥key加密data。 + 向该函数提供私钥会报错。 + + + options参数可以包含下文所述的选项设置。 + + + + + <function>pgp_pub_decrypt()</function> + + + pgp_pub_decrypt + + + + pgp_pub_decrypt_bytea + + + +pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text +pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea + + + 解密经过公钥加密的消息。key必须是与加密时所用公钥对应的私钥。 + 如果私钥受密码保护,则必须在psw中给出密码。 + 如果没有密码但想指定选项,则需要传入空密码。 + + + bytea数据不能用pgp_pub_decrypt解密。 + 这是为了避免输出无效字符数据。若原始数据本来是文本, + 则使用pgp_pub_decrypt_bytea解密也没有问题。 + + + options参数可以包含下文所述的选项设置。 + + + + + <function>pgp_key_id()</function> + + + pgp_key_id + + + +pgp_key_id(bytea) returns text + + + pgp_key_id提取 PGP 公钥或私钥的密钥 ID。 + 如果传入的是加密消息,则返回用于加密该数据的密钥 ID。 + + + 它可以返回两个特殊的密钥 ID: + + + + + SYMKEY + + + 该消息是用对称密钥加密的。 + + + + + ANYKEY + + + 该消息是用公钥加密的,但密钥 ID 已被移除。 + 这意味着你需要尝试自己的所有私钥,看看哪一个能解密它。 + pgcrypto本身不会生成这样的消息。 + + + + + 注意,不同的密钥可能具有相同的 ID。这种情况虽然罕见,但属于正常情况。 + 客户端应用此时应该尝试使用每一个密钥解密,以判断哪个匹配 — + 就像处理ANYKEY时一样。 + + + + + <function>armor()</function>, <function>dearmor()</function> + + + armor + + + + dearmor + + + +armor(data bytea [ , keys text[], values text[] ]) returns text +dearmor(data text) returns bytea + + + 这些函数将二进制数据封装/解封装为 PGP ASCII-armor 格式, + 它本质上就是带 CRC 和附加格式信息的 Base64。 + + + + 如果指定了keysvalues数组, + 则会为每个键/值对添加一个装甲头(armor header)。 + 两个数组都必须是一维的,且长度相同。键和值都不能包含任何非 ASCII 字符。 + + + + + <function>pgp_armor_headers</function> + + + pgp_armor_headers + + + +pgp_armor_headers(data text, key out text, value out text) returns setof record + + + pgp_armor_headers()data中提取 + 装甲头。返回值是一个包含两列的行集合,列名为 key 和 value。 + 如果键或值包含任何非 ASCII 字符,则按 UTF-8 处理。 + + + + + PGP 函数的选项 + + + 这些选项的命名方式与 GnuPG 类似。选项值应写在等号后面; + 各选项之间用逗号分隔。例如: + +pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256') + + + + + 除convert-crlf外,所有选项都只适用于加密函数。 + 解密函数会从 PGP 数据中获取这些参数。 + + + + 最值得关注的选项可能是compress-algounicode-mode。 + 其余选项应该都具有合理的默认值。 + + + + cipher-algo + + + 使用哪种密码算法。 + + +Values: bf, aes128, aes192, aes256 (OpenSSL-only: 3des, cast5) +Default: aes128 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt + + + + + compress-algo + + + 使用哪种压缩算法。仅当PostgreSQL在编译时包含 zlib 时才可用。 + + +Values: + 0 - no compression + 1 - ZIP compression + 2 - ZLIB compression (= ZIP plus meta-data and block CRCs) +Default: 0 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt + + + + + compress-level + + + 压缩程度。级别越高,压缩后越小,但速度也越慢。0 表示禁用压缩。 + + +Values: 0, 1-9 +Default: 6 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt + + + + + convert-crlf + + 是否在加密时将\n转换为\r\n,并在解密时将\r\n转换为\n。RFC 4880 规定文本数据应使用\r\n换行符存储。使用此选项可获得完全符合 RFC 的行为。 + +Values: 0, 1 +Default: 0 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt + + + + + disable-mdc + + 不使用 SHA-1 保护数据。使用此选项唯一合理的理由是兼容早于 RFC 4880 加入 SHA-1 保护包的古老 PGP 产品。较新的 gnupg.org 和 pgp.com 软件都能很好地支持它。 + +Values: 0, 1 +Default: 0 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt + + + + + sess-key + + + 使用单独的会话密钥。公钥加密总是使用单独的会话密钥; + 这个选项用于对称密钥加密,因为后者默认直接使用 S2K 密钥。 + + +Values: 0, 1 +Default: 0 +Applies to: pgp_sym_encrypt + + + + + s2k-mode + + + 使用哪一种 S2K 算法。 + + +Values: + 0 - Without salt. Dangerous! + 1 - With salt but with fixed iteration count. + 3 - Variable iteration count. +Default: 3 +Applies to: pgp_sym_encrypt + + + + + s2k-count + + + S2K 算法要使用的迭代次数。它必须是一个位于 1024 和 65011712 之间的值, + 首尾两个值包括在内。 + + +Default: A random value between 65536 and 253952 +Applies to: pgp_sym_encrypt, only with s2k-mode=3 + + + + + s2k-digest-algo + + + 要在 S2K 计算中使用哪种摘要算法。 + + +Values: md5, sha1 +Default: sha1 +Applies to: pgp_sym_encrypt + + + + + s2k-cipher-algo + + + 使用哪种密码算法来加密单独的会话密钥。 + + +Values: bf, aes, aes128, aes192, aes256 +Default: use cipher-algo +Applies to: pgp_sym_encrypt + + + + + unicode-mode + + + 是否将文本数据在数据库内部编码和 UTF-8 之间相互转换。 + 如果数据库已经是 UTF-8,则不会发生转换,但消息会被标记为 UTF-8。 + 如果不启用该选项,则不会进行这种标记。 + + +Values: 0, 1 +Default: 0 +Applies to: pgp_sym_encrypt, pgp_pub_encrypt + + + + + + 使用 GnuPG 生成 PGP 密钥 + + + 要生成新密钥: + +gpg --gen-key + + + + 首选的密钥类型是DSA 和 Elgamal。 + + + 对于 RSA 加密,你必须先创建一个仅用于签名的 DSA 或 RSA 主密钥, + 然后使用gpg --edit-key添加 RSA 加密子密钥。 + + + 要列出密钥: + +gpg --list-secret-keys + + + + 要以 ASCII-armor 格式导出公钥: + +gpg -a --export KEYID > public.key + + + + 要以 ASCII-armor 格式导出私钥: + +gpg -a --export-secret-keys KEYID > secret.key + + + + 在把这些密钥交给 PGP 函数之前,需要先用dearmor()处理它们。 + 或者,如果你能处理二进制数据,也可以从命令中去掉-a。 + + 更多细节见man gpgGNU Privacy Handbook以及上的其他文档。 + + + + PGP 代码的限制 + + + + + 不支持签名。这也意味着不会检查加密子密钥是否属于主密钥。 + + + + + 不支持使用加密密钥作为主密钥。由于通常不鼓励这种做法,这不应成为问题。 + + + + + 不支持多个子密钥。这看起来可能是个问题,因为这在实践中相当常见。 + 另一方面,你不应将常规的 GPG/PGP 密钥用于pgcrypto, + 而应新建一套密钥,因为其使用场景相当不同。 + + + + + + + + 原始加密函数 + + + 这些函数只是对数据应用密码算法;它们不具备 PGP 加密的任何高级特性。 + 因此存在一些严重问题: + + + + + 它们直接把用户提供的密钥用作密码算法的密钥。 + + + + + 它们不提供任何完整性检查,无法判断加密数据是否被修改。 + + + + + 它们希望用户自己管理所有加密参数,甚至是 IV。 + + + + + 它们无法处理文本。 + + + + + 因此,在引入 PGP 加密之后,不建议使用原始加密函数。 + + + + encrypt + + + + decrypt + + + + encrypt_iv + + + + decrypt_iv + + + +encrypt(data bytea, key bytea, type text) returns bytea +decrypt(data bytea, key bytea, type text) returns bytea + +encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea +decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea + + + 对数据进行加密/解密,所用加密算法由type指定。type字符串的语法是: +algorithm - mode /pad: padding +其中algorithm可以是: + bf — Blowfish + aes — AES(Rijndael-128、-192 或 -256) + mode可以是: + + cbc — 下一个块依赖于前一个块(默认) + + + ecb — 每个块单独加密(仅用于测试) + + padding可以是: + + + pkcs — 数据长度可以任意(默认) + + + + + none — 数据必须是分组大小的倍数 + + + + + + 因此,例如下面两种写法是等价的: + +encrypt(data, 'fooz', 'bf') +encrypt(data, 'fooz', 'bf-cbc/pad:pkcs') + + + + 在encrypt_ivdecrypt_iv中, + iv参数是 CBC 和 CFB 模式的初始值;对于 ECB,它会被忽略。 + 如果长度不等于块大小,则会被截断或用零填充。 + 在不带该参数的函数中,它默认为全零。 + + + + + 随机数据函数 + + + gen_random_bytes + + + +gen_random_bytes(count integer) returns bytea + + + 返回count个具有密码学安全强度的随机字节。 + 一次最多可提取 1024 个字节,以避免耗尽随机数生成器池。 + + + + gen_random_uuid + + + +gen_random_uuid() returns uuid + + 返回第 4 版(随机)UUID。 + + + + 注意事项 + + + 配置 + + pgcrypto会根据 PostgreSQL 主configure脚本的探测结果自行配置。影响它的选项有--with-zlib--with-openssl + + + 如果编译时包含 zlib,PGP 加密函数就能够在加密前压缩数据。 + + + 如果编译时包含 OpenSSL,则会有更多算法可用。同时公钥加密函数也会更快,因为 OpenSSL 具有更加优化的 BIGNUM 函数。 + + + 有无 OpenSSL 时的功能概要 + + + + 功能 + 内置 + 包含 OpenSSL 时 + + + + + MD5 + + + + + SHA1 + + + + + SHA224/256/384/512 + + 是(注 1) + + + 其他摘要算法 + + 是(注 2) + + + Blowfish + + + + + AES + + 是(注 3) + + + DES/3DES/CAST5 + + + + + 原始加密 + + + + + PGP 对称加密 + + + + + PGP 公钥加密 + + + + + +
+ + + 注: + + + + + + SHA2 算法是在 OpenSSL 0.9.8 版中加入的。对于更早的版本, + pgcrypto将使用内建代码。 + + + + + OpenSSL 支持的任何摘要算法都会自动被采用。加密算法则无法如此,必须显式添加支持。 + + + + + AES 是在 OpenSSL 0.9.7 版中引入的。对于更早的版本, + pgcrypto将使用内建代码。 + + + +
+ + + NULL 处理 + + + 按照 SQL 的标准,只要任一参数为 NULL,所有函数都返回 NULL。 + 这在使用不慎时可能带来安全风险。 + + + + + 安全性限制 + + + 所有pgcrypto函数都在数据库服务器内部运行。 + 这意味着所有数据和密码都会在pgcrypto与客户端应用之间以明文传输。 + 因此,你必须: + + + + + 使用本地连接或 SSL 连接。 + + + 同时信任系统管理员和数据库管理员。 + + + + + 如果做不到,最好在客户端应用内部执行密码学操作。 + + + 该实现无法抵御侧信道攻击。例如,对于给定大小的密文,pgcrypto解密函数完成所需的时间会因具体密文不同而变化。 + + + + 有用的阅读材料 + + + + + GNU 隐私手册。 + + + + 介绍 crypt-blowfish 算法。 + + + + + + 如何选择一个好的密码。 + + + + 选择密码的有趣思路。 + + + + + + 介绍好的和不好的密码学。 + + + + + + 技术参考资料 + + + + + OpenPGP 消息格式。 + + + + MD5 消息摘要算法。 + + + + HMAC:用于消息认证的密钥散列。 + + + + + + crypt-des、crypt-md5 和 bcrypt 算法的比较。 + + + + + + Fortuna CSPRNG 的描述。 + + + + Jean-Luc Cooke 基于 Fortuna 的 Linux /dev/random 驱动。 + + + +
+ + + 作者 + + + Marko Kreen markokr@gmail.com + + + + pgcrypto使用了来自以下来源的代码: + + + + + + + 算法 + 作者 + 源代码来源 + + + + + DES crypt + David Burren 及其他人 + FreeBSD libcrypt + + + MD5 crypt + Poul-Henning Kamp + FreeBSD libcrypt + + + Blowfish crypt + Solar Designer + www.openwall.com + + + Blowfish cipher + Simon Tatham + PuTTY + + + Rijndael cipher + Brian Gladman + OpenBSD sys/crypto + + + MD5 hash and SHA1 + WIDE Project + KAME kame/sys/crypto + + + SHA256/384/512 + Aaron D. Gifford + OpenBSD sys/crypto + + + BIGNUM math + Michael J. Fromberger + dartmouth.edu/~sting/sw/imath + + + + + + +
diff --git a/zh/9.6/pgdoccn-notes.sgml b/zh/9.6/pgdoccn-notes.sgml new file mode 100644 index 00000000..c0a43731 --- /dev/null +++ b/zh/9.6/pgdoccn-notes.sgml @@ -0,0 +1 @@ + diff --git a/zh/9.6/pgfreespacemap.sgml b/zh/9.6/pgfreespacemap.sgml new file mode 100644 index 00000000..9e4668ac --- /dev/null +++ b/zh/9.6/pgfreespacemap.sgml @@ -0,0 +1,99 @@ + + + + pg_freespacemap + + + pg_freespacemap + + + pg_freespacemap模块提供了一种检查空闲空间映射(FSM)的方法。它提供了一个名为pg_freespace的函数,更准确地说,是两个重载函数。这些函数显示FSM中为给定页面或关系中所有页面记录的值。 + + + 默认情况下,这些函数的公共访问权限已被撤销,以防存在潜在的安全问题。 + + + + 函数 + + + + + pg_freespace(rel regclass IN, blkno bigint IN) returns int2 + + pg_freespace + + + + + 返回该关系中由blkno指定页面上按 FSM 记录的空闲空间量。 + + + + + + + pg_freespace(rel regclass IN, blkno OUT bigint, avail OUT int2) + + + + 显示该关系中每个页面上按 FSM 记录的空闲空间量。返回一组(blkno bigint, avail int2)元组,其中关系中的每个页面对应一个元组。 + + + + + + 存储在空闲空间映射中的值并不精确。它们会被舍入到BLCKSZ的 1/256 精度(在默认 BLCKSZ 下为 32 字节),并且不会随着元组的插入和更新而完全保持最新。 + + + 对于索引,被跟踪的是完全未使用的页面,而不是页面内部的空闲空间。因此,这些值本身并无实际意义,只能表明某个页面是满的还是空的。 + + + 该接口在 8.4 版中进行了更改,以反映同一版本引入的新 FSM 实现。 + + + + + 示例输出 + + +postgres=# SELECT * FROM pg_freespace('foo'); + blkno | avail +-------+------- + 0 | 0 + 1 | 0 + 2 | 0 + 3 | 32 + 4 | 704 + 5 | 704 + 6 | 704 + 7 | 1216 + 8 | 704 + 9 | 704 + 10 | 704 + 11 | 704 + 12 | 704 + 13 | 704 + 14 | 704 + 15 | 704 + 16 | 704 + 17 | 704 + 18 | 704 + 19 | 3648 +(20 rows) + +postgres=# SELECT * FROM pg_freespace('foo', 7); + pg_freespace +-------------- + 1216 +(1 row) + + + + + 作者 + + 最初版本由 Mark Kirkwood markir@paradise.net.nz 编写。在 8.4 版中,Heikki Linnakangas heikki@enterprisedb.com 为适应新的 FSM 实现对其进行了重写。 + + + diff --git a/zh/9.6/pgprewarm.sgml b/zh/9.6/pgprewarm.sgml new file mode 100644 index 00000000..eecf61c2 --- /dev/null +++ b/zh/9.6/pgprewarm.sgml @@ -0,0 +1,53 @@ + + + + pg_prewarm — 将关系数据预热到缓冲区缓存中 + + + pg_prewarm + + + pg_prewarm模块提供了一种便捷方式,可将关系数据加载到操作系统缓冲区缓存或PostgreSQL缓冲区缓存中。 + + + 函数 + + +pg_prewarm(regclass, mode text default 'buffer', fork text default 'main', + first_block int8 default null, + last_block int8 default null) RETURNS int8 + + + + 第一个参数是要预热的关系。第二个参数是要使用的预热方法,下文会进一步讨论;第三个参数 + 是要预热的关系分支,通常为main。第四个参数是要预热的第一个块号 + (NULL可作为 0 的同义值);第五个参数是要预热的最后一个块号 + (NULL表示一直预热到该关系中的最后一个块)。返回值是已预热的块数。 + + + + 可用的预热方法有三种。prefetch会在支持的情况下向操作系统发出异步预取 + 请求,否则就会报错。read会读取所请求范围内的块;与 + prefetch不同,它是同步的,并且在所有平台和构建方式上都受支持,但可能 + 较慢。buffer会将所请求范围内的块读入数据库缓冲区缓存。 + + + + 请注意,无论采用哪种方法,如果试图预热的块数超过可缓存的数量,那么无论是在使用 + prefetchread时由操作系统缓存,还是在 + PostgreSQL使用buffer时缓存,都很可能会 + 在读入更高块号的块时逐出较低块号的块。预热后的数据同样不会得到任何特殊的缓存 + 逐出保护,因此其他系统活动可能会在这些新近预热的块被读入后不久就将其逐出;反过来,预热 + 也可能会把其他数据从缓存中逐出。因此,预热通常在启动时最有用,因为此时缓存大多还是空的。 + + + + + 作者 + + + Robert Haas rhaas@postgresql.org + + + + diff --git a/zh/9.6/pgrowlocks.sgml b/zh/9.6/pgrowlocks.sgml new file mode 100644 index 00000000..2870a263 --- /dev/null +++ b/zh/9.6/pgrowlocks.sgml @@ -0,0 +1,136 @@ + + + + pgrowlocks — 显示表的行锁信息 + + + pgrowlocks + + + + pgrowlocks 模块提供了一个函数,用于显示指定表的行锁信息。 + + + + + 概述 + + + pgrowlocks + + + +pgrowlocks(text) returns setof record + + + + 参数为表名。结果是一个记录集合,其中表中每个被锁定的行对应结果中的一行。输出列见 + 。 + + + + <function>pgrowlocks</function> 输出列 + + + + + 名称 + 类型 + 描述 + + + + + + locked_row + tid + 被锁定行的元组 ID(TID) + + + locker + xid + 持锁者的事务 ID;若为多事务,则为多事务 ID + + + multi + boolean + 如果持锁者为多事务,则为真 + + + xids + xid[] + 持锁者的事务 ID(若为多事务,则会有多个) + + + modes + text[] + 持锁者的锁模式(若为多事务,则会有多个),是由 Key ShareShareFor No Key UpdateNo Key UpdateFor UpdateUpdate 组成的数组。 + + + + pids + integer[] + 加锁后端的进程 ID(若为多事务,则会有多个) + + + + +
+ + + pgrowlocks 会对目标表获取 AccessShareLock, + 并逐行读取以收集行锁信息。对于大表,这样做速度并不快。请注意: + + + + + + 如果该表上存在 ACCESS EXCLUSIVE 锁, + pgrowlocks 将被阻塞。 + + + + + pgrowlocks 并不保证给出一个自一致快照。在其执行期间, + 可能会有新的行锁被获取,也可能有旧的锁被释放。 + + + + + + pgrowlocks 不显示被锁定行的内容。如果你想同时查看这些行的内容, + 可以这样做: + + +SELECT * FROM accounts AS a, pgrowlocks('accounts') AS p + WHERE p.locked_row = a.ctid; + + + 不过要注意,这样的查询效率会很低。 + +
+ + + 示例输出 + + +=# SELECT * FROM pgrowlocks('t1'); + locked_row | locker | multi | xids | modes | pids +------------+--------+-------+-------+----------------+-------- + (0,1) | 609 | f | {609} | {"For Share"} | {3161} + (0,2) | 609 | f | {609} | {"For Share"} | {3161} + (0,3) | 607 | f | {607} | {"For Update"} | {3107} + (0,4) | 607 | f | {607} | {"For Update"} | {3107} +(4 rows) + + + + + 作者 + + + Tatsuo Ishii + + + +
diff --git a/zh/9.6/pgstandby.sgml b/zh/9.6/pgstandby.sgml new file mode 100644 index 00000000..10fc6cdd --- /dev/null +++ b/zh/9.6/pgstandby.sgml @@ -0,0 +1,226 @@ + + + + + pg_standby + + + + pg_standby + 1 + 应用程序 + + + + pg_standby + 支持创建 PostgreSQL 温备服务器 + + + + + pg_standby + option + archivelocation + nextwalfile + xlogfilepath + restartwalfile + + + + + 描述 + + pg_standby 支持创建温备数据库服务器。它既是可用于生产环境的程序,也是可定制的模板,便于在有需要时进行特定修改。 + + pg_standby 设计为一个会等待的 restore_command,将标准归档恢复转变为温备运行需要这样的命令。此外还需要其他配置,服务器的主手册中对此均有介绍(参见 )。 + + 要配置备库使用 pg_standby,请将以下内容放入其 recovery.conf 配置文件: +restore_command = 'pg_standby archiveDir %f %p %r' +其中,archiveDir 是恢复 WAL 段文件时所读取的目录。 + 如果指定了 restartwalfile(通常使用 %r 宏),就会从 archivelocation 中删除逻辑上位于该文件之前的所有 WAL 文件。这样既能尽量减少需要保留的文件数量,又能保留崩溃后重启的能力。如果 archivelocation 是专供此备库使用的临时暂存区,就适合使用此参数;但如果 archivelocation 用作长期 WAL 归档区,就不应使用。 + pg_standby 假定 archivelocation 是服务器所属用户可读的目录。如果指定了 restartwalfile(或 -k),archivelocation 目录还必须可写。 + 主库发生故障时,切换到温备数据库服务器有以下两种方式: + + 智能故障切换 + + 在智能故障切换中,服务器会先应用归档中所有可用的 WAL 文件,再正式启动。即使备库已经落后,这也能做到零数据丢失;但如果尚未应用的 WAL 很多,备库可能需要很长时间才能就绪。要触发智能故障切换,请创建包含单词 smart 的触发文件,或者直接创建一个空的触发文件。 + + + + 快速故障切换 + + 在快速故障切换中,服务器会立即正式启动。归档中所有尚未应用的 WAL 文件都会被忽略,这些文件中的全部事务都会丢失。要触发快速故障切换,请创建触发文件,并在其中写入单词 fast。也可以配置 pg_standby,使其在指定时间间隔内没有出现新 WAL 文件时,自动执行快速故障切换。 + + + + + + + + + 选项 + + + pg_standby 接受以下命令行参数: + + + + + 使用 cpcopy 命令从归档恢复 WAL 文件。由于这是唯一受支持的行为,因此该选项没有实际作用。 + + + + + + + + 在 stderr 上输出大量调试日志。 + + + + + + + + archivelocation 中删除文件,使归档中保留的、位于当前文件之前的 WAL 文件不超过此数量。零(默认值)表示不从 archivelocation 中删除任何文件。如果指定了 restartwalfile,就会静默忽略此参数,因为前者能更准确地确定归档的正确截断点。从 PostgreSQL 8.3 起,已弃用此参数;指定 restartwalfile 参数更安全、更高效。设置过小可能删除备库重启仍需使用的文件,设置过大则会浪费归档空间。 + + + + + maxretries + + 设置复制命令失败后的最大重试次数(默认为 3)。每次失败后,都会等待 sleeptime * num_retries,因此等待时间会逐次增加。默认情况下,会依次等待 5 秒、10 秒、15 秒,然后才向备库报告失败。这会被解释为恢复结束,从而使备库完全启动。 + + + + + sleeptime + + 设置两次检查之间的等待秒数(最多 60 秒,默认 5 秒),检查的内容是待恢复的 WAL 文件是否已在归档中可用。默认设置不一定就是推荐设置;相关讨论参见 + + + + + triggerfile + + 指定触发文件;一旦该文件出现,就会引发故障切换。建议使用带有明确结构的文件名,例如 /tmp/pgsql.trigger.5432,以免同一系统上存在多个服务器时,混淆被触发的是哪个服务器。 + + + + + + + + 打印 pg_standby 的版本并退出。 + + + + + maxwaittime + + 设置等待下一个 WAL 文件的最长秒数,超过此时间后执行快速故障切换。零(默认值)表示永远等待。默认设置不一定就是推荐设置;相关讨论参见 + + + + + + + + 显示有关 pg_standby 命令行参数的帮助信息并退出。 + + + + + + + + + 说明 + + pg_standby 设计为与 PostgreSQL 8.2 及更高版本配合使用。 + PostgreSQL 8.3 提供了 %r 宏,用于让 pg_standby 知道需要保留的最后一个文件。使用 PostgreSQL 8.2 时,如果需要清理归档,就必须使用 -k 选项。该选项在 8.3 中仍然可用,但已弃用。 + PostgreSQL 8.4 提供了 recovery_end_command 选项。如果没有此选项,遗留的触发文件可能带来危险。 + + pg_standby 用 C 编写,源码易于修改,并专门标出了可根据自身需要进行修改的部分。 + + + + 示例 + + 在 Linux 或 Unix 系统上,可以使用: +archive_command = 'cp %p .../archive/%f' + +restore_command = 'pg_standby -d -s 2 -t /tmp/pgsql.trigger.5442 .../archive %f %p %r 2>>standby.log' + +recovery_end_command = 'rm -f /tmp/pgsql.trigger.5442' +这里,归档目录实际位于备库上,因此 archive_command 通过 NFS 访问该目录,但这些文件对备库而言是本地文件(因此可以使用 ln)。此配置会: + + 将调试输出写入 standby.log + + + 每隔 2 秒检查一次下一个 WAL 文件是否可用 + + + 仅在名为 /tmp/pgsql.trigger.5442 的触发文件出现时停止等待,并根据其内容执行故障切换 + + + 恢复结束时删除触发文件 + + + + 从归档目录中移除不再需要的文件 + + + + + + 在 Windows 上,可以使用: +archive_command = 'copy %p ...\\archive\\%f' + +restore_command = 'pg_standby -d -s 5 -t C:\pgsql.trigger.5442 ...\archive %f %p %r 2>>standby.log' + +recovery_end_command = 'del C:\pgsql.trigger.5442' +注意,需要双写反斜杠的设置是 archive_command,而无需双写的设置是 restore_commandrecovery_end_command。此配置会: + + 使用 copy 命令从归档恢复 WAL 文件 + + + 将调试输出写入 standby.log + + + 每隔 5 秒检查一次下一个 WAL 文件是否可用 + + + 仅在名为 C:\pgsql.trigger.5442 的触发文件出现时停止等待,并根据其内容执行故障切换 + + + 恢复结束时删除触发文件 + + + + 从归档目录中移除不再需要的文件 + + + + + + Windows 上的 copy 命令会在文件复制完成之前设置最终文件大小,这通常会让 pg_standby 产生误判。因此,pg_standby 一旦看到正确的文件大小,还会等待 sleeptime 秒。GNUWin32 的 cp 只有在文件复制完成后才设置文件大小。 + + 由于 Windows 示例在两端都使用 copy,任一服务器或两个服务器都可能通过网络访问归档目录。 + + + + + 作者 + + Simon Riggs simon@2ndquadrant.com + + + + 参见 + + + + + + diff --git a/zh/9.6/pgstatstatements.sgml b/zh/9.6/pgstatstatements.sgml new file mode 100644 index 00000000..d2d32bd3 --- /dev/null +++ b/zh/9.6/pgstatstatements.sgml @@ -0,0 +1,471 @@ + + + + pg_stat_statements + + + pg_stat_statements + + + pg_stat_statements模块提供了一种机制,用于跟踪服务器执行的所有 SQL 语句的执行统计信息。 + + 由于该模块需要额外的共享内存,因此必须通过在postgresql.conf中将pg_stat_statements加入来加载该模块。这意味着添加或移除此模块都需要重启服务器。 + + 启用pg_stat_statements后,它会跟踪该服务器上所有数据库的统计信息。为了访问和操作这些统计信息,该模块提供了视图pg_stat_statements,以及实用函数pg_stat_statements_resetpg_stat_statements。这些对象并非全局可用,但可以通过CREATE EXTENSION pg_stat_statements在特定数据库中启用。 + + + <structname>pg_stat_statements</structname> 视图 + + 该模块收集的统计信息可通过名为pg_stat_statements的视图获取。该视图为每个不同的数据库 ID、用户 ID 和查询 ID 包含一行(最多达到该模块能够跟踪的不同语句数量上限)。视图的列见 + + + <structname>pg_stat_statements</structname> 列 + + + + + 名称 + 类型 + 引用 + + 描述 + + + + + + userid + oid + pg_authid.oid + + 执行该语句的用户的 OID + + + + + dbid + oid + pg_database.oid + + 执行该语句所在数据库的 OID + + + + + queryid + bigint + + 根据语句的解析树计算的内部哈希码 + + + + query + text + + + 某个代表性语句的文本 + + + + + calls + bigint + + 执行次数 + + + + total_time + double precision + + 该语句所花费的总时间,单位为毫秒 + + + + min_time + double precision + + 该语句所花费的最短时间,单位为毫秒 + + + + max_time + double precision + + 该语句所花费的最长时间,单位为毫秒 + + + + mean_time + double precision + + 该语句所花费的平均时间,单位为毫秒 + + + + stddev_time + double precision + + 该语句所花费时间的总体标准差,单位为毫秒 + + + + rows + bigint + + + 该语句检索到或影响的总行数 + + + + + shared_blks_hit + bigint + + + 该语句的共享块缓存命中总数 + + + + + shared_blks_read + bigint + + + 该语句读取的共享块总数 + + + + + shared_blks_dirtied + bigint + + + 被该语句弄脏的共享块总数 + + + + + shared_blks_written + bigint + + + 该语句写入的共享块总数 + + + + + local_blks_hit + bigint + + + 该语句的本地块缓存命中总数 + + + + + local_blks_read + bigint + + + 该语句读取的本地块总数 + + + + + local_blks_dirtied + bigint + + + 被该语句弄脏的本地块总数 + + + + + local_blks_written + bigint + + + 该语句写入的本地块总数 + + + + + temp_blks_read + bigint + + + 该语句读取的临时块总数 + + + + + temp_blks_written + bigint + + + 该语句写入的临时块总数 + + + + + blk_read_time + double precision + + + 该语句读取块所花费的总时间,单位为毫秒 + (如果启用了 ,否则为零) + + + + + blk_write_time + double precision + + + 该语句写入块所花费的总时间,单位为毫秒 + (如果启用了 ,否则为零) + + + + + +
+ + + 出于安全原因,非超级用户不允许查看其他用户执行的查询的 SQL + 文本或queryid。不过,只要该视图已经安装在其数据库中,他们仍然可以查看统计信息。 + + + + 可进行计划的查询(即 SELECTINSERT、 + UPDATEDELETE),只要根据内部哈希计算 + 得出的查询结构相同,就会合并为单条 + pg_stat_statements 记录。通常,如果两个查询在语义上等价, + 仅在查询中出现的字面常量值不同,则会被视为相同。不过,实用命令(即所有其他命令) + 是严格按照其文本查询字符串进行比较的。 + + + + 当为了将某个查询与其他查询匹配而忽略常量值时,在 + pg_stat_statements 的显示中,该常量会被 + ? 替换。查询文本的其余部分则取自第一个具有该特定 + queryid 哈希值、并与该 + pg_stat_statements 记录相关联的查询。 + + + + 在某些情况下,文本明显不同的查询也可能被合并到同一条 + pg_stat_statements 记录中。通常,这只会发生在 + 语义等价的查询之间,但也存在很小的概率由于哈希冲突而把不相关的查询合并 + 为同一条记录。(不过,这种情况不会发生在属于不同用户或不同数据库的查询之间。) + + + + 由于 queryid 哈希值是根据查询经过解析分析后的表示形式计算出来的, + 相反的情况也可能发生:文本完全相同的查询,如果由于不同的 + search_path 设置等因素而具有不同含义,就可能显示为不同的记录。 + + + + pg_stat_statements 的使用者可能希望使用 + queryid(也许再结合 + dbiduserid)作为每条记录比查询文本更稳定、 + 更可靠的标识符。不过,必须理解的是, + queryid 哈希值的稳定性只得到有限保证。 + 由于该标识符源自解析分析后的语法树,它的取值除其他因素外,还取决于该表示形式中出现的内部对象标识符。 + 这会带来一些反直觉的结果。例如,如果两次查询执行之间, + 所引用的某张表被删除并重新创建,那么 + pg_stat_statements 会将两条看似完全相同的查询视为不同。 + 哈希过程也对机器架构差异以及平台的其他方面很敏感。 + 此外,也不能安全地假设 queryid 会在 + PostgreSQL 的主版本之间保持稳定。 + + + 通常可以假定queryid值是稳定且可比较的,前提是底层服务器版本和目录元数据细节始终完全相同。参与基于物理 WAL 重放的复制的两个服务器,对于同一查询可以期待具有相同的queryid值。然而,逻辑复制方案并不承诺在所有相关细节上保持副本完全一致,因此queryid不适合作为在一组逻辑副本之间累积开销的标识符。如有疑问,建议直接测试。 + + + 代表性查询文本保存在外部磁盘文件中,因此不消耗共享内存。即使非常长的查询文本也可以成功存储。 + 不过,如果积累了很多长查询文本,该外部文件可能会膨胀到难以管理的大小。若发生这种情况, + 作为一种恢复措施,pg_stat_statements 可能会选择丢弃这些查询文本, + 这样 pg_stat_statements 视图中的现有记录都会显示 + query 字段为 null,但与各个 + queryid 相关的统计信息仍会保留。如果发生这种情况,可考虑减小 + pg_stat_statements.max 以避免再次发生。 + +
+ + + 函数 + + + + pg_stat_statements_reset() returns void pg_stat_statements_reset + + + pg_stat_statements_reset会丢弃到目前为止由pg_stat_statements收集的所有统计信息。默认情况下,只有超级用户可以执行此函数。 + + + + + + pg_stat_statements(showtext boolean) returns setof record + + pg_stat_statements + 函数 + + + + + + pg_stat_statements 视图是根据一个同名的 + pg_stat_statements 函数定义的。客户端也可以直接调用 + pg_stat_statements 函数,并通过指定 + showtext := false 来省略查询文本(也就是说,与该视图 + query 列对应的 OUT 参数会返回 null)。 + 这一特性旨在支持某些外部工具,这些工具可能希望避免反复获取长度不定的查询文本所带来的开销。 + 这些工具可以自行缓存每条记录第一次观察到的查询文本,因为这正是 + pg_stat_statements 本身所做的事情,然后只在需要时再获取查询文本。 + 由于服务器会把查询文本存储在文件中,这种做法在反复检查 + pg_stat_statements 数据时可以减少物理 I/O。 + + + + + + + + 配置参数 + + + + + pg_stat_statements.max (integer) + + + + pg_stat_statements.max是该模块跟踪的语句最大数量(即pg_stat_statements视图中的最大行数)。如果观察到的不同语句超过该数量,则执行次数最少的语句信息会被丢弃。默认值为 5000。该参数只能在服务器启动时设置。 + + + + + + pg_stat_statements.track (enum) + + + + + pg_stat_statements.track 控制该模块统计哪些语句。 + 指定 top 可跟踪顶层语句(即直接由客户端发出的语句), + 指定 all 还会跟踪嵌套语句(例如在函数内调用的语句), + 而指定 none 则会禁用语句统计信息收集。 + 默认值为 top。 + 只有超级用户可以更改此设置。 + + + + + + + pg_stat_statements.track_utility (boolean) + + + + + pg_stat_statements.track_utility 控制该模块是否跟踪实用命令。 + 实用命令是除 SELECTINSERT、 + UPDATEDELETE 之外的所有命令。 + 默认值为 on。 + 只有超级用户可以更改此设置。 + + + + + + + pg_stat_statements.save (boolean) + + + + + pg_stat_statements.save 指定是否在服务器关闭后保留语句统计信息。 + 如果它为 off,则在关闭时不会保存统计信息,并且在服务器启动时也不会重新载入这些统计信息。 + 默认值为 on。 + 该参数只能在 postgresql.conf 文件中或在服务器命令行上设置。 + + + + + + + 该模块需要与 pg_stat_statements.max 成比例的额外共享内存。 + 注意,只要该模块被载入,就会消耗这部分内存,即使 + pg_stat_statements.track 被设置为 none 也是如此。 + + + 这些参数必须在postgresql.conf中设置。典型用法如下: +# postgresql.conf +shared_preload_libraries = 'pg_stat_statements' + +pg_stat_statements.max = 10000 +pg_stat_statements.track = all + + + + + + 示例输出 + + +bench=# SELECT pg_stat_statements_reset(); + +$ pgbench -i bench +$ pgbench -c10 -t300 bench + +bench=# \x +bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit / + nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent + FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5; +-[ RECORD 1 ]--------------------------------------------------------------------- +query | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?; +calls | 3000 +total_time | 9609.00100000002 +rows | 2836 +hit_percent | 99.9778970000200936 +-[ RECORD 2 ]--------------------------------------------------------------------- +query | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?; +calls | 3000 +total_time | 8015.156 +rows | 2990 +hit_percent | 99.9731126579631345 +-[ RECORD 3 ]--------------------------------------------------------------------- +query | copy pgbench_accounts from stdin +calls | 1 +total_time | 310.624 +rows | 100000 +hit_percent | 0.30395136778115501520 +-[ RECORD 4 ]--------------------------------------------------------------------- +query | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?; +calls | 3000 +total_time | 271.741999999997 +rows | 3000 +hit_percent | 93.7968855088209426 +-[ RECORD 5 ]--------------------------------------------------------------------- +query | alter table pgbench_accounts add primary key (aid) +calls | 1 +total_time | 81.42 +rows | 0 +hit_percent | 34.4947735191637631 + + + + + 作者 + + + Takahiro Itagaki itagaki.takahiro@oss.ntt.co.jp。 + 查询规范化功能由 Peter Geoghegan peter@2ndquadrant.com 添加。 + + + +
diff --git a/zh/9.6/pgstattuple.sgml b/zh/9.6/pgstattuple.sgml new file mode 100644 index 00000000..c4883453 --- /dev/null +++ b/zh/9.6/pgstattuple.sgml @@ -0,0 +1,503 @@ + + + + pgstattuple — 获取元组级统计信息 + + + pgstattuple + + + + pgstattuple 模块提供了多种用于获取元组级统计信息的函数。 + + + + 函数 + + + + + + pgstattuple + + pgstattuple(regclass) returns record + + + + + pgstattuple 返回一个关系的物理长度、 + 元组所占百分比以及其他信息。这可以帮助用户判断是否需要执行清理。参数是 + 目标关系的名称(可选带模式限定)或 OID。例如: + +test=> SELECT * FROM pgstattuple('pg_catalog.pg_proc'); +-[ RECORD 1 ]------+------- +table_len | 458752 +tuple_count | 1470 +tuple_len | 438896 +tuple_percent | 95.67 +dead_tuple_count | 11 +dead_tuple_len | 3157 +dead_tuple_percent | 0.69 +free_space | 8932 +free_percent | 1.95 + + 输出列的说明见 。 + + + + <function>pgstattuple</function> 输出列 + + + + + 类型 + 描述 + + + + + + table_len + bigint + 关系的物理长度(字节) + + + tuple_count + bigint + 活元组数量 + + + tuple_len + bigint + 活元组总长度(字节) + + + tuple_percent + float8 + 活元组百分比 + + + dead_tuple_count + bigint + 死元组数量 + + + dead_tuple_len + bigint + 死元组总长度(字节) + + + dead_tuple_percent + float8 + 死元组百分比 + + + free_space + bigint + 总空闲空间(字节) + + + free_percent + float8 + 空闲空间百分比 + + + + +
+ + + + table_len 总会大于 tuple_len、 + dead_tuple_lenfree_space + 之和。差值来自固定的页开销、每页的元组指针表,以及为保证元组正确 + 对齐而产生的填充。 + + + + + pgstattuple 只会在该关系上获取读锁。因此,结果并不 + 代表一个瞬时快照;并发更新会影响结果。 + + + + 如果 HeapTupleSatisfiesDirty 返回 false, + pgstattuple 就会将该元组判定为。 + +
+
+ + + + pgstattuple(text) returns record + + + + + 这与 pgstattuple(regclass) 相同,只是目标关系是以 + TEXT 指定的。该函数目前保留只是出于向后兼容考虑,并将在未来的某个 + 版本中弃用。 + + + + + + + + pgstatindex + + pgstatindex(regclass) returns record + + + + + pgstatindex 返回一条记录,显示有关 B-树索引的信息。 + 例如: + +test=> SELECT * FROM pgstatindex('pg_cast_oid_index'); +-[ RECORD 1 ]------+------ +version | 2 +tree_level | 0 +index_size | 16384 +root_block_no | 1 +internal_pages | 0 +leaf_pages | 1 +empty_pages | 0 +deleted_pages | 0 +avg_leaf_density | 54.27 +leaf_fragmentation | 0 + + + + + 输出列如下: + + + + + + + 类型 + 描述 + + + + + + version + integer + B-树版本号 + + + + tree_level + integer + 根页所在的树层级 + + + + index_size + bigint + 索引总大小(字节) + + + + root_block_no + bigint + 根页的位置(若无则为零) + + + + internal_pages + bigint + 内部(上层)页的数量 + + + + leaf_pages + bigint + 叶子页数量 + + + + empty_pages + bigint + 空页数量 + + + + deleted_pages + bigint + 已删除页数量 + + + + avg_leaf_density + float8 + 叶子页平均密度 + + + + leaf_fragmentation + float8 + 叶子页碎片化程度 + + + + + + + + + 报告的 index_size 通常会比 + internal_pages + leaf_pages + empty_pages + deleted_pages + 所对应的页数多 1 页,因为它还包含索引的元页。 + + + + 与 pgstattuple 一样,结果是逐页累积得到的,因此不 + 应期望它表示整个索引的瞬时快照。 + + + + + + + pgstatindex(text) returns record + + + + + 这与 pgstatindex(regclass) 相同,只是目标索引是以 + TEXT 指定的。该函数目前保留只是出于向后兼容考虑,并将在未来的某个 + 版本中弃用。 + + + + + + + + pgstatginindex + + pgstatginindex(regclass) returns record + + + + + pgstatginindex 返回一条记录,显示有关 GIN 索引的信息。 + 例如: + +test=> SELECT * FROM pgstatginindex('test_gin_index'); +-[ RECORD 1 ]--+-- +version | 1 +pending_pages | 0 +pending_tuples | 0 + + + + + 输出列如下: + + + + + + + 类型 + 描述 + + + + + + version + integer + GIN 版本号 + + + + pending_pages + integer + 待处理列表中的页数 + + + + pending_tuples + bigint + 待处理列表中的元组数 + + + + + + + + + + + + + pg_relpages + + pg_relpages(regclass) returns bigint + + + + + pg_relpages 返回该关系中的页数。 + + + + + + + pg_relpages(text) returns bigint + + + + + 这与 pg_relpages(regclass) 相同,只是目标关系是以 + TEXT 指定的。该函数目前保留只是出于向后兼容考虑,并将在未来的某个 + 版本中弃用。 + + + + + + + + pgstattuple_approx + + pgstattuple_approx(regclass) returns record + + + + + pgstattuple_approxpgstattuple + 的一种更快替代方案,返回近似结果。参数是目标关系的名称或 OID。 + 例如: + +test=> SELECT * FROM pgstattuple_approx('pg_catalog.pg_proc'::regclass); +-[ RECORD 1 ]--------+------- +table_len | 573440 +scanned_percent | 2 +approx_tuple_count | 2740 +approx_tuple_len | 561210 +approx_tuple_percent | 97.87 +dead_tuple_count | 0 +dead_tuple_len | 0 +dead_tuple_percent | 0 +approx_free_space | 11996 +approx_free_percent | 2.09 + + 输出列的说明见 。 + + + + pgstattuple 总是执行全表扫描,并返回活元组和死元组 + 的精确计数及其总长度,以及空闲空间;相比之下, + pgstattuple_approx 试图避免全表扫描,返回精确的 + 死元组统计信息,以及活元组数量、活元组总长度和空闲空间的近似值。 + + + + 它通过跳过那些根据可见性映射只包含可见元组的页来做到这一点(如果某页 + 设置了相应的 VM 位,就假定该页不包含死元组)。对于这类页,它从空闲 + 空间映射中得出空闲空间值,并假定页中其余空间都被活元组占用。 + + + + 对于不能跳过的页,它会扫描每个元组,在相应的计数器中记录其存在和 + 大小,并累加该页上的空闲空间。最后,它根据扫描过的页数和元组数来估算 + 活元组总数(方法与 VACUUM 估算 pg_class.reltuples 时相同)。 + + + + <function>pgstattuple_approx</function> 输出列 + + + + + 类型 + 描述 + + + + + + table_len + bigint + 关系的物理长度(字节,精确) + + + scanned_percent + float8 + 已扫描表的百分比 + + + approx_tuple_count + bigint + 活元组数量(估计) + + + approx_tuple_len + bigint + 活元组总长度(字节,估计) + + + approx_tuple_percent + float8 + 活元组百分比 + + + dead_tuple_count + bigint + 死元组数量(精确) + + + dead_tuple_len + bigint + 死元组总长度(字节,精确) + + + dead_tuple_percent + float8 + 死元组百分比 + + + approx_free_space + bigint + 总空闲空间(字节,估计) + + + approx_free_percent + float8 + 空闲空间百分比 + + + + +
+ + + 在上述输出中,空闲空间数值可能与 pgstattuple 的 + 输出不完全一致,因为空闲空间映射给出的数值是确定的,但不保证精确到 + 字节。 + + +
+
+ +
+
+ + + 作者 + + + Tatsuo Ishii、Satoshi Nagayasu 和 Abhijit Menon-Sen + + + +
diff --git a/zh/9.6/pgtrgm.sgml b/zh/9.6/pgtrgm.sgml new file mode 100644 index 00000000..05f25e43 --- /dev/null +++ b/zh/9.6/pgtrgm.sgml @@ -0,0 +1,344 @@ + + + + pg_trgm + + + pg_trgm + + + + pg_trgm模块提供函数和操作符,用于基于三字符组匹配确定字母数字文本的相似度, + 同时还提供支持快速搜索相似字符串的索引操作符类。 + + + + 三字符组(Trigram 或 Trigraph)概念 + + + 三字符组是一组从字符串中取出的三个连续字符。我们可以通过统计两个字符串共享的三字符组数量来度量它们的相似度。 + 这个简单的思想在度量许多自然语言中词的相似度时都非常有效。 + + + + + + 从字符串中提取三字符组时,pg_trgm会忽略非词字符(即非字母数字字符)。 + 在确定字符串所包含的三字符组集合时,认为每个词前面都有两个空格,后面都有一个空格。 + 例如,字符串cat的三字符组集合是 + c、 + ca、 + cat和 + at 。 + 字符串foo|bar的三字符组集合是 + f、 + fo、 + foo、 + oo 、 + b、 + ba、 + bar和 + ar 。 + + + + + + 函数和操作符 + + + pg_trgm模块提供的函数列在中,操作符列在中。 + + + + <filename>pg_trgm</filename>函数 + + + + 函数 + 返回值 + + 描述 + + + + + + + similarity(text, text)similarity + real + 返回一个表示两个参数有多相似的数值。结果范围从 0(表示两个字符串完全不同)到 1(表示两个字符串完全相同)。 + + + show_trgm(text)show_trgm + text[] + + 返回由给定字符串中所有三字符组构成的数组。 + (实际应用中,除了调试之外很少有用。) + + + + word_similarity(text, text) word_similarity + real + + 返回一个数值,表示第一个字符串中的三字符组集合与第二个字符串中的有序三字符组集合中任意连续区段之间的最大相似度。 + 详见下文说明。 + + + + show_limit()show_limit + real + 返回%操作符当前使用的相似度阈值。它设置两个单词被视为足够相似、例如可以看作彼此的拼写错误所需的最小相似度(已弃用)。 + + + set_limit(real)set_limit + real + 设置%操作符当前使用的相似度阈值。阈值必须介于 0 和 1 之间(默认值为 0.3)。返回传入的相同值(已弃用)。 + + + +
+ + 考虑以下示例: +# SELECT word_similarity('word', 'two words'); + word_similarity +----------------- + 0.8 +(1 row) +在第一个字符串中,三字符组集合为{" w"," wo","ord","wor","rd "}。在第二个字符串中,有序三字符组集合为{" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}。第二个字符串的有序三字符组集合中最相似的连续区段是{" w"," wo","wor","ord"},相似度为0.8。 + + + + 这个函数返回的值大致可以理解为第一个字符串与第二个字符串任意子串之间的最大相似度。 + 不过,该函数不会在这个区段的边界处添加填充。 + 因此,除了词边界不匹配的情况外,第二个字符串中额外存在的字符数不会被考虑在内。 + + + + <filename>pg_trgm</filename>操作符 + + + + 操作符 + 返回值 + + 描述 + + + + + + + text % text + boolean + + 如果参数之间的相似度大于pg_trgm.similarity_threshold设置的当前相似度阈值,则返回true。 + + + + text <% text + boolean + + 如果第一个参数中的三字符组集合与第二个参数中的有序三字符组集合某个连续区段之间的相似度大于 + pg_trgm.word_similarity_threshold参数设置的当前词相似度阈值, + 则返回true。 + + + + text %> text + boolean + + <%操作符的交换子。 + + + + text <-> text + real + 返回参数之间的距离,即 1 减去similarity()的值。 + + + text <<-> text + real + 返回参数之间的距离,即 1 减去word_similarity()的值。 + + + text <->> text + real + + <<->操作符的交换子。 + + + + +
+
+ + + GUC 参数 + + + + + + pg_trgm.similarity_threshold (real) + + pg_trgm.similarity_threshold 配置参数 + + + + + + 设置%操作符使用的当前相似度阈值。该阈值必须介于 0 和 1 之间(默认值为 0.3)。 + + + + + pg_trgm.word_similarity_thresholdrealpg_trgm.word_similarity_threshold 配置参数 + + + + 设置<%%>操作符使用的当前词相似度阈值。该阈值必须介于 0 和 1 之间(默认值为 0.6)。 + + + + + + + + 索引支持 + + pg_trgm模块提供 GiST 和 GIN 索引操作符类,允许你为文本列创建索引,以实现非常快速的相似度搜索。这些索引类型支持上述相似度操作符,还支持对LIKEILIKE~~*查询执行基于三字符组的索引搜索。(这些索引不支持等值操作符或简单比较操作符,因此你可能还需要一个常规 B-树索引。) + + + 示例: + + +CREATE TABLE test_trgm (t text); +CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops); + +或者 + +CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops); + + + + 此时,你已经在t列上有了一个可用于相似度搜索的索引。典型查询如下: +SELECT t, similarity(t, 'word') AS sml + FROM test_trgm + WHERE t % 'word' + ORDER BY sml DESC, t; +这将返回文本列中所有与以下词足够相似的值:word,按从最佳匹配到最差匹配的顺序排序。即使在非常大的数据集上,索引也会让这一操作保持高效。 + + 上述查询的一种变体是: +SELECT t, t <-> 'word' AS dist + FROM test_trgm + ORDER BY dist LIMIT 10; +GiST 索引可以相当高效地实现这一点,但 GIN 索引不能。当只需要少量最接近的匹配项时,它通常会优于第一种写法。 + + 还可以使用t列上的索引进行单词相似度查询。例如: +SELECT t, word_similarity('word', t) AS sml + FROM test_trgm + WHERE 'word' <% t + ORDER BY sml DESC, t; +这会返回文本列中所有满足以下条件的值:在其对应的有序三字符组集合中,存在一个连续区段,与以下词的三字符组集合足够相似:word,按从最佳匹配到最差匹配的顺序排序。即使在非常大的数据集上,索引也会让这一操作保持高效。 + + 上述查询的一种变体是: +SELECT t, 'word' <<-> t AS dist + FROM test_trgm + ORDER BY dist LIMIT 10; +GiST 索引可以非常高效地实现这一查询,但 GIN 索引不能。 + + + PostgreSQL9.1 起,这些索引类型还支持以下操作的索引搜索:LIKE以及ILIKE,例如: +SELECT * FROM test_trgm WHERE t LIKE '%foo%bar'; +索引搜索的工作方式是从搜索字符串中提取三字符组,然后在索引中查找这些三字符组。搜索字符串中包含的三字符组越多,索引搜索就越有效。与基于 B-树的搜索不同,搜索字符串不需要在左端锚定。 + + PostgreSQL9.3 起,这些索引类型还支持正则表达式匹配的索引搜索(~以及~*操作符),例如: +SELECT * FROM test_trgm WHERE t ~ '(foo|bar)'; +索引搜索的工作方式是从正则表达式中提取三字符组,然后在索引中查找这些三字符组。能从正则表达式中提取出的三字符组越多,索引搜索就越有效。与基于 B-树的搜索不同,搜索字符串不需要在左端锚定。 + + + 对于LIKE和正则表达式搜索,都要记住:无法提取出三字符组的模式会退化为全索引扫描。 + + + + GiST 和 GIN 索引之间如何取舍,取决于二者各自的相对性能特征;相关讨论见其他章节。 + + + + + 文本搜索集成 + + + 与全文索引结合使用时,三字符组匹配是非常有用的工具。 + 尤其是,它有助于识别那些因拼写错误而无法被全文搜索机制直接匹配的输入词。 + + + + 第一步是生成一个辅助表,其中包含文档中的全部唯一词: + + +CREATE TABLE words AS SELECT word FROM + ts_stat('SELECT to_tsvector(''simple'', bodytext) FROM documents'); + + + 其中documents是一个表,包含我们希望搜索的文本字段bodytext。 + 之所以对to_tsvector函数使用simple配置,而不是使用特定语言的配置, + 是因为我们需要原始的(未经词干提取的)词列表。 + + + + 接下来,在词列上创建一个三字符组索引: + + +CREATE INDEX words_idx ON words USING GIN (word gin_trgm_ops); + + + 现在,可以使用与前面示例类似的SELECT查询,为用户搜索词中拼错的单词提供拼写建议。 + 一个有用的附加测试是要求选出的词长度也与该拼错单词相近。 + + + + + + 由于words表是作为一张独立的静态表生成的,因此需要定期重新生成, + 以便与文档集合保持大致同步。 + 通常没有必要让它始终保持精确同步。 + + + + + + 参考 + + + GiST 开发站点 + + + + Tsearch2 开发站点 + + + + + + 作者 + + + Oleg Bartunov oleg@sai.msu.su,俄罗斯莫斯科,莫斯科大学 + + + Teodor Sigaev teodor@sigaev.ru,俄罗斯莫斯科,Delta-Soft Ltd. + + + Alexander Korotkov a.korotkov@postgrespro.ru,俄罗斯莫斯科,Postgres Professional + + + 文档:Christopher Kings-Lynne + + + 该模块由俄罗斯莫斯科的 Delta-Soft Ltd. 赞助。 + + + +
diff --git a/zh/9.6/pgvisibility.sgml b/zh/9.6/pgvisibility.sgml new file mode 100644 index 00000000..8dfa74f9 --- /dev/null +++ b/zh/9.6/pgvisibility.sgml @@ -0,0 +1,122 @@ + + + + pg_visibility + + + pg_visibility + + + + pg_visibility 模块提供了一种检查表的可见性映射(VM)以及页级可见性信息的手段。 + 它还提供了一些函数,用于检查可见性映射的完整性,并强制重建该映射。 + + + + 页级可见性信息使用三个不同的位来存储。可见性映射中的全部可见(all-visible)位表示该关系对应页中的每个元组对当前及未来的所有事务均可见。可见性映射中的全部冻结(all-frozen)位表示该页中的每个元组都已被冻结;也就是说,在该页上发生插入、更新、删除或锁定操作之前,未来的清理(VACUUM)都不需要修改该页。页头中的PD_ALL_VISIBLE位与可见性映射中的 all-visible 位含义相同,但它存储在数据页本身之中,而非单独的数据结构中。这两个位通常应当一致,但在崩溃恢复之后,有时页面上的 all-visible 位可能已被置位,而可见性映射中的对应位却仍然为零。由于在pg_visibility检查可见性映射之后、检查数据页之前可能发生变更,因此报告的值也可能不一致。任何导致数据损坏的事件也可能导致这些位不一致。 + + + + 显示PD_ALL_VISIBLE位信息的函数比仅查询可见性映射的函数代价高得多,因为它们必须读取关系的数据块,而不只是读取(小得多的)可见性映射。检查关系数据块的函数同样代价高昂。 + + + + 函数 + + + + pg_visibility_map(relation regclass, blkno bigint, all_visible OUT boolean, all_frozen OUT boolean) returns record + + + 返回给定关系中给定块在可见性映射中的 all-visible 位和 all-frozen 位。 + + + + + + pg_visibility(relation regclass, blkno bigint, all_visible OUT boolean, all_frozen OUT boolean, pd_all_visible OUT boolean) returns record + + + 返回给定关系中给定块在可见性映射中的 all-visible 位和 all-frozen 位, + 以及该块的 PD_ALL_VISIBLE 位。 + + + + + + pg_visibility_map(relation regclass, blkno OUT bigint, all_visible OUT boolean, all_frozen OUT boolean) returns setof record + + + 返回给定关系中每个块在可见性映射中的 all-visible 位和 all-frozen 位。 + + + + + + pg_visibility(relation regclass, blkno OUT bigint, all_visible OUT boolean, all_frozen OUT boolean, pd_all_visible OUT boolean) returns setof record + + + + 返回给定关系中每个块在可见性映射中的 all-visible 位和 all-frozen 位, + 以及每个块的 PD_ALL_VISIBLE 位。 + + + + + + pg_visibility_map_summary(relation regclass, all_visible OUT bigint, all_frozen OUT bigint) returns record + + + + 根据可见性映射,返回关系中 all-visible 页面和 all-frozen 页面的数量。 + + + + + + pg_check_frozen(relation regclass, t_ctid OUT tid) returns setof tid + + + + 返回存储在可见性映射中标记为 all-frozen 的页面里、实际上并未冻结的 + 元组的 TID。如果该函数返回的 TID 集合非空,则说明可见性映射已损坏。 + + + + + + pg_check_visible(relation regclass, t_ctid OUT tid) returns setof tid + + + + 返回存储在可见性映射中标记为 all-visible 的页面里、实际上并非对所有事务 + 都可见的元组的 TID。如果该函数返回的 TID 集合非空,则说明可见性映射已损坏。 + + + + + + pg_truncate_visibility_map(relation regclass) returns void + + + + 截断给定关系的可见性映射。如果你认为该关系的可见性映射已损坏并希望强制重建它,此函数就很有用。在执行此函数之后,对该关系执行的第一次VACUUM将会扫描关系中的每一页并重建可见性映射。(在此完成之前,查询会将可见性映射视为全部为零。) + + + + + + + 默认情况下,这些函数只能由超级用户执行。 + + + + + 作者 + + + Robert Haas rhaas@postgresql.org + + + + diff --git a/zh/9.6/planstats.sgml b/zh/9.6/planstats.sgml new file mode 100644 index 00000000..35cfa783 --- /dev/null +++ b/zh/9.6/planstats.sgml @@ -0,0 +1,246 @@ + + + + 规划器如何使用统计信息 + + + 本章建立在所介绍的内容之上,进一步说明规划器如何利用系统统计信息来估计查询各部分可能返回的行数。这是规划过程中的重要组成部分,为代价计算提供了大量原始材料。 + + + + 本章的目的不是详细说明代码,而是概述其工作方式。这或许能让随后想要阅读代码的人更容易入门。 + + + + 行估计示例 + + + 行估计 + 规划器 + + + 下面的示例使用 PostgreSQL 回归测试数据库中的表。所示输出取自版本 8.3,更早(或更晚)的版本行为可能有所不同。另外,由于 ANALYZE 在生成统计信息时使用随机采样,每次重新执行 ANALYZE 后,结果都会略有变化。 + + + 让我们从一个很简单的查询开始: + + +EXPLAIN SELECT * FROM tenk1; + + QUERY PLAN +------------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244) + + + 关于规划器如何确定tenk1的基数,中已经介绍过;这里为了完整起见再重复一次。页数和行数是从pg_class中查得的: + + +SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1'; + + relpages | reltuples +----------+----------- + 358 | 10000 + + + 这些数字反映的是该表最近一次VACUUMANALYZE时的情况。随后,规划器会取得该表当前实际的页数(这是一项开销很小的操作,无需扫描全表)。如果该值与relpages不同,就会相应地缩放reltuples,从而得到当前的行数估计。在上面的示例中,relpages的值是最新的,因此行数估计与reltuples相同。 + + + 接着看一个包含范围条件的示例,其条件位于WHERE子句中: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000; + + QUERY PLAN +-------------------------------------------------------------------------------- + Bitmap Heap Scan on tenk1 (cost=24.06..394.64 rows=1007 width=244) + Recheck Cond: (unique1 < 1000) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..23.80 rows=1007 width=0) + Index Cond: (unique1 < 1000) +规划器检查WHERE子句中的条件,并查找操作符<的选择率函数,所在的系统目录是pg_operator。此函数保存在oprrest列中,本例的条目为scalarltselscalarltsel函数取得unique1的直方图,来源为pg_statistics。手动查询时,查看更简单的pg_stats视图更方便: +SELECT histogram_bounds FROM pg_stats +WHERE tablename='tenk1' AND attname='unique1'; + + histogram_bounds +------------------------------------------------------ + {0,993,1997,3050,4040,5036,5957,7057,8029,9016,9995} +接下来,计算< 1000在直方图中所占的比例,这就是选择率。直方图把范围划分为等频率的桶,因此只需找到目标值所在的桶,计入该桶的部分和前面所有桶的全部。值 1000 显然在第二个桶(993-1997)中。假设每个桶内部的值呈线性分布,可以按如下方式计算选择率: +selectivity = (1 + (1000 - bucket[2].min)/(bucket[2].max - bucket[2].min))/num_buckets + = (1 + (1000 - 993)/(1997 - 993))/10 + = 0.100697 +即一个完整的桶加上第二个桶中的线性比例,再除以桶数。用选择率乘以表的基数,即可计算估计行数。这里所用的表为tenk1: + + +rows = rel_cardinality * selectivity + = 10000 * 0.100697 + = 1007 (rounding off) + + + + 接下来,考虑一个包含等值条件的示例,其条件位于WHERE子句中: +EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'CRAAAA'; + + QUERY PLAN +---------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..483.00 rows=30 width=244) + Filter: (stringu1 = 'CRAAAA'::name) +规划器同样会检查WHERE子句中的条件,查找操作符=的选择率函数,即eqsel。对于等值估计,直方图没有用;应该使用高频值MCV)列表来确定选择率。查看 MCV,并同时查看后面会用到的其他一些列: +SELECT null_frac, n_distinct, most_common_vals, most_common_freqs FROM pg_stats +WHERE tablename='tenk1' AND attname='stringu1'; + +null_frac | 0 +n_distinct | 676 +most_common_vals | {EJAAAA,BBAAAA,CRAAAA,FCAAAA,FEAAAA,GSAAAA,JOAAAA,MCAAAA,NAAAAA,WGAAAA} +most_common_freqs | {0.00333333,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003} + +由于CRAAAA出现在 MCV 列表中,选择率就是最常见值频率(MCF)列表中的对应条目: +selectivity = mcf[3] + = 0.003 +与之前一样,估计行数就是这个值与表的基数的乘积。这里所用的表为tenk1: + + +rows = 10000 * 0.003 + = 30 + + + + 现在考虑同一个查询,但使用一个不在MCV列表中的常量: +EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'xxx'; + + QUERY PLAN +---------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..483.00 rows=15 width=244) + Filter: (stringu1 = 'xxx'::name) +这是一个完全不同的问题:当值MCV列表中时,如何估计选择率。方法是利用该值不在列表中的事实,再结合所有MCV的频率信息: +selectivity = (1 - sum(mvf))/(num_distinct - num_mcv) + = (1 - (0.00333333 + 0.003 + 0.003 + 0.003 + 0.003 + 0.003 + + 0.003 + 0.003 + 0.003 + 0.003))/(676 - 10) + = 0.0014559 +也就是说,把所有MCV的频率加起来,用一减去这个总和,再除以其他非重复值的数量。这相当于假设该列中不属于任何 MCV 的那部分值,均匀分布在其他所有非重复值上。注意,这里没有空值,所以不必考虑它们(否则还应从分子中减去空值比例)。然后照常计算估计行数: +rows = 10000 * 0.0014559 + = 15 (rounding off) + + + + 前面条件为unique1 < 1000的示例,过度简化了scalarltsel的实际行为。现在已经看过 MCV 的使用示例,可以补充一些细节。前面的示例就其涉及的范围而言是正确的,因为unique1是唯一列,所以没有 MCV(显然,没有哪个值会比其他值更常见)。对于非唯一列,通常既有直方图,也有 MCV 列表,而且直方图不包括该列总体中由 MCV 表示的那一部分。这样处理可以使估计更准确。在这种情况下,scalarltsel直接将条件(例如< 1000)应用于 MCV 列表中的每个值,并将满足条件的 MCV 的频率相加。这可以精确估计表中 MCV 所代表部分的选择率。然后按前面所述的方法使用直方图,估计表中非 MCV 部分的选择率,再将两个值结合起来,估计整体选择率。例如,考虑以下查询: +EXPLAIN SELECT * FROM tenk1 WHERE stringu1 < 'IAAAAA'; + + QUERY PLAN +------------------------------------------------------------ + Seq Scan on tenk1 (cost=0.00..483.00 rows=3077 width=244) + Filter: (stringu1 < 'IAAAAA'::name) +我们已经看过stringu1的 MCV 信息,下面是它的直方图: +SELECT histogram_bounds FROM pg_stats +WHERE tablename='tenk1' AND attname='stringu1'; + + histogram_bounds +-------------------------------------------------------------------------------- + {AAAAAA,CQAAAA,FRAAAA,IBAAAA,KRAAAA,NFAAAA,PSAAAA,SGAAAA,VAAAAA,XLAAAA,ZZAAAA} +检查 MCV 列表可以发现,条件stringu1 < 'IAAAAA'被前六个条目满足,而后四个条目不满足,因此总体中 MCV 部分的选择率为: +selectivity = sum(relevant mvfs) + = 0.00333333 + 0.003 + 0.003 + 0.003 + 0.003 + 0.003 + = 0.01833333 +将所有 MCF 相加,还可以得知 MCV 所代表的部分占总体的比例为 0.03033333,因此直方图所代表的部分占比为 0.96966667(这里同样没有空值,否则还需要将其排除)。可以看到,值IAAAAA接近第三个直方图桶的末尾。规划器对不同字符的频率作出一些粗略假设,估计直方图所代表的总体中有 0.298387 的部分小于IAAAAA。然后将 MCV 和非 MCV 两部分的估计结合起来: +selectivity = mcv_selectivity + histogram_selectivity * histogram_fraction + = 0.01833333 + 0.298387 * 0.96966667 + = 0.307669 + +rows = 10000 * 0.307669 + = 3077 (rounding off) +在这个特定的示例中,MCV 列表带来的修正相当小,因为该列的分布实际上相当均匀(统计信息显示这些特定值比其他值更常见,主要是采样误差造成的)。更常见的情况是,某些值明显比其他值更常见;这时,这个复杂过程能够有效提高准确性,因为最常见值的选择率是精确求得的。 + + 现在考虑另一种情形,其中多个条件出现在WHERE子句中: +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000 AND stringu1 = 'xxx'; + + QUERY PLAN +-------------------------------------------------------------------------------- + Bitmap Heap Scan on tenk1 (cost=23.80..396.91 rows=1 width=244) + Recheck Cond: (unique1 < 1000) + Filter: (stringu1 = 'xxx'::name) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..23.80 rows=1007 width=0) + Index Cond: (unique1 < 1000) +规划器假定这两个条件相互独立,因此可以将各个子句的选择率相乘: +selectivity = selectivity(unique1 < 1000) * selectivity(stringu1 = 'xxx') + = 0.100697 * 0.0014559 + = 0.0001466 + +rows = 10000 * 0.0001466 + = 1 (rounding off) +注意,位图索引扫描的估计返回行数只反映用于索引的条件。这一点很重要,因为它会影响后续堆访问的代价估计。 + + 最后,来看一个涉及连接的查询: +EXPLAIN SELECT * FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 50 AND t1.unique2 = t2.unique2; + + QUERY PLAN +-------------------------------------------------------------------------------------- + Nested Loop (cost=4.64..456.23 rows=50 width=488) + -> Bitmap Heap Scan on tenk1 t1 (cost=4.64..142.17 rows=50 width=244) + Recheck Cond: (unique1 < 50) + -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.63 rows=50 width=0) + Index Cond: (unique1 < 50) + -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..6.27 rows=1 width=244) + Index Cond: (unique2 = t1.unique2) +对于tenk1, + unique1 < 50这一限制会在嵌套循环连接之前求值。处理方法与之前的范围查询示例类似。这次,值 50 落在unique1直方图的第一个桶中: +selectivity = (0 + (50 - bucket[1].min)/(bucket[1].max - bucket[1].min))/num_buckets + = (0 + (50 - 0)/(993 - 0))/10 + = 0.005035 + +rows = 10000 * 0.005035 + = 50 (rounding off) +连接的限制条件是t2.unique2 = t1.unique2。操作符就是熟悉的=,不过选择率函数来自oprjoin列,该列属于pg_operator,本例中为eqjoinsel。 + eqjoinsel查询两个表的统计信息,这两个表是tenk2tenk1: + + +SELECT tablename, null_frac,n_distinct, most_common_vals FROM pg_stats +WHERE tablename IN ('tenk1', 'tenk2') AND attname='unique2'; + +tablename | null_frac | n_distinct | most_common_vals +-----------+-----------+------------+------------------ + tenk1 | 0 | -1 | + tenk2 | 0 | -1 | +本例中,没有MCV信息可用于unique2,因为所有值看起来都是唯一的。因此使用的算法仅依赖于两个关系的非重复值数量及其空值比例: +selectivity = (1 - null_frac1) * (1 - null_frac2) * min(1/num_distinct1, 1/num_distinct2) + = (1 - 0) * (1 - 0) / max(10000, 10000) + = 0.0001 +也就是说,对两个关系分别用一减去空值比例,再除以两者非重复值数量中的较大者。预计连接输出的行数,通过将两个输入的笛卡尔积基数乘以选择率来计算: +rows = (outer_cardinality * inner_cardinality) * selectivity + = (50 * 10000) * 0.0001 + = 50 + + + + + 如果这两列存在 MCV 列表,eqjoinsel就会通过直接比较 MCV 列表,确定由 MCV 表示的那部分列值总体中的连接选择率。剩余部分总体的估计则沿用这里展示的相同方法。 + + + + 请注意,我们把inner_cardinality写成 10000,也就是tenk2未经修改的大小。单看EXPLAIN输出,似乎连接行数估计来自 50 * 1,也就是外表行数乘以对tenk2执行每次内表索引扫描得到的估计行数。但事实并非如此:连接关系的大小是在考虑任何具体连接计划之前估计的。如果一切正常,这两种连接大小估算方式会得到大致相同的答案,但由于舍入误差和其他因素,它们有时会出现明显偏差。 + + + + 如果想了解更多细节,表大小(在任何WHERE子句之前)的估计是在src/backend/optimizer/util/plancat.c中完成的。子句选择率的一般逻辑位于src/backend/optimizer/path/clausesel.c。按操作符区分的选择率函数大多位于src/backend/utils/adt/selfuncs.c中。 + + + + + 规划器统计信息与安全 + + + 对表pg_statistic的访问仅限于超级用户,这样普通用户就无法借此了解其他用户表中的内容。有些选择率估算函数会使用用户提供的操作符(查询中出现的操作符或与之相关的操作符)来分析已存储的统计信息。例如,为了判断某个已存储的高频值是否适用,选择率估算器必须运行合适的=操作符,将查询中的常量与已存储的值进行比较。因此,pg_statistic中的数据有可能被传递给用户定义的操作符。精心构造的操作符可以故意泄露传给它的操作数(例如记录到日志中,或写入另一张表),也可能无意中通过在错误消息中显示其值而泄露它们;无论哪种情况,都可能把pg_statistic中的数据暴露给本不应看到这些数据的用户。 + + + + 为了防止这种情况,以下规则适用于所有内置选择率估算函数。在规划查询时,若要使用已存储的统计信息,当前用户必须对该表或相关列具有SELECT权限,或者所用操作符必须是LEAKPROOF(更准确地说,是该操作符所基于的函数必须如此)。否则,选择率估算器会像不存在可用统计信息一样工作,规划器则会按默认或后备假设继续。 + + + 如果用户对表或列不具备所需权限,那么在许多情况下,查询最终都会收到一个权限被拒绝的错误,因此这一机制在实际中是不可见的。但如果用户是通过安全屏障视图读取数据,规划器可能会想要检查一个用户本来无权访问的底层表的统计信息。在这种情况下,操作符应当是 leakproof,否则统计信息不会被使用。对此没有直接反馈,除非计划可能不是最优的。如果怀疑发生了这种情况,可以尝试让权限更高的用户运行该查询,看看是否会得到不同的计划。 + + + + 此限制只适用于规划器需要对来自pg_statistic的一个或多个值执行用户定义操作符的情况。因此,不论访问权限如何,规划器仍可使用通用统计信息,例如空值比例或列中非重复值的数量。 + + + + 第三方扩展中的选择率估算函数如果可能会用用户定义的操作符处理统计信息,也应遵循同样的安全规则。可参考 PostgreSQL 源代码。 + + + diff --git a/zh/9.6/plhandler.sgml b/zh/9.6/plhandler.sgml new file mode 100644 index 00000000..c0c410e6 --- /dev/null +++ b/zh/9.6/plhandler.sgml @@ -0,0 +1,105 @@ + + + + 编写过程语言调用处理器 + + + 过程语言 + 调用处理器 + + + + 凡是不是使用当前针对编译型语言的版本 1接口编写的函数,在被调用时都会经过该语言专用的调用处理器函数。这包括用户定义过程语言中的函数、用 SQL 编写的函数,以及使用版本 0 编译语言接口的函数。调用处理器负责以恰当的方式执行该函数,例如解释所提供的源文本。本章概述如何编写新的过程语言调用处理器。 + + + + 过程语言的调用处理器是一个普通函数,必须使用诸如 C 这样的编译型语言、按照版本 1 接口编写,并在 PostgreSQL 中注册为不接受参数且返回 language_handler 类型。这个特殊伪类型会将该函数标识为调用处理器,并阻止它在 SQL 命令中被直接调用。有关 C 语言调用约定和动态装载的更多细节,见 。 + + + 调用处理器与其他函数的调用方式相同:它接收一个指向 FunctionCallInfoData struct 的指针,其中包含参数值和被调用函数的信息,并应返回一个 Datum 结果(如果要返回 SQL 空值,还需要设置 FunctionCallInfoData 结构体的 isnull 字段)。调用处理器与普通被调用函数的区别在于,FunctionCallInfoData 结构体的 flinfo->fn_oid 字段包含的是实际要调用的函数的 OID,而不是调用处理器自身的 OID。调用处理器必须使用该字段确定应执行哪个函数。此外,传入的参数列表也是按照目标函数的声明设置的,而不是按照调用处理器的声明。 + + + 调用处理器必须从系统目录 pg_proc 中取出该函数的条目,并分析被调用函数的参数类型和返回类型。该函数的 CREATE FUNCTION 命令中的 AS 子句内容,保存在 pg_proc 对应行的 prosrc 列中。这里通常是一段过程语言源文本,但理论上也可以是其他内容,例如某个文件的路径名,或者任何能详细告诉调用处理器该做什么的信息。 + + + + 同一个函数在执行一条 SQL 语句期间往往会被调用很多次。调用处理器可以利用 flinfo->fn_extra 字段,避免重复查找被调用函数的信息。该字段起初为 NULL,但调用处理器可以把它设置为指向与被调用函数有关的信息。在后续调用中,如果 flinfo->fn_extra 已经不是 NULL,就可以直接使用它并跳过信息查找步骤。调用处理器必须确保 flinfo->fn_extra 指向的内存至少能存活到当前查询结束,因为 FmgrInfo 数据结构可能会保留这么久。一种做法是在 flinfo->fn_mcxt 指定的内存上下文中分配这些额外数据;这类数据通常会与 FmgrInfo 本身具有相同的生命周期。不过,处理器也可以选择使用生命周期更长的内存上下文,以便跨查询缓存函数定义信息。 + + + 过程语言函数作为触发器调用时,不会按通常方式传入参数,但 FunctionCallInfoDatacontext 字段会指向一个 TriggerData 结构体,而不是像普通函数调用那样为 NULL。语言调用处理器应提供让过程语言函数获取触发器信息的机制。 + + 下面是用 C 编写的过程语言处理器模板: +#include "postgres.h" +#include "executor/spi.h" +#include "commands/trigger.h" +#include "fmgr.h" +#include "access/heapam.h" +#include "utils/syscache.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(plsample_call_handler); + +Datum +plsample_call_handler(PG_FUNCTION_ARGS) +{ + Datum retval; + + if (CALLED_AS_TRIGGER(fcinfo)) + { + /* + * 作为触发器过程调用 + */ + TriggerData *trigdata = (TriggerData *) fcinfo->context; + + retval = ... + } + else + { + /* + * 作为函数调用 + */ + + retval = ... + } + + return retval; +} +只需在省略号处添加几千行代码,就能完成这个调用处理器。 + + 将处理器函数编译为可加载模块后(参见 ),可用以下命令注册示例过程语言: +CREATE FUNCTION plsample_call_handler() RETURNS language_handler + AS 'filename' + LANGUAGE C; +CREATE LANGUAGE plsample + HANDLER plsample_call_handler; + + + + + 虽然仅提供调用处理器就足以创建一种最小可用的过程语言,但还可以额外提供另外两个函数,以便让该语言更易于使用。它们是验证器内联处理器。可以提供验证器,以便在 期间执行与该语言相关的检查。也可以提供内联处理器,以便让该语言支持通过 命令执行匿名代码块。 + + + + 如果过程语言提供了验证器,它必须声明为一个接受单个 oid 参数的函数。验证器的返回值会被忽略,因此习惯上将其声明为返回 void。在 CREATE FUNCTION 命令创建或更新了一个用该过程语言编写的函数之后,验证器会在该命令结束时被调用。传入的 OID 是该函数在 pg_proc 中对应行的 OID。验证器必须按通常方式取出这一行,并执行适当的检查。首先,应调用 CheckFunctionValidatorAccess(),以诊断那些用户无法通过 CREATE FUNCTION 实现的对验证器的显式调用。典型检查包括确认该语言支持该函数的参数类型和结果类型,以及确认函数体在该语言中语法正确。如果验证器认为该函数没有问题,就应直接返回;如果发现错误,则应通过常规的 ereport() 错误报告机制加以报告。抛出错误会强制事务回滚,从而阻止错误的函数定义被提交。 + + + 验证器函数通常应遵守 参数:关闭该参数时,应跳过代价高昂或依赖上下文的检查。如果语言允许在编译时执行代码,验证器必须禁止会引发这种执行的检查。特别是,pg_dump 会关闭此参数,以便装载过程语言函数,而无需担心副作用或函数体对其他数据库对象的依赖。(因此,调用处理器不应假定验证器已经完整检查了函数。验证器的目的不是让调用处理器省略检查,而是在 CREATE FUNCTION 命令存在明显错误时立即通知用户。)虽然具体检查哪些内容主要由验证器函数自行决定,但要注意,核心 CREATE FUNCTION 代码只有在 check_function_bodies 开启时,才执行函数附带的 SET 子句。因此,关闭 check_function_bodies 时,必须跳过那些结果可能受 GUC 参数影响的检查,以免重新装载转储时误报错误。 + + + 如果过程语言提供了内联处理器,它必须声明为一个接受单个 internal 参数的函数。内联处理器的返回值会被忽略,因此习惯上将其声明为返回 void。当执行指定了该过程语言的 DO 语句时,就会调用内联处理器。实际传入的参数是一个指向 InlineCodeBlock 结构体的指针,其中包含 DO 语句参数的相关信息,特别是待执行的匿名代码块文本。内联处理器应执行这段代码并返回。 + + + + 建议你将以上所有函数声明以及 CREATE LANGUAGE 命令本身都封装进一个扩展中,这样只需执行一条简单的 CREATE EXTENSION 命令就足以安装该语言。关于如何编写扩展,见 。 + + + + 标准发行版中附带的各过程语言,在尝试编写自己的语言调用处理器时都是很好的参考资料。请查看源码树中的 src/pl 子目录。 参考页中也包含一些有用的细节。 + + + diff --git a/zh/9.6/plperl.sgml b/zh/9.6/plperl.sgml new file mode 100644 index 00000000..54b5a25e --- /dev/null +++ b/zh/9.6/plperl.sgml @@ -0,0 +1,1376 @@ + + + + PL/Perl - Perl 过程语言 + + + PL/Perl + + + + Perl + + + PL/Perl 是一种可加载的过程语言,可以用 Perl 编程语言编写 PostgreSQL 函数。 + + 使用 PL/Perl 的主要好处,是可以在存储函数中使用 Perl 提供的多种字符串处理操作符和函数。与使用 PL/pgSQL 提供的字符串函数和控制结构相比,用 Perl 解析复杂字符串可能更容易。 + + + 要在特定数据库中安装 PL/Perl,可使用 + CREATE EXTENSION plperl,或者从 shell 命令行使用 + createlang plperl dbname。 + + + + + + 如果把某种语言安装到template1中,之后创建的所有数据库都会自动安装该语言。 + + + + + + + 使用源码包的用户必须在安装过程中专门启用 PL/Perl 的构建(更多信息见 + )。使用二进制包的用户可能会在单独的 + 子包中找到 PL/Perl。 + + + + + PL/Perl 函数和参数 + + + 要用 PL/Perl 语言创建一个函数,可使用标准的 + 语法: + + +CREATE FUNCTION funcname (argument-types) RETURNS return-type AS $$ + # PL/Perl 函数体 +$$ LANGUAGE plperl; + + + 函数的主体就是普通的 Perl 代码。事实上,PL/Perl 的粘合代码会把它 + 包裹在一个 Perl 子程序中。PL/Perl 函数在标量上下文中被调用,因此 + 不能返回列表。如下文所述,可以通过返回引用来返回 + 非标量值(数组、记录和集合)。 + + + + PL/Perl 也支持用 语句调用的匿名代码块: + + +DO $$ + # PL/Perl 代码 +$$ LANGUAGE plperl; + + + 匿名代码块不接收参数,它可能返回的任何值都会被丢弃。除此之外, + 它的行为与函数完全相同。 + + + + + + 在 Perl 中使用命名嵌套子程序是有危险的,特别是当它们引用外层作用域 + 中的词法变量时。因为 PL/Perl 函数被包装成一个子程序,任何放在其中的 + 命名子程序都会成为嵌套子程序。一般来说,创建通过代码引用 + (coderef)调用的匿名子程序会安全得多。更多信息可见 + perldiag手册页 + 中的Variable "%s" will not stay shared以及 + Variable "%s" is not available,或者在互联网上 + 搜索perl nested named subroutine。 + + + + + CREATE FUNCTION 命令的语法要求函数体写成一个 + 字符串常量。通常对该字符串常量使用美元引用(见 + )最方便。如果选择使用转义 + 字符串语法 E'',则必须将函数体中用到的单引号 + (')和反斜线(\)都写成双份(见 + )。 + + + + 参数和结果的处理与其他 Perl 子程序一样:参数通过 + @_ 传入,结果值通过 return + 返回,或者作为函数中最后一个被求值的表达式返回。 + + + + 例如,一个返回两个整数值中较大值的函数可以定义为: + + +CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ + if ($_[0] > $_[1]) { return $_[0]; } + return $_[1]; +$$ LANGUAGE plperl; + + + + + + + 参数会从数据库编码转换为 PL/Perl 中使用的 UTF-8,返回时再从 + UTF-8 转回数据库编码。 + + + + + 如果一个 SQL 空值空值在 PL/Perl 中被传给一个函数,在 + Perl 中该参数值将呈现为未定义。上述函数定义对于 + 空值输入的行为并不理想(实际上,它会把它们当作零)。我们可以在函数 + 定义中添加 STRICT,让 + PostgreSQL 采取更合理的做法:如果传入空值, + 函数将根本不会被调用,而是自动返回空值结果。另一种方式是在函数体中 + 检查未定义输入。例如,假设我们希望在 + perl_max 的两个参数中一个为空值、另一个非空值时,返回非空值参数而不是空值: + + +CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ + my ($x, $y) = @_; + if (not defined $x) { + return undef if not defined $y; + return $y; + } + return $x if not defined $y; + return $x if $x > $y; + return $y; +$$ LANGUAGE plperl; + + 如上所示,要从 PL/Perl 函数返回 SQL 空值,就返回一个未定义值。 + 无论函数是否为严格函数,都可以这样做。 + + + + 函数参数中任何不是引用的值都是字符串,其格式是相应数据类型的标准 + PostgreSQL 外部文本表示。对于普通的数值或 + 文本类型,Perl 通常会正确处理,程序员一般不必操心。不过在其他情况下, + 参数可能需要转换成 Perl 中更易用的形式。例如,可以用 + decode_bytea 函数把 bytea 类型的 + 参数转换成未转义的二进制数据。 + + + + 同样,返回给 PostgreSQL 的值也必须采用 + 外部文本表示格式。例如,可以用 encode_bytea + 函数把二进制数据转义成适合作为 bytea 类型返回值的形式。 + + + + Perl 可以把 PostgreSQL 数组返回为对 + Perl 数组的引用。这里有一个示例: + + +CREATE OR REPLACE function returns_array() +RETURNS text[][] AS $$ + return [['a"b','c,d'],['e\\f','g']]; +$$ LANGUAGE plperl; + +select returns_array(); + + + + + Perl 把 PostgreSQL 数组作为一个经 bless 的 + PostgreSQL::InServer::ARRAY 对象传递。该对象既可以当作 + 数组引用,也可以当作字符串处理,从而可以运行为 9.1 之前版本的 + PostgreSQL 编写的 Perl 代码,以保持向后兼容性。 + 例如: + + +CREATE OR REPLACE FUNCTION concat_array_elements(text[]) RETURNS TEXT AS $$ + my $arg = shift; + my $result = ""; + return undef if (!defined $arg); + + # 作为数组引用 + for (@$arg) { + $result .= $_; + } + + # 也可以作为字符串使用 + $result .= $arg; + + return $result; +$$ LANGUAGE plperl; + +SELECT concat_array_elements(ARRAY['PL','/','Perl']); + + + + + 多维数组按 Perl 程序员熟悉的方式表示为指向较低维数组的引用, + 而这些数组的元素又是引用。 + + + + + + 复合类型参数被作为哈希的引用传递给函数。哈希的键是复合类型的 + 属性名。这里是一个示例: + + +CREATE TABLE employee ( + name text, + basesalary integer, + bonus integer +); + +CREATE FUNCTION empcomp(employee) RETURNS integer AS $$ + my ($emp) = @_; + return $emp->{basesalary} + $emp->{bonus}; +$$ LANGUAGE plperl; + +SELECT name, empcomp(employee.*) FROM employee; + + + + + PL/Perl 函数也可以用相同的方法返回复合类型:返回一个具有所需属性的 + 哈希的引用。例如: + + +CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; + +SELECT * FROM perl_row(); + + + 在声明的结果数据类型中,凡是哈希中未出现的列都将作为空值返回。 + + + + PL/Perl 函数还可以返回标量类型或复合类型的集合。通常会希望逐行返回, + 这既能加快启动时间,也能避免把整个结果集排入内存。如下所示, + 可以用 return_next 实现这一点。注意,在最后一次 + return_next 之后,必须写上 + return 或者(更好)return + undef。 + + +CREATE OR REPLACE FUNCTION perl_set_int(int) +RETURNS SETOF INTEGER AS $$ + foreach (0..$_[0]) { + return_next($_); + } + return undef; +$$ LANGUAGE plperl; + +SELECT * FROM perl_set_int(5); + +CREATE OR REPLACE FUNCTION perl_set() +RETURNS SETOF testrowperl AS $$ + return_next({ f1 => 1, f2 => 'Hello', f3 => 'World' }); + return_next({ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }); + return_next({ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }); + return undef; +$$ LANGUAGE plperl; + + + 对于较小的结果集,可以返回一个数组引用,其中对简单类型、数组类型 + 和复合类型分别包含标量、数组引用或哈希引用。下面是把整个结果集 + 作为数组引用返回的几个简单示例: + + +CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$ + return [0..$_[0]]; +$$ LANGUAGE plperl; + +SELECT * FROM perl_set_int(5); + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT * FROM perl_set(); + + + + + 如果希望在代码中使用 strict 编译指示,可以有几种 + 选择。对于临时的全局用法,可以 SET + plperl.use_strict 为真。这会影响后续编译的 + PL/Perl 函数,但不会影响当前会话中已经编译的 + 函数。对于永久的全局用法,可以在 + postgresql.conf 文件中将 + plperl.use_strict 设为真。 + + + + 如果要在特定函数中永久启用它,可以简单地把 + +use strict; + + 放在函数体顶部。 + + + + 如果 Perl 版本为 5.10.0 或更高,也可以通过 use + 使用 feature 编译指示。 + + + + + + PL/Perl 中的数据值 + + + 提供给 PL/Perl 函数代码的参数值,就是转换成文本形式的输入参数 + (就像它们由 SELECT 语句显示出来一样)。相反, + returnreturn_next 命令 + 接受任何可作为该函数声明返回类型输入格式的字符串。 + + + + + 内置函数 + + + 从 PL/Perl 访问数据库 + + + 可以通过下列函数在 Perl 函数中访问数据库本身: + + + + + spi_exec_query(query [, max-rows]) spi_exec_query 在 PL/Perl 中 + + + spi_exec_query 执行 SQL 命令,并返回整个行集;返回值是对一个数组的引用,数组元素为哈希引用。只有在确定结果集会比较小时,才应使用此命令。下面是一个查询(SELECT 命令)的示例,指定了可选的最大行数: +$rv = spi_exec_query('SELECT * FROM my_table', 5); +这会返回最多 5 行,来自表 my_table。如果 my_table 包含列 my_column,就可以取得结果中第 $i 行的该列值,方法如下: +$foo = $rv->{rows}[$i]->{my_column}; +要取得 SELECT 查询返回的总行数,可以这样做: +$nrows = $rv->{processed} + + + + + 下面是使用另一种命令类型的示例: + +$query = "INSERT INTO my_table VALUES (1, 'test')"; +$rv = spi_exec_query($query); + + 可以这样访问命令状态(例如 SPI_OK_INSERT): + +$res = $rv->{status}; + + 要获取受影响的行数,可使用: + +$nrows = $rv->{processed}; + + + + + 这里是一个完整的示例: + +CREATE TABLE test ( + i int, + v varchar +); + +INSERT INTO test (i, v) VALUES (1, 'first line'); +INSERT INTO test (i, v) VALUES (2, 'second line'); +INSERT INTO test (i, v) VALUES (3, 'third line'); +INSERT INTO test (i, v) VALUES (4, 'immortal'); + +CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$ + my $rv = spi_exec_query('select i, v from test;'); + my $status = $rv->{status}; + my $nrows = $rv->{processed}; + foreach my $rn (0 .. $nrows - 1) { + my $row = $rv->{rows}[$rn]; + $row->{i} += 200 if defined($row->{i}); + $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v})); + return_next($row); + } + return undef; +$$ LANGUAGE plperl; + +SELECT * FROM test_munge(); + + + + + + + + spi_query(command) + + spi_query + 在 PL/Perl 中 + + + + spi_fetchrow(cursor) + + spi_fetchrow + 在 PL/Perl 中 + + + + spi_cursor_close(cursor) + + spi_cursor_close + 在 PL/Perl 中 + + + + + + + spi_queryspi_fetchrow + 需要配合使用,适用于结果集可能很大,或者希望在行到达时立即返回的情况。 + spi_fetchrow 只能 与 + spi_query 一起使用。下面的示例演示了它们的配合方式: + + +CREATE TYPE foo_type AS (the_num INTEGER, the_text TEXT); + +CREATE OR REPLACE FUNCTION lotsa_md5 (INTEGER) RETURNS SETOF foo_type AS $$ + use Digest::MD5 qw(md5_hex); + my $file = '/usr/share/dict/words'; + my $t = localtime; + elog(NOTICE, "opening file $file at $t" ); + open my $fh, '<', $file # 注意,这里访问了文件! + or elog(ERROR, "cannot open $file for reading: $!"); + my @words = <$fh>; + close $fh; + $t = localtime; + elog(NOTICE, "closed file $file at $t"); + chomp(@words); + my $row; + my $sth = spi_query("SELECT * FROM generate_series(1,$_[0]) AS b(a)"); + while (defined ($row = spi_fetchrow($sth))) { + return_next({ + the_num => $row->{a}, + the_text => md5_hex($words[rand @words]) + }); + } + return; +$$ LANGUAGE plperlu; + +SELECT * from lotsa_md5(500); + + + + + 通常,应重复调用 spi_fetchrow,直到它返回 + undef,这表示已经没有更多行可读。当 + spi_fetchrow 返回 undef 时, + spi_query 返回的游标会被自动释放。如果不打算读取 + 所有行,则应调用 spi_cursor_close 释放游标。 + 否则会导致内存泄漏。 + + + + + + + + spi_prepare(command, argument types) + + spi_prepare + 在 PL/Perl 中 + + + + spi_query_prepared(plan, arguments) + + spi_query_prepared + 在 PL/Perl 中 + + + + spi_exec_prepared(plan [, attributes], arguments) + + spi_exec_prepared + 在 PL/Perl 中 + + + + spi_freeplan(plan) + + spi_freeplan + 在 PL/Perl 中 + + + + + + spi_preparespi_query_preparedspi_exec_prepared,以及 spi_freeplan 实现相同的功能,但用于预备查询。spi_prepare 接受包含编号参数占位符($1、$2 等)的查询字符串,以及由参数类型字符串组成的列表: +$plan = spi_prepare('SELECT * FROM test WHERE id > $1 AND name = $2', + 'INTEGER', 'TEXT'); +调用 spi_prepare 预备查询计划后,就可以使用该计划代替查询字符串。可以将其用于 spi_exec_prepared,其结果与以下函数返回的结果相同:spi_exec_query;也可以用于 spi_query_prepared,它返回游标,行为与 spi_query 完全相同。随后可以将该游标传给 spi_fetchrowspi_exec_prepared 可选的第二个参数是属性哈希引用;目前唯一支持的属性是 limit,用于设置查询返回的最大行数。 + + + 预备查询的优点在于,一个准备好的计划可以用于多次查询执行。当计划 + 不再需要时,可以用 spi_freeplan 将其释放: + +CREATE OR REPLACE FUNCTION init() RETURNS VOID AS $$ + $_SHARED{my_plan} = spi_prepare('SELECT (now() + $1)::date AS now', + 'INTERVAL'); +$$ LANGUAGE plperl; + +CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$ + return spi_exec_prepared( + $_SHARED{my_plan}, + $_[0] + )->{rows}->[0]->{now}; +$$ LANGUAGE plperl; + +CREATE OR REPLACE FUNCTION done() RETURNS VOID AS $$ + spi_freeplan( $_SHARED{my_plan}); + undef $_SHARED{my_plan}; +$$ LANGUAGE plperl; + +SELECT init(); +SELECT add_time('1 day'), add_time('2 days'), add_time('3 days'); +SELECT done(); + + add_time | add_time | add_time +------------+------------+------------ + 2005-12-10 | 2005-12-11 | 2005-12-12 + + 请注意,spi_prepare 中的参数下标由 $1、$2、$3 + 等表示,因此应避免用双引号声明查询字符串,以免轻易引入难以察觉的错误。 + + + + 下面的另一个示例展示了如何在 + spi_exec_prepared 中使用可选参数: + +CREATE TABLE hosts AS SELECT id, ('192.168.1.'||id)::inet AS address + FROM generate_series(1,3) AS id; + +CREATE OR REPLACE FUNCTION init_hosts_query() RETURNS VOID AS $$ + $_SHARED{plan} = spi_prepare('SELECT * FROM hosts + WHERE address << $1', 'inet'); +$$ LANGUAGE plperl; + +CREATE OR REPLACE FUNCTION query_hosts(inet) RETURNS SETOF hosts AS $$ + return spi_exec_prepared( + $_SHARED{plan}, + {limit => 2}, + $_[0] + )->{rows}; +$$ LANGUAGE plperl; + +CREATE OR REPLACE FUNCTION release_hosts_query() RETURNS VOID AS $$ + spi_freeplan($_SHARED{plan}); + undef $_SHARED{plan}; +$$ LANGUAGE plperl; + +SELECT init_hosts_query(); +SELECT query_hosts('192.168.1.0/30'); +SELECT release_hosts_query(); + + query_hosts +----------------- + (1,192.168.1.1) + (2,192.168.1.2) +(2 rows) + + + + + + + + + PL/Perl 中的辅助函数 + + + + + elog(level, msg) + + elog + 在 PL/Perl 中 + + + + + + 发出日志消息或错误信息。可用级别包括 + DEBUGLOGINFO、 + NOTICEWARNING以及ERROR。 + ERROR 会引发错误条件;如果周围的 Perl 代码没有 + 捕获它,错误就会传播到调用查询,导致当前事务或子事务被中止。这实质上 + 等同于 Perl 的 die 命令。其他级别只会生成不同优先级 + 的消息。某一优先级的消息是报告给客户端、写入服务器日志,还是两者兼有, + 由配置变量 和 + 控制。详见 + 。 + + + + + + + quote_literal(string) + + quote_literal + 在 PL/Perl 中 + + + + + + 返回给定字符串适当加引号后的形式,以便把它用作 SQL 语句字符串中的 + 字符串字面量。嵌入的单引号和反斜线会被正确地双写。注意,对于 + undef 输入,quote_literal 会返回 undef; + 如果参数可能为 undef,quote_nullable + 往往更合适。 + + + + + + + quote_nullable(string) + + quote_nullable + 在 PL/Perl 中 + + + + + + 返回给定字符串适当加引号后的形式,以便把它用作 SQL 语句字符串中的 + 字符串字面量;如果参数为 undef,则返回未加引号的字符串 + "NULL"。嵌入的单引号和反斜线会被正确地双写。 + + + + + + + quote_ident(string) + + quote_ident + 在 PL/Perl 中 + + + + + + 返回给定字符串适当加引号后的形式,以便把它用作 SQL 语句字符串中的 + 标识符。只有在必要时才会添加引号,也就是当字符串包含非标识符字符, + 或者会发生大小写折叠时。嵌入的引号会被正确地双写。 + + + + + + + decode_bytea(string) + + decode_bytea + 在 PL/Perl 中 + + + + + + 返回由给定字符串的内容表示的未转义二进制数据,该字符串应为 + bytea 编码形式。 + + + + + + + encode_bytea(string) + + encode_bytea + 在 PL/Perl 中 + + + + + + 返回给定字符串中二进制数据内容的 bytea 编码形式。 + + + + + + + encode_array_literal(array) + + encode_array_literal + 在 PL/Perl 中 + + + + encode_array_literal(array, delimiter) + + + + + 将引用数组的内容以数组字面量格式(见 + )返回为字符串。若参数不是数组引用, + 则原样返回该参数值。如果未指定分隔符,或者分隔符为 undef,则数组 + 字面量元素之间默认使用 ", " 作为分隔符。 + + + + + + + encode_typed_literal(value, typename) + + encode_typed_literal + 在 PL/Perl 中 + + + + + + 将一个 Perl 变量转换为第二个参数指定的数据类型的值,并返回该值的 + 字符串表示。它能正确处理嵌套数组和复合类型的值。 + + + + + + + encode_array_constructor(array) + + encode_array_constructor + 在 PL/Perl 中 + + + + + + 将引用数组的内容以数组构造器格式( + )返回为字符串。其中 + 每个值都使用 quote_nullable 加引号。如果参数 + 不是数组引用,则返回使用 quote_nullable + 加引号后的参数值。 + + + + + + + looks_like_number(string) + + looks_like_number + 在 PL/Perl 中 + + + + + + 如果按照 Perl 的规则看,给定字符串的内容像数字,则返回真值, + 否则返回假值。如果参数为 undef,则返回 undef。前导和尾随空格 + 会被忽略。InfInfinity + 被视为数字。 + + + + + + + is_array_ref(argument) + + is_array_ref + 在 PL/Perl 中 + + + + + 如果给定参数可被视为数组引用,则返回真值;也就是该参数的 + ref 值为 ARRAY 或 + PostgreSQL::InServer::ARRAY。否则返回假值。 + + + + + + + + + + + PL/Perl 中的全局值 + + + 可以使用全局哈希 %_SHARED 在当前会话的整个生命 + 周期内跨函数调用存储数据,包括代码引用。 + + + + 这是共享数据的一个简单示例: + +CREATE OR REPLACE FUNCTION set_var(name text, val text) RETURNS text AS $$ + if ($_SHARED{$_[0]} = $_[1]) { + return 'ok'; + } else { + return "cannot set shared variable $_[0] to $_[1]"; + } +$$ LANGUAGE plperl; + +CREATE OR REPLACE FUNCTION get_var(name text) RETURNS text AS $$ + return $_SHARED{$_[0]}; +$$ LANGUAGE plperl; + +SELECT set_var('sample', 'Hello, PL/Perl! How''s tricks?'); +SELECT get_var('sample'); + + + + + 下面是一个稍复杂一些的、使用代码引用的示例: + + +CREATE OR REPLACE FUNCTION myfuncs() RETURNS void AS $$ + $_SHARED{myquote} = sub { + my $arg = shift; + $arg =~ s/(['\\])/\\$1/g; + return "'$arg'"; + }; +$$ LANGUAGE plperl; + +SELECT myfuncs(); /* 初始化函数 */ + +/* 创建一个使用加引号函数的函数 */ + +CREATE OR REPLACE FUNCTION use_quote(TEXT) RETURNS text AS $$ + my $text_to_quote = shift; + my $qfunc = $_SHARED{myquote}; + return &$qfunc($text_to_quote); +$$ LANGUAGE plperl; + + + (上面的代码也可以替换为单行 + return $_SHARED{myquote}->($_[0]);, + 但代价是可读性会变差。) + + + + 出于安全原因,PL/Perl 会在每个 SQL 角色各自独立的 Perl 解释器中执行该角色调用的函数。这可以防止一个用户意外或恶意地干扰另一个用户的 + PL/Perl 函数行为。每个这样的解释器都有自己的 + %_SHARED 变量值和其他全局状态。因此,当且仅当两个 PL/Perl 函数都由同一 SQL 角色执行时,它们才会共享 + %_SHARED 的值。在某些应用中,一个会话可能会在多个 + SQL 角色下执行代码(通过 SECURITY DEFINER 函数、 + 使用 SET ROLE 等),这时可能需要显式采取措施, + 确保 PL/Perl 函数能够通过 %_SHARED 共享数据。为此, + 要确保需要相互通信的函数由同一用户拥有,并将它们标记为 + SECURITY DEFINER。当然,必须注意不要让这些函数被 + 用于任何非预期用途。 + + + + + 受信任与不受信任的 PL/Perl + + + 受信任的 + PL/Perl + + + + 通常,PL/Perl 会被安装为一种名为 plperl 的 + 受信任的编程语言。在这种设置下,为了保持安全性,某些 Perl + 操作会被禁用。一般来说,受限制的是那些与环境交互的操作,包括文件句柄 + 操作、requireuse + (针对外部模块)。它无法像 C 函数那样访问数据库服务器进程的内部, + 也无法以服务器进程的权限获取操作系统级访问。因此,可以允许任何无特权 + 的数据库用户使用这种语言。 + + + + 下面示例中的函数将无法工作,因为出于安全原因不允许它做文件操作: + +CREATE FUNCTION badfunc() RETURNS integer AS $$ + my $tmpfile = "/tmp/badfile"; + open my $fh, '>', $tmpfile + or elog(ERROR, qq{could not open the file "$tmpfile": $!}); + print $fh "Testing writing to a file\n"; + close $fh or elog(ERROR, qq{could not close the file "$tmpfile": $!}); + return 1; +$$ LANGUAGE plperl; + + 这个函数的创建会失败,因为验证器会捕捉到它使用了禁用的操作。 + + + + 有时需要编写不受这些限制的 Perl 函数。例如,可能需要一个能发送邮件的 + Perl 函数。为处理这类情况,也可以把 PL/Perl 安装成一种 + 不受信任的语言(通常称为 + PL/PerlUPL/PerlU)。 + 在这种情况下,完整的 Perl 语言都可用。安装该语言时,使用语言名 + plperlu 就会选择不受信任的 PL/Perl 变体。 + + + + PL/PerlU 函数的编写者必须注意,函数不能被 + 用于任何非预期用途,因为它能够执行以数据库管理员身份登录的用户所能 + 做的任何事情。请注意,数据库系统只允许数据库超级用户用不受信任的语言创建 + 函数。 + + + + 如果上述函数是由超级用户使用 plperlu 语言创建的, + 执行就会成功。 + + + + 同样地,如果把语言指定为 plperlu 而不是 + plperl,用 Perl 编写的匿名代码块也可以使用原本受限的 + 操作,但调用者必须是超级用户。 + + + + + + 虽然 PL/Perl 函数会为每个 SQL 角色在独立的 + Perl 解释器中运行,但给定会话中执行的所有 + PL/PerlU 函数都运行在同一个 Perl 解释器中 + (而且这个解释器并不是任何一个用于 PL/Perl + 函数的解释器)。这允许 PL/PerlU 函数自由共享 + 数据,但 PL/Perl 与 + PL/PerlU 函数之间不能进行通信。 + + + + + + + Perl 无法在单个进程内支持多个解释器,除非构建时使用了适当的标志, + 即 usemultiplicityuseithreads + 之一。(除非确实需要使用线程,否则更推荐 + usemultiplicity。更多细节见 + perlembed + 手册页。)如果 PL/Perl 使用的是未按这种方式 + 构建的 Perl 副本,那么每个会话中就只能有一个 Perl 解释器,因此任一 + 会话只能执行 PL/PerlU 函数,或者执行全部由同一 + SQL 角色调用的 PL/Perl 函数。 + + + + + + + PL/Perl 触发器 + + + PL/Perl 可用于编写触发器函数。在触发器函数中,哈希引用 + $_TD 包含有关当前触发器事件的信息。 + $_TD 是一个全局变量,对触发器的每一次调用都会得到 + 一个单独的局部值。$_TD 哈希引用包含以下字段: + + + + $_TD->{new}{foo} + + + 列 fooNEW 值 + + + + + + $_TD->{old}{foo} + + + 列 fooOLD 值 + + + + + + $_TD->{name} + + + 被调用的触发器名称 + + + + + + $_TD->{event} + + + 触发器事件:INSERTUPDATE、 + DELETETRUNCATE或者UNKNOWN + + + + + + $_TD->{when} + + + 触发器被调用的时机:BEFORE、 + AFTERINSTEAD OF 或 + UNKNOWN + + + + + + $_TD->{level} + + + 触发器级别:ROWSTATEMENT或者UNKNOWN + + + + + + $_TD->{relid} + + + 触发该触发器的表的 OID + + + + + + $_TD->{table_name} + + + 触发该触发器的表名 + + + + + + $_TD->{relname} + + + 触发该触发器的表名。该字段已弃用,并且可能会在未来版本中移除。 + 请改用 $_TD->{table_name}。 + + + + + + $_TD->{table_schema} + + + 触发该触发器的表所在模式的名称 + + + + + + $_TD->{argc} + + + 触发器函数的参数数目 + + + + + + @{$_TD->{args}} + + + 触发器函数的参数。如果 $_TD->{argc} 为 0,则该字段不存在 + + + + + + + + + 行级触发器可以返回下列之一: + + + + return; + + + 执行操作 + + + + + + "SKIP" + + + 不执行操作 + + + + + + "MODIFY" + + + 指示触发器函数修改了 NEW 行 + + + + + + + 下面的触发器函数示例演示了上述部分内容: +CREATE TABLE test ( + i int, + v varchar +); + +CREATE OR REPLACE FUNCTION valid_id() RETURNS trigger AS $$ + if (($_TD->{new}{i} >= 100) || ($_TD->{new}{i} <= 0)) { + return "SKIP"; # 跳过 INSERT/UPDATE 命令 + } elsif ($_TD->{new}{v} ne "immortal") { + $_TD->{new}{v} .= "(modified by trigger)"; + return "MODIFY"; # 修改行并执行 INSERT/UPDATE 命令 + } else { + return; # 执行 INSERT/UPDATE 命令 + } +$$ LANGUAGE plperl; + +CREATE TRIGGER test_valid_id_trig + BEFORE INSERT OR UPDATE ON test + FOR EACH ROW EXECUTE PROCEDURE valid_id(); + + + + + + PL/Perl 事件触发器 + + + PL/Perl 可用于编写事件触发器函数。在事件触发器函数中,哈希引用 + $_TD 包含有关当前触发器事件的信息。 + $_TD 是一个全局变量,对触发器的每一次调用都会得到 + 一个单独的局部值。$_TD 哈希引用包含以下字段: + + + + $_TD->{event} + + + 该触发器所针对的事件名称。 + + + + + + $_TD->{tag} + + + 该触发器所针对的命令标签。 + + + + + + + 触发器函数的返回值会被忽略。 + + 下面的事件触发器函数示例演示了上述部分内容: +CREATE OR REPLACE FUNCTION perlsnitch() RETURNS event_trigger AS $$ + elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " "); +$$ LANGUAGE plperl; + +CREATE EVENT TRIGGER perl_a_snitch + ON ddl_command_start + EXECUTE PROCEDURE perlsnitch(); + + + + + + PL/Perl 内部机制 + + + + 配置 + + + 本节列出影响 PL/Perl 的配置参数。 + + + + + + + + plperl.on_init (string) + + plperl.on_init 配置参数 + + + + + + 指定在 Perl 解释器首次初始化时、在它被专用于 + plperlplperlu 之前,要执行 + 的 Perl 代码。执行这段代码时 SPI 函数不可用。如果代码因错误而失败, + 就会中止解释器初始化,并把错误传播到调用查询,导致当前事务或子事务 + 被中止。 + + + + Perl 代码仅限一个字符串。更长的代码可以放在模块中,并由 + on_init 字符串加载。示例: + +plperl.on_init = 'require "plperlinit.pl"' +plperl.on_init = 'use lib "/my/app"; use MyApp::PgInit;' + + + + + 任何通过 plperl.on_init 直接或间接加载的模块,都可 + 供 plperl 使用。这可能带来安全风险。要查看已经加载了 + 哪些模块,可以使用: + +DO 'elog(WARNING, join ", ", sort keys %INC)' LANGUAGE plperl; + + + + + 如果 plperl 库被包含在 + 中,初始化就会在 + postmaster 中发生;在这种情况下,需要额外考虑使 postmaster + 不稳定的风险。使用此特性的主要原因是,通过 + plperl.on_init 加载的 Perl 模块只需在 + postmaster 启动时加载一次,之后在各个数据库会话中都可立即使用, + 而无需承担加载开销。不过要记住,这种开销只会对数据库会话使用的 + 第一个 Perl 解释器被省掉,也就是 PL/PerlU,或者第一个调用 + PL/Perl 函数的 SQL 角色所对应的 PL/Perl。数据库会话中后来创建的 + 任何额外 Perl 解释器,都必须重新执行 + plperl.on_init。此外,在 Windows 上预加载完全 + 不会带来节省,因为在 postmaster 进程中创建的 Perl 解释器不会传播到 + 子进程中。 + + + + 这个参数只能在 postgresql.conf 文件中,或在服务器命令行上设置。 + + + + + + + + plperl.on_plperl_init (string) + + plperl.on_plperl_init 配置参数 + + + + + plperl.on_plperlu_init (string) + + plperl.on_plperlu_init 配置参数 + + + + + + 这些参数分别指定在 Perl 解释器被专用于 plperl + 或 plperlu 时要执行的 Perl 代码。这会在数据库 + 会话中首次执行 PL/Perl 或 PL/PerlU 函数时发生;如果因为调用另一种 + 语言,或某个新的 SQL 角色调用了 PL/Perl 函数而需要创建额外解释器时, + 也会发生。这是在 plperl.on_init 完成的任何初始化 + 之后进行的。执行这段代码时 SPI 函数不可用。 + plperl.on_plperl_init 中的 Perl 代码是在对解释器 + 进行锁定之后执行的,因此只能执行受信任的操作。 + + + + 如果代码因错误而失败,就会中止初始化,并把错误传播到调用查询, + 导致当前事务或子事务被中止。在 Perl 中已经完成的任何动作都不会被 + 撤销;不过,该解释器将不再被使用。如果再次使用该语言,就会在一个 + 新的 Perl 解释器中再次尝试初始化。 + + + + 只有超级用户可以更改这些设置。尽管这些设置可以在会话中修改,但这类 + 更改不会影响已经用于执行函数的 Perl 解释器。 + + + + + + + + plperl.use_strict (boolean) + + plperl.use_strict 配置参数 + + + + + + 如果将其设为真,后续编译的 PL/Perl 函数都会启用 + strict 编译指示。该参数不影响当前会话中已经编译 + 的函数。 + + + + + + + + + 限制与缺失特性 + + + PL/Perl 目前仍缺少下列特性,但欢迎为此作出贡献。 + + + + + PL/Perl 函数不能直接调用彼此。 + + + + + + SPI 尚未完全实现。 + + + + + + 如果使用 spi_exec_query 取回非常大的数据集, + 应注意这些数据都会进入内存。可以像前面所示那样,通过使用 + spi_query/spi_fetchrow 来避免 + 这种情况。 + + + 如果集合返回函数通过 return 把一个大型行集 + 返回给 PostgreSQL,也会出现类似问题。 + 前面已经说明过,可以改为对每一行使用 + return_next,从而避免这个问题。 + + + + + + 当会话正常结束(而不是由于致命错误结束)时,任何已经定义的 + END 块都会被执行。目前不会执行其他操作。具体来说, + 文件句柄不会自动刷写,对象也不会自动销毁。 + + + + + + + + + diff --git a/zh/9.6/plpgsql.sgml b/zh/9.6/plpgsql.sgml new file mode 100644 index 00000000..90f59129 --- /dev/null +++ b/zh/9.6/plpgsql.sgml @@ -0,0 +1,4039 @@ + + + + <application>PL/pgSQL</application> — <acronym>SQL</acronym> 过程语言 + + + PL/pgSQL + + + + 概述 + + + PL/pgSQL 是一种用于 PostgreSQL 数据库系统的可载入过程语言。PL/pgSQL 的设计目标,是创建一种具备以下特性的可载入过程语言: + + + + 可用于创建函数和触发器函数, + + + + 为SQL语言增加控制结构, + + + + + 可以执行复杂计算, + + + + 继承所有用户定义的类型、函数和操作符, + + + + 可定义为受服务器信任的语言, + + + + + 便于使用。 + + + + + + + 用 PL/pgSQL 创建的函数,可以用于任何可以使用内置函数的地方。例如,可以创建带有复杂条件逻辑的计算函数,随后用它们定义操作符,或者把它们用于索引表达式。 + + + + 在 PostgreSQL 9.0 及更高版本中,PL/pgSQL 默认安装。不过它仍然是一个可载入模块,因此对安全性特别敏感的管理员也可以选择移除它。 + + + + + 使用<application>PL/pgSQL</application>的优点 + + + SQLPostgreSQL 以及大多数其他关系数据库使用的查询语言。它具有可移植性,也容易学习。但每条 SQL 语句都必须由数据库服务器单独执行。 + + + + 这意味着客户端应用必须把每个查询发送给数据库服务器,等待其处理完成,接收并处理结果,做一些计算,然后再向服务器发送更多查询。这一切都会产生进程间通信开销;如果客户端与数据库服务器不在同一台机器上,还会额外产生网络开销。 + + + + 借助 PL/pgSQL,你可以把一段计算逻辑和一系列查询放在数据库服务器内部执行。这样既具备了过程语言的能力,也保留了 SQL 的易用性,同时还能显著减少客户端/服务器通信开销。 + + + + + + 消除了客户端和服务器之间额外的往返通信 + + + + + 客户端不需要的中间结果不必在服务器与客户端之间编组或传输 + + + + + 可以避免多轮查询解析 + + + + + 与不使用存储函数的应用相比,这可能带来相当可观的性能提升。 + + + + 此外,通过 PL/pgSQL 你可以使用 SQL 的全部数据类型、操作符和函数。 + + + + + 支持的参数和结果数据类型 + + + 用 PL/pgSQL 编写的函数,可以接受服务器支持的任意标量或数组数据类型作为参数,也可以返回这些类型中的任意一种。它们还可以接受或返回任何按名称指定的复合类型(行类型)。也可以把 PL/pgSQL 函数声明为返回 record,这表示结果是一种行类型,其列由调用查询中的指定内容决定,如 所述。 + + + + PL/pgSQL 函数还可以通过使用 VARIADIC 标记声明为接受可变数量的参数。这与 SQL 函数中的行为完全相同,详见 。 + + + PL/pgSQL函数也可以声明为接受和返回多态类型anyelementanyarrayanynonarrayanyenumanyrange。多态函数处理的实际数据类型可以在不同调用之间变化,如所述。示例见 + + + PL/pgSQL 函数还可以声明为返回任意可单独返回的数据类型的集合(或表)。这类函数可以通过对结果集中的每个目标元素执行 RETURN NEXT 生成输出,也可以通过 RETURN QUERY 输出某个查询的结果。 + + + + 最后,如果某个 PL/pgSQL 函数没有有意义的返回值,可以将其声明为返回 void。 + + + + PL/pgSQL 函数也可以声明为使用输出参数,而不是显式指定返回类型。这并没有为该语言增加新的基本能力,但通常会更方便,尤其是在需要返回多个值时。RETURNS TABLE 记法也可以用来代替 RETURNS SETOF。 + + + + 具体示例见 。 + + + + + + <application>PL/pgSQL</application>的结构 + + + 通过执行 命令,可以把用 PL/pgSQL 编写的函数定义到服务器中。这类命令通常类似于: + +CREATE FUNCTION somefunc(integer, text) RETURNS integer +AS 'function body text' +LANGUAGE plpgsql; + + 就 CREATE FUNCTION 而言,函数体只是一个字符串字面量。编写函数体时,通常最好使用美元引用(见 ),而不是普通的单引号语法。如果不使用美元引用,函数体中的任何单引号或反斜线都必须通过双写进行转义。本章几乎所有示例的函数体都使用了美元引用。 + + + + PL/pgSQL 是一种块结构语言。函数体的完整文本必须是一个。块的定义如下: + + + <<label>> + DECLARE + declarations +BEGIN + statements +END label ; + + + + + 块中的每个声明和每条语句都以分号结束。如上所示,嵌套在其他块中的块,必须在 END 之后写分号;但是结束整个函数体的最后一个 END 不需要分号。 + + + + + + 一个常见错误是在 BEGIN 后面立刻写分号。这是不正确的,并会导致语法错误。 + + + + + 只有在你希望通过 EXIT 语句标识某个块,或者希望用块名限定块内声明的变量名时,才需要 label。如果在 END 之后写了标签,它必须与块开始处的标签一致。 + + + + 所有关键字都不区分大小写。标识符除非用双引号括起,否则会像普通 SQL 命令中那样被隐式转换为小写。 + + + + PL/pgSQL 代码中的注释与普通 SQL 相同。双连字符(--)开始一段持续到行尾的注释。/* 开始一段块注释,它会持续到匹配的 */。块注释可以嵌套。 + + + + 块的语句部分中的任意语句都可以是一个子块。子块可用于逻辑分组,或者把变量局部化到一小组语句中。在子块存续期间,子块中声明的变量会遮蔽外层块中的同名变量。不过,如果使用块标签限定外层变量名,仍然可以访问它们。例如: + +CREATE FUNCTION somefunc() RETURNS integer AS $$ +<< outerblock >> +DECLARE + quantity integer := 30; +BEGIN + RAISE NOTICE 'Quantity here is %', quantity; -- Prints 30 + quantity := 50; + -- + -- 创建一个子块 + -- + DECLARE + quantity integer := 80; + BEGIN + RAISE NOTICE 'Quantity here is %', quantity; -- Prints 80 + RAISE NOTICE 'Outer quantity here is %', outerblock.quantity; -- Prints 50 + END; + + RAISE NOTICE 'Quantity here is %', quantity; -- Prints 50 + + RETURN quantity; +END; +$$ LANGUAGE plpgsql; + + + + + + + 实际上,任何 PL/pgSQL 函数体外面都包着一个隐藏的外层块。该块提供函数参数(如果有)的声明,以及一些诸如 FOUND 之类的特殊变量(见 )。这个外层块带有函数名标签,这意味着参数和特殊变量可以用函数名来限定。 + + + + 需要特别注意,不要把BEGIN/ENDPL/pgSQL中用于分组语句的用法,与用于事务控制的同名 SQL 命令混淆。PL/pgSQL中的BEGIN/END只用于分组,不会开始或结束事务。函数和触发器函数总是在外层查询建立的事务中执行 — 它们不能开始或提交该事务,否则就没有可供其执行的上下文。但是,包含EXCEPTION子句的块实际上会形成一个可回滚而不影响外层事务的子事务。详见 + + + + 声明 + + + 在一个块中使用的所有变量,都必须在该块的声明部分声明。(唯一的例外是:在整数范围上迭代的 FOR 循环变量会被自动声明为整数变量;同样,在游标结果上迭代的 FOR 循环变量会被自动声明为记录变量。) + + + + PL/pgSQL 变量可以是任意 SQL 数据类型,例如 integervarcharchar。 + + + + 这里是变量声明的一些示例: + +user_id integer; +quantity numeric(5); +url varchar; +myrow tablename%ROWTYPE; +myfield tablename.columnname%TYPE; +arow RECORD; + + + + + 一个变量声明的一般语法是: + +name CONSTANT type COLLATE collation_name NOT NULL { DEFAULT | := | = } expression ; + + 如果给定 DEFAULT 子句,它会指定进入该块时赋给该变量的初始值。如果没有给出 DEFAULT 子句,则变量会被初始化为 SQL 空值。CONSTANT 选项会阻止该变量在初始化之后再次被赋值,因此其值在整个块的持续期间保持不变。COLLATE 选项指定该变量使用的排序规则(见 )。如果指定了 NOT NULL,给该变量赋空值将导致运行时错误。所有声明为 NOT NULL 的变量都必须指定非空默认值。等号(=)可以代替兼容 PL/SQL 的 :=。 + + + + 变量的默认值会在每次进入该块时重新计算并赋给该变量,而不是每次函数调用只计算一次。因此,例如把 now() 赋给一个 timestamp 类型变量,会使该变量得到当前函数调用时的时间,而不是函数预编译时的时间。 + + + 例如: +quantity integer DEFAULT 32; +url varchar := 'http://mysite.com'; +user_id CONSTANT integer := 10; + + + + + 声明函数参数 + + + 传递给函数的参数使用标识符 $1$2 等命名。也可以为这些 $n 形式的参数名声明别名,以提高可读性。之后既可以使用别名,也可以使用数字标识符来引用参数值。 + + + + 有两种方式可以创建别名。推荐的方式是在 CREATE FUNCTION 命令中直接为参数命名。例如: + +CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$ +BEGIN + RETURN subtotal * 0.06; +END; +$$ LANGUAGE plpgsql; + + 另一种方式是使用声明语法显式声明别名。 + + +name ALIAS FOR $n; + + + 同样的示例,按这种写法如下: + +CREATE FUNCTION sales_tax(real) RETURNS real AS $$ +DECLARE + subtotal ALIAS FOR $1; +BEGIN + RETURN subtotal * 0.06; +END; +$$ LANGUAGE plpgsql; + + + + + + + 这两个示例并不完全等价。在第一种情况下,subtotal 可以写成 sales_tax.subtotal 来引用;但在第二种情况下则不行。(如果我们给内层块附上一个标签,那么 subtotal 可以用那个标签来限定。) + + + + + 更多一些示例: + +CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$ +DECLARE + v_string ALIAS FOR $1; + index ALIAS FOR $2; +BEGIN + -- 这里是一些使用 v_string 和 index 的计算 +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURNS text AS $$ +BEGIN + RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7; +END; +$$ LANGUAGE plpgsql; + + + + + 当 PL/pgSQL 函数声明了输出参数时,输出参数也会像普通输入参数一样获得 $n 名称和可选别名。输出参数本质上是一个初始值为 NULL 的变量,应在函数执行期间给它赋值。该参数的最终值就是返回值。例如,销售税的示例也可以这样写: + + +CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$ +BEGIN + tax := subtotal * 0.06; +END; +$$ LANGUAGE plpgsql; + + + 注意这里省略了 RETURNS real — 当然也可以写上,但那只是冗余。 + + + + 当需要返回多个值时,输出参数尤其有用。下面是一个简单示例: + + +CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$ +BEGIN + sum := x + y; + prod := x * y; +END; +$$ LANGUAGE plpgsql; + + + 如 所述,这实际上会为函数结果创建一个匿名记录类型。如果写了 RETURNS 子句,它必须是 RETURNS record。 + + + + 声明 PL/pgSQL 函数的另一种方式是使用 RETURNS TABLE,例如: + + +CREATE FUNCTION extended_sales(p_itemno int) +RETURNS TABLE(quantity int, total numeric) AS $$ +BEGIN + RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s + WHERE s.itemno = p_itemno; +END; +$$ LANGUAGE plpgsql; + + + 这与声明一个或多个 OUT 参数并指定 RETURNS SETOF sometype 完全等效。 + + + 如果一个PL/pgSQL函数的返回类型被声明为多态类型(anyelement, + anyarrayanynonarrayanyenumanyrange),就会创建特殊参数$0。其数据类型是函数的实际返回类型,根据实际输入类型推导得出(见)。这使函数可以访问其实际返回类型,方式见。 + $0被初始化为 null,函数可以修改它,因此需要时可以用它保存返回值,但这并不是必需的。$0也可以设置别名。例如,下面的函数适用于任何具有+操作符的数据类型: +CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement) +RETURNS anyelement AS $$ +DECLARE + result ALIAS FOR $0; +BEGIN + result := v1 + v2 + v3; + RETURN result; +END; +$$ LANGUAGE plpgsql; + + + + + 把一个或多个输出参数声明为多态类型,也可以达到同样的效果。在这种情况下,不使用特殊参数 $0,输出参数本身就承担相同作用。例如: + + +CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement, + OUT sum anyelement) +AS $$ +BEGIN + sum := v1 + v2 + v3; +END; +$$ LANGUAGE plpgsql; + + + + + + <literal>ALIAS</literal> + + +newname ALIAS FOR oldname; + + + + ALIAS 语法比上一节所展示的更一般化:你可以为任何变量声明别名,而不只是函数参数。它最主要的实际用途,是为那些名称预先固定的变量指定另一个名字,例如触发器函数中的 NEWOLD。 + + + + 示例: + +DECLARE + prior ALIAS FOR old; + updated ALIAS FOR new; + + + + + 由于 ALIAS 为同一个对象提供了两种命名方式,滥用它会让代码变得混乱。最好只把它用于改写那些预先固定的名称。 + + + + + 复制类型 + + +variable%TYPE + + + + %TYPE提供变量或表列的数据类型。可以用它声明将保存数据库值的变量。例如,假设有一个名为user_id的列,位于users表中。要声明一个数据类型与users.user_id相同的变量,可以写: +user_id users.user_id%TYPE; + + + + + 使用 %TYPE 的好处是,你不必知道所引用结构的实际数据类型;更重要的是,如果被引用项的数据类型将来发生变化(例如把 user_id 的类型从 integer 改成 real),你可能就不需要修改函数定义。 + + + + %TYPE 在多态函数中特别有价值,因为内部变量所需的数据类型可能在不同调用之间变化。可以把 %TYPE 应用到函数参数或结果占位符上,以创建合适的变量。 + + + + + + 行类型 + + +name table_name%ROWTYPE; +name composite_type_name; + + + + 复合类型的变量称为变量(或行类型变量)。只要查询的列集合与该变量声明的类型相匹配,这种变量就可以保存 SELECTFOR 查询结果中的整行。行值的各个字段可以使用通常的点号记法访问,例如 rowvar.field。 + + + + 行变量既可以通过 table_name%ROWTYPE 记法声明为与现有表或视图的行具有相同类型,也可以通过给出某个复合类型的名称来声明。(由于每个表都有一个同名的关联复合类型,所以在 PostgreSQL 中实际上写不写 %ROWTYPE 并无区别;不过带 %ROWTYPE 的形式可移植性更好。) + + + + 函数参数也可以是复合类型(完整的表行)。在这种情况下,相应的标识符 $n 就是一个行变量,并且可以从中选取字段,例如 $1.user_id。 + + + 在行类型变量中,只能访问表行中用户定义的列,不能访问 OID 或其他系统列(因为该行可能来自视图)。对于char(n)这样的数据类型,行类型中的字段会继承表字段的大小或精度。 + + + 下面是一个使用复合类型的示例。table1table2 是已经存在的表,它们至少包含下面提到的字段: + + +CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$ +DECLARE + t2_row table2%ROWTYPE; +BEGIN + SELECT * INTO t2_row FROM table2 WHERE ... ; + RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7; +END; +$$ LANGUAGE plpgsql; + +SELECT merge_fields(t.*) FROM table1 t WHERE ... ; + + + + + + + 记录类型 + + +name RECORD; + + + + 记录变量与行类型变量类似,但没有预定义结构。它会在 SELECTFOR 命令为其赋值时采用相应行的实际结构。记录变量的内部结构在每次被赋值时都可能变化。其后果是:在记录变量第一次被赋值之前,它没有任何子结构,任何试图访问其中字段的行为都会引发运行时错误。 + + + + 注意,RECORD 并不是真正的数据类型,它只是一个占位符。还需要认识到,PL/pgSQL 函数被声明为返回 record,与记录变量并不是完全相同的概念,尽管这样的函数可能会用记录变量保存结果。这两种情况下,在编写函数时都不知道实际的行结构;但对于返回 record 的函数,实际结构会在解析调用查询时确定,而记录变量的行结构则可以在运行过程中随时变化。 + + + + + + <application>PL/pgSQL</application>变量的排序规则 + + + 排序规则 + 在 PL/pgSQL 中 + + + + 当 PL/pgSQL 函数具有一个或多个支持排序规则的数据类型参数时,每次函数调用都会根据分配给实际参数的排序规则确定出一个排序规则,如 所述。如果该排序规则能成功确定出来(即参数之间的隐式排序规则没有冲突),那么所有支持排序规则的参数都会被视为隐式带有该排序规则。这会影响函数中那些受排序规则影响的操作。例如,考虑 + + +CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$ +BEGIN + RETURN a < b; +END; +$$ LANGUAGE plpgsql; + +SELECT less_than(text_field_1, text_field_2) FROM table1; +SELECT less_than(text_field_1, text_field_2 COLLATE "C") FROM table1; + + + 第一次调用 less_than 时,比较会使用 text_field_1text_field_2 的共同排序规则;第二次则会使用 C 排序规则。 + + + + 此外,确定出的排序规则也会被视为任何支持排序规则的数据类型局部变量的排序规则。因此,即使把这个函数写成下面这样,其行为也不会有任何不同: + + +CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$ +DECLARE + local_a text := a; + local_b text := b; +BEGIN + RETURN local_a < local_b; +END; +$$ LANGUAGE plpgsql; + + + + + 如果函数没有支持排序规则的数据类型的参数,或者无法为它们确定共同排序规则,那么参数和局部变量将使用其数据类型的默认排序规则(通常是数据库默认排序规则,但对于域类型变量也可能不同)。 + + + + 通过在支持排序规则的数据类型局部变量的声明中加入 COLLATE 选项,可以为其指定不同的排序规则,例如 + + +DECLARE + local_a text COLLATE "en_US"; + + + 这个选项会覆盖按上述规则原本应赋给该变量的排序规则。 + + + + 当然,如果某个函数希望在特定操作中强制使用特定排序规则,也可以在函数内部显式写出 COLLATE 子句。例如: + + +CREATE FUNCTION less_than_c(a text, b text) RETURNS boolean AS $$ +BEGIN + RETURN a < b COLLATE "C"; +END; +$$ LANGUAGE plpgsql; + + + 这会覆盖表达式中表列、参数或局部变量所关联的排序规则,就像在普通 SQL 命令中一样。 + + + + + + 表达式 + + PL/pgSQL语句中使用的所有表达式都由服务器的主SQL执行器处理。例如,编写如下PL/pgSQL语句时: +IF expression THEN ... + + PL/pgSQL会把如下查询交给主 SQL 引擎,以对表达式求值: +SELECT expression +在形成SELECT命令时,所有出现的PL/pgSQL变量名都会被替换为参数,详见。这样,SELECT的查询计划只需准备一次,随后就可以在变量值不同时重复使用。因此,首次使用表达式时,实际发生的事情本质上是执行一个PREPARE命令。例如,如果已经声明了两个整数变量x和y,并写下: +IF x < y THEN ... +背后发生的事情就等同于: +PREPARE statement_name(integer, integer) AS SELECT $1 < $2; +随后,对该预备语句调用EXECUTE,以完成每次IF语句的执行,并把PL/pgSQL变量的当前值作为参数值提供。通常,这些细节对PL/pgSQL用户并不重要,但在诊断问题时了解它们很有帮助。更多信息见。 + + + + + 基本语句 + + 在本节和后续各节中,我们描述PL/pgSQL能够明确理解的所有语句类型。凡是不被识别为这些类型之一的内容,都被视为 SQL 命令并发送给主数据库引擎执行,详见 + + + 赋值 + + PL/pgSQL变量赋值的写法为: +variable { := | = } expression; +如前所述,这种语句中的表达式通过发送给主数据库引擎的 SQLSELECT命令求值。表达式必须产生单个值(如果变量是行变量或记录变量,也可以是行值)。目标变量可以是简单变量(可以用块名限定)、行变量或记录变量的字段,或者由简单变量或字段表示的数组中的元素。可以使用等号(=)代替符合 PL/SQL 的:=。 + + + + 如果该表达式的结果数据类型不匹配变量的数据类型,该值将被强制为变量 + 的类型,就好像做了赋值类型转换一样(见)。 + 如果没有用于所涉及到的数据类型的赋值类型转换可用, + PL/pgSQL解释器将尝试以文本的方式转换结果值,也就 + 是在应用结果类型的输出函数之后再应用变量类型的输入函数。注意如果结果 + 值的字符串形式无法被输入函数所接受,这可能会导致由输入函数产生的运行 + 时错误。 + + + 例如: +tax := subtotal * 0.06; +my_record.user_id := 20; + + + + + + 执行没有结果的命令 + + 对于不返回行的 SQL 命令,例如没有RETURNING子句的INSERT,只需写出命令,就可以在PL/pgSQL函数中执行它。 + + 命令文本中出现的任何PL/pgSQL变量名都被视为参数,然后在运行时将变量的当前值作为参数值提供。这与前面描述的表达式处理完全相同;详情见 + + 以这种方式执行 SQL 命令时,PL/pgSQL可能会缓存和复用命令的执行计划,详见 + + 有时,对表达式或SELECT查询求值但丢弃结果会很有用,例如调用有副作用但没有有用结果值的函数时。要在PL/pgSQL中这样做,可以使用PERFORM语句: +PERFORM query; +这会执行query并丢弃结果。编写query的方式与编写 SQLSELECT命令相同,只需把开头的关键字SELECT替换为PERFORM。对于WITH查询,使用PERFORM,然后用圆括号括住查询。(此时查询只能返回一行。)PL/pgSQL变量会被替换到查询中,方式与不返回结果的命令相同,计划也会以相同方式缓存。另外,特殊变量FOUND在查询至少产生一行时设为真,没有产生行时设为假(见)。 + + + + + 我们可能期望直接写SELECT能实现这个结果,但是当前唯一被接受的方式是PERFORM。一个能返回行的 SQL 命令(例如SELECT)将被当成一个错误拒绝,除非它像下一节中讨论的有一个INTO子句。 + + + + + 一个示例: + +PERFORM create_mv('cs_session_page_requests_mv', my_query); + + + + + + 执行返回单行结果的查询 + + + SELECT INTO + 在 PL/pgSQL 中 + + + + RETURNING INTO + 在 PL/pgSQL 中 + + + 产生单行(可能有多列)结果的 SQL 命令,其结果可以赋给记录变量、行类型变量或标量变量列表。方法是在基本 SQL 命令中添加INTO子句。例如: +SELECT select_expressions INTO STRICT target FROM ...; +INSERT ... RETURNING expressions INTO STRICT target; +UPDATE ... RETURNING expressions INTO STRICT target; +DELETE ... RETURNING expressions INTO STRICT target; +其中,target可以是记录变量、行变量,或逗号分隔的简单变量及记录/行字段列表。PL/pgSQL变量会被替换到查询的其余部分,计划也会被缓存,正如上面描述的不返回行的命令一样。这适用于SELECT, + INSERT/UPDATE/DELETE带有RETURNING的情况,以及返回行集结果的工具命令(例如EXPLAIN)。除了INTO子句以外,SQL 命令的写法与在PL/pgSQL之外的写法相同。 + + + + + 注意带INTOSELECT的这种解释和PostgreSQL常规的SELECT INTO命令有很大的不同,后者的INTO目标是一个新创建的表。如果你想要在一个PL/pgSQL函数中从一个SELECT的结果创建一个表,请使用语法CREATE TABLE ... AS SELECT。 + + + + 如果使用行变量或变量列表作为目标,查询结果列的数量和数据类型必须与目标结构完全匹配,否则会发生运行时错误。如果目标是记录变量,它会自动将自身配置为查询结果列的行类型。 + + + INTO子句几乎可以出现在 SQL 命令中的任何位置。通常它被写成刚好在SELECT命令中的select_expressions列表之前或之后,或者在其他命令类型的命令最后。我们推荐你遵循这种惯例,以防PL/pgSQL的解析器在未来的版本中变得更严格。 + + + 如果STRICT没有在INTO子句中指定,那么target会被设为查询返回的第一行;如果查询没有返回行,则设为空值。(注意,第一行并没有明确定义,除非使用了ORDER BY。)第一行之后的所有结果行都会被丢弃。可以检查特殊的FOUND变量(见),以确定是否返回了行: +SELECT * INTO myrec FROM emp WHERE empname = myname; +IF NOT FOUND THEN + RAISE EXCEPTION 'employee % not found', myname; +END IF; +如果指定STRICT选项,查询必须恰好返回一行,否则会报告运行时错误:NO_DATA_FOUND(没有行)或TOO_MANY_ROWS(多于一行)。如果希望捕获错误,可以使用异常块,例如: +BEGIN + SELECT * INTO STRICT myrec FROM emp WHERE empname = myname; + EXCEPTION + WHEN NO_DATA_FOUND THEN + RAISE EXCEPTION 'employee % not found', myname; + WHEN TOO_MANY_ROWS THEN + RAISE EXCEPTION 'employee % not unique', myname; +END; +成功执行带有STRICT的命令总会将FOUND设为真。 + + + 对于带有RETURNINGINSERT/UPDATE/DELETE,即使没有指定STRICTPL/pgSQL也会针对多于一个返回行的情况报告一个错误。这是因为没有类似于ORDER BY的选项可以用来决定应该返回哪个被影响的行。 + + + 如果print_strict_params已为该函数启用,那么当不满足STRICT要求而抛出错误时,错误消息的DETAIL部分将包含传给查询的参数信息。可以为所有函数更改print_strict_params设置,方法是设置plpgsql.print_strict_params,不过只会影响随后编译的函数。也可以通过编译器选项逐函数启用,例如: +CREATE FUNCTION get_userid(username text) RETURNS int +AS $$ +#print_strict_params on +DECLARE +userid int; +BEGIN + SELECT users.userid INTO STRICT userid + FROM users WHERE users.username = get_userid.username; + RETURN userid; +END; +$$ LANGUAGE plpgsql; +失败时,该函数可能产生如下错误消息: +ERROR: query returned no rows +DETAIL: parameters: $1 = 'nosuchuser' +CONTEXT: PL/pgSQL function get_userid(text) line 6 at SQL statement + + + + + + + STRICT选项匹配 Oracle PL/SQL 的SELECT INTO和相关语句的行为。 + + + + 对于需要处理 SQL 查询的多个结果行的情况,请参见 + + + + + 执行动态命令 + + + 很多时候你将想要在PL/pgSQL函数中产生动态命令,也就是每次执行中会涉及到不同表或不同数据类型的命令。PL/pgSQL通常对于命令所做的缓存计划尝试(如中讨论)在这种情境下无法工作。要处理这一类问题,提供了EXECUTE语句: + + +EXECUTE command-string INTO STRICT target USING expression , ... ; + + + 其中command-string是一个能得到一个包含要被执行命令字符串(类型text)的表达式。可选的target是一个记录变量、一个行变量或者一个逗号分隔的简单变量以及记录/行字段的列表,该命令的结果将存储在其中。可选的USING表达式提供要被插入到该命令中的值。 + + + + 在计算得到的命令字符串中,不会做PL/pgSQL变量的替换。任何所需的变量值必须在命令字符串被构造时被插入其中,或者你可以使用下面描述的参数。 + + + 此外,通过 EXECUTE 执行的命令不会缓存计划,而是在每次运行该语句时重新规划。因此,可以在函数中动态构造命令字符串,对不同的表和列执行操作。 + + + INTO子句指定一个返回行的 SQL 命令的结果应该被赋值到哪里。如果提供了一个行变量或变量列表,它必须完全匹配查询命令结果的结构(当一个记录变量被提供时,它会自动把它自己配置为匹配结果结构)。如果返回多个行,只有第一个行会被赋值给INTO变量。如果没有返回行,NULL 会被赋值给INTO变量。如果没有指定INTO子句,该查询结果会被抛弃。 + + + + 如果给出了STRICT选项,除非该查询刚好产生一行,否则将会报告一个错误。 + + + + 命令字符串可以使用参数值,它们在命令中用$1$2等引用。这些符号引用在USING子句中提供的值。这种方法通常比把数据值作为文本插入命令字符串更可取:它避免了将该值转换为文本以及转换回来的运行时负荷,并且它更不容易被 SQL 注入攻击,因为不需要引用或转义。一个示例是: + +EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2' + INTO c + USING checked_user, checked_date; + + + + 注意,参数符号只能用于数据值 — 如果要使用动态确定的表名或列名,必须将其作为文本插入命令字符串。例如,如果前面的查询需要针对动态选择的表执行,可以这样写: +EXECUTE 'SELECT count(*) FROM ' + || quote_ident(tabname) + || ' WHERE inserted_by = $1 AND inserted <= $2' + INTO c + USING checked_user, checked_date; +更清晰的方式是使用format()%I格式说明来处理表名或列名(以换行分隔的字符串会被连接起来): +EXECUTE format('SELECT count(*) FROM %I ' + 'WHERE inserted_by = $1 AND inserted <= $2', tabname) + INTO c + USING checked_user, checked_date; +参数符号的另一个限制是,它们只能用于SELECTINSERTUPDATE以及DELETE命令。在其他语句类型(统称为工具语句)中,即使只是数据值,也必须以文本形式插入。 + + + 在上面第一个示例中,带有一个简单的常量命令字符串和一些USING参数的EXECUTE命令在功能上等效于直接用PL/pgSQL写的命令,并且允许自动发生PL/pgSQL变量替换。重要的不同之处在于,EXECUTE会在每一次执行时根据当前的参数值重新计划该命令,而PL/pgSQL则可能创建一个通用计划并且将其缓存以便重用。在最佳计划强依赖于参数值的情况中,使用EXECUTE来明确地保证不会选择一个通用计划是很有帮助的。 + + + + EXECUTE目前不支持SELECT INTO。但是可以执行一个纯的SELECT命令并且指定INTO作为EXECUTE本身的一部分。 + + + + PL/pgSQL中的EXECUTE语句与这一由PostgreSQL服务器支持的 SQL 语句无关。服务器的EXECUTE语句不能直接在PL/pgSQL函数中使用(并且也没有必要)。 + + + + 在动态查询中为值加引号 + + + quote_ident + 在 PL/pgSQL 中使用 + + + + quote_literal + 在 PL/pgSQL 中使用 + + + + quote_nullable + 在 PL/pgSQL 中使用 + + + + format + 在 PL/pgSQL 中使用 + + + + 在使用动态命令时,经常需要处理单引号的转义。我们推荐在函数体中使用美元引用来引用固定文本。(如果你有未使用美元引用的旧代码,请参阅中的概述;在把这类代码转换成更合理的写法时,它会帮你省下一些工夫。) + + + + 动态值需要被小心地处理,因为它们可能包含引号字符。一个使用 + format()的示例(这假设你用美元符号引用了函数 + 体,因此引号不需要被双写): + +EXECUTE format('UPDATE tbl SET %I = $1 ' + 'WHERE key = $2', colname) USING newvalue, keyvalue; + + 还可以直接调用引用函数: + +EXECUTE 'UPDATE tbl SET ' + || quote_ident(colname) + || ' = ' + || quote_literal(newvalue) + || ' WHERE key = ' + || quote_literal(keyvalue); + + + + + 这个示例展示了quote_identquote_literal函数的用法(见)。为了安全起见,在插入动态查询之前,包含列名或表名标识符的表达式应先传给quote_ident。在构造出的命令中应作为字符串字面量出现的值,则应传给quote_literal。这两个函数都会采取适当措施,分别返回用双引号或单引号括起来的输入文本,并正确转义其中嵌入的特殊字符。 + + + + 由于quote_literal被标记为STRICT,因此用 null 参数调用时它总会返回 null。在上面的示例中,如果newvaluekeyvalue为 null,整个动态查询字符串都会变成 null,进而导致EXECUTE报错。可以通过使用quote_nullable函数来避免这个问题;它与quote_literal的工作方式相同,只是在用 null 参数调用时会返回字符串NULL。例如: + +EXECUTE 'UPDATE tbl SET ' + || quote_ident(colname) + || ' = ' + || quote_nullable(newvalue) + || ' WHERE key = ' + || quote_nullable(keyvalue); + + 如果正在处理的参数值可能为空值,那么通常应该用quote_nullable来代替quote_literal。 + + + + 通常,必须小心地确保查询中的空值不会产生意料之外的结果。例如如果keyvalue为空值,下面的WHERE子句 + +'WHERE key = ' || quote_nullable(keyvalue) + + 永远不会成功,因为在=操作符中使用空值操作数得到的结果总是空值。如果想让空值像普通键值一样工作,你应该将上面的命令重写成 + +'WHERE key IS NOT DISTINCT FROM ' || quote_nullable(keyvalue) + + (目前,IS NOT DISTINCT FROM的处理效率不如=,因此只有在非常必要时才这样做。关于空值和IS DISTINCT的详细信息请见)。 + + + + 请注意美元符号引用只对引用固定文本有用。尝试写出下面这个示例是一个非常糟糕的主意: + +EXECUTE 'UPDATE tbl SET ' + || quote_ident(colname) + || ' = $$' + || newvalue + || '$$ WHERE key = ' + || quote_literal(keyvalue); + + 因为如果newvalue的内容碰巧含有$$,那么这段代码就会出问题。同样的问题也适用于你选择的任何其他美元符号引用定界符。因此,要想安全地引用事先不知道的文本,必须恰当地使用quote_literalquote_nullablequote_ident。 + + + + 动态 SQL 语句也可以使用format(见)函数来安全地构造。例如: + +EXECUTE format('UPDATE tbl SET %I = %L ' + 'WHERE key = %L', colname, newvalue, keyvalue); + + %I等效于quote_ident并且 + %L等效于quote_nullable。 + format函数可以和 + USING子句一起使用: + +EXECUTE format('UPDATE tbl SET %I = $1 WHERE key = $2', colname) + USING newvalue, keyvalue; + + 这种形式更好,因为变量被以它们天然的数据类型格式处理,而不是无 + 条件地把它们转换成文本并且通过%L引用它们。这也效率 + 更高。 + + + + + 动态命令和EXECUTE的一个更大的示例可以在中找到,它会构建并且执行一个CREATE FUNCTION命令来定义一个新的函数。 + + + + + 获取结果状态 + + + 有好几种方法可以判断一条命令的效果。第一种方法是使用GET DIAGNOSTICS命令,其形式如下: + + +GET CURRENT DIAGNOSTICS variable { = | := } item , ... ; + + + 这条命令允许检索系统状态指示符。CURRENT是一个噪声词(另见中的GET STACKED DIAGNOSTICS)。每个item是一个关键字, 它标识一个要被赋予给指定variable的状态值(变量应具有正确的数据类型来接收状态值)。中展示了当前可用的状态项。冒号等号(:=)可以被用来取代 SQL 标准的=符号。例如: + +GET DIAGNOSTICS integer_var = ROW_COUNT; + + + + + 可用的诊断项 + + + + + 名称 + 类型 + 描述 + + + + + ROW_COUNT + bigint + 最近的SQL命令处理的行数 + + + RESULT_OID + oid + 最近一条SQL命令所插入的最后一行的 OID(仅在对具有 OID 的表执行INSERT命令后有用) + + + PG_CONTEXT + text + 描述当前调用栈的文本行(见 + + + +
+ + + 第二种确定命令效果的方法是检查名为FOUND的特殊变量,类型为boolean。 + 在每次PL/pgSQL函数调用中,FOUND的初始值都是 false。 + 它由以下类型的语句设置: + + + + + SELECT INTO语句在为目标赋上一行值时将FOUND设置为true, + 如果没有返回行则设置为false。 + + + + + PERFORM语句在生成(和丢弃)一个或多个行时将FOUND设置为true, + 如果没有生成行则设置为false。 + + + + UPDATEINSERTDELETE语句在至少影响一行时将FOUND设置为真,如果没有影响行则设置为假。 + + + + FETCH语句在返回行时将FOUND设置为true, + 如果没有返回行则设置为false。 + + + + + MOVE语句在成功重新定位游标时将FOUND设置为true, + 否则设置为false。 + + + + + FORFOREACH语句在迭代一次或多次时将 + FOUND设置为true,否则设置为false。 + 当循环退出时,FOUND会按上述方式设置; + 在循环执行过程中,FOUND不会被循环语句修改, + 尽管它可能会被循环体内的其他语句执行修改。 + + + + + RETURN QUERYRETURN QUERY + EXECUTE语句在查询返回至少一行时将FOUND设置为true, + 如果没有返回行则设置为false。 + + + + + 其他PL/pgSQL语句不会改变FOUND的状态。 + 特别注意,EXECUTE会改变GET DIAGNOSTICS的输出, + 但不会改变FOUND。 + + + + FOUND是每个PL/pgSQL函数的局部变量;任何对它的修改只影响当前的函数。 + + +
+ + + 什么也不做 + + + 有时一个什么也不做的占位语句也很有用。例如,它能够指示 if/then/else 链中故意留出的空分支。可以使用NULL语句达到这个目的: + + +NULL; + + + + + 例如,下面的两段代码是等价的: + +BEGIN + y := x / 0; +EXCEPTION + WHEN division_by_zero THEN + NULL; -- 忽略错误 +END; + + + +BEGIN + y := x / 0; +EXCEPTION + WHEN division_by_zero THEN -- 忽略错误 +END; + + 究竟使用哪一种取决于各人的喜好。 + + + + + + 在 Oracle 的 PL/SQL 中,不允许出现空语句列表,并且因此在这种情况下必须使用NULL语句。而PL/pgSQL允许你什么也不写。 + + + + +
+ + + 控制结构 + + + 控制结构可能是PL/pgSQL中最有用的(以及最重要)的部分了。利用PL/pgSQL的控制结构,你可以以非常灵活而且强大的方法操纵PostgreSQL的数据。 + + + + 从函数返回 + + + 有两个命令让我们能够从函数中返回数据:RETURNRETURN NEXT。 + + + + <command>RETURN</command> + + +RETURN expression; + + + + 带有一个表达式的RETURN用于终止函数并把expression的值返回给调用者。这种形式被用于不返回集合的PL/pgSQL函数。 + + + + 如果函数返回的是标量类型,表达式结果会按照赋值部分的说明自动转换为函数的返回类型。但如果要返回一个复合(行)值,你必须写出一个恰好生成所需列集合的表达式。这可能需要显式类型转换。 + + + + 如果你声明带输出参数的函数,那么就只需要写不带表达式的RETURN。输出参数变量的当前值将被返回。 + + + + 如果你声明函数返回void,一个RETURN语句可以被用来提前退出函数;但是不要在RETURN后面写一个表达式。 + + + + 一个函数的返回值不能是未定义。如果控制到达了函数最顶层块的末尾而没有碰到一个RETURN语句,那么会发生一个运行时错误。不过,这个限制不适用于带输出参数的函数以及返回void的函数。在这些情况中,如果顶层的块结束,将自动执行一个RETURN语句。 + + + + 一些示例: + + +-- 返回一个标量类型的函数 +RETURN 1 + 2; +RETURN scalar_var; + +-- 返回一个复合类型的函数 +RETURN composite_type_var; +RETURN (1, 2, 'three'::text); -- 必须把列类型转换成正确的类型 + + + + + + <command>RETURN NEXT</command> 和 <command>RETURN QUERY</command> + + RETURN NEXT + 在 PL/pgSQL 中 + + + RETURN QUERY + 在 PL/pgSQL 中 + + + +RETURN NEXT expression; +RETURN QUERY query; +RETURN QUERY EXECUTE command-string USING expression , ... ; + + + + 当 PL/pgSQL 函数被声明为返回 SETOF sometype 时,返回过程会略有不同。在这种情况下,要返回的各个项通过一系列 RETURN NEXTRETURN QUERY 命令指定,最后再用一个不带参数的 RETURN 命令表明函数已经执行完毕。RETURN NEXT 可用于标量和复合数据类型;对于复合结果类型,会返回完整的结果RETURN QUERY 会把查询执行结果追加到函数的结果集中。在同一个集合返回函数中,RETURN NEXTRETURN QUERY 可以自由混用,此时它们的结果会被串接起来。 + + + + RETURN NEXTRETURN QUERY实际上不会从函数中返回 — 它们简单地向函数的结果集中追加零或多行。然后会继续执行PL/pgSQL函数中的下一条语句。随着后继的RETURN NEXTRETURN QUERY命令的执行,结果集就建立起来了。最后一个RETURN(应该没有参数)会导致控制退出该函数(或者你可以让控制到达函数的结尾)。 + + + + RETURN QUERY有一种变体RETURN QUERY EXECUTE,它可以动态指定要被执行的查询。可以通过USING向计算出的查询字符串插入参数表达式,这和在EXECUTE命令中的方式相同。 + + + + 如果你声明函数带有输出参数,只需要写不带表达式的RETURN NEXT。在每一次执行时,输出参数变量的当前值将被保存下来用于最终返回为结果的一行。注意为了创建一个带有输出参数的集合返回函数,在有多个输出参数时,你必须声明函数为返回SETOF record;或者如果只有一个类型为sometype的输出参数时,声明函数为SETOF sometype。 + + + + 下面是一个使用RETURN NEXT的函数示例: + + +CREATE TABLE foo (fooid INT, foosubid INT, fooname TEXT); +INSERT INTO foo VALUES (1, 2, 'three'); +INSERT INTO foo VALUES (4, 5, 'six'); + +CREATE OR REPLACE FUNCTION get_all_foo() RETURNS SETOF foo AS +$BODY$ +DECLARE + r foo%rowtype; +BEGIN + FOR r IN + SELECT * FROM foo WHERE fooid > 0 + LOOP + -- 这里可以做一些处理 + RETURN NEXT r; -- 返回 SELECT 的当前行 + END LOOP; + RETURN; +END; +$BODY$ +LANGUAGE plpgsql; + +SELECT * FROM get_all_foo(); + + + + 下面的函数示例使用了RETURN QUERY: + + +CREATE FUNCTION get_available_flightid(date) RETURNS SETOF integer AS +$BODY$ +BEGIN + RETURN QUERY SELECT flightid + FROM flight + WHERE flightdate >= $1 + AND flightdate < ($1 + 1); + + -- Since execution is not finished, we can check whether rows were returned + -- and raise exception if not. + IF NOT FOUND THEN + RAISE EXCEPTION 'No flight at %.', $1; + END IF; + + RETURN; + END; +$BODY$ +LANGUAGE plpgsql; + +-- Returns available flights or raises exception if there are no +-- available flights. +SELECT * FROM get_available_flightid(CURRENT_DATE); + + + + + + + 如上所述,目前RETURN NEXTRETURN QUERY的实现在从函数返回之前会把整个结果集都保存起来。这意味着如果一个PL/pgSQL函数生成一个非常大的结果集,性能可能会很差:数据将被写到磁盘上以避免内存耗尽,但是函数本身在整个结果集都生成之前不会退出。将来的PL/pgSQL版本可能会允许用户定义没有这种限制的集合返回函数。目前,数据开始被写入到磁盘的时机由配置变量控制。拥有足够内存来存储大型结果集的管理员可以考虑增大这个参数。 + + + + + + + 条件语句 + + + IFCASE 语句让你可以根据条件执行不同的命令。PL/pgSQL 有三种形式的 IF: + + + IF ... THEN ... END IF + + + IF ... THEN ... ELSE ... END IF + + + IF ... THEN ... ELSIF ... THEN ... ELSE ... END IF + + + + 以及两种形式的CASE: + + + CASE ... WHEN ... THEN ... ELSE ... END CASE + + + CASE WHEN ... THEN ... ELSE ... END CASE + + + + + + <literal>IF-THEN</literal> + + +IF boolean-expression THEN + statements +END IF; + + + + IF-THEN语句是IF的最简单形式。 如果条件为真,在THENEND IF之间的语句将被执行。否则,将忽略它们。 + + + + 示例: + +IF v_user_id <> 0 THEN + UPDATE users SET email = v_email WHERE user_id = v_user_id; +END IF; + + + + + + <literal>IF-THEN-ELSE</literal> + + +IF boolean-expression THEN + statements +ELSE + statements +END IF; + + + + IF-THEN-ELSE语句对IF-THEN进行了增加,它让你能够指定一组在条件不为真时应该被执行的语句(注意这也包括条件为 NULL 的情况)。 + + + + 示例: + +IF parentid IS NULL OR parentid = '' +THEN + RETURN fullname; +ELSE + RETURN hp_true_filename(parentid) || '/' || fullname; +END IF; + + + +IF v_count > 0 THEN + INSERT INTO users_count (count) VALUES (v_count); + RETURN 't'; +ELSE + RETURN 'f'; +END IF; + + + + + + <literal>IF-THEN-ELSIF</literal> + + +IF boolean-expression THEN + statements + ELSIF boolean-expression THEN + statements + ELSIF boolean-expression THEN + statements + ... + + + ELSE + statements +END IF; + + + + 有时会有多于两种选择。IF-THEN-ELSIF则提供了一个简便的方法来检查多个条件。IF条件会被一个接一个测试,直到找到第一个为真的。然后执行相关语句,然后控制会被交给END IF之后的下一个语句(后续的任何IF条件不会被测试)。如果没有一个IF条件为真,那么ELSE块(如果有)将被执行。 + + + + 这里有一个示例: + + +IF number = 0 THEN + result := 'zero'; +ELSIF number > 0 THEN + result := 'positive'; +ELSIF number < 0 THEN + result := 'negative'; +ELSE + -- 嗯,唯一的其他可能性是 number 为 null + result := 'NULL'; +END IF; + + + + + 关键词ELSIF也可以被拼写成ELSEIF。 + + + + 另一个可以完成相同任务的方法是嵌套IF-THEN-ELSE语句,如下例: + + +IF demo_row.sex = 'm' THEN + pretty_sex := 'man'; +ELSE + IF demo_row.sex = 'f' THEN + pretty_sex := 'woman'; + END IF; +END IF; + + + + + 不过,这种方法需要为每个IF都写一个匹配的END IF,因此当有很多选择时,这种方法比使用ELSIF要麻烦得多。 + + + + + 简单 <literal>CASE</literal> + + +CASE search-expression + WHEN expression , expression ... THEN + statements + WHEN expression , expression ... THEN + statements + ... + ELSE + statements +END CASE; + + + + CASE 的简单形式提供了基于操作数等值判断的条件执行。search-expression 会被计算一次,然后依次与各个 WHEN 子句中的 expression 比较。如果找到匹配,就执行相应的 statements,随后控制转到 END CASE 之后的下一条语句(后续的 WHEN 表达式不会再被计算)。如果没有找到匹配,则执行 ELSE statements;但如果没有 ELSE,则会抛出 CASE_NOT_FOUND 异常。 + + + + 这里是一个简单的示例: + + +CASE x + WHEN 1, 2 THEN + msg := 'one or two'; + ELSE + msg := 'other value than one or two'; +END CASE; + + + + + + 搜索式 <literal>CASE</literal> + + +CASE + WHEN boolean-expression THEN + statements + WHEN boolean-expression THEN + statements + ... + ELSE + statements +END CASE; + + + + CASE的搜索式形式根据布尔表达式的真假进行条件执行。每个WHEN子句的boolean-expression都会依次求值,直到找到一个结果为true的表达式。然后执行相应的statements,控制接着转到END CASE之后的下一条语句。(后续的WHEN表达式不会再被求值。)如果没有找到为真的结果,就执行ELSE statements;但如果不存在ELSE,则会抛出CASE_NOT_FOUND异常。 + + + + 这里是一个示例: + + +CASE + WHEN x BETWEEN 0 AND 10 THEN + msg := 'value is between zero and ten'; + WHEN x BETWEEN 11 AND 20 THEN + msg := 'value is between eleven and twenty'; +END CASE; + + + + 这种形式的CASEIF-THEN-ELSIF完全等价,唯一的区别是:如果执行到被省略的ELSE子句,就会报错,而不是简单地什么也不做。 + + + + + + 简单循环 + + + 循环 + 在 PL/pgSQL 中 + + + + 使用LOOPEXITCONTINUEWHILEFORFOREACH语句,你可以安排PL/pgSQL函数重复一系列命令。 + + + + <literal>LOOP</literal> + + + <<label>> +LOOP + statements +END LOOP label ; + + + + LOOP定义一个无条件的循环,它会无限重复直到被EXITRETURN语句终止。可选的label可以被EXITCONTINUE语句用在嵌套循环中指定这些语句引用的是哪一层循环。 + + + + + <literal>EXIT</literal> + + + EXIT + 在 PL/pgSQL 中 + + + +EXIT label WHEN boolean-expression ; + + + + 如果没有给出label,那么最内层的循环会被终止,然后跟在END LOOP后面的语句会被执行。如果给出了label,那么它必须是当前或者更高层的嵌套循环或者语句块的标签。然后该命名循环或块就会被终止,并且控制会转移到该循环/块相应的END之后的语句上。 + + + + 如果指定了WHEN,只有boolean-expression为真时才会发生循环退出。否则,控制会转移到EXIT之后的语句。 + + + + EXIT可以被用在所有类型的循环中,它并不限于在无条件循环中使用。 + + + + 与 BEGIN 块一起使用时,EXIT 会把控制转交给该块结束后的下一条语句。需要注意的是,为此必须使用标签;未加标签的 EXIT 永远不会被视为匹配某个 BEGIN 块。这与 PostgreSQL 8.4 之前的版本不同,旧版本允许未加标签的 EXIT 匹配 BEGIN 块。 + + + + 示例: + +LOOP + -- 一些计算 + IF count > 0 THEN + EXIT; -- 退出循环 + END IF; +END LOOP; + +LOOP + -- 一些计算 + EXIT WHEN count > 0; -- 和前一个示例相同的结果 +END LOOP; + +<<ablock>> +BEGIN + -- 一些计算 + IF stocks > 100000 THEN + EXIT ablock; -- 导致从 BEGIN 块中退出 + END IF; + -- 当stocks > 100000时,这里的计算将被跳过 +END; + + + + + + <literal>CONTINUE</literal> + + + CONTINUE + 在 PL/pgSQL 中 + + + +CONTINUE label WHEN boolean-expression ; + + + + 如果没有给出label,最内层循环的下一次迭代会开始。也就是,循环体中剩余的所有语句将被跳过,并且控制会返回到循环控制表达式(如果有)来决定是否需要另一次循环迭代。如果label存在,它指定应该继续执行的循环的标签。 + + + + 如果指定了WHEN,该循环的下一次迭代只有在boolean-expression为真时才会开始。否则,控制会传递给CONTINUE后面的语句。 + + + + CONTINUE可以被用在所有类型的循环中,它并不限于在无条件循环中使用。 + + + + 示例: + +LOOP + -- 一些计算 + EXIT WHEN count > 100; + CONTINUE WHEN count < 50; + -- 一些用于 count IN [50 .. 100] 的计算 +END LOOP; + + + + + + + <literal>WHILE</literal> + + + WHILE + 在 PL/pgSQL 中 + + + + <<label>> +WHILE boolean-expression LOOP + statements +END LOOP label ; + + + + 只要boolean-expression被计算为真,WHILE语句就会重复一个语句序列。在每次进入到循环体之前都会检查该表达式。 + + + + 例如: + +WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP + -- 这里是一些计算 +END LOOP; + +WHILE NOT done LOOP + -- 这里是一些计算 +END LOOP; + + + + + + + <literal>FOR</literal>(整型变体) + + + <<label>> +FOR name IN REVERSE expression .. expression BY expression LOOP + statements +END LOOP label ; + + + + 这种形式的FOR会创建一个在一个整数范围上迭代的循环。变量name会自动定义为类型integer并且只在循环内存在(任何该变量名的现有定义在此循环内都将被忽略)。给出范围上下界的两个表达式在进入循环的时候计算一次。如果没有指定BY子句,迭代步长为 1,否则步长是BY中指定的值,该值也只在循环进入时计算一次。如果指定了REVERSE,那么在每次迭代后会减去步长,而不是加上步长。 + + + + 整数FOR循环的一些示例: + +FOR i IN 1..10 LOOP + -- i 在循环中将取值 1,2,3,4,5,6,7,8,9,10 +END LOOP; + +FOR i IN REVERSE 10..1 LOOP + -- i 在循环中将取值 10,9,8,7,6,5,4,3,2,1 +END LOOP; + +FOR i IN REVERSE 10..1 BY 2 LOOP + -- i 在循环中将取值 10,8,6,4,2 +END LOOP; + + + + + 如果下界大于上界(或者在REVERSE情况下是小于),循环体根本不会被执行。而且不会抛出任何错误。 + + + + 如果一个label被附加到FOR循环,那么整数循环变量可以用一个使用那个label的限定名引用。 + + + + + + 遍历查询结果 + + + 使用一种不同类型的FOR循环,你可以通过一个查询的结果进行迭代并且操纵相应的数据。语法是: + + <<label>> +FOR target IN query LOOP + statements +END LOOP label ; + + target 可以是记录变量、行变量,或者由标量变量组成的逗号分隔列表。query 产生的每一行都会依次赋给 target,并为每一行执行一次循环体。下面是一个示例: + +CREATE FUNCTION refresh_mviews() RETURNS integer AS $$ +DECLARE + mviews RECORD; +BEGIN + RAISE NOTICE 'Refreshing all materialized views...'; + + FOR mviews IN + SELECT n.nspname AS mv_schema, + c.relname AS mv_name, + pg_catalog.pg_get_userbyid(c.relowner) AS owner + FROM pg_catalog.pg_class c + LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) + WHERE c.relkind = 'm' + ORDER BY 1 + LOOP + + -- Now "mviews" has one record with information about the materialized view + + RAISE NOTICE 'Refreshing materialized view %.% (owner: %)...', + quote_ident(mviews.mv_schema), + quote_ident(mviews.mv_name), + quote_ident(mviews.owner); + EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name); + END LOOP; + + RAISE NOTICE 'Done refreshing materialized views.'; + RETURN 1; +END; +$$ LANGUAGE plpgsql; + + + 如果循环被一个EXIT语句终止,那么在循环之后你仍然可以访问最后被赋予的行值。 + + + + 在这类 FOR 语句中使用的 query,可以是任何向调用者返回行的 SQL 命令:最常见的是 SELECT,但也可以是带 RETURNING 子句的 INSERTUPDATEDELETE。某些工具命令,如 EXPLAIN,也可以用于此处。 + + + PL/pgSQL变量会被替换到查询文本中,并且如中详细讨论的,查询计划会被缓存以用于可能的重用。 + + + FOR-IN-EXECUTE语句是在行上迭代的另一种方式: + + <<label>> +FOR target IN EXECUTE text_expression USING expression , ... LOOP + statements +END LOOP label ; + + 这个示例类似前面的形式,只不过源查询被指定为一个字符串表达式,在每次进入FOR循环时都会计算它并且重新计划。这允许程序员在一个预先计划好了的命令的速度和一个动态命令的灵活性之间进行选择,就像一个纯EXECUTE语句那样。与EXECUTE一样,可以通过USING将参数值插入到动态命令中。 + + + + 另一种指定要对其结果迭代的查询的方式是将它声明为一个游标。这会在中描述。 + + + + + 遍历数组 + + 这种FOREACH循环与FOR循环很相似,但它遍历的是数组值中的元素,而不是 SQL 查询返回的行。(一般来说,FOREACH旨在遍历复合值表达式的组成部分;未来可能会增加用于遍历数组以外复合值的变体。)用于遍历数组的FOREACH语句为: + <<label>> +FOREACH target SLICE number IN ARRAY expression LOOP + statements +END LOOP label ; + + + + + 如果没有写SLICE,或者指定的是SLICE 0,循环就会遍历计算expression所得数组的各个独立元素。target变量会依次接收每个元素值,并对每个元素执行一次循环体。下面是一个遍历整数数组元素的示例: + + +CREATE FUNCTION sum(int[]) RETURNS int8 AS $$ +DECLARE + s int8 := 0; + x int; +BEGIN + FOREACH x IN ARRAY $1 + LOOP + s := s + x; + END LOOP; + RETURN s; +END; +$$ LANGUAGE plpgsql; + + + 元素会按存储顺序访问,而不管数组有多少维。尽管target通常只是单个变量,但在遍历复合值(记录)数组时,它也可以是一个变量列表。在这种情况下,每个数组元素都会按复合值的连续列给这些变量赋值。 + + + + 当SLICE为正值时,FOREACH遍历的是数组切片,而不是单个元素。SLICE值必须是一个不大于数组维数的整数常量。target变量必须是数组,并且它会依次接收数组值的各个切片,其中每个切片都具有SLICE指定的维数。下面是一个遍历一维切片的示例: + + +CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$ +DECLARE + x int[]; +BEGIN + FOREACH x SLICE 1 IN ARRAY $1 + LOOP + RAISE NOTICE 'row = %', x; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +SELECT scan_rows(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]); + +NOTICE: row = {1,2,3} +NOTICE: row = {4,5,6} +NOTICE: row = {7,8,9} +NOTICE: row = {10,11,12} + + + + + + 捕获错误 + + + 异常 + 在 PL/pgSQL 中 + + + + 默认情况下,PL/pgSQL函数中发生的任何错误都会中止函数及其外围事务的执行。你可以使用带有EXCEPTION子句的BEGIN块来捕获错误并从中恢复。其语法是在普通BEGIN块语法上的扩展: + + + <<label>> + DECLARE + declarations +BEGIN + statements +EXCEPTION + WHEN condition OR condition ... THEN + handler_statements + WHEN condition OR condition ... THEN + handler_statements + ... +END; + + + + + 如果没有发生错误,这种形式的块只是简单地执行所有statements, 并且接着控制转到END之后的下一个语句。但是如果在statements内发生了一个错误,则会放弃对statements的进一步处理,然后控制会转到EXCEPTION列表。系统会在列表中寻找匹配所发生错误的第一个condition。如果找到一个匹配,则执行对应的handler_statements,并且接着把控制转到END之后的下一个语句。如果没有找到匹配,该错误就会传播出去,就好像根本没有EXCEPTION一样:错误可以被一个带有EXCEPTION的外围块捕捉,如果没有这样的块则中止该函数的处理。 + + + + condition的名字可以是中显示的任何名字。一个分类名匹配其中所有的错误。特殊的条件名OTHERS匹配除了QUERY_CANCELEDASSERT_FAILURE之外的所有错误类型(虽然通常并不明智,还是可以用名字捕获这两种错误类型)。条件名是大小写无关的。一个错误条件也可以通过SQLSTATE代码指定,例如以下是等价的: + +WHEN division_by_zero THEN ... +WHEN SQLSTATE '22012' THEN ... + + + + + 如果在选中的handler_statements内发生了新的错误,那么它不能被这个EXCEPTION子句捕获,而是被传播出去。一个外层的EXCEPTION子句可以捕获它。 + + + + 当一个错误被EXCEPTION捕获时,PL/pgSQL函数的局部变量会保持错误发生时的值,但是该块中所有对持久数据库状态的改变都会被回滚。例如,考虑这个片段: + + +INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones'); +BEGIN + UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones'; + x := x + 1; + y := x / 0; +EXCEPTION + WHEN division_by_zero THEN + RAISE NOTICE 'caught division_by_zero'; + RETURN x; +END; + + + 当控制到达对y赋值的地方时,它会带着一个division_by_zero错误失败。这个错误将被EXCEPTION子句捕获。而在RETURN语句中返回的值将是x增加过后的值。但是UPDATE命令的效果将已经被回滚。不过,在该块之前的INSERT将不会被回滚,因此最终的结果是数据库包含Tom Jones但不包含Joe Jones。 + + + + + 进入和退出一个包含EXCEPTION子句的块要比不包含该子句的块开销大得多。因此,只在必要的时候使用EXCEPTION。 + + + + + + <command>UPDATE</command>/<command>INSERT</command>的异常 + + + 这个示例使用异常处理来酌情执行UPDATE或 + INSERT。我们推荐应用使用带有 + ON CONFLICT DO UPDATEINSERT + 而不是真正使用这种模式。下面的示例主要是为了展示 + PL/pgSQL如何控制流程: + + +CREATE TABLE db (a INT PRIMARY KEY, b TEXT); + +CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS +$$ +BEGIN + LOOP + -- 先尝试更新该键 + UPDATE db SET b = data WHERE a = key; + IF found THEN + RETURN; + END IF; + -- 该键不存在,因此尝试插入 + -- 如果其他某人并发地插入同一个键, + -- 就可能发生违反唯一约束的错误 + BEGIN + INSERT INTO db(a,b) VALUES (key, data); + RETURN; + EXCEPTION WHEN unique_violation THEN + -- 什么也不做,并且循环再次尝试 UPDATE + END; + END LOOP; +END; +$$ +LANGUAGE plpgsql; + +SELECT merge_db(1, 'david'); +SELECT merge_db(1, 'dennis'); + + + 这段代码假定unique_violation错误是INSERT造成,并且不是由该表上一个触发器函数中的INSERT导致。如果在该表上有多于一个唯一索引,也可能会发生不正确的行为,因为不管哪个索引导致该错误它都将重试该操作。通过接下来要讨论的特性来检查被捕获的错误是否为所预期的会更安全。 + + + + + 获取错误信息 + + + 异常处理器经常需要识别所发生的具体错误。有两种方法可以获取PL/pgSQL中当前异常的信息:特殊变量和GET STACKED DIAGNOSTICS命令。 + + + + 在一个异常处理器内,特殊变量SQLSTATE包含了对应于被抛出异常的错误代码(可能的错误代码列表见)。特殊变量SQLERRM包含与该异常相关的错误消息。这些变量在异常处理器外是未定义的。 + + + + 在一个异常处理器内,我们也可以用GET STACKED DIAGNOSTICS命令检索有关当前异常的信息,该命令的形式为: + + +GET STACKED DIAGNOSTICS variable { = | := } item , ... ; + + + 每个item是一个关键词,它标识一个被赋予给指定variable(应该具有接收该值的正确数据类型)的状态值。中显示了当前可用的状态项。 + + + + 错误诊断项 + + + + + 名称 + 类型 + 描述 + + + + + + RETURNED_SQLSTATE + text + 该异常的 SQLSTATE 错误代码 + + + + COLUMN_NAME + text + 与异常相关的列名 + + + + CONSTRAINT_NAME + text + 与异常相关的约束名 + + + + PG_DATATYPE_NAME + text + 与异常相关的数据类型名 + + + + MESSAGE_TEXT + text + 该异常的主要消息的文本 + + + + TABLE_NAME + text + 与异常相关的表名 + + + + SCHEMA_NAME + text + 与异常相关的模式名 + + + + PG_EXCEPTION_DETAIL + text + 该异常的详细消息文本(如果有) + + + + PG_EXCEPTION_HINT + text + 该异常的提示消息文本(如果有) + + + + PG_EXCEPTION_CONTEXT + text + 描述产生异常时调用栈的文本行(见 + + + +
+ + + 如果异常没有为一个项设置值,将返回一个空字符串。 + + + + 这里是一个示例: + +DECLARE + text_var1 text; + text_var2 text; + text_var3 text; +BEGIN + -- 某些可能导致异常的处理 + ... +EXCEPTION WHEN OTHERS THEN + GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT, + text_var2 = PG_EXCEPTION_DETAIL, + text_var3 = PG_EXCEPTION_HINT; +END; + + +
+
+ + + + 获得执行位置信息 + + + GET DIAGNOSTICS(之前在中描述)命令检索有关当前执行状态的信息(反之上文讨论的GET STACKED DIAGNOSTICS命令报告先前发生错误时的执行状态信息)。它的PG_CONTEXT状态项可用于标识当前执行位置。状态项PG_CONTEXT将返回一个文本字符串,其中包含一行或多行描述该调用栈的文本。第一行会指向当前函数以及当前正在执行的GET DIAGNOSTICS命令。第二行及其后的行表示调用栈中更上层的调用函数。例如: + + +CREATE OR REPLACE FUNCTION outer_func() RETURNS integer AS $$ +BEGIN + RETURN inner_func(); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$ +DECLARE + stack text; +BEGIN + GET DIAGNOSTICS stack = PG_CONTEXT; + RAISE NOTICE E'--- Call Stack ---\n%', stack; + RETURN 1; +END; +$$ LANGUAGE plpgsql; + +SELECT outer_func(); + +NOTICE: --- Call Stack --- +PL/pgSQL function inner_func() line 5 at GET DIAGNOSTICS +PL/pgSQL function outer_func() line 3 at RETURN +CONTEXT: PL/pgSQL function outer_func() line 3 at RETURN + outer_func + ------------ + 1 +(1 row) + + + + + + GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT返回同类的栈跟踪,但是它描述检测到错误的位置而不是当前位置。 + + +
+ + + 游标 + + + 游标 + 在 PL/pgSQL 中 + + + + 和一次执行整个查询不同,可以建立一个游标来封装该查询,并且接着一次读取该查询结果的一些行。这样做的原因之一是在结果中包含大量行时避免内存不足(不过,PL/pgSQL用户通常不需要担心这些,因为FOR循环在内部会自动使用一个游标来避免内存问题)。一种更有趣的用法是返回一个函数已经创建的游标的引用,允许调用者读取行。这提供了一种有效的方法从函数中返回大型行集。 + + + + 声明游标变量 + + + 所有在PL/pgSQL中对游标的访问都会通过游标变量,它总是特殊的数据类型refcursor。创建游标变量的一种方法是把它声明为一个类型为refcursor的变量。另外一种方法是使用游标声明语法,通常是: + +name NO SCROLL CURSOR ( arguments ) FOR query; + + (为兼容 Oracle,可以用 IS 代替 FOR。)如果指定了 SCROLL,游标就支持向后滚动;如果指定了 NO SCROLL,向后提取会被拒绝;如果两者都未指定,是否允许向后提取则取决于查询本身。如果指定了 arguments,它就是一个由 name datatype 对组成的逗号分隔列表,这些名字会在给定查询中被参数值替换。实际替换这些名字的值会在打开游标时提供。 + + + 一些示例: + +DECLARE + curs1 refcursor; + curs2 CURSOR FOR SELECT * FROM tenk1; + curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key; + + 所有这三个变量都是refcursor类型,但是第一个可以用于任何查询,而第二个已经被绑定了一个完全指定的查询,并且最后一个被绑定了一个参数化查询。(游标被打开时,key将被一个整数参数值替换)。变量curs1被称为未绑定,因为它没有被绑定到任何特定查询。 + + + + + 打开游标 + + + 在能够使用游标检索行之前,必须先将其打开(这等效于 SQL 命令DECLARE CURSOR)。PL/pgSQL有三种形式的OPEN命令,其中两种用于未绑定游标变量,另一种用于已绑定游标变量。 + + + + + 可以通过中描述的FOR语句在不显式打开游标的情况下使用已绑定的游标变量。 + + + + + <command>OPEN FOR</command> <replaceable>query</replaceable> + + +OPEN unbound_cursorvar NO SCROLL FOR query; + + + + 该游标变量会被打开,并被赋予要执行的指定查询。该游标不能已经处于打开状态,并且它必须已被声明为未绑定游标变量(即,一个简单的refcursor变量)。该查询必须是SELECT,或其他会返回行的命令(例如EXPLAIN)。该查询会以与PL/pgSQL中其他 SQL 命令相同的方式处理:替换PL/pgSQL变量名,并缓存查询计划以备后续重用。当一个PL/pgSQL变量被替换到游标查询中时,被替换的是它在OPEN时刻所具有的值;之后对该变量的更改不会影响游标的行为。SCROLLNO SCROLL选项与已绑定游标中的含义相同。 + + + + 一个示例: + +OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey; + + + + + + <command>OPEN FOR EXECUTE</command> + + +OPEN unbound_cursorvar NO SCROLL FOR EXECUTE query_string + USING expression , ... ; + + + + 该游标变量会被打开,并被赋予要执行的指定查询。该游标不能已经处于打开状态,并且必须已被声明为未绑定游标变量(即,一个简单的refcursor变量)。该查询以字符串表达式的形式给出,这一点与EXECUTE命令相同。像往常一样,这提供了灵活性,因此查询计划可以在不同执行之间变化(见),同时也意味着不会在该命令字符串上执行变量替换。与EXECUTE一样,可以通过format()USING把参数值插入动态命令中。SCROLLNO SCROLL选项与已绑定游标中的含义相同。 + + + + 一个示例: + +OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING keyvalue; + + 在这个示例中,表名通过format()插入到查询中。 + col1的比较值通过USING参数插入, + 因此无需加引号。 + + + + + 打开已绑定游标 + + +OPEN bound_cursorvar ( argument_name := argument_value , ... ) ; + + + + 这种形式的OPEN用于打开一个在声明时就已经绑定查询的游标变量。该游标不能已经处于打开状态。当且仅当该游标被声明为接收参数时,才必须提供实际参数值表达式列表。这些值会被替换到查询中。 + + + + 已绑定游标的查询计划始终被视为可缓存;在这种情况下没有与EXECUTE对应的形式。注意,不能在OPEN中指定SCROLLNO SCROLL,因为游标的滚动行为已经确定。 + + + + 使用位置命名记号可以传递参数值。在位置记号中,所有参数都必须按照顺序指定。在命名记号中,每一个参数的名字使用:=与参数表达式分隔。类似于中描述的调用函数,也允许混合位置和命名记号。 + + + + 示例(这些示例使用上面示例中的游标声明): + +OPEN curs2; +OPEN curs3(42); +OPEN curs3(key := 42); + + + + + 因为已绑定游标的查询会进行变量替换,实际上有两种方式把值传给游标:要么向OPEN传入显式参数,要么在查询中隐式引用PL/pgSQL变量。不过,只有在声明已绑定游标之前就已声明的变量才会被替换到查询中。在这两种情况下,要传递的值都在OPEN时确定。例如,获得与上面curs3示例相同效果的另一种方式是 + +DECLARE + key integer; + curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key; +BEGIN + key := 42; + OPEN curs4; + + + + + + + 使用游标 + + + 一旦一个游标已经被打开,那么就可以用这里描述的语句操作它。 + + + + 这些操作不必发生在最初打开该游标的同一个函数中。你可以从函数中返回一个refcursor值,让调用者来操作该游标。(在内部,refcursor值只是一个所谓 portal 的字符串名称,该 portal 包含了该游标活动查询的状态。这个名称可以被传递、赋给其他refcursor变量等等,而不会干扰该 portal。) + + + + 所有 portal 都会在事务结束时被隐式关闭。因此,refcursor值只能在事务结束之前用于引用一个打开的游标。 + + + + <literal>FETCH</literal> + + +FETCH direction { FROM | IN } cursor INTO target; + + + FETCH从游标中检索下一行到目标中,目标可以是一个行变量、记录变量或者逗号分隔的简单变量列表,就像SELECT INTO一样。如果没有下一行,目标会被设置为 NULL。与SELECT INTO一样,可以检查特殊变量FOUND来看是否获得了一行。 + + + direction子句可以是 SQL 命令中允许的任何变体,除了那些能够取得多于一行的。即它可以是 + NEXT、 + PRIOR、 + FIRST、 + LAST、 + ABSOLUTE count、 + RELATIVE count、 + FORWARD或者 + BACKWARD。 + 省略direction和指定NEXT是一样的。在使用count的形式中,count可以是任意的整数值表达式(与SQL命令FETCH不一样,后者仅允许整数常量)。除非游标被使用SCROLL选项声明或打开,否则要求反向移动的direction值很可能会失败。 + + + + cursor必须是一个引用已打开游标 portal 的refcursor变量名。 + + + + 示例: + +FETCH curs1 INTO rowvar; +FETCH curs2 INTO foo, bar, baz; +FETCH LAST FROM curs3 INTO x, y; +FETCH RELATIVE -2 FROM curs4 INTO x; + + + + + + <literal>MOVE</literal> + + +MOVE direction { FROM | IN } cursor; + + + MOVE重新定位一个游标而不检索任何数据。MOVE的工作方式与FETCH完全一样,只是仅重新定位游标,而不返回移动到的行。与SELECT INTO一样,可以检查特殊变量FOUND来看是否存在可移动到的下一行。 + + + 示例: + +MOVE curs1; +MOVE LAST FROM curs3; +MOVE RELATIVE -2 FROM curs4; +MOVE FORWARD 2 FROM curs4; + + + + + + <literal>UPDATE/DELETE WHERE CURRENT OF</literal> + + +UPDATE table SET ... WHERE CURRENT OF cursor; +DELETE FROM table WHERE CURRENT OF cursor; + + + + 当游标定位在某个表行上时,可以使用该游标来标识该行,并对其执行更新或删除。游标查询的形式有一些限制(尤其不能包含分组),并且在这类场景中最好对游标使用 FOR UPDATE。详见 参考页。 + + + + 一个示例: + +UPDATE foo SET dataval = myval WHERE CURRENT OF curs1; + + + + + + <literal>CLOSE</literal> + + +CLOSE cursor; + + + + CLOSE关闭打开游标所基于的 portal。这样就可以在事务结束之前提前释放资源,或者释放该游标变量以便再次打开。 + + + + 一个示例: + +CLOSE curs1; + + + + + + 返回游标 + + + PL/pgSQL函数可以向调用者返回游标。这对于返回多行或多列,特别是非常大的结果集时很有用。要做到这一点,函数需要打开游标,并把游标名返回给调用者(或者直接使用调用者指定或已知的 portal 名称来打开游标)。随后调用者就可以从该游标中提取行。游标既可以由调用者关闭,也会在事务结束时自动关闭。 + + + + 游标使用的 portal 名称既可以由程序员指定,也可以自动生成。要指定 portal 名称,只需在打开refcursor变量之前给它赋一个字符串值。OPEN会把该refcursor变量的字符串值用作底层 portal 的名称。不过,如果refcursor变量为 null,OPEN就会自动生成一个与任何现有 portal 都不冲突的名称,并把它赋回给refcursor变量。 + + + + + 已绑定游标变量会被初始化为表示其名称的字符串值,因此除非程序员在打开游标之前通过赋值覆盖它,否则 portal 名称与游标变量名相同。而未绑定游标变量在初始时默认为空值,因此除非被覆盖,否则它会得到一个自动生成的唯一名称。 + + + + + 下面的示例显示了一个调用者提供游标名字的方法: + + +CREATE TABLE test (col text); +INSERT INTO test VALUES ('123'); + +CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS ' +BEGIN + OPEN $1 FOR SELECT col FROM test; + RETURN $1; +END; +' LANGUAGE plpgsql; + +BEGIN; +SELECT reffunc('funccursor'); +FETCH ALL IN funccursor; +COMMIT; + + + + + 下面的示例使用了自动游标名生成: + + +CREATE FUNCTION reffunc2() RETURNS refcursor AS ' +DECLARE + ref refcursor; +BEGIN + OPEN ref FOR SELECT col FROM test; + RETURN ref; +END; +' LANGUAGE plpgsql; + +-- 需要在一个事务中使用游标。 +BEGIN; +SELECT reffunc2(); + + reffunc2 +-------------------- + <unnamed cursor 1> +(1 row) + +FETCH ALL IN "<unnamed cursor 1>"; +COMMIT; + + + + + 下面的示例展示了从一个函数中返回多个游标的一种方法: + + +CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$ +BEGIN + OPEN $1 FOR SELECT * FROM table_1; + RETURN NEXT $1; + OPEN $2 FOR SELECT * FROM table_2; + RETURN NEXT $2; +END; +$$ LANGUAGE plpgsql; + +-- 需要在一个事务中使用游标。 +BEGIN; + +SELECT * FROM myfunc('a', 'b'); + +FETCH ALL FROM a; +FETCH ALL FROM b; +COMMIT; + + + + + + + 遍历游标结果 + + + 有一种FOR语句的变体,它允许通过游标返回的行进行迭代。语法是: + + + <<label>> +FOR recordvar IN bound_cursorvar ( argument_name := argument_value , ... ) LOOP + statements +END LOOP label ; + + + 该游标变量必须在声明时已经被绑定到某个查询,并且它不能已经被打开。FOR语句会自动打开游标,并且在退出循环时自动关闭游标。当且仅当游标被声明要使用参数时,才必须出现一个实际参数值表达式的列表。这些值会被替换到查询中,采用OPEN期间的方式(见)。 + + + + 变量recordvar会被自动定义为record类型,并且只存在于循环内部(循环中该变量名任何已有定义都会被忽略)。每一个由游标返回的行都会被陆续地赋值给这个记录变量并且执行循环体。 + + + + + + + 错误和消息 + + + 报告错误和消息 + + + RAISE + 在 PL/pgSQL 中 + + + + 报告错误 + 在 PL/pgSQL 中 + + + 使用RAISE语句报告消息和抛出错误。 +RAISE level 'format' , expression , ... USING option = expression , ... ; +RAISE level condition_name USING option = expression , ... ; +RAISE level SQLSTATE 'sqlstate' USING option = expression , ... ; +RAISE level USING option = expression , ... ; +RAISE ; +其中,level选项指定错误的严重程度。允许的级别为DEBUG, + LOGINFO, + NOTICEWARNING以及EXCEPTION,其中EXCEPTION是默认值。EXCEPTION会抛出错误(通常会中止当前事务);其他级别只会生成不同优先级的消息。特定优先级的消息是报告给客户端、写入服务器日志,还是两者都做,由配置变量控制。更多信息见中的说明。 + + level(如果有)之后,可以写一个format(必须是简单的字符串字面量,不能是表达式)。格式字符串指定要报告的错误消息文本。格式字符串之后可以跟上可选的参数表达式,其值将被插入消息中。在格式字符串内,%会被替换为下一个可选参数值的字符串表示。写成%%可以输出一个字面的%。参数个数必须与格式字符串中%占位符的个数匹配,否则会在函数编译期间报错。 + + + 在这个示例中,v_job_id的值将替换字符串中的%: + +RAISE NOTICE 'Calling cs_create_job(%)', v_job_id; + + + + 可以为错误报告附加额外信息,方法是写出USING,后面跟上option = expression项目。每个expression都可以是任意字符串值表达式。允许的option关键字为: + + MESSAGE + + 设置错误消息文本。该选项不能用于在USING之前包含格式字符串的RAISE形式。 + + + + + DETAIL + + 提供错误的详细信息。 + + + + + HINT + + 提供一个提示消息。 + + + + + ERRCODE + + 指定要报告的错误代码(SQLSTATE),可以用中所示的条件名,或者直接作为一个五字符 SQLSTATE 代码。 + + + + + COLUMN + CONSTRAINT + DATATYPE + TABLE + SCHEMA + + 提供一个相关对象的名称。 + + + + + + 这个例子会中止事务,并给出指定的错误消息和提示: +RAISE EXCEPTION 'Nonexistent ID --> %', user_id + USING HINT = 'Please check your user ID'; + + + + + 这两个示例展示了设置 SQLSTATE 的两种等价的方法: + +RAISE 'Duplicate user ID: %', user_id USING ERRCODE = 'unique_violation'; +RAISE 'Duplicate user ID: %', user_id USING ERRCODE = '23505'; + + + + + 还有第二种RAISE语法,其中主参数是要报告的条件名或 SQLSTATE,例如: + +RAISE division_by_zero; +RAISE SQLSTATE '22012'; + + 在这种语法中,USING可以用来提供自定义的错误消息、细节或提示。另一种达到前面示例同样效果的方式是: + +RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id; + + + + + 还有另一种变体是写RAISE USINGRAISE level USING,并把其余内容都放在USING列表里。 + + + + RAISE的最后一种变体根本没有参数。这种形式只能被用在一个BEGIN块的EXCEPTION子句中,它导致当前正在被处理的错误被重新抛出。 + + + + + + 在PostgreSQL 9.1 之前,没有参数的RAISE被解释为重新抛出来自包含活动异常处理器的块的错误。因此一个嵌套在那个处理器中的EXCEPTION子句无法捕捉它,即使RAISE位于嵌套EXCEPTION子句的块中也是这样。这种行为很奇怪,也并不兼容 Oracle 的 PL/SQL。 + + + + + 如果在RAISE EXCEPTION命令中没有指定条件名称或SQLSTATE, + 则默认使用ERRCODE_RAISE_EXCEPTION (P0001)。 + 如果没有指定消息文本,则默认使用条件名称或SQLSTATE作为消息文本。 + + + + + + 当用 SQLSTATE 代码指定错误代码时,你并不受限于预定义错误代码,而是可以选择任何由五位数字和/或大写 ASCII 字母构成的错误代码,唯一不能使用的是 00000。我们建议尽量避免抛出以三个零结尾的错误代码,因为这些是类别代码,只能通过捕获整个类别来捕获这类错误。 + + + + + + + + 检查断言 + + + ASSERT + 在 PL/pgSQL 中 + + + + 断言 + 在 PL/pgSQL 中 + + + + plpgsql.check_asserts配置参数 + + + + ASSERT语句是一种向 + PL/pgSQL函数中插入调试检查的方便方法。 + + +ASSERT condition , message ; + + + condition是一个布尔 + 表达式,它被期望总是计算为真。如果确实如此, + ASSERT语句不会再做什么。但如果结果是假或者空值,那么将发生一个ASSERT_FAILURE异常(如果在计算 + condition时发生错误, + 它会被报告为一个普通错误)。 + + + + 如果提供了可选的message, + 它是一个结果(如果不为 NULL)被用来替换默认错误消息文本 + assertion failed的表达式(如果 + condition失败)。 + message表达式在 + 断言成功的普通情况下不会被计算。 + + + + 通过配置参数plpgsql.check_asserts可以启用或者禁用断言测试, + 这个参数接受布尔值且默认为on。如果这个参数为off, + 则ASSERT语句什么也不做。 + + + + 注意ASSERT是为了检测程序的 bug,而不是 + 报告普通的错误情况。如果要报告普通错误,请使用前面介绍的 + RAISE语句。 + + + + + + + + 触发器函数 + + + 触发器 + 在 PL/pgSQL 中 + + + + PL/pgSQL可以被用来在数据更改或者数据库事件上定义触发器函数。触发器函数用CREATE FUNCTION命令创建,它被声明为一个没有参数并且返回类型为trigger(对于数据更改触发器)或者event_trigger(对于数据库事件触发器)的函数。名为TG_something的特殊局部变量将被自动创建用以描述触发该调用的条件。 + + + + 数据更改触发器 + + + 一个数据更改触发器被声明为一个没有参数并且返回类型为trigger的函数。注意,如下所述,即便该函数准备接收一些在CREATE TRIGGER中指定的参数 — 这类参数通过TG_ARGV传递,也必须把它声明为没有参数。 + + + 当一个PL/pgSQL函数作为触发器被调用时,会在顶层块中自动创建一些特殊变量。它们是: + + NEW + + 数据类型为RECORD;该变量保存行级触发器中用于INSERT/UPDATE操作的新数据行。在语句级触发器和DELETE操作中,该变量未被赋值。 + + + + + OLD + + 数据类型为RECORD;该变量保存行级触发器中用于UPDATE/DELETE操作的旧数据行。在语句级触发器和INSERT操作中,该变量未被赋值。 + + + + + TG_NAME + + + 数据类型为 name;包含实际触发的触发器名称的变量。 + + + + + + TG_WHEN + + + 数据类型为 text;根据触发器定义,其值为字符串 BEFOREAFTERINSTEAD OF。 + + + + + + TG_LEVEL + + + 数据类型为 text;根据触发器定义,其值为字符串 ROWSTATEMENT。 + + + + + + TG_OP + + + 数据类型为 text;表示触发器对应操作的字符串:INSERTUPDATEDELETETRUNCATE。 + + + + + + TG_RELID + + 数据类型为oid;导致触发器调用的表的对象 ID。 + + + + + TG_RELNAME + + + 数据类型为 name;导致触发器调用的表名。该变量已弃用,未来版本可能移除;请改用 TG_TABLE_NAME。 + + + + + + TG_TABLE_NAME + + + 数据类型为 name;导致触发器调用的表名。 + + + + + + TG_TABLE_SCHEMA + + + 数据类型为 name;导致触发器调用的表所在模式名。 + + + + + + TG_NARGS + + 数据类型为integerCREATE TRIGGER语句中传给触发器函数的参数个数。 + + + + + TG_ARGV[] + + 数据类型为text数组;来自CREATE TRIGGER语句的参数。索引从 0 开始;非法索引(小于 0 或大于等于tg_nargs)返回 null。 + + + + + + + 一个触发器函数必须返回NULL或者是一个与触发器为之引发的表结构完全相同的记录/行值。 + + + + BEFORE 行级触发器可以返回 null,以通知触发器管理器跳过该行后续的操作(也就是说,不再触发后续触发器,并且不会对该行执行INSERT/UPDATE/DELETE)。如果返回非 null 值,则操作会继续进行,并使用该行值。返回一个不同于原始NEW的行值会改变即将插入或更新的行。因此,如果触发器函数希望触发动作正常成功而不修改行值,就必须返回NEW(或与之相等的值)。若要修改将被存储的行,可以直接替换NEW中的单个值并返回修改后的NEW,或者构造一个完整的新记录/行来返回。对于作用于DELETE的 before 触发器,返回值本身没有直接效果,但必须为非 null 才能让触发器动作继续。注意,在DELETE触发器中NEW为 null,因此通常没有理由返回它。在DELETE触发器中,常见写法是返回OLD。 + + + + INSTEAD OF触发器(总是行级触发器,并且只能用于视图)能够返回 null 来表示它们没有执行任何更新,并且对该行剩余的操作应被跳过(即后续的触发器不会被引发,并且该行不会被计入外围INSERT/UPDATE/DELETE的行影响状态中)。否则应该返回一个非 null 值用以表示该触发器执行了所请求的操作。对于INSERTUPDATE操作,返回值应该是NEW,触发器函数可能对它进行了修改来支持INSERT RETURNINGUPDATE RETURNING(这也将影响被传递给任何后续触发器的行值,或者被传递给带有ON CONFLICT DO UPDATEINSERT语句中一个特殊的EXCLUDED别名引用)。对于DELETE操作,返回值应该是OLD。 + + + + 一个AFTER行级触发器或一个BEFOREAFTER语句级触发器的返回值总是会被忽略,因此也可以返回 null。不过,任何这些类型的触发器可能仍会通过抛出一个错误来中止整个操作。 + + + + 展示了PL/pgSQL中一个触发器函数的示例。 + + + + 一个 <application>PL/pgSQL</application> 触发器函数 + + + 这个示例触发器保证:任何时候一个行在表中被插入或更新时,当前用户名和时间也会被标记在该行中。并且它会检查给出了一个雇员的姓名以及薪水是一个正值。 + + + +CREATE TABLE emp ( + empname text, + salary integer, + last_date timestamp, + last_user text +); + +CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$ + BEGIN + -- Check that empname and salary are given + IF NEW.empname IS NULL THEN + RAISE EXCEPTION 'empname cannot be null'; + END IF; + IF NEW.salary IS NULL THEN + RAISE EXCEPTION '% cannot have null salary', NEW.empname; + END IF; + + -- Who works for us when they must pay for it? + IF NEW.salary < 0 THEN + RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; + END IF; + + -- Remember who changed the payroll when + NEW.last_date := current_timestamp; + NEW.last_user := current_user; + RETURN NEW; + END; +$emp_stamp$ LANGUAGE plpgsql; + +CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp + FOR EACH ROW EXECUTE PROCEDURE emp_stamp(); + + + + + 另一种记录对表的改变的方法涉及到创建一个新表来为每一个发生的插入、更新或删除保持一行。这种方法可以被认为是对一个表的改变的审计。展示了PL/pgSQL中一个审计触发器函数的示例。 + + + + 一个用于审计的 <application>PL/pgSQL</application> 触发器函数 + + + 这个示例触发器保证了在emp表上的任何插入、更新或删除一行的动作都被记录(即审计)在emp_audit表中。当前时间和用户名会被记录到行中,还有在其上执行的操作类型。 + + + +CREATE TABLE emp ( + empname text NOT NULL, + salary integer +); + +CREATE TABLE emp_audit( + operation char(1) NOT NULL, + stamp timestamp NOT NULL, + userid text NOT NULL, + empname text NOT NULL, + salary integer +); + +CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$ + BEGIN + -- + -- Create a row in emp_audit to reflect the operation performed on emp, + -- make use of the special variable TG_OP to work out the operation. + -- + IF (TG_OP = 'DELETE') THEN + INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*; + RETURN OLD; + ELSIF (TG_OP = 'UPDATE') THEN + INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*; + RETURN NEW; + ELSIF (TG_OP = 'INSERT') THEN + INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*; + RETURN NEW; + END IF; + RETURN NULL; -- result is ignored since this is an AFTER trigger + END; +$emp_audit$ LANGUAGE plpgsql; + +CREATE TRIGGER emp_audit +AFTER INSERT OR UPDATE OR DELETE ON emp + FOR EACH ROW EXECUTE PROCEDURE process_emp_audit(); + + + + + 前一个示例的一个变体使用视图把主表和审计表连接起来,以显示每个条目最后一次被修改的时间。这种方法仍然记录了表更改的完整审计轨迹,同时也提供了一个简化视图,只显示从审计轨迹中导出的每个条目的最后修改时间戳。展示了PL/pgSQL中一个定义在视图上的审计触发器示例。 + + + + 一个用于审计的 <application>PL/pgSQL</application> 视图触发器函数 + + + 这个示例在视图上使用了一个触发器让它变得可更新,并且确保视图中一行的任何插入、更新或删除被记录(即审计)在emp_audit表中。当前时间和用户名会被与执行的操作类型一起记录,并且该视图会显示每一行的最后修改时间。 + + + +CREATE TABLE emp ( + empname text PRIMARY KEY, + salary integer +); + +CREATE TABLE emp_audit( + operation char(1) NOT NULL, + userid text NOT NULL, + empname text NOT NULL, + salary integer, + stamp timestamp NOT NULL +); + +CREATE VIEW emp_view AS + SELECT e.empname, + e.salary, + max(ea.stamp) AS last_updated + FROM emp e + LEFT JOIN emp_audit ea ON ea.empname = e.empname + GROUP BY 1, 2; + +CREATE OR REPLACE FUNCTION update_emp_view() RETURNS TRIGGER AS $$ + BEGIN + -- + -- Perform the required operation on emp, and create a row in emp_audit + -- to reflect the change made to emp. + -- + IF (TG_OP = 'DELETE') THEN + DELETE FROM emp WHERE empname = OLD.empname; + IF NOT FOUND THEN RETURN NULL; END IF; + + OLD.last_updated = now(); + INSERT INTO emp_audit VALUES('D', user, OLD.*); + RETURN OLD; + ELSIF (TG_OP = 'UPDATE') THEN + UPDATE emp SET salary = NEW.salary WHERE empname = OLD.empname; + IF NOT FOUND THEN RETURN NULL; END IF; + + NEW.last_updated = now(); + INSERT INTO emp_audit VALUES('U', user, NEW.*); + RETURN NEW; + ELSIF (TG_OP = 'INSERT') THEN + INSERT INTO emp VALUES(NEW.empname, NEW.salary); + + NEW.last_updated = now(); + INSERT INTO emp_audit VALUES('I', user, NEW.*); + RETURN NEW; + END IF; + END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER emp_audit +INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_view + FOR EACH ROW EXECUTE PROCEDURE update_emp_view(); + + + + + 触发器的一种用途是维护另一个表的汇总表。得到的汇总结果可以在某些查询中替代原始表使用,而且通常能大幅缩短运行时间。这种技术在数据仓库中很常见,其中用于度量或观察数据的表(称为事实表)可能会非常大。展示了PL/pgSQL中一个为数据仓库事实表维护汇总表的触发器函数示例。 + + + + + 一个用于维护汇总表的 <application>PL/pgSQL</application> 触发器函数 + + + 这里详述的模式有一部分是基于 Ralph Kimball 所作的数据仓库工具包中的Grocery Store示例。 + + + +-- +-- Main tables - time dimension and sales fact. +-- +CREATE TABLE time_dimension ( + time_key integer NOT NULL, + day_of_week integer NOT NULL, + day_of_month integer NOT NULL, + month integer NOT NULL, + quarter integer NOT NULL, + year integer NOT NULL +); +CREATE UNIQUE INDEX time_dimension_key ON time_dimension(time_key); + +CREATE TABLE sales_fact ( + time_key integer NOT NULL, + product_key integer NOT NULL, + store_key integer NOT NULL, + amount_sold numeric(12,2) NOT NULL, + units_sold integer NOT NULL, + amount_cost numeric(12,2) NOT NULL +); +CREATE INDEX sales_fact_time ON sales_fact(time_key); + +-- +-- Summary table - sales by time. +-- +CREATE TABLE sales_summary_bytime ( + time_key integer NOT NULL, + amount_sold numeric(15,2) NOT NULL, + units_sold numeric(12) NOT NULL, + amount_cost numeric(15,2) NOT NULL +); +CREATE UNIQUE INDEX sales_summary_bytime_key ON sales_summary_bytime(time_key); + +-- +-- Function and trigger to amend summarized column(s) on UPDATE, INSERT, DELETE. +-- +CREATE OR REPLACE FUNCTION maint_sales_summary_bytime() RETURNS TRIGGER +AS $maint_sales_summary_bytime$ + DECLARE + delta_time_key integer; + delta_amount_sold numeric(15,2); + delta_units_sold numeric(12); + delta_amount_cost numeric(15,2); + BEGIN + + -- Work out the increment/decrement amount(s). + IF (TG_OP = 'DELETE') THEN + + delta_time_key = OLD.time_key; + delta_amount_sold = -1 * OLD.amount_sold; + delta_units_sold = -1 * OLD.units_sold; + delta_amount_cost = -1 * OLD.amount_cost; + + ELSIF (TG_OP = 'UPDATE') THEN + + -- forbid updates that change the time_key - + -- (probably not too onerous, as DELETE + INSERT is how most + -- changes will be made). + IF ( OLD.time_key != NEW.time_key) THEN + RAISE EXCEPTION 'Update of time_key : % -> % not allowed', + OLD.time_key, NEW.time_key; + END IF; + + delta_time_key = OLD.time_key; + delta_amount_sold = NEW.amount_sold - OLD.amount_sold; + delta_units_sold = NEW.units_sold - OLD.units_sold; + delta_amount_cost = NEW.amount_cost - OLD.amount_cost; + + ELSIF (TG_OP = 'INSERT') THEN + + delta_time_key = NEW.time_key; + delta_amount_sold = NEW.amount_sold; + delta_units_sold = NEW.units_sold; + delta_amount_cost = NEW.amount_cost; + + END IF; + + + -- Insert or update the summary row with the new values. + <<insert_update>> + LOOP + UPDATE sales_summary_bytime + SET amount_sold = amount_sold + delta_amount_sold, + units_sold = units_sold + delta_units_sold, + amount_cost = amount_cost + delta_amount_cost + WHERE time_key = delta_time_key; + + EXIT insert_update WHEN found; + + BEGIN + INSERT INTO sales_summary_bytime ( + time_key, + amount_sold, + units_sold, + amount_cost) + VALUES ( + delta_time_key, + delta_amount_sold, + delta_units_sold, + delta_amount_cost + ); + + EXIT insert_update; + + EXCEPTION + WHEN UNIQUE_VIOLATION THEN + -- do nothing + END; + END LOOP insert_update; + + RETURN NULL; + + END; +$maint_sales_summary_bytime$ LANGUAGE plpgsql; + +CREATE TRIGGER maint_sales_summary_bytime +AFTER INSERT OR UPDATE OR DELETE ON sales_fact + FOR EACH ROW EXECUTE PROCEDURE maint_sales_summary_bytime(); + +INSERT INTO sales_fact VALUES(1,1,1,10,3,15); +INSERT INTO sales_fact VALUES(1,2,1,20,5,35); +INSERT INTO sales_fact VALUES(2,2,1,40,15,135); +INSERT INTO sales_fact VALUES(2,3,1,10,1,13); +SELECT * FROM sales_summary_bytime; +DELETE FROM sales_fact WHERE product_key = 1; +SELECT * FROM sales_summary_bytime; +UPDATE sales_fact SET units_sold = units_sold * 2; +SELECT * FROM sales_summary_bytime; + + + + + + + + 事件触发器 + + + PL/pgSQL可以被用来定义事件触发器。PostgreSQL要求一个可以作为事件触发器调用的函数必须被声明为没有参数并且返回类型为event_trigger。 + + + 当一个PL/pgSQL函数作为事件触发器被调用时,会在顶层块中自动创建一些特殊变量。它们是: + + TG_EVENT + + + 数据类型为 text;表示触发器被触发时对应事件的字符串。 + + + + + + TG_TAG + + + 数据类型为 text;包含触发该触发器的命令标签的变量。 + + + + + + + + 展示了PL/pgSQL中一个事件触发器函数的示例。 + + + + 一个 <application>PL/pgSQL</application> 事件触发器函数 + + + 这个示例触发器在受支持命令每一次被执行时会简单地抛出一个NOTICE消息。 + + + +CREATE OR REPLACE FUNCTION snitch() RETURNS event_trigger AS $$ +BEGIN + RAISE NOTICE 'snitch: % %', tg_event, tg_tag; +END; +$$ LANGUAGE plpgsql; + +CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE PROCEDURE snitch(); + + + + + + + + <application>PL/pgSQL</application> 内部机制 + + 本节讨论一些实现细节,了解这些细节对 PL/pgSQL 用户通常很重要。 + + + 变量替换 + + PL/pgSQL函数中,SQL 语句和表达式可以引用函数的变量和参数。在内部,PL/pgSQL会用查询参数替换这些引用。只有在语法上允许参数或列引用的位置才会进行参数替换。作为一个极端例子,来看下面这种不良编程风格: +INSERT INTO foo (foo) VALUES (foo); +第一次出现的foo在语法上必须是表名,所以不会被替换,即使函数有一个变量也叫foo。第二次出现的位置必须是该表的列名,所以也不会被替换。只有第三次出现的位置才可能是对函数变量的引用。 + + + PostgreSQL 9.0 以前的版本会尝试在这三个位置都进行变量替换,从而导致语法错误。 + + + + 由于变量名在语法上与表列名没有区别,所以在同时引用表的语句中就可能产生歧义:某个给定名称到底是指表列,还是变量?把前面的示例改成下面这样: + +INSERT INTO dest (col) SELECT foo + bar FROM src; + + 这里,destsrc 必须是表名,col 也必须是 dest 的一列,但 foobar 既可能是该函数的变量,也可能是 src 的列。 + + + 默认情况下,如果 SQL 语句中的名称既可能指变量,也可能指表列,PL/pgSQL就会报告错误。可以通过重命名变量或列、限定有歧义的引用,或者告诉PL/pgSQL优先采用哪种解释来解决这类问题。 + + + 最简单的解决方案是重命名变量或列。一种常用的编码规则是为PL/pgSQL变量使用一种不同于列名的命名习惯。例如,如果你将函数变量统一地命名为v_something,而你的列名不会开始于v_,就不会发生冲突。 + + + + 另外你可以限定有歧义的引用让它们变清晰。在上面的示例中,src.foo将是对表列的一种无歧义的引用。要创建对一个变量的无歧义引用,在一个被标记的块中声明它并且使用块的标签(见)。例如 + +<<block>> +DECLARE + foo int; +BEGIN + foo := ...; + INSERT INTO dest (col) SELECT block.foo + bar FROM src; + + 这里block.foo表示变量,即使在src中有一个列foo。函数参数以及诸如FOUND的特殊变量,都能通过函数的名称被限定,因为它们被隐式地声明在一个带有该函数名称的外层块中。 + + + + 有时候在一个大型的PL/pgSQL代码体中修复所有的有歧义引用是不现实的。在这种情况下,你可以指定PL/pgSQL应该将有歧义的引用作为变量(这与PL/pgSQLPostgreSQL 9.0 之前的行为兼容)或表列(这与某些其他系统兼容,例如Oracle)解决。 + + + + plpgsql.variable_conflict配置参数 + + + + 要在系统范围内改变这种行为,将配置参数plpgsql.variable_conflict设置为erroruse_variable或者use_column(这里error是出厂设置)之一。这个参数会影响PL/pgSQL函数中语句的后续编译,但是不会影响在当前会话中已经编译过的语句。因为改变这个设置能够导致PL/pgSQL函数中行为的意想不到的改变,所以只能由一个超级用户来更改它。 + + + + 你也可以按函数单独设置这一行为,方法是在函数文本开头插入以下特殊命令之一: + +#variable_conflict error +#variable_conflict use_variable +#variable_conflict use_column + + 这些命令只影响它们所属的函数,并且会覆盖plpgsql.variable_conflict的设置。一个示例是: + +CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$ + #variable_conflict use_variable + DECLARE + curtime timestamp := now(); + BEGIN + UPDATE users SET last_modified = curtime, comment = comment + WHERE users.id = id; + END; +$$ LANGUAGE plpgsql; + + 在UPDATE命令中,curtimecomment以及id将引用该函数的变量和参数,不管users有没有这些名称的列。注意,我们不得不在WHERE子句中对users.id的引用加以限定,以便让它引用表列。但是我们不需要对UPDATE列表中作为目标的comment引用加以限定,因为语法上那必须是users的一列。我们可以用下面的方式写一个相同的不依赖于variable_conflict设置的函数: + +CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$ + <<fn>> + DECLARE + curtime timestamp := now(); + BEGIN + UPDATE users SET last_modified = fn.curtime, comment = stamp_user.comment + WHERE users.id = stamp_user.id; + END; +$$ LANGUAGE plpgsql; + + + + + 传递给 EXECUTE 及其变体的命令字符串中,不会发生变量替换。如果你需要向这种命令中插入变化的值,应在构造字符串值时完成,或者像 所说明的那样使用 USING。 + + + 目前,变量替换只在SELECTINSERTUPDATEDELETE命令中生效,因为主 SQL 引擎只允许在这些命令中使用查询参数。若要在其他语句类型(统称为工具语句)中使用非常量名称或值,就必须把该工具语句构造为字符串,再用EXECUTE执行。 + + + + + + 计划缓存 + + + 在函数第一次被调用时(每个会话中都会如此),PL/pgSQL解释器会解析函数源文本,并生成一棵内部的二进制指令树。该指令树完整表示了PL/pgSQL语句结构,但函数中使用的各个SQL表达式和SQL命令并不会立即被分析。 + + + + + 准备一个查询 + 在 PL/pgSQL 中 + + 当函数中的某个表达式或 SQL 命令第一次执行时,PL/pgSQL 解释器会使用 SPI 管理器的 SPI_prepare 函数,对该命令进行解析和分析,以创建预备语句。之后再次执行该表达式或命令时,就会重用该预备语句。因此,对于那些很少访问的条件分支,函数不会承担分析当前会话中从未执行到的命令的开销。缺点是,某个具体表达式或命令中的错误,只有在执行到函数的那一部分时才能被发现。(简单语法错误会在最初的解析阶段发现,但更深层的问题只有在执行时才会显现。) + + + + PL/pgSQL(更准确地说,是 SPI 管理器)还可以尝试缓存与某个预备语句相关的执行计划。如果没有使用缓存计划,那么每次执行该语句时都会生成新的执行计划,而当前参数值(也就是 PL/pgSQL 变量值)可用于优化所选计划。如果该语句没有参数,或者会被执行很多次,SPI 管理器就会考虑创建一个不依赖具体参数值的通用计划,并将其缓存起来供重复使用。通常只有在执行计划对其中引用的 PL/pgSQL 变量值不太敏感时,才会这样做。如果执行计划对参数值非常敏感,那么每次重新生成计划总体上反而更划算。关于预备语句的行为,详见 。 + + + + 由于PL/pgSQL保存预备语句并且有时候以这种方式保存执行计划,直接出现在一个PL/pgSQL函数中的 SQL 命令必须在每次执行时引用相同的表和列。也就是说,你不能在一个 SQL 命令中把一个参数用作表或列的名字。要绕过这种限制,你可以构建PL/pgSQL EXECUTE使用的动态命令,但是会付出在每次执行时需要执行新解析分析以及构建新执行计划的代价。 + + + + 记录变量的可变特性在这里还会带来另一个问题。当记录变量的字段被用于表达式或语句中时,这些字段的数据类型不能在函数的不同调用之间发生变化,因为每个表达式都会按照第一次执行到它时所看到的数据类型来分析。必要时,可以用EXECUTE绕过这个问题。 + + + + 如果同一个函数被用作多个表的触发器,PL/pgSQL会针对每个这样的表独立地准备并缓存语句。也就是说,缓存是按“触发器函数 + 表”的组合建立的,而不是每个函数只有一个缓存。这缓解了数据类型变化带来的部分问题;例如,即使不同表中名为key的列类型不同,一个触发器函数也仍然能够成功使用它。 + + + + 同样,具有多态参数类型的函数也会为它们已经被调用的每一种实参类型组合都保留一个独立的缓存,这样数据类型差异不会导致意想不到的失败。 + + + + 语句缓存有时可能在解释时间敏感的值时产生令人惊讶的效果。例如这两个函数做的事情就有区别: + + +CREATE FUNCTION logfunc1(logtxt text) RETURNS void AS $$ + BEGIN + INSERT INTO logtable VALUES (logtxt, 'now'); + END; +$$ LANGUAGE plpgsql; + + + 以及: + + +CREATE FUNCTION logfunc2(logtxt text) RETURNS void AS $$ + DECLARE + curtime timestamp; + BEGIN + curtime := 'now'; + INSERT INTO logtable VALUES (logtxt, curtime); + END; +$$ LANGUAGE plpgsql; + + + + + 在logfunc1中,PostgreSQL的主解析器在分析INSERT时就知道字符串'now'应该被解释为timestamp,因为logtable的目标列是这种类型。因此,在INSERT被分析时'now'将被转换为一个timestamp常量,并且在该会话的生命周期内被用于所有对logfunc1的调用。不用说,这不是程序员想要的。一个更好的主意是使用now()current_timestamp函数。 + + + + 在logfunc2中,PostgreSQL的主解析器不知道应该把'now'解释成什么类型,因此返回一个text类型的数据值,其中包含字符串now。在随后给局部变量curtime赋值时,PL/pgSQL解释器通过调用用于该转换的textouttimestamp_in函数,把这个字符串转换为timestamp类型。因此,计算得到的时间戳会按程序员预期在每次执行时更新。虽然这恰好能得到预期结果,但效率并不高,因此使用now()函数仍然是更好的主意。 + + + + + + + + <application>PL/pgSQL</application>开发提示 + + + 在PL/pgSQL中进行开发的一种好方法,是用你喜欢的文本编辑器编写函数,并在另一个窗口里使用psql载入和测试这些函数。如果你采用这种方式,最好使用CREATE OR REPLACE FUNCTION来编写函数。这样只需重新载入该文件,就可以更新函数定义。例如: + +CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $$ + .... +$$ LANGUAGE plpgsql; + + + + + 在运行psql期间,你可以用下面的命令载入或者重载这样一个函数定义文件: + +\i filename.sql + + 并且接着立即发出 SQL 命令来测试该函数。 + + + + 另一种使用PL/pgSQL进行开发的好方法,是使用有助于过程语言开发的 GUI 数据库访问工具。这类工具的一个例子是pgAdmin,当然也有其他工具。它们通常提供一些方便的特性,例如转义单引号,以及让重新创建和调试函数变得更容易。 + + + + 引号的处理 + + + 一个PL/pgSQL函数的代码在一个CREATE FUNCTION中被指定为一个字符串字面量。如果你用通常的方式把该字符串写在单引号中间,那么该函数体中的任何单引号都必须被双写;同样任何反斜线也必须被双写(假定使用了转义字符串语法)。双写引号本身就很繁琐,并且在更复杂的情况中代码会变得完全无法理解,因为你很容易发现你需要半打或者更多相邻的引号。我们推荐你转而把函数体写成一个美元引用的字符串(见)。在美元引用方法中,你从不需要双写任何引号。但是要注意为你需要的每一层嵌套选择一个不同的美元引用定界符。例如,你可能把CREATE FUNCTION命令写成: + +CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $PROC$ + .... +$PROC$ LANGUAGE plpgsql; + + 在这里面,你可以在 SQL 命令中为简单字符串使用引号并且用$$来界定被你组装成字符串的 SQL 命令片段。如果你需要引用包括$$的文本,你可以使用$Q$等等。 + + + + 下表展示了在不使用美元引用时,编写引号必须采取的做法。在把旧式的非美元引用代码改写成更易理解的形式时,它可能会有所帮助。 + + + + + 1 个引号 + + + + 用来开始和结束函数体,例如: + +CREATE FUNCTION foo() RETURNS integer AS ' + .... +' LANGUAGE plpgsql; + + 在一个单引号引用的函数体中的任何位置,引号必须成对出现。 + + + + + + 2 个引号 + + + + 用于函数体内的字符串字面量,例如: + +a_output := ''Blah''; +SELECT * FROM users WHERE f_name=''foobar''; + + 在美元引用方法中,你只需要写: + +a_output := 'Blah'; +SELECT * FROM users WHERE f_name='foobar'; + + 这恰好就是PL/pgSQL解析器在两种情况中会看到的。 + + + + + + 4 个引号 + + + + 当你在函数内的一个字符串常量中需要一个单引号时,例如: + +a_output := a_output || '' AND name LIKE ''''foobar'''' AND xyz'' + + 实际会被追加到a_output的值将是: AND name LIKE 'foobar' AND xyz。 + + + + 在美元引用方法中,你可以写: + +a_output := a_output || $$ AND name LIKE 'foobar' AND xyz$$ + + 要小心在这周围的任何美元引用定界符都不能是$$。 + + + + + + 6 个引号 + + + + 当在函数体内的一个字符串中的一个单引号与该字符串常量末尾相邻,例如: + +a_output := a_output || '' AND name LIKE ''''foobar'''''' + + 被追加到a_output的值则将是: AND name LIKE 'foobar'。 + + + + 在美元引用方法中,这会变成: + +a_output := a_output || $$ AND name LIKE 'foobar'$$ + + + + + + + 10 个引号 + + + + 当字符串常量中需要两个单引号(这需要 8 个引号),而且它们紧邻该字符串常量的末尾(还需 2 个引号)时。通常只有在编写生成其他函数的函数时(如所示),才会需要这种写法。例如: + +a_output := a_output || '' if v_'' || + referrer_keys.kind || '' like '''''''''' + || referrer_keys.key_string || '''''''''' + then return '''''' || referrer_keys.referrer_type + || ''''''; end if;''; + + a_output的值将是: + +if v_... like ''...'' then return ''...''; end if; + + + + + 在美元引用方法中,这会变成: + +a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$ + || referrer_keys.key_string || $$' + then return '$$ || referrer_keys.referrer_type + || $$'; end if;$$; + + 这里我们假定我们只需要把单引号放在a_output中,因为在使用前它将被再引用。 + + + + + + + + 额外的编译时检查 + + + 为了辅助用户在一些简单但常见的问题产生危害之前找到它们, + PL/PgSQL提供了额外的检查。当被启用时, + 根据配置,它们可以在一个函数的编译期间被用来发出 + WARNING或者ERROR。一个已经收到了 + WARNING的函数可以被继续执行而不会产生进一步的消息, + 因此建议你在一个单独的开发环境中进行测试。 + + + 这些额外检查通过配置变量启用,其中plpgsql.extra_warnings用于警告,plpgsql.extra_errors用于错误。两者都可以设为逗号分隔的检查项列表、"none""all"。默认值为"none"。当前可用的检查只有一项: + + shadowed_variables + + + 检查声明是否遮蔽了先前定义的变量。 + + + + 下面的例子展示了此设置的效果:plpgsql.extra_warnings设为shadowed_variables: + +SET plpgsql.extra_warnings TO 'shadowed_variables'; + +CREATE FUNCTION foo(f1 int) RETURNS int AS $$ +DECLARE +f1 int; +BEGIN +RETURN f1; +END; +$$ LANGUAGE plpgsql; +WARNING: variable "f1" shadows a previously defined variable +LINE 3: f1 int; + ^ +CREATE FUNCTION + + + + + + + + + 从<productname>Oracle</productname> PL/SQL 移植 + + + Oracle + 从 PL/SQL 移植到 PL/pgSQL + + + + PL/SQL (Oracle) + 移植到 PL/pgSQL + + + + 这一节解释了PostgreSQLPL/pgSQL语言和 Oracle 的PL/SQL语言之间的差别,用以帮助那些从OraclePostgreSQL移植应用的人。 + + + + PL/pgSQL在许多方面都与 PL/SQL 类似。它是一种具有块结构的命令式语言,所有变量都必须声明。赋值、循环和条件语句也都很相似。在从PL/SQL移植到PL/pgSQL时,应当记住以下主要差异: + + 如果 SQL 命令中使用的名字既可能是表的列名,也可能是对函数中变量的引用,PL/SQL会将其当作列名。这对应于PL/pgSQLplpgsql.variable_conflict = use_column行为,这不是默认行为,如所述。通常最好一开始就避免这种歧义,但如果必须移植大量依赖这种行为的代码,设置variable_conflict可能是最好的解决办法。 + + + + + 在PostgreSQL中,函数体必须写成字符串字面量。因此你需要使用美元符引用或者转义函数体中的单引号(见)。 + + + + + + 数据类型名称往往需要改写。例如,在 Oracle 中字符串值通常被声明为varchar2类型,这并不是 SQL 标准类型。在PostgreSQL中,应改用varchartext类型。类似地,应将number替换为numeric,或者在适当时替换为其他数值数据类型。 + + + + + + 应该用模式把函数组织成不同的分组,而不是用包。 + + + + + + 因为没有包,所以也没有包级变量。这一点有时会带来不便。你可以改为在临时表中保存会话级状态。 + + + + + + 带有REVERSE的整数FOR循环的工作方式不同:PL/SQL中是从第二个数向第一个数倒数,而PL/pgSQL是从第一个数向第二个数倒数,因此在移植时需要交换循环边界。不幸的是这种不兼容性是不太可能改变的(见)。 + + + + + + 查询上的FOR循环(不是游标)的工作方式同样不同:目标变量必须已经被声明,而PL/SQL总是会隐式地声明它们。但是这样做的优点是在退出循环后,变量值仍然可以访问。 + + + + + + 在使用游标变量方面,存在一些记法差异。 + + + + + + + + 移植示例 + + + 展示了如何从PL/SQL移植一个简单的函数到PL/pgSQL中。 + + + + + 从<application>PL/SQL</application>移植一个简单的函数到<application>PL/pgSQL</application> + + + 这里有一个Oracle PL/SQL函数: + +CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar2, + v_version varchar2) +RETURN varchar2 IS +BEGIN + IF v_version IS NULL THEN + RETURN v_name; + END IF; + RETURN v_name || '/' || v_version; +END; +/ +show errors; + + + + + 让我们过一遍这个函数并且看看与PL/pgSQL相比有什么样的不同: + + + + + 类型名称varchar2被改成了varchar或者text。在这一节的示例中,我们将使用varchar,但如果不需要特定的字符串长度限制,text常常是更好的选择。 + + + + + + 在函数原型中(不是函数体中)的RETURN关键字在PostgreSQL中变成了RETURNS。还有,IS变成了AS,并且你还需要增加一个LANGUAGE子句,因为PL/pgSQL并非唯一可用的函数语言。 + + + + + + 在PostgreSQL中,函数体被认为是一个字符串字面量,所以你需要使用引号或者美元引用定界符包围它。这代替了Oracle 方法中的用于终止的/。 + + + + + + 在PostgreSQL中没有show errors命令, 并且也不需要这个命令,因为错误是自动报告的。 + + + + + + + 这个函数被移植到PostgreSQL后看起来会是这样: + + +CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar, + v_version varchar) +RETURNS varchar AS $$ +BEGIN + IF v_version IS NULL THEN + RETURN v_name; + END IF; + RETURN v_name || '/' || v_version; +END; +$$ LANGUAGE plpgsql; + + + + + + 展示了如何移植一个会创建另一个函数的函数,以及如何处理引号问题。 + + + + 从<application>PL/SQL</application>移植一个创建另一个函数的函数到<application>PL/pgSQL</application> + + 下面的过程从 SELECT 语句读取行,并将结果写入 IF 语句,从而构造一个大型函数,以提高效率。 + + + 这是 Oracle 版本: + +CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS + CURSOR referrer_keys IS + SELECT * FROM cs_referrer_keys + ORDER BY try_order; + func_cmd VARCHAR(4000); +BEGIN + func_cmd := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host IN VARCHAR2, + v_domain IN VARCHAR2, v_url IN VARCHAR2) RETURN VARCHAR2 IS BEGIN'; + + FOR referrer_key IN referrer_keys LOOP + func_cmd := func_cmd || + ' IF v_' || referrer_key.kind + || ' LIKE ''' || referrer_key.key_string + || ''' THEN RETURN ''' || referrer_key.referrer_type + || '''; END IF;'; + END LOOP; + + func_cmd := func_cmd || ' RETURN NULL; END;'; + + EXECUTE IMMEDIATE func_cmd; +END; +/ +show errors; + + + + 下面是这个函数的最终移植结果,目标数据库为PostgreSQL: + +CREATE OR REPLACE FUNCTION cs_update_referrer_type_proc() RETURNS void AS $func$ +DECLARE + referrer_keys CURSOR IS + SELECT * FROM cs_referrer_keys + ORDER BY try_order; + func_body text; + func_cmd text; +BEGIN + func_body := 'BEGIN'; + + FOR referrer_key IN referrer_keys LOOP + func_body := func_body || + ' IF v_' || referrer_key.kind + || ' LIKE ' || quote_literal(referrer_key.key_string) + || ' THEN RETURN ' || quote_literal(referrer_key.referrer_type) + || '; END IF;' ; + END LOOP; + + func_body := func_body || ' RETURN NULL; END;'; + + func_cmd := + 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host varchar, + v_domain varchar, + v_url varchar) + RETURNS varchar AS ' + || quote_literal(func_body) + || ' LANGUAGE plpgsql;' ; + + EXECUTE func_cmd; +END; +$func$ LANGUAGE plpgsql; +注意,这里单独构造函数体,然后将其传给quote_literal,使其中的每个引号都变成两个。这种技术是必需的,因为不能安全地使用美元引用来定义新函数:我们无法确定会插入什么字符串,其来源是referrer_key.key_string字段。(这里假定referrer_key.kind可信,其值总是hostdomainurl,但是referrer_key.key_string可能是任何内容,尤其可能包含美元符号。)这个函数实际上改进了 Oracle 原版:当referrer_key.key_stringreferrer_key.referrer_type中包含引号时,它也不会生成有问题的代码。 + + + + 展示了如何移植一个带有OUT参数和字符串处理的函数。PostgreSQL没有内置的instr函数,但是你可以用其它函数的组合来创建一个。在中有一个instrPL/pgSQL实现,你可以用它让你的移植变得更容易。 + + + + 从<application>PL/SQL</application>移植一个带有字符串操作以及<literal>OUT</literal>参数的过程到<application>PL/pgSQL</application> + + + 下面的Oracle PL/SQL 过程被用来解析一个 URL 并且返回一些元素(主机、路径和查询)。 + + + + 这是 Oracle 版本: + +CREATE OR REPLACE PROCEDURE cs_parse_url( + v_url IN VARCHAR2, + v_host OUT VARCHAR2, -- 这个值将被返回 + v_path OUT VARCHAR2, -- 这个也是 + v_query OUT VARCHAR2) -- 还有这个 +IS + a_pos1 INTEGER; + a_pos2 INTEGER; +BEGIN + v_host := NULL; + v_path := NULL; + v_query := NULL; + a_pos1 := instr(v_url, '//'); + + IF a_pos1 = 0 THEN + RETURN; + END IF; + a_pos2 := instr(v_url, '/', a_pos1 + 2); + IF a_pos2 = 0 THEN + v_host := substr(v_url, a_pos1 + 2); + v_path := '/'; + RETURN; + END IF; + + v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2); + a_pos1 := instr(v_url, '?', a_pos2 + 1); + + IF a_pos1 = 0 THEN + v_path := substr(v_url, a_pos2); + RETURN; + END IF; + + v_path := substr(v_url, a_pos2, a_pos1 - a_pos2); + v_query := substr(v_url, a_pos1 + 1); +END; +/ +show errors; + + + + + 这里给出一种可能的 PL/pgSQL 写法: + +CREATE OR REPLACE FUNCTION cs_parse_url( + v_url IN VARCHAR, + v_host OUT VARCHAR, -- 这个值将被返回 + v_path OUT VARCHAR, -- 这个也是 + v_query OUT VARCHAR) -- 还有这个 +AS $$ +DECLARE + a_pos1 INTEGER; + a_pos2 INTEGER; +BEGIN + v_host := NULL; + v_path := NULL; + v_query := NULL; + a_pos1 := instr(v_url, '//'); + + IF a_pos1 = 0 THEN + RETURN; + END IF; + a_pos2 := instr(v_url, '/', a_pos1 + 2); + IF a_pos2 = 0 THEN + v_host := substr(v_url, a_pos1 + 2); + v_path := '/'; + RETURN; + END IF; + + v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2); + a_pos1 := instr(v_url, '?', a_pos2 + 1); + + IF a_pos1 = 0 THEN + v_path := substr(v_url, a_pos2); + RETURN; + END IF; + + v_path := substr(v_url, a_pos2, a_pos1 - a_pos2); + v_query := substr(v_url, a_pos1 + 1); +END; +$$ LANGUAGE plpgsql; + + + 这个函数可以这样使用: + +SELECT * FROM cs_parse_url('http://foobar.com/query.cgi?baz'); + + + + + + 展示了如何移植一个使用了多种 Oracle 专属特性的过程。 + + + + 从<application>PL/SQL</application>移植一个过程到<application>PL/pgSQL</application> + + Oracle 版本: +CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS + a_running_job_count INTEGER; + PRAGMA AUTONOMOUS_TRANSACTION; +BEGIN + LOCK TABLE cs_jobs IN EXCLUSIVE MODE; + + SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; + + IF a_running_job_count > 0 THEN + COMMIT; -- free lock + raise_application_error(-20000, + 'Unable to create a new job: a job is currently running.'); + END IF; + + DELETE FROM cs_active_job; + INSERT INTO cs_active_job(job_id) VALUES (v_job_id); + + BEGIN + INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, now()); + EXCEPTION + WHEN dup_val_on_index THEN NULL; -- don't worry if it already exists + END; + COMMIT; +END; +/ +show errors + + + + 这样的过程可以很容易地转换为PostgreSQL中返回void的函数。这个过程尤其值得关注,因为它能说明以下几点: PostgreSQL中没有PRAGMA语句。 如果在PL/pgSQL中执行LOCK TABLE,锁要等到调用事务结束时才会释放。 不能在PL/pgSQL函数中发出COMMIT。函数运行在某个外层事务之中,因此COMMIT意味着终止函数的执行。不过,在这个特定情形中本来也不需要提交,因为抛出错误时,LOCK TABLE获得的锁就会被释放。 + + 下面展示了如何移植这个过程,目标语言为PL/pgSQL: + + +CREATE OR REPLACE FUNCTION cs_create_job(v_job_id integer) RETURNS void AS $$ +DECLARE + a_running_job_count integer; +BEGIN + LOCK TABLE cs_jobs IN EXCLUSIVE MODE; + + SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; + + IF a_running_job_count > 0 THEN + RAISE EXCEPTION 'Unable to create a new job: a job is currently running'; + END IF; + + DELETE FROM cs_active_job; + INSERT INTO cs_active_job(job_id) VALUES (v_job_id); + + BEGIN + INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, now()); + EXCEPTION + WHEN unique_violation THEN + -- don't worry if it already exists + END; +END; +$$ LANGUAGE plpgsql; + + + + + + RAISE的语法与 Oracle 的语句相当不同,尽管基本的形式RAISE exception_name工作起来是相似的。 + + + + + PL/pgSQL所支持的异常名称不同于 Oracle。内置的异常名称集合要更大(见)。目前没有办法声明用户定义的异常名称,尽管你能够抛出用户选择的 SQLSTATE 值。 + + + 这个过程与 Oracle 对应过程的主要功能差异在于,cs_jobs表上的排他锁会一直保持到调用事务完成。此外,如果调用者随后中止(例如由于错误),这个过程的效果也会回滚。 + + + + + 其他要关注的事项 + + + 这一节解释了在移植 Oracle PL/SQL函数到PostgreSQL中时要关注的一些其他问题。 + + + + + 异常后隐式回滚 + + + 在PL/pgSQL中,当异常被EXCEPTION子句捕获后,自该块的BEGIN以来所做的所有数据库更改都会被自动回滚。也就是说,这种行为等效于你在 Oracle 中使用下面的代码所得到的效果: + + +BEGIN + SAVEPOINT s1; + ... code here ... +EXCEPTION + WHEN ... THEN + ROLLBACK TO s1; + ... code here ... + WHEN ... THEN + ROLLBACK TO s1; + ... code here ... +END; + + + 如果你正在移植一个以这种方式使用SAVEPOINTROLLBACK TO的 Oracle 过程,工作会比较简单:只要省略SAVEPOINTROLLBACK TO即可。如果你的 Oracle 过程以不同方式使用SAVEPOINTROLLBACK TO,那就需要认真思考了。 + + + + + <command>EXECUTE</command> + + + PL/pgSQL中的EXECUTEPL/SQL中的版本工作方式相似,但必须记得按照中的说明使用quote_literalquote_ident。像 EXECUTE 'SELECT * FROM $1'; 这样的写法如果不借助这些函数,将无法可靠地工作。 + + + + + + 优化 <application>PL/pgSQL</application> 函数 + + + PostgreSQL提供了两种函数创建修饰符来优化执行:volatility(对于给定的相同参数,函数是否总是返回相同的结果)以及strictness (如果任何参数为空值,函数是否返回空值)。详见参考页。 + + + + 在利用这些优化属性时,你的CREATE FUNCTION语句可能像这样: + + +CREATE FUNCTION foo(...) RETURNS integer AS $$ +... +$$ LANGUAGE plpgsql STRICT IMMUTABLE; + + + + + + + + 附录 + + + 这一节包含了一组 Oracle 兼容的instr函数代码,你可以用它来简化你的移植工作。 + + + + instr 函数 + + + 0 THEN + temp_str := substring(string FROM beg_index); + pos := position(string_to_search_for IN temp_str); + + IF pos = 0 THEN + RETURN 0; + ELSE + RETURN pos + beg_index - 1; + END IF; + ELSIF beg_index < 0 THEN + ss_length := char_length(string_to_search_for); + length := char_length(string); + beg := length + 1 + beg_index; + + WHILE beg > 0 LOOP + temp_str := substring(string FROM beg FOR ss_length); + IF string_to_search_for = temp_str THEN + RETURN beg; + END IF; + + beg := beg - 1; + END LOOP; + + RETURN 0; + ELSE + RETURN 0; + END IF; +END; +$$ LANGUAGE plpgsql STRICT IMMUTABLE; + +CREATE FUNCTION instr(string varchar, string_to_search_for varchar, + beg_index integer, occur_index integer) +RETURNS integer AS $$ +DECLARE + pos integer NOT NULL DEFAULT 0; + occur_number integer NOT NULL DEFAULT 0; + temp_str varchar; + beg integer; + i integer; + length integer; + ss_length integer; +BEGIN + IF occur_index <= 0 THEN + RAISE 'argument ''%'' is out of range', occur_index + USING ERRCODE = '22003'; + END IF; + + IF beg_index > 0 THEN + beg := beg_index - 1; + FOR i IN 1..occur_index LOOP + temp_str := substring(string FROM beg + 1); + pos := position(string_to_search_for IN temp_str); + IF pos = 0 THEN + RETURN 0; + END IF; + beg := beg + pos; + END LOOP; + + RETURN beg; + ELSIF beg_index < 0 THEN + ss_length := char_length(string_to_search_for); + length := char_length(string); + beg := length + 1 + beg_index; + + WHILE beg > 0 LOOP + temp_str := substring(string FROM beg FOR ss_length); + IF string_to_search_for = temp_str THEN + occur_number := occur_number + 1; + IF occur_number = occur_index THEN + RETURN beg; + END IF; + END IF; + + beg := beg - 1; + END LOOP; + + RETURN 0; + ELSE + RETURN 0; + END IF; +END; +$$ LANGUAGE plpgsql STRICT IMMUTABLE; +]]> + + + + + +
diff --git a/zh/9.6/plpython.sgml b/zh/9.6/plpython.sgml new file mode 100644 index 00000000..b1c7ac54 --- /dev/null +++ b/zh/9.6/plpython.sgml @@ -0,0 +1,1087 @@ + + + + PL/Python - Python 过程语言 + + PL/Python + Python + + PL/Python 过程语言允许用 Python 语言编写 PostgreSQL 函数。 + + + 要在特定数据库中安装 PL/Python,可使用CREATE EXTENSION plpythonu,或者从 shell 命令行使用createlang plpythonu dbname(另见)。 + + + + + + 如果把某种语言安装到template1中,之后创建的所有数据库都会自动安装该语言。 + + + + + PL/Python 只能作为一种不受信任的语言使用,这意味着它不提供任何机制来限制用户能在其中做什么,因此其名称为plpythonu。如果将来能在 Python 中开发出安全的执行机制,受信任的变体plpython可能会出现。使用不受信任的 PL/Python 编写函数时,函数编写者必须确保该函数不会被用来做任何不希望发生的事情,因为它能够做到任何以数据库管理员身份登录的用户所能做的事情。只有超级用户才能在plpythonu这类不受信任的语言中创建函数。 + + + + + + 源码包用户必须在安装过程中专门启用 PL/Python 的构建(更多信息请参阅安装说明)。二进制包用户则可能会在单独的子包中找到 PL/Python。 + + + + + Python 2 与 Python 3 + + + PL/Python 同时支持 Python 2 和 Python 3 语言变体。(PostgreSQL 安装说明中可能包含有关所支持 Python 精确次版本号的更多信息。)由于 Python 2 和 Python 3 语言变体在一些重要方面不兼容,PL/Python 使用以下命名和过渡方案以避免混用: + + + + + PostgreSQL 中名为plpython2u的语言实现了基于 Python 2 语言变体的 PL/Python。 + + + + + + PostgreSQL 中名为plpython3u的语言实现了基于 Python 3 语言变体的 PL/Python。 + + + + + + 名为plpythonu的语言实现了基于默认 Python 语言变体的 PL/Python,当前默认为 Python 2。(此默认值独立于任何本地 Python 安装可能认为的其默认版本,例如/usr/bin/python可能是哪个版本。)默认值将来可能会在较远的 PostgreSQL 版本中改为 Python 3,具体取决于 Python 社区向 Python 3 迁移的进展。 + + + + + 该方案类似于PEP 394中关于python命令命名和过渡的建议。 + + + + 基于 Python 2 或 Python 3 的 PL/Python 是否可用(或者两者都可用),取决于构建配置或已安装的软件包。 + + + + + 所构建的变体取决于安装时找到的 Python 版本,或使用PYTHON环境变量显式设置的版本;请参见。要在同一个安装中同时提供两种 PL/Python 变体,需要对源代码树进行两次配置和构建。 + + + + 由此可以采用以下使用和迁移策略: + + 现有用户和目前不打算使用 Python 3 的用户,可以使用语言名 plpythonu,在可预见的将来无需更改任何内容。建议通过迁移到 Python 2.6/2.7,逐步让代码适应未来,以简化最终迁移到 Python 3 的工作。 + + + 实际上,许多 PL/Python 函数只需很少或不做改动即可迁移到 Python 3。 + + + + + 如果用户清楚自己的代码严重依赖 Python 2,且不打算更改,可以使用语言名 plpython2u。它在很久以后仍会继续工作,直到 PostgreSQL 可能完全停止支持 Python 2。 + + + + 希望开始使用 Python 3 的用户可以使用语言名 plpython3u,按照目前的标准,它会一直可用。在遥远的未来,当 Python 3 可能成为默认版本时,用户也许会出于美观而去掉名称中的3 + + + + 希望构建仅包含 Python 3 的操作系统环境的大胆用户,可以修改 pg_pltemplate 的内容,使 plpythonu 等同于 plpython3u,但要记住,这会使该安装环境与绝大多数其他环境不兼容。 + + + + + 有关迁移到 Python 3 的更多信息,也可参见文档 Python 3.0 的新变化 + + 不允许在同一个会话中同时使用基于 Python 2 和基于 Python 3 的 PL/Python,因为动态模块中的符号会冲突,可能导致 PostgreSQL 服务器进程崩溃。系统会检查同一会话是否混用了不同的 Python 大版本,如果发现不匹配,就会中止会话。不过,可以在同一数据库的不同会话中分别使用这两种 PL/Python。 + + + + PL/Python 函数 + + + PL/Python 中的函数通过标准的语法声明: + + +CREATE FUNCTION funcname (argument-list) + RETURNS return-type +AS $$ + # PL/Python 函数体 +$$ LANGUAGE plpythonu; + + + + 函数体就是一段 Python 脚本。调用函数时,参数作为列表 args 的元素传入;命名参数还会作为普通变量传给 Python 脚本。使用命名参数通常更易读。Python 代码按通常方式用 returnyield(返回结果集时)返回结果。如果没有提供返回值,Python 默认返回 NonePL/Python 会将 Python 的 None 转换为 SQL 空值。 + + + 例如,一个返回两个整数中较大值的函数可以定义为: + + +CREATE FUNCTION pymax (a integer, b integer) + RETURNS integer +AS $$ + if a > b: + return a + return b +$$ LANGUAGE plpythonu; + + + 函数定义体中给出的 Python 代码会被转换成一个 Python 函数。例如,上面的定义会变成: + + +def __plpython_procedure_pymax_23456(): + if a > b: + return a + return b + + + 这里假定 23456 是由PostgreSQL分配给该函数的 OID。 + + + + 参数会被设置为全局变量。由于 Python 的作用域规则,这会带来一个微妙的结果:除非在代码块中再次将该变量声明为全局变量,否则不能在函数内部把参数变量重新赋值为一个引用该变量名自身的表达式的值。例如,下面这样是行不通的: + +CREATE FUNCTION pystrip(x text) + RETURNS text +AS $$ + x = x.strip() # 错误 + return x +$$ LANGUAGE plpythonu; + + 因为对x赋值会使x在整个代码块中都成为局部变量,因此赋值语句右侧的x指向的是尚未赋值的局部变量x,而不是 PL/Python 函数参数。使用global语句后,这样写就能工作: + +CREATE FUNCTION pystrip(x text) + RETURNS text +AS $$ + global x + x = x.strip() # 现在可以正常工作 + return x +$$ LANGUAGE plpythonu; + + 但是,建议不要依赖 PL/Python 的这一实现细节。最好将函数参数视为只读。 + + + + + 数据值 + + 一般来说,PL/Python 的目标是在 PostgreSQL 世界和 Python 世界之间提供一种自然的映射。下面描述的数据映射规则就是基于这一目标。 + + + + 数据类型映射 + + 调用 PL/Python 函数时,其参数会从 PostgreSQL 数据类型转换为相应的 Python 类型: + + + + + PostgreSQL boolean 会转换为 Python bool。 + + + + + PostgreSQL 的 smallintint 转换为 Python 的 int。PostgreSQL 的 bigintoid 在 Python 2 中转换为 long,在 Python 3 中转换为 int + + + + + PostgreSQL realdouble 会转换为 + Python float。 + + + + + + PostgreSQL numeric 会转换为 + Python Decimal。如果可用,将从 + cdecimal 包导入这种类型。 + 否则,将使用标准库中的 decimal.Decimal。 + cdecimal 明显快于 decimal。 + 不过在 Python 3.3 及更高版本中, + cdecimal 已经以 decimal 这一名称并入标准库, + 因此不再有区别。 + + + + + PostgreSQL 的 bytea 在 Python 2 中转换为 str,在 Python 3 中转换为 bytes。在 Python 2 中,应将该字符串视为不带任何字符编码的字节序列。 + + + + + 所有其他数据类型,包括 PostgreSQL 字符串类型,都会转换为 Python + str。在 Python 2 中,该字符串采用 PostgreSQL 服务器编码; + 在 Python 3 中,它与所有字符串一样,都是 Unicode 字符串。 + + + + + + 对于非标量数据类型,请参见下文。 + + + + + + + 当 PL/Python 函数返回时,其返回值会按如下方式转换成该函数声明的 PostgreSQL 返回数据类型: + + + + + 当 PostgreSQL 返回类型为boolean时,返回值会按照Python规则进行真值判定。也就是说,0 和空字符串为假,但值得注意的是,'f' 为真。 + + + + + 如果 PostgreSQL 返回类型是 bytea,会先使用相应的 Python 内置函数,将返回值转换为字符串(Python 2)或 bytes(Python 3),再将结果转换为 bytea + + + + + 对于所有其他 PostgreSQL 返回类型,返回值会使用 Python 内置函数str转换为字符串,然后将结果传给 PostgreSQL 数据类型的输入函数。(如果 Python 值是float,则会使用repr内置函数而不是str来转换,以避免精度损失。) + + + + Python 2 中的字符串传给 PostgreSQL 时,必须采用 PostgreSQL 服务器编码。 + 在当前服务器编码中无效的字符串会引发错误,但并非所有编码不匹配都能被检测到, + 因此处理不当仍可能产生乱码数据。Unicode 字符串会自动转换为正确的编码, + 因而使用它们可能更安全、更方便。在 Python 3 中,所有字符串都是 Unicode 字符串。 + + + + + + 对于非标量数据类型,请参见下文。 + + + + + 请注意,即使声明的 PostgreSQL 返回类型与实际返回对象的 Python 数据类型在逻辑上并不匹配,也不会报错;无论如何该值都会被转换。 + + + + + 空值、None + 如果将 SQL 空值空值在 PL/Python 中传递给函数,该参数值在 Python 中会表现为 None。例如,函数 pymax 的定义(见 )对空值输入会返回错误结果。可以在函数定义中添加 STRICT,让 PostgreSQL 采取更合理的行为:如果传入空值,就完全不调用函数,而是自动返回空值结果。也可以在函数体中检查空值输入: +CREATE FUNCTION pymax (a integer, b integer) + RETURNS integer +AS $$ + if (a is None) or (b is None): + return None + if a > b: + return a + return b +$$ LANGUAGE plpythonu; +如上所示,要从 PL/Python 函数返回 SQL 空值,只需返回 None。无论函数是否严格,都可以这样做。 + + + + + 数组、列表 + + + SQL 数组值会作为 Python 列表传入 PL/Python。要从 PL/Python 函数返回 SQL 数组值,请返回一个 Python 序列,例如列表或元组: + + +CREATE FUNCTION return_arr() + RETURNS int[] +AS $$ +return (1, 2, 3, 4, 5) +$$ LANGUAGE plpythonu; + +SELECT return_arr(); + return_arr +------------- + {1,2,3,4,5} +(1 row) + + + 请注意,在 Python 中,字符串也是序列,这可能会带来一些 Python 程序员熟悉但并不理想的效果: + + +CREATE FUNCTION return_str_arr() + RETURNS varchar[] +AS $$ +return "hello" +$$ LANGUAGE plpythonu; + +SELECT return_str_arr(); + return_str_arr +---------------- + {h,e,l,l,o} +(1 row) + + + + + + 复合类型 + + 复合类型参数会以 Python 映射的形式传给函数。映射中的元素名就是复合类型的属性名。如果传入行中的某个属性为空值,那么它在映射中的值就是None。例如: + + +CREATE TABLE employee ( + name text, + salary integer, + age integer +); + +CREATE FUNCTION overpaid (e employee) + RETURNS boolean +AS $$ + if e["salary"] > 200000: + return True + if (e["age"] < 30) and (e["salary"] > 100000): + return True + return False +$$ LANGUAGE plpythonu; + + + + + 有多种方法可以从 Python 函数返回行类型或复合类型。以下示例假定我们有: + + +CREATE TYPE named_value AS ( + name text, + value integer +); + + + 复合结果可以按以下形式返回: + + + + 序列类型(元组或列表,但不能是集合,因为集合不可通过索引访问) + + 返回的序列对象,其项目数必须与复合结果类型的字段数相同。索引 0 的项目赋给复合类型的第一个字段,索引 1 的项目赋给第二个字段,依此类推。例如: +CREATE FUNCTION make_pair (name text, value integer) + RETURNS named_value +AS $$ + return [ name, value ] + # 也可以使用元组: return ( name, value ) +$$ LANGUAGE plpythonu; +要为某一列返回 SQL 空值,请将 None 放在对应位置。 + + + + + 映射(字典) + + 结果类型中每一列的值,都使用列名作为键从映射中取得。例如: +CREATE FUNCTION make_pair (name text, value integer) + RETURNS named_value +AS $$ + return { "name": name, "value": value } +$$ LANGUAGE plpythonu; +字典中多余的键值对会被忽略,缺少键则会被视为错误。要为某一列返回 SQL 空值,请插入 None,并以对应列名为键。 + + + + + 对象(提供方法__getattr__的任何对象) + + + 其工作方式与映射相同。例如: + + +CREATE FUNCTION make_pair (name text, value integer) + RETURNS named_value +AS $$ + class named_value: + def __init__ (self, n, v): + self.name = n + self.value = v + return named_value(name, value) + + # 或者简写为 + class nv: pass + nv.name = name + nv.value = value + return nv +$$ LANGUAGE plpythonu; + + + + + + + + + 也支持带OUT参数的函数。例如: + +CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ +return (1, 2) +$$ LANGUAGE plpythonu; + +SELECT * FROM multiout_simple(); + + + + + + 集合返回函数 + + PL/Python函数也可以返回标量类型或复合类型的集合。实现方式有多种,因为返回的对象在内部会被转换成一个迭代器。以下示例假定我们有如下复合类型: + + +CREATE TYPE greeting AS ( + how text, + who text +); + + + 集合结果可以通过以下对象返回: + + + + 序列类型(元组、列表、集合) + + + +CREATE FUNCTION greet (how text) + RETURNS SETOF greeting +AS $$ + # 返回包含列表的元组,以列表表示复合类型 + # 其他组合方式也都可用 + return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] ) +$$ LANGUAGE plpythonu; + + + + + + + 迭代器(任何提供 __iter__next 方法的对象) + + + +CREATE FUNCTION greet (how text) + RETURNS SETOF greeting +AS $$ + class producer: + def __init__ (self, how, who): + self.how = how + self.who = who + self.ndx = -1 + + def __iter__ (self): + return self + + def next (self): + self.ndx += 1 + if self.ndx == len(self.who): + raise StopIteration + return ( self.how, self.who[self.ndx] ) + + return producer(how, [ "World", "PostgreSQL", "PL/Python" ]) +$$ LANGUAGE plpythonu; + + + + + + + 生成器(yield + + + +CREATE FUNCTION greet (how text) + RETURNS SETOF greeting +AS $$ + for who in [ "World", "PostgreSQL", "PL/Python" ]: + yield ( how, who ) +$$ LANGUAGE plpythonu; + + + + + 由于 Python 的缺陷 #1483133, + 某些调试版本的 Python 2.4(以--with-pydebug选项配置并编译) + 在使用迭代器返回集合结果时会导致PostgreSQL服务器崩溃。 + 未打补丁的 Fedora 4 就包含此缺陷。在 Python 的正式版本或已打补丁的 Fedora 4 + 中不会发生这种情况。 + + + + + + + + + + 也支持带OUT参数的集合返回函数(使用RETURNS SETOF record)。例如: + +CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$ +return [(1, 2)] * n +$$ LANGUAGE plpythonu; + +SELECT * FROM multiout_simple_setof(3); + + + + + + + + 共享数据 + + + 全局字典SD可用于在同一函数的多次调用之间保存私有数据。全局字典GD则是可供一个会话中的所有 Python 函数使用的公共数据;使用时要小心。全局数据 + 在 PL/Python 中 + + + + 每个函数在 Python 解释器中都有自己的执行环境,因此myfunc中的全局数据和函数参数对myfunc2不可见。例外是前面提到的GD字典中的数据。 + + + + + + 匿名代码块 + + + PL/Python 也支持通过语句调用的匿名代码块: + + +DO $$ + # PL/Python 代码 +$$ LANGUAGE plpythonu; + + + 匿名代码块不接受任何参数,而且无论返回什么值都会被丢弃。除此之外,它的行为与函数完全相同。 + + + + + + 触发器函数 + + + 触发器 + 在 PL/Python 中 + + + + 当函数被用作触发器时,字典TD包含与触发器相关的值: + + + TD["event"] + + + 以字符串形式包含事件:INSERTUPDATEDELETE或者TRUNCATE。 + + + + + + TD["when"] + + + 包含BEFOREAFTER或者INSTEAD OF之一。 + + + + + + TD["level"] + + + 包含ROW或者STATEMENT。 + + + + + + TD["new"] + TD["old"] + + + 对于行级触发器,这两个字段中的一个或两个会根据触发器事件包含相应的触发行。 + + + + + + TD["name"] + + + 包含触发器名称。 + + + + + + TD["table_name"] + + + 包含触发器所在表的名称。 + + + + + + TD["table_schema"] + + + 包含触发器所在表的模式。 + + + + + + TD["relid"] + + + 包含触发器所在表的 OID。 + + + + + + TD["args"] + + + 如果CREATE TRIGGER命令包含参数,这些参数可在TD["args"][0]TD["args"][n-1]中取得。 + + + + + + + + 如果TD["when"]BEFOREINSTEAD OF,且TD["level"]ROW,那么可以从 Python 函数返回None"OK"来表示该行未被修改,返回"SKIP"来中止该事件;如果TD["event"]INSERTUPDATE,还可以返回"MODIFY"来表示你已经修改了新行。否则返回值会被忽略。 + + + + + 数据库访问 + + + PL/Python 语言模块会自动导入一个名为plpy的 Python 模块。该模块中的函数和常量在 Python 代码中可以通过plpy.foo这样的形式访问。 + + + + 数据库访问函数 + + + plpy模块提供若干函数来执行数据库命令: + + + + + plpy.execute(query [, max-rows]) + + + 使用查询字符串和可选的行数限制参数调用plpy.execute会执行该查询,并把结果作为结果对象返回。 + + + + 结果对象的行为类似于列表或字典对象。可以通过行号和列名来访问结果对象。例如: + +rv = plpy.execute("SELECT * FROM my_table", 5) + + 会从my_table中返回最多 5 行。如果my_table有一列名为my_column,则可以这样访问它: + +foo = rv[i]["my_column"] + + 返回的行数可以用内置的len函数获取。 + + + + 结果对象还具有以下附加方法: + + + nrows() + + + 返回该命令处理的行数。注意,这不一定与返回的行数相同。例如,UPDATE命令会设置这个值,但不会返回任何行(除非使用RETURNING)。 + + + + + + status() + + + SPI_execute()的返回值。 + + + + + + colnames() + coltypes() + coltypmods() + + + 分别返回列名列表、列类型 OID 列表以及列的类型相关修饰符列表。 + + + + 如果在来自不产生结果集的命令的结果对象上调用这些方法,就会引发异常,例如不带RETURNINGUPDATEDROP TABLE。但在包含零行的结果集上使用这些方法是没有问题的。 + + + + + + __str__() + + + 还定义了标准的__str__方法,例如可以使用plpy.debug(rv)来调试查询执行结果。 + + + + + + + + 结果对象是可修改的。 + + + + 注意,调用plpy.execute会把整个结果集读入内存。只有在确信结果集相对较小时才应使用这个函数。获取大型结果时,如果不想承担过高的内存占用风险,应使用plpy.cursor而不是plpy.execute。 + + + + + + plpy.prepare(query [, argtypes]) + plpy.execute(plan [, arguments [, max-rows]]) + + + + 准备查询在 PL/Python 中 + plpy.prepare为查询准备执行计划。若查询中有参数引用,则它接受查询字符串和参数类型列表作为参数。例如: + +plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"]) + + text是你要传给$1的变量类型。如果查询不需要任何参数,第二个参数可以省略。 + + + + 准备好语句后,可以使用plpy.execute函数的另一种调用形式来执行它: + +rv = plpy.execute(plan, ["name"], 5) + + 将计划对象作为第一个参数传递(而不是查询字符串),并将要代入查询的值列表作为第二个参数传递。如果查询不需要任何参数,第二个参数可以省略。和前面一样,第三个参数仍然是可选的行数限制。 + + + + 查询参数和结果行字段会按照中所述,在 PostgreSQL 与 Python 数据类型之间进行转换。 + + + + 当你使用 PL/Python 模块准备一个计划时,它会被自动保存。关于这意味着什么,请参阅 SPI 文档()。为了在函数调用之间有效利用这一点,需要使用持久存储字典SDGD之一(见)。例如: + +CREATE FUNCTION usesavedplan() RETURNS trigger AS $$ + if "plan" in SD: + plan = SD["plan"] + else: + plan = plpy.prepare("SELECT 1") + SD["plan"] = plan + # 函数的其余部分 +$$ LANGUAGE plpythonu; + + + + + + + plpy.cursor(query) + plpy.cursor(plan [, arguments]) + + + plpy.cursor函数接受与plpy.execute相同的参数(只是没有行数限制),并返回一个游标对象,使你可以分块处理大型结果集。与plpy.execute一样,可以使用查询字符串,也可以使用带参数列表的计划对象。 + + + 游标对象提供 fetch 方法,接受一个整数参数并返回结果对象。每次调用 fetch,返回对象都包含下一批行,行数不会超过参数值。所有行都取完后,fetch 开始返回空的结果对象。游标对象还提供迭代器接口,每次产生一行,直到所有行取完。通过这种方式取得的数据不是结果对象,而是字典,每个字典对应一行结果。 + + + 下面示例展示了处理大表中数据的两种方式: + +CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$ +odd = 0 +for row in plpy.cursor("select num from largetable"): + if row['num'] % 2: + odd += 1 +return odd +$$ LANGUAGE plpythonu; + +CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$ +odd = 0 +cursor = plpy.cursor("select num from largetable") +while True: + rows = cursor.fetch(batch_size) + if not rows: + break + for row in rows: + if row['num'] % 2: + odd += 1 +return odd +$$ LANGUAGE plpythonu; + +CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$ +odd = 0 +plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"]) +rows = list(plpy.cursor(plan, [2])) + +return len(rows) +$$ LANGUAGE plpythonu; + + + + + 游标会被自动释放。但如果想显式释放游标持有的全部资源,可使用close方法。一旦关闭,就不能再从该游标中取数。 + + + + 不要将 plpy.cursor 创建的对象,与 Python 数据库 API 规范定义的 DB-API 游标混淆。除了名称相同,它们没有共同之处。 + + + + + + + + + 捕获错误 + + + 访问数据库的函数可能会遇到错误,这会导致它们中止并抛出异常。plpy.executeplpy.prepare都可能抛出plpy.SPIError某个子类的实例,默认情况下这会终止函数。这个错误可以像其他 Python 异常一样,通过try/except结构来处理。例如: + +CREATE FUNCTION try_adding_joe() RETURNS text AS $$ + try: + plpy.execute("INSERT INTO users(username) VALUES ('joe')") + except plpy.SPIError: + return "something went wrong" + else: + return "Joe added" +$$ LANGUAGE plpythonu; + + + + 所抛出异常的实际类,对应引发错误的具体条件。参见 中列出的可能条件。模块 plpy.spiexceptions 为每种 PostgreSQL 条件定义了一个异常类,类名由条件名派生。例如,division_by_zero 变为 DivisionByZerounique_violation 变为 UniqueViolationfdw_error 变为 FdwError,依此类推。所有这些异常类都继承自 SPIError。这样区分之后,更容易处理特定错误,例如: +CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$ +from plpy import spiexceptions +try: + plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"]) + plpy.execute(plan, [numerator, denominator]) +except spiexceptions.DivisionByZero: + return "denominator cannot equal zero" +except spiexceptions.UniqueViolation: + return "already have that fraction" +except plpy.SPIError, e: + return "other error, SQLSTATE %s" % e.sqlstate +else: + return "fraction inserted" +$$ LANGUAGE plpythonu; +注意,因为 plpy.spiexceptions 模块中的所有异常都继承自 SPIError,所以处理它的 except 子句会捕获任何数据库访问错误。 + + + 作为处理不同错误情况的另一种方法,你可以捕获SPIError异常,并在except块中通过查看异常对象的sqlstate属性来判断具体的错误条件。该属性是一个包含SQLSTATE错误代码的字符串值。这种方法大体上提供了相同的功能。 + + + + + + 显式子事务 + + + 如中所述,从数据库访问引发的错误中恢复,可能会造成一种不理想的情况:在某个操作失败之前,其他一些操作已经成功,而在从该错误恢复后,数据却处于不一致状态。PL/Python 以显式子事务的形式为这个问题提供了解决方案。 + + + + 子事务上下文管理器 + + 考虑以下实现两个账户之间转账的函数: +CREATE FUNCTION transfer_funds() RETURNS void AS $$ +try: + plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") + plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") +except plpy.SPIError, e: + result = "error transferring funds: %s" % e.args +else: + result = "funds transferred correctly" +plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) +plpy.execute(plan, [result]) +$$ LANGUAGE plpythonu; +如果第二条 UPDATE 语句引发异常,此函数会报告错误,但第一条 UPDATE 的结果仍会提交。换句话说,资金会从 Joe 的账户中扣除,却不会转入 Mary 的账户。 + + 为避免此类问题,可以将 plpy.execute 调用放在显式子事务中。plpy 模块提供了用于管理显式子事务的辅助对象,可通过 plpy.subtransaction() 函数创建。此函数创建的对象实现了上下文管理器接口。使用显式子事务后,可以将函数改写为: +CREATE FUNCTION transfer_funds2() RETURNS void AS $$ +try: + with plpy.subtransaction(): + plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") + plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") +except plpy.SPIError, e: + result = "error transferring funds: %s" % e.args +else: + result = "funds transferred correctly" +plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) +plpy.execute(plan, [result]) +$$ LANGUAGE plpythonu; +注意,仍需要使用 try/catch。否则,异常会传播到 Python 调用栈顶层,使整个函数因 PostgreSQL 错误而中止,从而不会向 operations 表插入任何行。子事务上下文管理器不会捕获错误,只保证在其作用域内执行的所有数据库操作以原子方式提交或回滚。任何异常退出都会使子事务块回滚,并不限于数据库访问错误。显式子事务块中抛出的普通 Python 异常,也会导致该子事务回滚。 + + + + 较早的 Python 版本 + + 使用 with 关键字的上下文管理器语法,从 Python 2.6 起默认可用。如果 PL/Python 使用更早的 Python 版本,仍然可以使用显式子事务,只是没那么方便。可以通过便捷别名 enterexit,调用子事务管理器的 __enter____exit__ 函数。转账示例函数可以写成: +CREATE FUNCTION transfer_funds_old() RETURNS void AS $$ +try: + subxact = plpy.subtransaction() + subxact.enter() + try: + plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") + plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") + except: + import sys + subxact.exit(*sys.exc_info()) + raise + else: + subxact.exit(None, None, None) +except plpy.SPIError, e: + result = "error transferring funds: %s" % e.args +else: + result = "funds transferred correctly" + +plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) +plpy.execute(plan, [result]) +$$ LANGUAGE plpythonu; + + + + + 虽然 Python 2.5 已实现上下文管理器,但在该版本中使用 with 语法,需要使用 future 语句。不过,由于实现细节的限制,PL/Python 函数中不能使用 future 语句。 + + + + + + + 辅助函数 + + + plpy模块还提供以下函数: + + plpy.debug(msg, **kwargs) + plpy.log(msg, **kwargs) + plpy.info(msg, **kwargs) + plpy.notice(msg, **kwargs) + plpy.warning(msg, **kwargs) + plpy.error(msg, **kwargs) + plpy.fatal(msg, **kwargs) + + elog在 PL/Python 中 + plpy.errorplpy.fatal实际上会引发 Python 异常;如果这些异常未被捕获,就会传播到调用查询,导致当前事务或子事务中止。raise plpy.Error(msg)raise plpy.Fatal(msg)分别等价于调用plpy.error(msg)plpy.fatal(msg),不过raise形式不允许传递关键字参数。其他函数只会生成不同优先级的消息。某一优先级的消息是报告给客户端、写入服务器日志还是两者兼有,由配置变量控制。更多信息见。 + + + + msg参数作为位置参数给出。为了向后兼容,也可以给出多个位置参数。在这种情况下,这些位置参数所构成元组的字符串表示会成为报告给客户端的消息。 + + + + 可接受以下仅限关键字的参数: + + detail + hint + sqlstate + schema_name + table_name + column_name + datatype_name + constraint_name + + 作为仅限关键字参数传入的对象,其字符串表示会被用来丰富报告给客户端的消息。例如: + + +CREATE FUNCTION raise_custom_exception() RETURNS void AS $$ +plpy.error("custom exception message", + detail="some info about exception", + hint="hint for users") +$$ LANGUAGE plpythonu; + +=# SELECT raise_custom_exception(); +ERROR: plpy.Error: custom exception message +DETAIL: some info about exception +HINT: hint for users +CONTEXT: Traceback (most recent call last): + PL/Python function "raise_custom_exception", line 4, in <module> + hint="hint for users") +PL/Python function "raise_custom_exception" + + + + + 另一组辅助函数是plpy.quote_literal(string)plpy.quote_nullable(string)以及plpy.quote_ident(string)。它们等价于中描述的内置加引号函数。在构造临时查询时,这些函数很有用。中动态 SQL 的一个 PL/Python 等价写法如下: + +plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % ( + plpy.quote_ident(colname), + plpy.quote_nullable(newvalue), + plpy.quote_literal(keyvalue))) + + + + + + + 环境变量 + + + Python 解释器接受的某些环境变量也可用于影响 PL/Python 的行为。这些变量需要在 PostgreSQL 主服务器进程的环境中设置,例如在启动脚本中设置。可用的环境变量取决于 Python 版本;详情请参阅 Python 文档。在撰写本文时,假定 Python 版本足以支持这些变量,下列环境变量会影响 PL/Python: + + + PYTHONHOME + + + + PYTHONPATH + + + + PYTHONY2K + + + + PYTHONOPTIMIZE + + + + PYTHONDEBUG + + + + PYTHONVERBOSE + + + + PYTHONCASEOK + + + + PYTHONDONTWRITEBYTECODE + + + + PYTHONIOENCODING + + + + PYTHONUSERBASE + + + + PYTHONHASHSEED + + + + (这似乎是 Python 的实现细节,超出了 PL/Python 的控制范围:列在python手册页中的某些环境变量只在命令行解释器中有效,而在嵌入式 Python 解释器中无效。) + + + diff --git a/zh/9.6/pltcl.sgml b/zh/9.6/pltcl.sgml new file mode 100644 index 00000000..e654926e --- /dev/null +++ b/zh/9.6/pltcl.sgml @@ -0,0 +1,635 @@ + + + + PL/Tcl - Tcl 过程语言 + + + PL/Tcl + + + + Tcl + + + PL/Tcl 是 PostgreSQL 数据库系统的一种可加载过程语言,可以使用 Tcl 语言编写函数和触发器函数。 + + + + + + 概述 + + + PL/Tcl 提供了函数编写者在 C 语言中所具备的大部分能力,但会施加少量限制,并额外提供 Tcl 可用的强大字符串处理库。 + + + + 其中一个颇具吸引力的良性限制是,所有内容都在 Tcl 解释器的安全上下文中执行。除了安全 Tcl 受限的命令集之外,只提供了少数通过 SPI 访问数据库以及通过elog()发出消息的命令。与 C 函数不同,PL/Tcl 不提供访问数据库服务器内部机制的方法,也不能在PostgreSQL服务器进程的权限下获得操作系统级访问。因此,可以信任非特权数据库用户使用这种语言;它不会授予他们无限制的权限。 + + + + 另一项值得注意的实现限制是,Tcl 函数不能用来为新数据类型创建输入/输出函数。 + + + + 有时需要编写不受安全 Tcl 限制的 Tcl 函数。例如,可能希望某个 Tcl 函数能够发送电子邮件。为处理这类情况,PL/Tcl 提供了一个名为PL/TclU的变体(其中 U 表示不受信任的 Tcl)。除了使用完整的 Tcl 解释器之外,它与前者是完全相同的语言。如果使用PL/TclU,就必须将其安装为一种不受信任的过程语言,这样就只有数据库超级用户才能在其中创建函数。PL/TclU函数的编写者必须确保该函数不会被用来做任何不希望发生的事情,因为它能够执行任何以数据库管理员身份登录的用户所能做的事情。 + + + + 如果在安装过程的配置步骤中指定了 Tcl 支持,则PL/TclPL/TclU调用处理器的共享对象代码会自动构建并安装到PostgreSQL的库目录中。要在某个特定数据库中安装PL/Tcl和/或PL/TclU,请使用CREATE EXTENSION命令或createlang程序,例如createlang pltcl dbnamecreatelang pltclu dbname。 + + + + + + + PL/Tcl 函数和参数 + + + 要用PL/Tcl语言创建函数,可使用标准的语法: + + +CREATE FUNCTION funcname (argument-types) RETURNS return-type AS $$ + # PL/Tcl 函数体 +$$ LANGUAGE pltcl; + + + PL/TclU的写法相同,只是语言必须指定为pltclu。 + + + 函数体就是一段 Tcl 脚本。调用函数时,参数值以变量 $1 ... $n 的形式传入 Tcl 脚本。Tcl 代码按通常方式使用 return 语句返回结果。 + + + 例如,一个返回两个整数值中较大值的函数可以定义为: + + +CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS $$ + if {$1 > $2} {return $1} + return $2 +$$ LANGUAGE pltcl STRICT; + + + 注意STRICT子句,它让我们不必考虑空值输入:如果传入的是空值,函数根本不会被调用,而是会自动返回空值结果。 + + + + 在非严格函数中,如果某个参数的实际值为空值,对应的$n变量会被设置为空串。要检测某个特定参数是否为空值,可使用函数argisnull。例如,假设我们希望在tcl_max的两个参数中一个为空值、一个非空值时返回非空值参数,而不是返回空值: + + +CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS $$ + if {[argisnull 1]} { + if {[argisnull 2]} { return_null } + return $2 + } + if {[argisnull 2]} { return $1 } + if {$1 > $2} {return $1} + return $2 +$$ LANGUAGE pltcl; + + + + + 如上所示,要从 PL/Tcl 函数返回空值,请执行return_null。无论函数是否为严格函数,都可以这样做。 + + + + 复合类型参数会作为 Tcl 数组传递给函数。数组元素名就是该复合类型的属性名。如果传入行中的某个属性为空值,它就不会出现在数组中。下面是一个示例: + + +CREATE TABLE employee ( + name text, + salary integer, + age integer +); + +CREATE FUNCTION overpaid(employee) RETURNS boolean AS $$ + if {200000.0 < $1(salary)} { + return "t" + } + if {$1(age) < 30 && 100000.0 < $1(salary)} { + return "t" + } + return "f" +$$ LANGUAGE pltcl; + + + + + 目前尚不支持返回复合类型的结果值,也不支持返回集合。 + + + + PL/Tcl目前对域类型还没有完整支持:它把域 + 当作底层标量类型同样对待。这意味着与该域关联的约束不会被强制执行。 + 对于函数参数来说这不是问题,但如果把PL/Tcl + 函数声明为返回域类型,就会带来隐患。 + + + + + + + PL/Tcl 中的数据值 + + + 提供给 PL/Tcl 函数代码的参数值,只是将输入参数转换成文本形式(就像用SELECT语句将它们显示出来一样)。反过来,return命令会接受任何字符串,只要它是该函数声明返回类型的可接受输入格式。因此,在 PL/Tcl 函数内部,所有值都只是文本字符串。 + + + + + + + PL/Tcl 中的全局数据 + + + 全局数据 + 在 PL/Tcl 中 + + + + 有时需要在一次函数调用结束后保留某些全局数据,供下一次调用继续使用,或者在不同函数之间共享全局数据。在 PL/Tcl 中这很容易做到,但必须理解其中的一些限制。 + + + + 出于安全原因,PL/Tcl 会针对每个 SQL 角色,在该角色各自独立的 Tcl 解释器中执行其调用的函数。这样可以防止一个用户无意或恶意地干扰另一用户的 PL/Tcl 函数行为。每个这样的解释器都为任何global Tcl 变量维护各自的值。因此,当且仅当两个 PL/Tcl 函数由同一个 SQL 角色执行时,它们才会共享同一组全局变量。在某些应用中,一个会话会在多个 SQL 角色下执行代码(例如通过SECURITY DEFINER函数、使用SET ROLE等),这时你可能需要显式采取一些步骤,确保 PL/Tcl 函数能够共享数据。为此,应确保需要互相通信的函数由同一用户拥有,并将它们标记为SECURITY DEFINER。当然,你必须小心,确保这类函数不会被用来执行任何非预期操作。 + + + + 一个会话中使用的所有 PL/TclU 函数都在同一个 Tcl 解释器中执行,而这个解释器当然不同于 PL/Tcl 函数所使用的解释器。因此,PL/TclU 函数之间会自动共享全局数据。这不被视为安全风险,因为所有 PL/TclU 函数都在相同的信任级别上执行,也就是数据库超级用户的级别。 + + + + 为帮助防止 PL/Tcl 函数无意间彼此干扰,每个函数都可通过upvar命令访问一个全局数组。该变量的全局名称是函数的内部名称,局部名称是GD。建议将GD用于保存函数的持久私有数据。只有当你明确希望某些值在多个函数之间共享时,才应使用常规的 Tcl 全局变量。(注意GD数组只在某个特定解释器内部是全局的,因此它们不会绕过上文提到的安全限制。) + + + + 下面spi_execp的示例展示了GD的用法。 + + + + + 从 PL/Tcl 访问数据库 + + + 下列命令可用于从 PL/Tcl 函数体中访问数据库: + + + + + spi_exec -count n -array name command loop-body + + + 执行以字符串形式给出的 SQL 命令。命令出错时会引发错误。否则,spi_exec的返回值是该命令处理的行数(选出、插入、更新或删除的行),如果命令是工具语句则返回零。此外,如果命令是SELECT语句,则所选列的值会按下文所述放入 Tcl 变量中。 + + 可选的 -count 值告诉 spi_exec 此命令最多处理多少行,其效果类似于将查询设为游标后执行 FETCH n + + 如果命令是SELECT语句,则结果列的值会放入以列名命名的 Tcl 变量中。 + 如果给定了-array选项,则列值会存储在指定的关联数组元素中, + 列名用作数组索引。此外,结果中的当前行号(从零开始计数)将存储在数组元素中, + 该数组元素的名称为.tupno,除非该名称在结果中已被用作列名。 + + + 如果命令是SELECT语句且未给出loop-body + 脚本,则只会把结果的第一行存入 Tcl 变量或数组元素中;其余行如果存在,会被忽略。 + 如果查询没有返回任何行,则不会存储任何内容。(可以通过检查spi_exec的结果来检测这种情况。) + 例如: + +spi_exec "SELECT count(*) AS cnt FROM pg_proc" + + 会把 Tcl 变量$cnt设置为pg_proc系统目录中的行数。 + + + 如果给出了可选的loop-body参数,它是一段 Tcl 脚本,会对查询结果中的每一行执行一次。 + (如果给定命令不是SELECT,则loop-body会被忽略。) + 在每次迭代前,当前行各列的值都会被存入 Tcl 变量或数组元素中。 + 例如: + +spi_exec -array C "SELECT * FROM pg_class" { + elog DEBUG "have table $C(relname)" +} + + 会为pg_class的每一行打印一条日志消息。这个特性与其他 Tcl 循环结构的工作方式类似;特别是continuebreak在循环体内按通常方式工作。 + + + 如果查询结果中的某一列为空值,则对应的目标变量会被unset,而不是被设值。 + + + + + + spi_prepare query typelist + + + + 准备并保存一个查询计划以供后续执行。保存的计划会在当前会话的整个生命周期内保留。 + 准备查询 + 在 PL/Tcl 中 + + + + 该查询可以使用参数,也就是在实际执行计划时提供值的占位符。 + 在查询字符串中,通过符号$1 ... $n引用参数。 + 如果查询使用了参数,则必须把参数类型名称作为 Tcl 列表给出。 + (如果不使用参数,则给typelist写一个空列表。) + + + + spi_prepare的返回值是一个查询 ID,供后续调用spi_execp时使用。示例参见spi_execp。 + + + + + + spi_execp -count n -array name -nulls string queryid value-list loop-body + + + + 执行先前用spi_prepare准备好的查询。 + queryidspi_prepare返回的 ID。 + 如果查询引用了参数,则必须提供value-list。 + 这是参数实际值构成的 Tcl 列表。该列表的长度必须与先前提供给spi_prepare的参数类型列表相同。 + 如果查询没有参数,则省略value-list。 + + + + 可选的-nulls值是由空格和'n'字符组成的字符串,用来告诉spi_execp哪些参数是空值。 + 如果给出,它的长度必须与value-list完全相同。如果未给出,则所有参数值都视为非空值。 + + + + 除了指定查询及其参数的方式不同之外,spi_execp的工作方式与spi_exec完全一样。 + -count-arrayloop-body选项都相同,返回值也相同。 + + + + 下面是使用已准备计划的 PL/Tcl 函数示例: + + +CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS $$ + if {![ info exists GD(plan) ]} { + # 在首次调用时准备并保存计划 + set GD(plan) [ spi_prepare \ + "SELECT count(*) AS cnt FROM t1 WHERE num >= \$1 AND num <= \$2" \ + [ list int4 int4 ] ] + } + spi_execp -count 1 $GD(plan) [ list $1 $2 ] + return $cnt +$$ LANGUAGE pltcl; + + + 我们需要在传给spi_prepare的查询字符串中加入反斜杠, + 以确保$n标记会原样传递给 + spi_prepare,而不会被 Tcl 执行变量替换。 + + + + + + + spi_lastoid spi_lastoid 在 PL/Tcl 中 + + 如果最后一次 spi_execspi_execp 执行的是单行 INSERT,且被修改的表包含 OID,则返回所插入行的 OID。(否则返回零。) + + + + + + quote string + + + + 将给定字符串中的所有单引号和反斜杠字符都加倍。 + 这可用于安全地为那些要插入到传给spi_execspi_prepare的 SQL 命令中的字符串加引号。 + 例如,考虑如下 SQL 命令字符串: + + +"SELECT '$val' AS ret" + + + 其中 Tcl 变量val的实际内容是doesn't。这会得到最终命令字符串: + + +SELECT 'doesn't' AS ret + + + 这会在spi_execspi_prepare期间导致解析错误。 + 要使其正常工作,提交的命令应当包含: + + +SELECT 'doesn''t' AS ret + + + 在 PL/Tcl 中,可以用下面这种方式构造它: + + +"SELECT '[ quote $val ]' AS ret" + + + spi_execp的一个优点是,你不必像这样给参数值加引号,因为这些参数永远不会作为 SQL 命令字符串的一部分被解析。 + + + + + + + elog level msg + + elog + 在 PL/Tcl 中 + + + + + + 发出日志或错误消息。可用级别包括 + DEBUGLOGINFO, + NOTICEWARNINGERROR和 + FATALERROR + 会引发错误条件;如果外围 Tcl 代码没有捕获它, + 该错误就会传播到调用查询,导致当前事务或子事务中止。这实际上与 Tcl 的error命令相同。 + FATAL会中止事务并导致当前会话关闭。(在 PL/Tcl 函数中使用这个错误级别可能并没有什么充分理由,但为了完整性仍提供它。) + 其他级别只会生成不同优先级的消息。 + 某个特定优先级的消息是报告给客户端、 + 写入服务器日志,还是两者都做,由 + 和 + 配置变量控制。参见了解更多信息。 + + + + + + + + + + + PL/Tcl 中的触发器函数 + + + 触发器 + 在 PL/Tcl 中 + + + 可以用 PL/Tcl 编写触发器函数。PostgreSQL 要求作为触发器调用的函数必须声明为无参数、返回类型为 trigger 的函数。 + 触发器管理器的信息通过以下变量传入函数体: + + + $TG_name + + + CREATE TRIGGER语句中触发器的名称。 + + + + + + $TG_relid + + 导致触发器函数被调用的表的对象 ID。 + + + + + $TG_table_name + + 导致触发器函数被调用的表的名称。 + + + + + $TG_table_schema + + 导致触发器函数被调用的表所属的模式。 + + + + + $TG_relatts + + + 表列名构成的 Tcl 列表,其前面带有一个空列表元素。因此,使用Tcllsearch命令在该列表中查找列名时,返回的元素编号会从 1 开始表示第一列,这与PostgreSQL中列的惯常编号方式一致。(空列表元素也会出现在已删除列的位置上,这样其右侧列的属性编号仍然正确。) + + + + + + $TG_when + + + 其值为字符串BEFOREAFTERINSTEAD OF,具体取决于触发器事件的类型。 + + + + + + $TG_level + + + 其值为字符串ROWSTATEMENT,取决于触发器事件的类型。 + + + + + + $TG_op + + + 其值为字符串INSERTUPDATEDELETETRUNCATE,取决于触发器事件的类型。 + + + + + + $NEW + + + 对于INSERTUPDATE动作,这是一个包含新表行值的关联数组;对于DELETE则为空。该数组以列名为索引。值为空值的列不会出现在数组中。对于语句级触发器,不会设置这个变量。 + + + + + + $OLD + + + 对于UPDATEDELETE动作,这是一个包含旧表行值的关联数组;对于INSERT则为空。该数组以列名为索引。值为空值的列不会出现在数组中。对于语句级触发器,不会设置这个变量。 + + + + + + $args + + 一个 Tcl 列表,包含 CREATE TRIGGER 语句中给出的触发器函数参数。在函数体中,也可以通过 $1 ... $n 访问这些参数。 + + + + + + + 触发器函数可以返回字符串 OKSKIP,也可以返回列名与值的配对列表。如果返回 OK,触发它的操作(INSERT/UPDATE/DELETE)会正常继续。SKIP 告诉触发器管理器静默地取消针对该行的操作。如果返回列表,则告诉 PL/Tcl 向触发器管理器返回修改后的行;修改后行的内容由列表中的列名和值指定,未在列表中提及的列设为空值。返回修改后的行只对以下触发器有意义:行级 BEFORE INSERTUPDATE 触发器,此时将插入修改后的行,替代 $NEW 中给出的行;以及行级 INSTEAD OF INSERTUPDATE 触发器,此时返回的行用作 INSERT RETURNINGUPDATE RETURNING 子句的源数据。对于行级 BEFORE DELETEINSTEAD OF DELETE 触发器,返回修改后的行与返回 OK 的效果相同,即继续执行操作。其他所有类型的触发器都会忽略返回值。 + + + + + 可使用 Tcl 的array get命令,根据修改后元组的数组表示构造结果列表。 + + + + 下面是一个简单的触发器函数示例,强制用表中的一个整数值记录该行的更新次数。新插入的行将此值初始化为 0,以后每次更新操作都将其递增。 +CREATE FUNCTION trigfunc_modcount() RETURNS trigger AS $$ + switch $TG_op { + INSERT { + set NEW($1) 0 + } + UPDATE { + set NEW($1) $OLD($1) + incr NEW($1) + } + default { + return OK + } + } + return [array get NEW] +$$ LANGUAGE pltcl; + +CREATE TABLE mytab (num integer, description text, modcnt integer); + +CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab + FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt'); +注意,触发器函数本身不知道列名,列名通过触发器参数提供。这样,同一个触发器函数就可以在不同表上复用。 + + + + PL/Tcl 中的事件触发器函数 + + + 事件触发器 + 在 PL/Tcl 中 + + + 可以用 PL/Tcl 编写事件触发器函数。PostgreSQL 要求作为事件触发器调用的函数必须声明为无参数、返回类型为 event_trigger 的函数。 + 触发器管理器的信息通过以下变量传入函数体: + + + $TG_event + + + 该触发器所针对的事件名称。 + + + + + + $TG_tag + + + 该触发器所针对的命令标签。 + + + + + + + 触发器函数的返回值会被忽略。 + + 下面是一个简单的事件触发器函数示例,每当执行受支持的命令时,它只发出一条 NOTICE 消息: +CREATE OR REPLACE FUNCTION tclsnitch() RETURNS event_trigger AS $$ + elog NOTICE "tclsnitch: $TG_event $TG_tag" +$$ LANGUAGE pltcl; + +CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE PROCEDURE tclsnitch(); + + + + + + PL/Tcl 中的错误处理 + + + 异常 + 在 PL/Tcl 中 + + + + PL/Tcl 函数内的 Tcl 代码,或从 PL/Tcl 函数调用的 Tcl 代码,都可能引发错误:要么是执行了某个非法操作,要么是通过 Tcl 的error命令或 PL/Tcl 的elog命令主动生成错误。在 Tcl 中,可以使用catch命令捕获这类错误。如果错误没有被捕获,而是一路传播到 PL/Tcl 函数执行的顶层,它们会转变为数据库错误。 + + + + 反过来,在 PL/Tcl 的spi_execspi_preparespi_execp命令中发生的数据库错误会作为 Tcl 错误报告,因此也可以被 Tcl 的catch命令捕获。同样地,如果这些错误在未被捕获的情况下传播到顶层,它们又会重新变回数据库错误。 + + + + Tcl 提供了一个errorCode变量,它以便于 Tcl 程序解释的形式携带关于错误的附加信息。该变量的内容采用 Tcl 列表格式,第一个词标识报告该错误的子系统或库;其后的内容则由相应子系统或库自行定义。对于由 PL/Tcl 命令报告的数据库错误,第一个词是POSTGRES,第二个词是 PostgreSQL 版本号,后续内容则是字段名/字段值对,用来提供关于该错误的详细信息。字段SQLSTATEconditionmessage总是会提供(前两个分别对应中所示的错误代码和条件名称)。可能出现的字段包括 + detailhintcontext、 + schematablecolumn、 + datatypeconstraint、 + statementcursor_position、 + filenamelineno以及 + funcname。 + + + + 处理 PL/Tcl 的errorCode信息时,一种方便的办法是把它载入数组中,这样字段名就变成了数组下标。对应的代码可能如下所示: + +if {[catch { spi_exec $sql_command }]} { + if {[lindex $::errorCode 0] == "POSTGRES"} { + array set errorArray $::errorCode + if {$errorArray(condition) == "undefined_table"} { + # 处理表不存在的情况 + } else { + # 处理其他类型的 SQL 错误 + } + } +} + + (双冒号显式指定errorCode是一个全局变量。) + + + + + + 模块与<function>unknown</function>命令 + + PL/Tcl 支持在使用时自动装载 Tcl 代码。它会识别一张特殊的表 + pltcl_modules,该表被假定包含若干 Tcl 代码模块。 + 如果这张表存在,模块unknown会从该表取出,并在 + 数据库会话中第一次执行 PL/Tcl 函数之前立即装载到 Tcl 解释器中。 + (如果一个会话中使用了多个 Tcl 解释器,则对每个解释器分别执行这一 + 过程;参见。) + + + 虽然unknown模块实际上可以包含你需要的任何初始化 + 脚本,但它通常会定义一个 Tcl 的unknown过程,每当 + Tcl 无法识别被调用的过程名时,就会调用该过程。PL/Tcl的这一过程的标准 + 版本会尝试在pltcl_modules中找到一个能够定义所需 + 过程的模块。如果找到了,就把它装载到解释器中,然后允许继续执行最初 + 尝试的过程调用。辅助表pltcl_modfuncs提供了哪个模块 + 定义了哪些函数的索引,使查找相当快速。 + + + PostgreSQL发行版中包含用于维护这些表的 + 支持脚本:pltcl_loadmodpltcl_listmod、 + pltcl_delmod,以及标准unknown模块 + 的源代码,位于share/unknown.pltcl。要支持自动装载 + 机制,最初必须把这个模块装载到每个数据库中。 + + + 表pltcl_modulespltcl_modfuncs + 必须对所有用户可读,但最好只让数据库管理员拥有并可以写入它们。作为 + 一种安全预防措施,除非pltcl_modules由超级用户拥有, + 否则 PL/Tcl 会忽略它(因而不尝试装载unknown模块)。 + 不过,如果你足够信任其他用户,可以把这张表的 UPDATE 权限授予他们。 + + + + + + Tcl 过程名 + + + 在PostgreSQL中,只要参数个数或参数类型不同,就可以复用同一个函数名。不过,Tcl 要求所有过程名都必须不同。PL/Tcl 处理这一问题的方式是:在内部 Tcl 过程名中包含系统表 pg_proc 中的函数对象 ID 作为其名称的一部分。因此,名称相同但参数类型不同的PostgreSQL函数,也会对应不同的 Tcl 过程。这通常不是 PL/Tcl 程序员需要关心的事情,但在调试时可能会看见。 + + + + diff --git a/zh/9.6/postgres-fdw.sgml b/zh/9.6/postgres-fdw.sgml new file mode 100644 index 00000000..f5abaa13 --- /dev/null +++ b/zh/9.6/postgres-fdw.sgml @@ -0,0 +1,553 @@ + + + + postgres_fdw + + + postgres_fdw + + + + postgres_fdw 模块提供外部数据包装器 + postgres_fdw,可用于访问存储在外部 + PostgreSQL 服务器中的数据。 + + + + 本模块提供的功能与较旧的 模块在很大程度上重叠。 + 但 postgres_fdw 为访问远程表提供了更透明且符合标准的语法, + 并且在许多情况下性能更好。 + + + + 要准备通过 postgres_fdw 进行远程访问: + + + + 安装 postgres_fdw 扩展,可使用 。 + + + + + 使用 创建外部服务器对象, + 用来表示每个要连接的远程数据库。将除 user 和 + password 之外的连接信息指定为服务器对象的选项。 + + + + + 对于每个需要获准访问各个外部服务器的数据库用户,使用 + 创建用户映射。将要使用的 + 远程用户名和密码指定为用户映射的 user 和 + password 选项。 + + + + + 对于每个要访问的远程表,使用 + 或 创建外部表。 + 外部表的列必须与被引用的远程表匹配。不过,如果在外部表对象的选项中 + 指定正确的远程名称,也可以使用与远程表不同的表名和/或列名。 + + + + + + + 现在,只需对外部表执行 SELECT,即可访问其底层远程表中 + 存储的数据。也可以使用 INSERTUPDATE 或 + DELETE 修改远程表。 + (当然,在用户映射中指定的远程用户必须拥有执行这些操作的权限。) + + + + 请注意,postgres_fdw 当前不支持带有 + ON CONFLICT DO UPDATE 子句的 + INSERT 语句。不过,在省略唯一索引推断规范的前提下,支持 + ON CONFLICT DO NOTHING 子句。 + + + + 通常建议将外部表的列声明为与被引用远程表的对应列具有完全相同的数据类型, + 并在适用时具有相同的排序规则。尽管 postgres_fdw + 目前在按需执行数据类型转换方面相当宽容,但当类型或排序规则不匹配时, + 仍可能出现令人意外的语义异常,因为远程服务器对查询条件的解释可能与 + 本地服务器不同。 + + + + 请注意,外部表的声明可以比其底层远程表少一些列,或者使用不同的列顺序。 + 与远程表列的匹配是按名称而不是按位置进行的。 + + + + postgres_fdw 的 FDW 选项 + + + 连接选项 + + 使用postgres_fdw外部数据包装器的外部服务器,可以使用与libpq连接字符串所接受的相同选项,详见,但以下选项不被允许: + + userpassword(应改为在用户映射中指定) + + + + client_encoding(会根据本地服务器编码自动设置) + + + + + fallback_application_name(始终设置为 + postgres_fdw) + + + + + + 只有超级用户才能不使用密码认证连接到外部服务器,因此应始终为属于非超级用户的用户映射指定 password 选项。 + + + + 对象名称选项 + + + 这些选项可用于控制发送到远程 PostgreSQL + 服务器的 SQL 语句中所使用的名称。当创建外部表时所用的名称与其底层 + 远程表的名称不同时,就需要这些选项。 + + + + + + schema_name + + + 该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的模式名。 + 如果省略,则使用外部表自身所在模式的名称。 + + + + + + table_name + + + 该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的表名。 + 如果省略,则使用外部表自身的名称。 + + + + + + column_name + + + 该选项可为外部表的某个列指定,用于给出在远程服务器上为该列使用的列名。 + 如果省略,则使用该列自身的名称。 + + + + + + + + + + 代价估算选项 + + + postgres_fdw 通过在远程服务器上执行查询来获取远程数据, + 因此,理想情况下,扫描外部表的估计代价应当等于在远程服务器上完成该操作的 + 代价,再加上一些通信开销。获得这种估算最可靠的方法,是向远程服务器询问, + 再把开销加上去;但对于简单查询,为了取得代价估算而额外发送一次远程查询, + 可能并不划算。因此 postgres_fdw 提供以下选项来控制 + 代价估算的方式: + + + + + + use_remote_estimate + + + 该选项可为外部表或外部服务器指定,用于控制 + postgres_fdw 是否发出远程 EXPLAIN + 命令来获取代价估算。外部表上的设置会覆盖其所属服务器的设置,但只对 + 该表生效。默认值为 false。 + + + + + + fdw_startup_cost + + + 该选项可为外部服务器指定,是一个数值,会被加到该服务器上任何 + 外部表扫描的估计启动代价中。它表示建立连接、在远程端解析并规划查询等 + 额外开销。默认值为 100。 + + + + + + fdw_tuple_cost + + + 该选项可为外部服务器指定,是一个数值,用作该服务器上外部表扫描的 + 每个元组的额外代价。它表示服务器之间数据传输的额外开销。可以增大或 + 减小该数值,以反映到远程服务器更高或更低的网络延迟。默认值为 + 0.01。 + + + + + + + + 当 use_remote_estimate 为真时, + postgres_fdw 从远程服务器获取行数和代价估算,然后 + 将 fdw_startup_costfdw_tuple_cost + 加到代价估算中。当 use_remote_estimate 为假时, + postgres_fdw 在本地执行行数和代价估算,然后再将 + fdw_startup_costfdw_tuple_cost + 加到代价估算中。除非有远程表统计信息的本地副本可用,否则这种本地估算 + 不太可能非常准确。更新本地统计信息的方法,是在外部表上运行 + ;这样会扫描远程表,然后像对待本地表一样 + 计算并存储统计信息。保留本地统计信息可以有效减少远程表每次查询的规划开销; + 但如果远程表经常更新,本地统计信息很快就会过时。 + + + + + + 远程执行选项 + + + 默认情况下,只有使用内置操作符和函数的 WHERE 子句 + 才会被考虑在远程服务器上执行。涉及非内置函数的子句会在取回行之后 + 在本地检查。如果这些函数在远程服务器上也可用,并且可以确信其结果与 + 本地相同,则将这类 WHERE 子句发送到远程端执行 + 可以提高性能。可以使用以下选项控制此行为: + + + + + + extensions + + + 该选项是一个以逗号分隔的 PostgreSQL 扩展 + 名称列表,这些扩展必须在本地和远程服务器上都已安装且版本兼容。 + 属于列出扩展且不可变的函数和操作符,将被视为可下推到远程服务器 + 执行。该选项只能为外部服务器指定,不能按表指定。 + + + + 使用 extensions 选项时, + 确保所列扩展在本地和远程服务器上都存在且行为完全一致, + 属于用户自己的责任。否则,远程查询可能失败或出现意外行为。 + + + + + + fetch_size + + + 该选项指定 postgres_fdw 在每次取回操作中应获取的 + 行数。它可为外部表或外部服务器指定。表上指定的选项会覆盖服务器上 + 指定的选项。默认值为 100。 + + + + + + + + + + 可更新性选项 + + + 默认情况下,所有使用 postgres_fdw 的外部表都被 + 假定为可更新。这一点可以通过以下选项覆盖: + + + + + + updatable + + + 该选项控制 postgres_fdw 是否允许使用 + INSERTUPDATE 和 + DELETE 命令修改外部表。它可为外部表或外部服务器 + 指定。表级选项会覆盖服务器级选项。默认值为 true。 + + + + 当然,如果远程表实际上不可更新,最终仍会报错。该选项的主要作用是 + 允许在本地直接抛出错误,而无需查询远程服务器。但请注意, + information_schema 视图会根据该选项的设置,将 + postgres_fdw 外部表报告为可更新(或不可更新), + 而不会对远程服务器进行任何检查。 + + + + + + + + + 导入选项 + + + postgres_fdw 可以使用 + 导入外部表定义。该命令会在 + 本地服务器上创建外部表定义,以匹配远程服务器上的表或视图。如果要导入的 + 远程表列使用用户定义数据类型,则本地服务器必须存在同名且兼容的类型。 + + + + 可使用以下选项(在 IMPORT FOREIGN SCHEMA 命令中给出) + 自定义导入行为: + + + + + import_collate + + + 该选项控制从外部服务器导入的外部表定义中是否包含列的 + COLLATE 选项。默认值为 true。 + 如果远程服务器的排序规则名称集合与本地服务器不同,则可能需要关闭此 + 选项;如果远程服务器运行在不同操作系统上,这种情况尤其可能发生。 + 不过,如果这样做,导入表列的排序规则就存在与底层数据不匹配的严重风险,从而导致查询行为异常。 + + + + 即使将此参数设置为 true,导入排序规则为远程服务器 + 默认值的列仍可能有风险。这些列会以 + COLLATE "default" 导入,这将选择本地服务器的默认 + 排序规则,而它可能并不相同。 + + + + + import_default + + + 该选项控制从外部服务器导入的外部表定义中是否包含列的 + DEFAULT 表达式。默认值为 false。 + 如果启用此选项,需要警惕那些在本地服务器上的计算结果可能与远程服务器 + 不同的默认值;nextval() 是常见的问题来源。 + 如果导入的默认值表达式使用了本地不存在的函数或操作符,则整个 + IMPORT 将失败。 + + + + + import_not_null + + + 该选项控制从外部服务器导入的外部表定义中是否包含列的 + NOT NULL 约束。默认值为 true。 + + + + + + + 请注意,除 NOT NULL 之外的约束永远不会从远程表导入。 + 虽然 PostgreSQL 确实支持在外部表上定义 + CHECK 约束,但由于约束表达式在本地和远程服务器上可能求值不同,系统不会 + 自动导入它们。此类行为不一致的CHECK 约束,可能导致查询优化中难以发现的 + 错误。因此,如果希望导入CHECK 约束,必须手工完成,并应仔细核实每一个 + 约束的语义。有关外部表上CHECK 约束处理方式的更多细节,请参见 + 。 + + + + + + + 连接管理 + + + postgres_fdw 在首次执行使用与某个外部服务器关联的 + 外部表的查询时,会建立到该外部服务器的连接。该连接会在同一会话中保留并供后续查询重用。如果使用多个用户标识 + (用户映射)访问该外部服务器,则会为每个用户映射建立一个连接。 + + + + + 事务管理 + + + 在引用某个外部服务器上任意远程表的查询期间,如果当前本地事务尚未在该 + 远程服务器上打开对应的事务,postgres_fdw 就会在 + 该远程服务器上打开一个事务。本地事务提交或中止时,远程事务也会提交或 + 中止。保存点也会通过创建对应的远程保存点进行类似管理。 + + + + 当本地事务的隔离级别为 SERIALIZABLE 时,远程事务使用 + SERIALIZABLE;否则使用 + REPEATABLE READ 隔离级别。这一选择确保如果一个查询 + 在远程服务器上执行多次表扫描,所有扫描都能获得快照一致的结果。其结果是, + 同一事务中的后续查询会看到来自远程服务器的相同数据,即使远程服务器由于 + 其他活动正在发生并发更新。对于使用 SERIALIZABLE 或 + REPEATABLE READ 隔离级别的本地事务,这种行为本来就 + 符合预期;但对于 READ COMMITTED 本地事务,则可能令人 + 意外。未来的 PostgreSQL 版本可能会修改这些 + 规则。 + + + + 请注意,postgres_fdw 当前不支持为两阶段提交预备远程事务。 + + + + + 远程查询优化 + + + postgres_fdw 会尽力优化远程查询,以减少从外部 + 服务器传输的数据量。这是通过将查询的 WHERE 子句发送到 + 远程服务器执行,以及不获取当前查询不需要的表列来实现的。为降低查询被 + 错误执行的风险,只有当 WHERE 子句使用的所有数据类型、 + 操作符和函数都是内置的,或属于外部服务器 extensions + 选项列出的扩展时,才会将该子句发送到远程服务器。这类子句中的操作符和函数还必须是 + IMMUTABLE。对于 UPDATE 或 + DELETE 查询,postgres_fdw 会在 + 查询中不存在无法发送到远程服务器的 WHERE 子句、没有 + 本地连接操作、目标表上没有行级本地 BEFORE 或 + AFTER 触发器,也没有来自父视图的 + CHECK OPTION 约束时,尝试将整个查询发送到远程服务器 + 以优化执行。在 UPDATE 中,为了降低查询被错误执行的 + 风险,赋给目标列的表达式也必须只使用内置数据类型、 + IMMUTABLE 操作符或 IMMUTABLE 函数。 + + + + 当 postgres_fdw 遇到同一外部服务器上的外部表之间的 + 连接时,除非由于某种原因它认为分别从各表取回行会更高效,或者相关表引用 + 受不同用户映射约束,否则会将整个连接发送到远程服务器。在发送 + JOIN 子句时,它也会采取与前述 + WHERE 子句相同的预防措施。 + + + + 可以使用 EXPLAIN VERBOSE 查看实际发送给远程服务器 + 执行的查询。 + + + + + 远程查询执行环境 + + + 在 postgres_fdw 打开的远程会话中, + 参数会被设置为仅包含 + pg_catalog,这样无需模式限定就只能看到内置对象。 + 这对 postgres_fdw 自身生成的查询不是问题,因为它 + 总是提供这种限定。然而,这可能会对那些通过远程表上的触发器或规则在 + 远程服务器上执行的函数带来风险。例如,如果远程表实际上是一个视图, + 则该视图中使用的任何函数都会在受限的搜索路径下执行。建议在这类函数中 + 对所有名称都写成带模式限定的形式,或者为这类函数附加 + SET search_path 选项(见 + ),以建立其预期的搜索路径环境。 + + + + postgres_fdw 同样会为参数 + 、 + + 建立远程会话设置。这些设置通常不像 search_path 那样 + 容易出问题,但如果有需要,也可以通过函数的 SET 选项处理。 + + + + 建议通过修改这些参数的会话级设置来覆盖这种行为; + 这很可能导致 postgres_fdw 工作异常。 + + + + + 跨版本兼容性 + + + postgres_fdw 可用于最早追溯到 + PostgreSQL 8.3 的远程服务器。只读能力可追溯到 + 8.1。 + + + 不过有一个限制是,postgres_fdw 通常假定: + 如果外部表的 WHERE 子句中出现不可变的内置函数和 + 操作符,那么把它们发送到远程服务器执行是安全的。因此,某个在远程服务器 + 所属发行版本之后才加入的内置函数,可能仍会被发送到该远程服务器执行, + 从而导致function does not exist或类似错误。可以通过 + 重写查询绕过这类失败,例如把外部表引用放入一个带 + OFFSET 0 的子 SELECT 中,作为优化 + 栅栏,并将有问题的函数或操作符放到子 SELECT 之外。 + + + + + 示例 + + + 下面是使用 postgres_fdw 创建外部表的一个示例。 + 首先安装扩展: + + + +CREATE EXTENSION postgres_fdw; + + + + 然后使用 创建外部服务器。 + 在本示例中,希望连接到一台 PostgreSQL 服务器, + 它运行在主机 192.83.123.89 上并监听 + 5432 端口。要连接的数据库在远程服务器上名为 + foreign_db: + + +CREATE SERVER foreign_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host '192.83.123.89', port '5432', dbname 'foreign_db'); + + + + + 还需要用 定义一个用户映射, + 以标识在远程服务器上使用哪个角色: + + +CREATE USER MAPPING FOR local_user + SERVER foreign_server + OPTIONS (user 'foreign_user', password 'password'); + + + + 现在可以通过创建外部表。在本例中,要访问远程服务器上的表some_schema.some_table,其本地名称为foreign_table: + + +CREATE FOREIGN TABLE foreign_table ( + id integer NOT NULL, + data text +) + SERVER foreign_server + OPTIONS (schema_name 'some_schema', table_name 'some_table'); +必须确保在CREATE FOREIGN TABLE中声明的列的数据类型和其他属性,与实际远程表匹配。列名也必须匹配,除非为各列附加column_name选项,指明它们在远程表中的名称。在许多情况下,使用优于手工构造外部表定义。 + + + + 作者 + + Shigeru Hanada shigeru.hanada@gmail.com + + + + diff --git a/zh/9.6/postgres.sgml b/zh/9.6/postgres.sgml new file mode 100644 index 00000000..48c68985 --- /dev/null +++ b/zh/9.6/postgres.sgml @@ -0,0 +1,223 @@ + + + +%version; + +%filelist; + + + + + +]> + + + PostgreSQL &version; 手册 + + + PostgreSQL 全球开发组 + 翻译:冯若航Pigsty 项目组 + PostgreSQL + &version; + &legal; + &pgdoccn-notes; + + + &intro; + + + + 教程 + + + + + 欢迎阅读PostgreSQL教程。本教程旨在介绍PostgreSQL、关系数据库概念以及 SQL 语言。我们假定读者具备基本的计算机使用常识,不要求具备特定的 Unix 或编程经验。本教程旨在让读者通过动手实践了解PostgreSQL系统的一些重要方面。它并不试图对所涵盖的主题作全面论述。 + + + + 在成功完成本教程后,你可能会希望阅读部分,以更深入地理解 SQL 语言;或者阅读,了解如何使用PostgreSQL开发应用程序。自行部署并管理 PostgreSQL 安装的读者也应阅读。 + + + + &start; + &query; + &advanced; + + + + + + SQL 语言 + + + + + 本部分描述SQL语言在PostgreSQL中的用法。我们首先介绍SQL的一般语法,然后说明如何创建表、如何填充数据库以及如何查询数据库。中间部分列出可在SQL命令中使用的数据类型和函数。最后,我们讨论数据库调优中的若干重要方面。 + + + + 本部分的内容安排方式使初学者能够从头到尾依次阅读,并在不必过多向后查阅的情况下充分理解这些主题。各章力求自成体系,因此高级用户也可以按需单独阅读。相关内容按主题单元以叙述方式展开。希望获得某个命令完整描述的读者,建议查阅。 + + + + 读者应当知道如何连接到PostgreSQL数据库并发出SQL命令。不熟悉这些内容的读者,建议先阅读SQL命令通常通过PostgreSQL的交互式终端程序psql输入,但也可以使用具有类似功能的其他程序。 + + + + &syntax; + &ddl; + &dml; + &queries; + &datatype; + &func; + &typeconv; + &indices; + &textsearch; + &mvcc; + &perform; + ∥ + + + + + 服务器管理 + + + + + 本部分涵盖PostgreSQL管理员关心的主题,包括软件安装、服务器配置、用户和数据库管理以及维护任务。任何运行PostgreSQL服务器的人,即使仅供个人使用,尤其是在生产环境中,都应熟悉这些主题。 + + + + 本部分的内容大体按照新用户应当阅读的顺序来安排。各章自成体系,也可按需单独阅读。相关内容按主题单元以叙述方式展开。希望获得某个命令完整描述的读者,建议查阅。 + + + + 开头几章写得无需预备知识即可理解,因此需要自行搭建服务器的新用户可以从这里开始探索。本部分其余内容涉及调优和管理;这些材料假定读者已经熟悉PostgreSQL数据库系统的一般用法。建议读者再参阅部分,以获取更多信息。 + + + + &installation; + &installw; + &runtime; + &config; + &client-auth; + &user-manag; + &manage-ag; + &charset; + &maintenance; + &backup; + &high-availability; + &recovery-config; + &monitoring; + &diskusage; + &wal; + ®ress; + + + + + + 客户端接口 + + + + + 本部分描述随PostgreSQL一起发布的客户端编程接口。这些章节彼此独立,可以分别阅读。还有许多面向客户端程序的外部编程接口是单独发布的,它们各自也附带文档(列出了一些较常用的接口)。本部分的读者应熟悉如何使用SQL操作和查询数据库(见),当然还应熟悉自己所选的编程语言。 + + + + &libpq; + &lobj; + &ecpg; + &infoschema; + + + + + + 服务器编程 + + + + + 本部分介绍如何使用用户定义函数、数据类型、触发器等扩展服务器功能。这些属于高级主题,通常应在读者已经理解其他有关PostgreSQL的用户文档之后再来学习。本部分后面的章节介绍PostgreSQL发行版中提供的服务器端编程语言,以及与服务器端编程有关的一般问题。在深入学习服务器端编程的内容之前,至少应先阅读的前几节(涵盖函数)。 + + + + &extend; + &trigger; + &event-trigger; + &rules; + + &xplang; + &plsql; + &pltcl; + &plperl; + &plpython; + + &spi; + &bgworker; + &logicaldecoding; + &replication-origins; + + + + &reference; + + + 内部 + + + + + 本部分包含一些可能对PostgreSQL开发人员有用的各类信息。 + + + + &arch-dev; + &catalogs; + &protocol; + &sources; + &nls; + &plhandler; + &fdwhandler; + &tablesample-method; + &custom-scan; + &geqo; + &indexam; + &generic-wal; + &gist; + &spgist; + &gin; + &brin; + &storage; + &bki; + &planstats; + + + + + 附录 + + &errcodes; + &datetime; + &keywords; + &features; + &release; + &contrib; + &external-projects; + &sourcerepo; + &docguide; + &acronyms; + + + + &biblio; + + ]]> + + diff --git a/zh/9.6/problems.sgml b/zh/9.6/problems.sgml new file mode 100644 index 00000000..161df99a --- /dev/null +++ b/zh/9.6/problems.sgml @@ -0,0 +1,223 @@ + + + + 缺陷报告指南 + + + 当你在PostgreSQL中发现缺陷时,我们希望得知此事。你的缺陷报告对于提高 + PostgreSQL的可靠性十分重要,因为即使再怎么小心,也无法保证 + PostgreSQL的每一部分都能在每个平台、任何情况下正常工作。 + + + + 以下建议旨在帮助你组织缺陷报告,以便能够高效处理。没有人必须遵循这些建议,但这样做通常对各方都有好处。 + + + + 我们无法承诺立刻修复每一个缺陷。如果该缺陷明显、严重,或者影响大量用户,很可能会有人去调查它。也可能我们会让你升级到更新的版本,看看在新版本中是否也会出现同样的缺陷。或者,我们可能会认定,在某些计划中的重大重写完成之前,这个缺陷无法修复。再或者,它只是过于棘手,而当前还有更重要的事情要处理。如果你需要立即获得帮助,请考虑购买商业支持合同。 + + + + 识别缺陷 + + + 在报告缺陷之前,请反复阅读文档,以确认你确实可以完成正在尝试做的事情。如果从文档中无法明确看出某件事是否可行,也请报告;那就是文档中的缺陷。如果程序实际做的事情与文档描述不同,那就是缺陷。这可能包括但不限于以下情况: + + + + + 程序因致命信号而终止,或者给出一条表明程序本身存在问题的操作系统错误消息。(反例可能是 + 磁盘满之类的消息,因为那得由你自己解决。) + + + + + + 程序对给定输入产生了错误的输出。 + + + + + + 程序拒绝接受有效输入(按文档定义)。 + + + + + + 程序接受了无效输入,却没有给出提示或错误消息。但请记住,你认为的无效输入,可能正是我们认为的扩展功能,或者是对传统做法的兼容。 + + + + + + 在受支持的平台上,PostgreSQL无法按照说明完成编译、构建或安装。 + + + + + 这里的程序指的是任何可执行程序,不只是后端进程。 + + + + 运行缓慢或消耗大量资源,并不一定就是缺陷。请阅读文档,或在某个邮件列表中寻求调优应用程序的帮助。不符合 + SQL标准,也不一定是缺陷,除非明确声称该特定特性符合标准。 + + + + 在继续之前,请查看 TODO 列表和 FAQ,确认你的缺陷是否已经是已知问题。如果你看不懂 TODO 列表中的信息,也请报告你的问题。至少我们可以把 TODO 列表写得更清楚。 + + + + + 报告哪些内容 + + + 关于缺陷报告,最重要的一点是陈述全部事实,而且只陈述事实。不要猜测你觉得哪里出了问题、它看起来像是在做什么,或者程序的哪一部分有错。如果你不熟悉实现,十有八九会猜错,对我们毫无帮助。即使你熟悉实现,有根据的解释也只能是事实的有益补充,而不能替代事实。如果我们要修复这个缺陷,仍然必须先亲自重现它。陈述这些基本事实并不难做(你大概可以直接从屏幕上复制粘贴),但太多时候,重要细节会被遗漏,因为有人觉得它们不重要,或者认为即使不说清楚,报告也一样能被理解。 + + + 以下各项应包含在每一个缺陷报告中: + + + 重现问题所需的精确步骤序列,而且必须是从程序启动开始。这些步骤应当是自包含的;如果输出依赖表中的数据,那么仅仅发来一条 + SELECT语句,而不附带前面的CREATE TABLE和 + INSERT语句,是不够的。我们没有时间去逆向推导你的数据库模式;如果要靠我们自己编造数据,多半会错过问题。 + + + + 对于 SQL 相关问题,测试用例的最佳形式是一个可由psql前端运行、并能展示问题的文件。(请务必确保你的~/.psqlrc启动文件中没有任何内容。)创建这个文件的一个简便方法,是用pg_dump导出搭建场景所需的表声明和数据,再附上触发问题的查询。我们鼓励你尽量缩小示例规模,但这并非绝对必要。如果缺陷可重现,我们无论如何都能找出来。 + + + + 如果你的应用使用的是别的客户端接口,例如PHP,那么请尽量隔离出触发问题的查询。我们大概不会为了重现你的问题去搭建一个 Web 服务器。无论如何,都请记得提供精确的输入文件;不要笼统地猜测问题发生在 + 大文件中等大小的数据库等情况下,因为这些信息不够精确,派不上用场。 + + + + + + 你实际得到的输出。请不要只说它没起作用崩溃了。如果有错误消息,请把它贴出来,即使你看不懂。如果程序因操作系统错误而终止,请说明是哪一种错误。如果什么都没发生,也请说清楚。即使你的测试用例导致程序崩溃,或出现其他显而易见的问题,这种情况在我们的平台上也未必会发生。如果可以,最简单的做法就是直接从终端复制输出。 + + + + 如果你要报告错误消息,请尽量获取其最详细的形式。在psql中,请预先执行\set + VERBOSITY verbose。如果你是从服务器日志中提取消息,请将运行时参数设为verbose,以便记录全部细节。 + + + + + 在致命错误的情况下,客户端报告的错误消息可能不包含全部可用信息。也请查看数据库服务器的日志输出。如果你平时没有保留服务器日志,现在正是开始这样做的好时机。 + + + + + + + 你期望得到的输出,也非常重要。如果你只是写 + 这个命令给了我那个输出。这不是我期望的。,我们可能会自己运行一遍,扫一眼输出,然后觉得它看起来没问题,也正是我们所期望的。我们不应该花时间去揣摩你的命令背后的确切语义。尤其不要只是说 + 这不是 SQL 规定的/Oracle 所做的。SQL标准中找出正确行为并不是一件轻松的事,而且我们也不都了解其他所有关系数据库的行为。(如果你的问题是程序崩溃,显然可以省略这一项。) + + + + + + 任何命令行选项和其他启动选项,包括所有被你改动过、且与问题相关的环境变量或配置文件。同样,请提供精确的信息。如果你使用的是一种预打包发行版,并且它会在系统启动时自动启动数据库服务器,那么你应当尽量弄清楚它是怎么做到的。 + + + + + + 任何偏离安装说明的做法。 + + + + + + PostgreSQL的版本。你可以运行命令 + SELECT version();来查明所连接服务器的版本。大多数可执行程序也支持 + 选项;至少postgres --version和 + psql --version应当可以工作。如果这个函数或这些选项都不存在,那就说明你的版本已经老到足以该升级了。如果你运行的是预打包版本,例如 RPM,请说明这一点,并附上该软件包可能带有的子版本号。如果你说的是一个 Git 快照,也请说明,并给出提交哈希值。 + + + + 如果你的版本早于 &version;,我们几乎肯定会建议你升级。每个新版本都会包含大量缺陷修复和改进,因此你在旧版 + PostgreSQL中遇到的缺陷,很可能已经被修复。对于使用旧版 + PostgreSQL的站点,我们只能提供有限支持;如果你需要超出这一范围的帮助,请考虑购买商业支持合同。 + + + + + + + + 平台信息。这包括内核名称和版本、C 库、处理器、内存信息等。在大多数情况下,报告供应商和版本就足够了,但不要想当然地认为所有人都知道 + Debian具体包含什么,或者所有人都运行在 x86_64 上。如果你遇到的是安装问题,那么你机器上的工具链信息(编译器、make等)也是必需的。 + + + 不要担心你的缺陷报告会变得很长;这很正常。第一次就把所有内容都报告出来,总比让我们反复追问细节要好。另一方面,如果你的输入文件非常大,先问问是否有人愿意看一看,也是合理的。这里有一篇文章,概述了更多关于报告缺陷的建议。 + + + 不要把全部时间都花在找出输入中的哪些变化会让问题消失上。这多半无助于解决问题。如果最后发现这个缺陷无法立刻修复,你仍然有时间去寻找并分享变通办法。还有,再强调一次,不要浪费时间去猜测这个缺陷为什么存在。我们迟早会查明原因。 + + + + 在编写缺陷报告时,请避免使用含混的术语。整个软件包叫作PostgreSQL,有时简称 + Postgres。如果你特指后端进程,请明确说出来,不要只是说 + PostgreSQL 崩溃了。单个后端进程崩溃,与父postgres进程崩溃,是完全不同的事情;当你指的是某个单独的后端进程挂掉时,请不要说 + 服务器崩溃了,反过来也一样。此外,客户端程序,例如交互式前端 + psql,与后端完全是分开的。请尽量明确问题是在客户端还是服务器端。 + + + + + 到哪里报告缺陷 + + + 一般来说,请将缺陷报告发送到缺陷报告邮件列表 + pgsql-bugs@lists.postgresql.org。请为电子邮件使用描述性的主题,必要时可以摘取部分错误消息。 + + + + 另一种方法是填写项目 + 网站上提供的缺陷报告网页表单。通过这种方式提交的缺陷报告,也会被发送到 + pgsql-bugs@lists.postgresql.org邮件列表。 + + + + 如果你的缺陷报告涉及安全影响,而且你不希望它立刻出现在公开归档中,请不要发送到 + pgsql-bugs。安全问题可以私下报告给 + security@postgresql.org。 + + + + 不要将缺陷报告发送到任何用户邮件列表,例如 + pgsql-sql@lists.postgresql.org或 + pgsql-general@lists.postgresql.org。这些邮件列表用于回答用户问题,其订阅者通常并不希望收到缺陷报告。更重要的是,他们也不太可能去修复这些缺陷。 + + + + 此外,请不要把报告发送到开发者邮件列表 + pgsql-hackers@lists.postgresql.org。这个列表是用来讨论 + PostgreSQL开发的,最好把缺陷报告和这类讨论分开。如果这个问题需要更深入的审查,我们可能会选择在 + pgsql-hackers上继续讨论你的缺陷报告。 + + + + 如果你遇到的是文档问题,报告它的最佳地点是文档邮件列表 + pgsql-docs@lists.postgresql.org。请明确指出你对文档的哪一部分不满意。 + + + + 如果你的缺陷是在某个不受支持平台上出现的可移植性问题,请发送邮件到 + pgsql-hackers@lists.postgresql.org,这样我们(以及你)就可以共同推进 + PostgreSQL在该平台上的移植工作。 + + + + + 由于垃圾邮件泛滥,上述所有列表对于未订阅者发来的邮件都会先进行审核。这意味着邮件送达前会有一定延迟。如果你希望订阅这些列表,请访问 + 获取说明。 + + + + diff --git a/zh/9.6/protocol.sgml b/zh/9.6/protocol.sgml new file mode 100644 index 00000000..9d6aa290 --- /dev/null +++ b/zh/9.6/protocol.sgml @@ -0,0 +1,3588 @@ + + + + 前端/后端协议 + + + 协议 + 前端-后端 + + + + PostgreSQL使用一种基于消息的协议在前端和后端(客户端与服务器)之间进行通信。该协议既支持TCP/IP,也支持 Unix 域套接字。端口号 5432 已在 IANA 注册为支持该协议的服务器的惯用 TCP 端口号,但实际上任何非特权端口号都可以使用。 + + + + 本文描述协议 3.0 版本,自 PostgreSQL 7.4 起实现。关于更早协议版本的说明,请参阅之前发布的 PostgreSQL 文档。一台服务器可以支持多个协议版本。初始启动请求消息会告知服务器客户端正尝试使用哪个协议版本。如果客户端请求的主版本号不被服务器支持,则连接会被拒绝(例如,如果客户端请求协议版本 4.0,而在本文编写时该版本并不存在,就会出现这种情况)。如果客户端请求的次版本号不被服务器支持(例如客户端请求 3.1,而服务器只支持 3.0),服务器可以拒绝连接,也可以返回一条 NegotiateProtocolVersion 消息,其中包含它所支持的最高次协议版本。客户端随后可以选择使用指定的协议版本继续连接,或者中止连接。 + + + + 为了高效地为多个客户端提供服务,服务器会为每个客户端启动一个新的后端进程。在当前实现中,一旦检测到传入连接,就会立刻创建新的子进程。不过,这一点对协议而言是透明的。就协议而言,术语后端服务器可以互换;同样,前端客户端也可以互换。 + + + + + 概述 + + + 协议分为启动和正常操作两个阶段。在启动阶段,前端打开到服务器的连接,并完成服务器所要求的认证。(这可能只涉及一条消息,也可能因所用认证方法不同而需要多条消息。)如果一切顺利,服务器随后会向前端发送状态信息,并最终进入正常操作。除最初的启动请求消息外,协议的这一部分由服务器驱动。 + + + + 在正常操作中,前端向后端发送查询及其他命令,后端则返回查询结果和其他响应。少数情况下(例如 NOTIFY),后端会发送未请求的消息,但会话中的绝大多数交互仍由前端请求驱动。 + + + + 会话通常由前端选择终止,但在某些情况下也可能由后端强制终止。无论哪种情况,后端关闭连接时,都会在退出前回滚所有打开的(未完成的)事务。 + + + + 在正常操作中,SQL 命令可以通过两种子协议之一执行。在简单查询协议中,前端只需发送文本形式的查询字符串,后端会立即对其进行解析并执行。在扩展查询协议中,查询处理被拆分为多个步骤:解析、参数值绑定以及执行。这带来了更高的灵活性和性能收益,但代价是额外的复杂性。 + + + + 正常操作还包含用于COPY等特殊操作的额外子协议。 + + + + + 消息概述 + + + 所有通信都通过消息流进行。消息的第一个字节标识消息类型,接下来的四个字节给出消息其余部分的长度(该长度计数包含自身,但不包括消息类型字节)。消息剩余内容由消息类型决定。由于历史原因,客户端发送的第一条消息(启动消息)没有开头的消息类型字节。 + + + + 为了避免与消息流失去同步,服务器和客户端通常都会先根据字节计数把整条消息读入缓冲区,然后再处理其内容。这样一来,如果在处理内容时检测到错误,就比较容易恢复。在极端情况下(例如没有足够内存缓冲整条消息),接收方也可以利用字节计数判断在恢复读取消息之前需要跳过多少输入。 + + + + 反过来,服务器和客户端也必须注意绝不能发送不完整的消息。通常的做法是在开始发送之前,先在缓冲区中整理好整条消息。如果在发送或接收消息的途中发生通信故障,唯一合理的做法就是放弃连接,因为几乎不可能重新恢复消息边界的同步。 + + + + + + 扩展查询概述 + + + 在扩展查询协议中,SQL 命令的执行被拆分为多个步骤。各步骤之间保留的状态由两类对象表示:预备语句portal。预备语句表示对文本查询字符串完成解析和语义分析后的结果。预备语句本身还不能直接执行,因为它可能缺少特定的参数值。portal 表示一条已经可以执行、或已经部分执行过的语句,其中所有缺失的参数值都已补齐。(对于SELECT语句,portal 等价于一个打开的游标;但由于游标不能处理非SELECT语句,这里采用不同术语。) + + + + 整个执行周期包括一个解析步骤,它从文本查询字符串创建预备语句; + 一个绑定步骤,它根据预备语句和所需参数值创建 portal; + 以及一个执行步骤,用于执行 portal 中的查询。对于返回行的查询(SELECTSHOW等),可以要求执行步骤只取回有限数量的行,因此可能需要多次执行步骤才能完成整个操作。 + + + + 后端可以跟踪多个预备语句和 portal(但请注意,它们只存在于单个会话内,绝不会在会话之间共享)。已有的预备语句和 portal 都通过创建时赋予的名称来引用。此外,还存在一个未命名的预备语句和 portal。虽然它们的行为与有名对象大体相同,但对未命名对象的操作是为“只执行一次然后丢弃”的场景优化的,而对有名对象的操作则是基于会被多次使用的预期进行优化的。 + + + + + + 格式和格式代码 + + + 某一特定数据类型的数据可以使用多种不同的格式之一进行传输。自 PostgreSQL 7.4 起,当前只支持文本二进制两种格式,但协议为未来扩展留出了空间。任意值所需的格式由格式代码指定。客户端可以为每个传输的参数值以及查询结果的每一列指定格式代码。文本格式的代码为零,二进制格式的代码为一,其他格式代码则保留供将来定义。 + + + + 值的文本表示是相应数据类型的输入/输出转换函数生成和接受的字符串。在传输形式中,值的末尾没有空字符;前端若要将收到的值作为 C 字符串处理,必须自行添加一个。(文本格式也不允许内嵌空字符。) + + + + 整数的二进制表示采用网络字节序(最高有效字节在前)。至于其他数据类型,请查阅文档或源代码了解其二进制表示形式。要注意,复杂数据类型的二进制表示可能会在不同服务器版本之间发生变化;文本格式通常是可移植性更好的选择。 + + + + + + + 消息流 + + + 本节描述消息流以及各种消息类型的语义(每种消息的精确格式见)。根据连接所处的状态不同,存在若干不同的子协议:启动、查询、函数调用、COPY以及终止。对于异步操作(包括通知响应和命令取消)还有专门规定,它们可能在启动阶段结束后的任何时刻发生。 + + + + 启动 + + + 要开始一个会话,前端会打开到服务器的连接并发送一条启动消息。该消息包含用户名以及用户希望连接的数据库名;它还指明要使用的协议版本。(启动消息也可以选择性地包含运行时参数的附加设置。)随后服务器会结合这些信息以及配置文件(例如 pg_hba.conf)的内容,初步判断是否接受该连接,以及需要何种额外认证(如果需要)。 + + + + 接着服务器会发送适当的认证请求消息,前端必须以适当的认证响应消息进行回应(例如密码)。对于除 GSSAPI 和 SSPI 之外的认证方法,最多只会有一次请求和一次响应。在某些方法中,前端根本不需要发送响应,因此也不会出现认证请求。对于 GSSAPI 和 SSPI,则可能需要多轮报文交换才能完成认证。 + + + + 认证周期要么以服务器拒绝连接(ErrorResponse)结束,要么以 AuthenticationOk 结束。 + + + 服务器在此阶段可能发送的消息如下: + + ErrorResponse + + + 连接请求被拒绝。然后服务器马上关闭连接。 + + + + + + AuthenticationOk + + + 认证交换成功完成。 + + + + + + AuthenticationKerberosV5 + + + 前端现在必须参与与服务器之间的 Kerberos V5 认证对话(此处不再描述,它属于 Kerberos 规范的一部分)。如果对话成功,服务器会响应 AuthenticationOk;否则响应 ErrorResponse。该机制已不再受支持。 + + + + + + AuthenticationCleartextPassword + + + 前端现在必须发送一个以明文形式包含密码的 PasswordMessage。如果密码正确,服务器响应 AuthenticationOk;否则响应 ErrorResponse。 + + + + + + AuthenticationMD5Password + + + 前端现在必须发送一个 PasswordMessage,其中包含密码;该密码先与用户名一起经过 MD5 加密,再使用 AuthenticationMD5Password 消息中指定的 4 字节随机盐重新加密。如果密码正确,服务器响应 AuthenticationOk;否则响应 ErrorResponse。实际的 PasswordMessage 可以用 SQL 计算:concat('md5', md5(concat(md5(concat(password, username)), random-salt)))。(请记住 md5() 函数返回的是十六进制字符串。) + + + + + + AuthenticationSCMCredential + + + 此响应仅适用于支持 SCM 凭据消息的平台上的本地 Unix 域连接。前端必须发出一条 SCM 凭据消息,然后发送一个单字节数据。(该数据字节的内容无关紧要;它仅用于确保服务器等待足够长的时间来接收凭据消息。)如果凭据可接受,服务器响应 AuthenticationOk;否则响应 ErrorResponse。(该消息类型仅由 9.1 之前的服务器发出。它最终可能会从协议规范中移除。) + + + + + + AuthenticationGSS + + + 前端现在必须发起一次 GSSAPI 协商。前端将发送一条带有 GSSAPI 数据流第一部分的 PasswordMessage 消息作为响应。如果还需要进一步的消息,服务器会响应 AuthenticationGSSContinue。 + + + + + + AuthenticationSSPI + + + 前端现在必须发起一次 SSPI 协商。前端将发送一条带有 SSPI 数据流第一部分的 PasswordMessage 作为响应。如果还需要进一步的消息,服务器会响应 AuthenticationGSSContinue。 + + + + + + AuthenticationGSSContinue + + + 该消息包含前一步 GSSAPI 或 SSPI 协商(AuthenticationGSS、AuthenticationSSPI 或前一个 AuthenticationGSSContinue)的响应数据。如果该消息中的 GSSAPI 或 SSPI 数据表明完成认证还需要更多数据,前端必须把所需数据作为另一条 PasswordMessage 消息发送。如果该消息已完成 GSSAPI 或 SSPI 认证,服务器接下来会发送 AuthenticationOk 表示认证成功,或发送 ErrorResponse 表示认证失败。 + + + + + + NegotiateProtocolVersion + + + 服务器不支持客户端请求的协议次版本,但支持更早的协议版本;此消息指明其所支持的最高次版本。如果客户端在启动包中请求了不受支持的协议选项(即以 _pq_. 开头的选项),也会发送此消息。此消息后面会跟随一条 ErrorResponse 或一条指示认证成功或失败的消息。 + + + + + + + + + 如果前端不支持服务器要求的认证方式,那么它应该马上关闭连接。 + + + + 在收到 AuthenticationOk 消息之后,前端必须继续等待来自服务器的后续消息。在这个阶段,一个后端进程正在启动,而前端只是旁观者。启动尝试仍可能失败(ErrorResponse),服务器也可能拒绝支持所请求的协议次版本(NegotiateProtocolVersion);但通常情况下,后端会发送一些 ParameterStatus 消息、BackendKeyData,以及最后的 ReadyForQuery。 + + + + 在这个阶段,后端会尝试应用启动消息中给出的任何额外运行时参数设置。如果成功,这些值就会成为会话的默认值。发生错误则会导致 ErrorResponse 并退出。 + + + + 这个阶段来自后端的可能消息是: + + + + BackendKeyData + + + 该消息提供密钥数据。如果前端希望稍后发送取消请求,就必须保存这些数据。前端不应响应该消息,而应继续等待 ReadyForQuery 消息。 + + + + + + ParameterStatus + + + 该消息告知前端后端参数的当前(初始)设置,例如。前端可以忽略这些信息,也可以记录下来供后续使用;详见。前端不应响应该消息,而应继续等待 ReadyForQuery 消息。 + + + + + + ReadyForQuery + + + 启动成功,前端现在可以发出命令。 + + + + + + ErrorResponse + + + 启动失败,在发送完这个消息之后连接被关闭。 + + + + + + NoticeResponse + + + 已发出一条警告消息。前端应显示该消息,但仍应继续等待 ReadyForQuery 或 ErrorResponse。 + + + + + + + + ReadyForQuery 消息与后端在每个命令周期结束后发出的消息是同一个。前端可根据自身编码需要,将 ReadyForQuery 视为一个命令周期的开始,或视为启动阶段以及后续每个命令周期的结束。 + + + + + 简单查询 + + + 一个简单查询周期由前端向后端发送一条 Query 消息来启动。该消息包含一条或多条以文本字符串表示的 SQL 命令。后端随后会根据查询命令串的内容向前端发送一条或多条响应消息,最后再发送一条 ReadyForQuery 响应消息。ReadyForQuery 通知前端它现在可以安全地发送新的命令。(实际上,前端并不一定非要等到收到 ReadyForQuery 才发送下一条命令,但这样一来,前端就必须自己处理“先前命令失败而后续已发出的命令成功”这种情况。) + + + + 后端可能返回的响应消息有: + + + + CommandComplete + + + 一条 SQL 命令正常完成。 + + + + + + CopyInResponse + + + 后端已准备好把数据从前端复制到表中;参见。 + + + + + + CopyOutResponse + + + 后端已准备好把数据从表中复制到前端;参见。 + + + + + + RowDescription + + + 表示即将返回行作为对SELECTFETCH等查询的响应。 + 此消息的内容描述了行的列布局。该消息之后,每个返回给前端的行都对应一条 DataRow 消息。 + + + + + + DataRow + + + 由SELECTFETCH等查询返回的一组行中的一个。 + + + + + + EmptyQueryResponse + + + 识别出了一条空查询字符串。 + + + + + + ErrorResponse + + + 发生了一个错误。 + + + + + + ReadyForQuery + + + 查询字符串的处理已完成。发送一个单独的消息来指示这一点,因为查询字符串可能包含多个SQL命令。 + (CommandComplete标记了一个SQL命令的处理结束,而不是整个字符串的结束。) + 无论处理是成功还是出现错误,都将始终发送ReadyForQuery。 + + + + + + NoticeResponse + + + 与查询相关的警告消息已发出。 + 通知是其他响应的补充,即后端将继续处理命令。 + + + + + + + + SELECT 查询(或其他返回行集的查询,如 EXPLAINSHOW)的响应通常包含 RowDescription、零条或多条 DataRow 消息,以及最后的 CommandComplete。在前端与服务器之间执行 COPY 输入或输出时,会使用 所述的特殊协议。所有其他类型的查询通常只产生一条 CommandComplete 消息。 + + + 由于查询字符串可能包含若干条查询(以分号分隔),因此在后端完成整个查询字符串的处理之前,可能会出现多个这样的响应序列。只有在整个字符串处理完毕且后端已准备好接受新的查询字符串时,才会发出 ReadyForQuery 消息。 + + + + 如果收到的是一条完全为空的查询字符串(除了空白字符之外没有任何内容),响应就是一条 EmptyQueryResponse,后面跟着 ReadyForQuery。 + + + + 一旦发生错误,就会发出一条 ErrorResponse 消息,后面跟着 ReadyForQuery。ErrorResponse 会中止该查询字符串中后续所有处理(即使其中还包含其他查询)。请注意,这种情况可能发生在处理单条查询所生成的消息序列中途。 + + + + 在简单查询模式中,取回值的格式总是文本,除非给出的命令是在使用 BINARY 选项声明的游标上执行FETCH。在这种情况下,取回的值将采用二进制格式。RowDescription 消息中给出的格式代码会告诉我们使用的是哪种格式。 + + + + 当前端正在等待其他类型的消息时,也必须准备好接收 ErrorResponse 和 NoticeResponse 消息。参见,了解后端因外部事件而可能生成的消息。 + + + + 建议以状态机的方式编写前端,使其能够在任何合理的时机接收相应类型的消息,而不把消息确切顺序的假设写死在代码中。 + + + + + + 扩展查询 + + + 扩展查询协议把上文描述的简单查询协议拆分成多个步骤。准备步骤的结果可以重复使用,从而提高效率。此外,它还提供了额外特性,例如可以把数据值作为独立参数提供,而不必直接插入查询字符串中。 + + + + 在扩展协议中,前端首先发送一条 Parse 消息,其中包含文本查询字符串、可选的参数占位符数据类型信息,以及目标预备语句对象的名称(空字符串表示未命名预备语句)。响应要么是 ParseComplete,要么是 ErrorResponse。参数数据类型可以用 OID 指定;如果未给出,解析器会像处理无类型字面字符串常量那样尝试推断其数据类型。 + + + + + + 一个参数的数据类型可以通过设为零,或让参数类型 OID 数组短于查询字符串中参数符号($n)的数量来保持未指定。另一个特例是,参数类型可以指定为 void(即伪类型 void 的 OID)。这样做是为了允许参数符号用于那些实际上是 OUT 参数的函数参数。通常并不存在可使用 void 参数的上下文,但如果这样的参数符号出现在函数参数列表中,它实际上会被忽略。例如,像 foo($1,$2,$3,$4) 这样的函数调用,如果 $3$4 被指定为 void 类型,就可能匹配一个带有两个 IN 参数和两个 OUT 参数的函数。 + + + + + + + Parse 消息中的查询字符串不能包含多于一条 SQL 语句;否则会报告语法错误。这个限制在简单查询协议中并不存在,但在扩展协议中必须如此,因为若允许预备语句或 portal 包含多条命令,会使协议变得过于复杂。 + + + + + 如果成功创建了一个有名的预备语句对象,它会一直持续到当前会话结束,除非被显式销毁。未命名预备语句只会持续到下一条把未命名语句作为目标的 Parse 消息发出为止。(注意,简单 Query 消息也会销毁未命名语句。)有名预备语句在被另一条 Parse 消息重新定义之前必须显式关闭,但未命名语句则无此要求。有名预备语句也可以在 SQL 命令级别通过PREPAREEXECUTE来创建和访问。 + + + + 一旦预备语句存在,就可以用 Bind 消息把它准备为可执行状态。Bind 消息给出源预备语句的名称(空字符串表示未命名预备语句)、目标 portal 的名称(空字符串表示未命名 portal),以及预备语句中所有参数占位符应使用的值。所提供的参数集必须与预备语句所需参数相匹配。(如果你在 Parse 消息中声明了任何 void 参数,那么在 Bind 消息中应为它们传递 NULL 值。)Bind 还会指定查询返回数据所使用的格式;格式既可以统一指定,也可以按列指定。响应要么是 BindComplete,要么是 ErrorResponse。 + + + + + + 输出采用文本还是二进制格式,由 Bind 中给出的格式代码决定,而与涉及的 SQL 命令无关。在使用扩展查询协议时,游标声明中的 BINARY 属性并不起作用。 + + + + + 通常会在处理 Bind 消息时进行查询规划。如果预备语句没有参数,或者会被重复执行,服务器可能会保存生成的计划,并在后续针对同一预备语句的 Bind 消息中重用它。不过,只有当它发现可以创建一个效率并不比依赖具体参数值的计划差很多的通用计划时,才会这样做。就协议而言,这一切都是透明的。 + + + + 如果成功创建了一个有名 portal 对象,它会一直持续到当前事务结束,除非被显式销毁。未命名 portal 会在事务结束时销毁,或者在下一条把未命名 portal 作为目标的 Bind 消息发出时立即销毁。(注意,简单 Query 消息也会销毁未命名 portal。)有名 portal 在被另一条 Bind 消息重新定义之前必须显式关闭,而未命名 portal 则无此要求。有名 portal 也可以在 SQL 命令级别通过DECLARE CURSORFETCH来创建和访问。 + + + + 一旦 portal 存在,就可以使用 Execute 消息执行它。Execute 消息指定 portal 的名称(空字符串表示未命名 portal)以及一个最大的结果行计数(零表示取回全部行)。结果行计数只对包含返回行集命令的 portal 有意义;在其他情况下,命令总会执行到完成,而行计数会被忽略。Execute 的可能响应与通过简单查询协议发出的查询相同,只是 Execute 不会导致后端发送 ReadyForQuery 或 RowDescription。 + + + + 如果 Execute 在 portal 执行完成之前终止(因为达到了非零的结果行计数),它会发送一条 PortalSuspended 消息;该消息表明前端应当针对同一个 portal 再发出一条 Execute 消息,以完成此次操作。在 portal 执行完成之前,不会发送表示源 SQL 命令结束的 CommandComplete 消息。因此,一个 Execute 阶段总会以下列消息中的恰好一条结束:CommandComplete、EmptyQueryResponse(如果 portal 是从空查询字符串创建的)、ErrorResponse 或 PortalSuspended。 + + + + 每一组扩展查询消息完成后,前端都应发送一条 Sync 消息。这条无参数消息会让后端关闭当前事务,如果当前事务并不处在 BEGIN/COMMIT 事务块内(这里的关闭指的是:无错误则提交,有错误则回滚)。随后会发送一条 ReadyForQuery 响应。Sync 的目的是为错误恢复提供一个重新同步点。如果在处理任何扩展查询消息时检测到错误,后端会发出 ErrorResponse,然后持续读取并丢弃消息,直到遇到 Sync,再发送 ReadyForQuery 并回到正常的消息处理流程。(但要注意,如果是在处理 Sync 期间检测到错误,则不会跳过任何消息,这样就能保证每条 Sync 恰好对应一条 ReadyForQuery。) + + + + + + Sync 不会关闭由BEGIN打开的事务块。之所以能够检测出这种情况,是因为 ReadyForQuery 消息中包含事务状态信息。 + + + + + 除了这些基本的、必需的操作之外,在扩展查询协议里还有几种可选的操作可以使用。 + + + + Describe 消息(portal 变体)指定一个现有 portal 的名称(或者用空字符串表示未命名 portal)。响应要么是一条 RowDescription 消息,用于描述执行该 portal 时将返回的行;要么是一条 NoData 消息,如果该 portal 不包含会返回行的查询;要么是 ErrorResponse,如果不存在这样的 portal。 + + + + Describe 消息(语句变体)指定一个现有预备语句的名称(或者用空字符串表示未命名预备语句)。响应是一条 ParameterDescription 消息,用于描述该语句所需的参数,随后是一条 RowDescription 消息,用于描述该语句最终执行时将返回的行(如果该语句不返回行,则为 NoData 消息)。如果不存在这样的预备语句,则返回 ErrorResponse。请注意,由于还没有发出 Bind,后端尚不知道返回列将使用什么格式;因此在这种情况下,RowDescription 消息中的格式代码字段将为零。 + + + + + + 在大多数场景下,前端都应在发出 Execute 之前先发送某一种 Describe 变体,以确保它知道应如何解释接收到的结果。 + + + + + Close 消息会关闭一个现有的预备语句或 portal,并释放相关资源。针对不存在的语句或 portal 发出 Close 并不算错误。响应通常是 CloseComplete,但如果在释放资源时遇到困难,也可能返回 ErrorResponse。请注意,关闭预备语句会隐式关闭所有基于该语句构造出的打开 portal。 + + + + Flush 消息本身不会产生任何特定输出,但会强制后端发送其输出缓冲区中所有尚待发送的数据。如果前端希望在发出更多命令之前检查某条扩展查询命令的结果,那么除了 Sync 之外,任何扩展查询命令之后都必须发送 Flush。如果没有 Flush,后端返回的消息会尽量合并成最少的数据包,以降低网络开销。 + + + + + + 简单 Query 消息大致等价于一串 Parse、Bind、portal Describe、Execute、Close、Sync 操作,它们使用未命名预备语句和未命名 portal 对象,并且不带参数。不同之处在于,简单 Query 会接受包含多条 SQL 语句的查询字符串,并依次自动为每条语句执行绑定、描述和执行序列;另一个区别是它不会返回 ParseComplete、BindComplete、CloseComplete 或 NoData 消息。 + + + + + + + 函数调用 + + + 函数调用子协议允许客户端请求直接调用数据库pg_proc系统目录中的任意函数。客户端必须具有该函数的执行权限。 + + + + + + 函数调用子协议是一个遗留的特性,在新代码里可能最好避免用它。类似的结果可以通过设置一个执行SELECT function($1, ...)的预备语句得到。这样函数调用周期就可以用 Bind/Execute 代替。 + + + + + 函数调用周期由前端向后端发送一条 FunctionCall 消息来启动。后端随后根据函数调用的结果发送一条或多条响应消息,最后发送一条 ReadyForQuery 响应消息。ReadyForQuery 告知前端,可以安全地发送新的查询或函数调用。 + + + + 来自后端的可能的响应消息是: + + + + ErrorResponse + + + 发生了一个错误。 + + + + + + FunctionCallResponse + + + 函数调用完成并且在消息中返回一个结果(请注意函数调用协议只能处理单个标量结果,不能处理行类型或者结果集合)。 + + + + + + ReadyForQuery + + + 函数调用处理完成。ReadyForQuery将总是被发送,不管是成功完成处理还是发生一个错误。 + + + + + + NoticeResponse + + + 发出了一条有关该函数调用的警告信息。通知是附加在其他响应上的,也就是说,后端将继续处理该命令。 + + + + + + + + + COPY操作 + + + COPY命令允许在服务器和客户端之间进行高速大批量数据传输。拷贝入和拷贝出操作每个都把连接切换到一个独立的子协议中,并且持续到操作结束。 + + + + 拷贝入模式(向服务器传输数据)在后端执行COPY FROM STDIN SQL 语句时启动。后端会向前端发送一条 CopyInResponse 消息。随后前端应发送零条或多条 CopyData 消息,构成一条输入数据流。(消息边界与行边界之间没有任何对应关系要求,尽管让它们对齐通常是合理的选择。)前端可以通过发送 CopyDone 消息来结束拷贝入模式(允许成功结束),也可以发送 CopyFail 消息(这会使COPY语句以错误失败)。然后后端会恢复到COPY开始之前的命令处理模式,也就是简单查询协议或扩展查询协议。接下来它会发送 CommandComplete(成功时)或 ErrorResponse(失败时)。 + + + + 如果在拷贝入模式期间后端检测到错误(包括收到 CopyFail 消息),后端会发出一条 ErrorResponse 消息。如果COPY命令是通过扩展查询消息发出的,那么后端会从此开始丢弃前端消息,直到收到一条 Sync 消息,然后发出 ReadyForQuery 并恢复正常处理。如果COPY命令是在简单 Query 消息中发出的,那么该消息的剩余部分会被丢弃,并发送 ReadyForQuery。无论哪种情况,前端随后发出的任何 CopyData、CopyDone 或 CopyFail 消息都会被直接丢弃。 + + + + 后端会忽略在拷贝入模式期间收到的 Flush 和 Sync 消息。收到任何其他非拷贝类型的消息都会构成错误,并按上述方式中止拷贝入状态。(Flush 和 Sync 的例外是为了方便那些总是在 Execute 消息之后发送 Flush 或 Sync、而不检查待执行命令是否为COPY FROM STDIN的客户端库。) + + + + 拷贝出模式(数据从服务器发出)是在后端执行一个COPY TO STDOUT语句时启动的。后端发出一个CopyOutResponse消息给前端,后面跟着零或者多个CopyData消息(总是每行一个),然后跟着CopyDone。然后后端回退到它在COPY开始之前的命令处理模式,然后发送CommandComplete。前端不能中止传输(除非是关闭连接或者发出一个Cancel请求),但是它可以抛弃不需要的CopyData和CopyDone消息。 + + + + 在拷贝出模式中,如果后端检测到错误,那么它将发出一个ErrorResponse消息并且回到正常的处理。前端应该把收到ErrorResponse当作终止拷贝出模式的标志。 + + + + NoticeResponse 和 ParameterStatus 消息可能穿插在 CopyData 消息之间;前端必须处理这些情况,并应准备好处理其他异步消息类型(参见)。除此之外,可以将任何除 CopyData 或 CopyDone 以外的消息类型视为拷贝出模式的终止标志。 + + + + 还有另一种与拷贝相关的模式,称为双向拷贝(copy-both),它允许高速批量地向服务器发送数据以及从服务器接收数据。当处于 walsender 模式的后端执行START_REPLICATION语句时,会启动双向拷贝模式。后端会向前端发送一条 CopyBothResponse 消息。此后,前端和后端都可以发送 CopyData 消息,直到任一方发送 CopyDone 消息。客户端发送 CopyDone 后,连接会从双向拷贝模式切换到拷贝出模式,客户端也不得再发送 CopyData。类似地,当服务器发送 CopyDone 后,连接会进入拷贝入模式,服务器也不得再发送 CopyData。当双方都发送完 CopyDone 后,拷贝模式结束,后端恢复到原先的命令处理模式。如果双向拷贝模式期间发生后端检测到的错误,后端会发出 ErrorResponse,丢弃前端消息直到收到 Sync,然后发出 ReadyForQuery 并返回正常处理。前端应将收到 ErrorResponse 视为双向拷贝终止的信号;在这种情况下不应再发送 CopyDone。关于在双向拷贝模式上传输的子协议,见。 + + + + CopyInResponse、CopyOutResponse 和 CopyBothResponse 消息包含一些字段,用于告知前端每行的列数以及每列所使用的格式代码。(在当前实现中,同一次COPY操作的所有列都使用相同格式,但消息设计并不作此假设。) + + + + + + 异步操作 + + + 在若干情况下,后端会发送并非由前端命令流直接触发的消息。前端必须随时准备处理这些消息,即使当前并未处于查询过程中。至少,在开始读取查询响应之前应检查这些情况。 + + + + NoticeResponse 消息可能因外部活动而产生;例如,如果数据库管理员发起一次快速数据库关闭,后端会在关闭连接之前发送一条 NoticeResponse 说明这一事实。因此,前端应始终准备好接收并显示 NoticeResponse 消息,即使连接表面上处于空闲状态。 + + + + 只要后端认为前端应当知晓的某个参数的当前有效值发生变化,就会生成 ParameterStatus 消息。最常见的情况是响应前端执行的SET命令,这种情况实际上是同步的;但也可能是管理员修改了配置文件,然后向服务器发送SIGHUP信号,从而导致参数状态发生变化。同样,如果某条SET命令被回滚,也会生成适当的 ParameterStatus 消息,用于报告当前生效的值。 + + + + 目前,会为一组固定的参数生成 ParameterStatus,参数如下: + server_versionserver_encodingclient_encodingapplication_nameis_superusersession_authorizationDateStyleIntervalStyleTimeZoneinteger_datetimesstandard_conforming_strings。 + (8.0 之前的版本不报告 server_encodingTimeZoneinteger_datetimes;8.1 之前的版本不报告 standard_conforming_strings;8.4 之前的版本不报告 IntervalStyle;9.0 之前的版本不报告 application_name。) + 注意,server_versionserver_encodinginteger_datetimes 是启动后不能改变的伪参数。这组参数将来可能变化,甚至可能变为可配置。因此,前端应直接忽略其不理解或不关心的参数的 ParameterStatus。 + + + + 如果前端发出LISTEN命令,那么每当针对同一通道名执行NOTIFY命令时,后端都会发送一条 NotificationResponse 消息(不要与 NoticeResponse 混淆)。 + + + + + + 目前,NotificationResponse只能在一个事务外面发送,因此它将不会在一个命令响应序列中间出现,但是它可能正好在ReadyForQuery之前出现。不过,在前端逻辑中做上述假设是不明智的。好的做法是在协议的任何点上都可以接受NotificationResponse。 + + + + + + 取消正在处理的请求 + + + 在一条查询正在处理的时候,前端可以请求取消该查询。这种取消请求不是直接通过打开的连接发送给后端的,这么做是因为实现的效率:我们不希望后端在处理查询的过程中不停地检查前端来的输入。 取消请求应该相对而言比较少见,所以我们把取消做得稍微笨拙一些,以便不影响正常状况的性能。 + + + + 要发出取消请求,前端会新建到服务器的连接,并发送 CancelRequest 消息,而不是新连接通常发送的 StartupMessage 消息。服务器处理该请求后便会关闭连接。出于安全原因,服务器不会直接回复取消请求消息。 + + + + 除非CancelRequest消息包含在连接启动过程中传递给前端的相同的密钥数据(PID 和密钥),否则它将被忽略。如果该请求匹配当前运行着的后端的PID和密钥, 则中止当前查询的处理(目前的实现里采用的方法是向正在处理该查询的后端进程发送一个特殊的信号)。 + + + + 取消信号可能有效,也可能无效;例如,如果它在后端已经处理完查询之后才到达,就不会起作用。如果取消生效,当前命令就会以一条错误消息提前终止。 + + + + 这么做是对安全性和效率通盘考虑的结果,前端没有直接的方法获知一个取消请求是否成功。它必须继续等待后端对查询响应。发出一个取消仅仅是增加了当前查询快些结束的可能性, 同时也增加了当前查询会伴随着一条错误消息失败而不是成功执行的可能性。 + + + + 由于取消请求是通过一条新的连接发送给服务器,而不是通过常规的前端/后端通信链路发送,因此发出取消请求的可以是任意进程,而不一定非要是要取消查询的那个前端。这为构建多进程应用提供了额外的灵活性,同时也带来了安全风险,因为未授权用户可能会尝试取消查询。通过要求在取消请求中提供动态生成的密钥,可以缓解这一安全风险。 + + + + + 终止 + + + 通常优雅的终止过程是前端发送一条Terminate消息并且立刻关闭连接。一旦收到消息,后端马上关闭连接并且终止。 + + + + 在少数情况下(比如一个管理员命令数据库关闭),后端可能在没有任何前端请求的情况下断开连接。在这种情况下,后端将在它断开连接之前尝试发送一个错误或者通知消息给出断开的原因。 + + + + 其他终止场景来自各种故障,例如任一端发生 core dump、通信链路中断、消息边界同步丢失等。如果前端或后端看到连接意外关闭,就应清理并终止。若前端不想自行终止,也可以重新联系服务器以启动一个新的后端。如果收到无法识别的消息类型,同样建议关闭连接,因为这通常意味着消息边界同步已经丢失。 + + + + 不管是正常还是不正常的终止,任何打开的事务都会回滚而不是提交。不过,我们应该注意的是如果一个前端在一个非SELECT查询正在处理的时候断开, 那么后端很可能在发现断开之前先完成查询的处理。如果查询处于任何事务块之外(BEGIN ... COMMIT序列),那么其结果可能在发现连接断开之前被提交。 + + + + + <acronym>SSL</acronym>会话加密 + + + 如果编译PostgreSQL时启用了SSL支持,那么前端/后端通信就可以使用SSL加密。这为攻击者可能截获会话流量的环境提供了通信安全性。有关使用SSL加密PostgreSQL会话的更多信息,请参阅。 + + + 要发起 SSL 加密连接,前端首先发送 SSLRequest 消息,而不是 StartupMessage。服务器随后返回包含 SN 的单个字节,分别表示愿意或不愿意进行 SSL 通信。如果前端不满意此响应,可以在此时关闭连接。要在收到 S 后继续,应与服务器执行 SSL 启动握手(这里不作说明,它属于 SSL 规范的一部分)。如果握手成功,则继续发送通常的 StartupMessage。此时,StartupMessage 及其后的所有数据都将经过 SSL 加密。要在收到 N 后继续,则发送通常的 StartupMessage,并以不加密的方式继续通信。 + + 前端还应准备好处理服务器对 SSLRequest 返回的 ErrorMessage 响应。只有当服务器版本早于 PostgreSQL 引入 SSL 支持时,才会发生这种情况。(这样的服务器已经非常古老,现实中可能已不存在。)此时必须关闭连接,但前端可以选择建立一个新连接,并在不请求 SSL 的情况下继续通信。 + + + 当可以执行 SSL 加密时,服务器应仅发送单个 S 字节,然后等待前端启动 SSL 握手。如果此时有其他可读取的字节,则很可能意味着中间人正在尝试执行缓冲区填充攻击(CVE-2021-23222)。前端应该编写代码,要么从套接字中恰好读取一个字节,然后将套接字交给所用的 SSL 库,要么在发现已经读取到额外的字节时将其视为协议违规。 + + + + 如果建立连接是为了发送 CancelRequest 消息,也可以先发送 SSLRequest。 + + + + 虽然协议本身没有提供让服务器强制使用SSL加密的方法,但管理员可以配置服务器,使其在认证检查中拒绝未加密的会话。 + + + + + + +流复制协议 + +要发起流式复制,前端在启动消息中发送 replication 参数。布尔值 true 告知后端进入 walsender 模式,在该模式下可以发出一小组复制命令,而不是 SQL 语句。walsender 模式只能使用简单查询协议。启用 后,复制命令会记录到服务器日志中。传入值 database 会指示 walsender 连接到 dbname 参数指定的数据库,从而允许此连接用于从该数据库进行逻辑复制。 +为了测试复制命令,可以通过psql或其他使用libpq的工具建立复制连接,连接字符串中应包含replication选项,例如: +psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" +不过,通常更有用的做法是使用(用于物理复制)或(用于逻辑复制)。 + +walsender 模式接受以下命令: + + IDENTIFY_SYSTEM + 识别系统 + + + 请求服务器标识自身。服务器返回一个只有一行的结果集,包含四个字段: + + + + + + systemid (text) + + + 标识数据库集簇的唯一系统标识符。可用于检查初始化备库的基础备份是否来自同一个数据库集簇。 + + + + + timeline (int4) + + 当前时间线 ID。也可用于检查备库是否与主库一致。 + + + + + + xlogpos (text) + + + 当前 xlog 刷盘位置。可用于获取事务日志中一个已知的位置,以便从该处开始流式传输。 + + + + + + dbname (text) + + + 所连接的数据库,或 null。 + + + + + + + + + + + TIMELINE_HISTORY tli + TIMELINE_HISTORY + + + 请求服务器发送时间线 tli 的时间线历史文件。服务器返回一个只有一行的结果集,包含两个字段。虽然这些字段被标记为 textbytea,它们实际返回的是原始字节,不进行转义或编码转换: + + + + + + filename (text) + + + + 时间线历史文件的文件名,例如,00000002.history。 + + + + + + + content (bytea) + + + 时间线历史文件的内容。 + + + + + + + + + + CREATE_REPLICATION_SLOT slot_name { PHYSICAL [ RESERVE_WAL ] | LOGICAL output_plugin } + CREATE_REPLICATION_SLOT + + + + 创建一个物理或逻辑复制槽。查看了解更多关于复制槽的信息。 + + + + slot_name + + + 要创建的复制槽名称。必须是合法的复制槽名称(参见)。 + + + + + + output_plugin + + 用于逻辑解码的输出插件名称(参见 )。 + + + + + + RESERVE_WAL + + 指定此物理复制槽立即保留 WAL。否则,只有在流复制客户端连接时才会保留 WAL + + + + + + + + + + + + START_REPLICATION [ SLOT slot_name ] [ PHYSICAL ] XXX/XXX [ TIMELINE tli ] + 开始复制 + + + + 指示服务器开始流式传输 WAL,从 WAL 位置 XXX/XXX 开始。 + 如果指定了TIMELINE选项,则流式传输将从时间线tli开始; + 否则,将选择服务器当前的时间线。如果请求的WAL部分已经被回收,服务器可能会回复错误。 + 成功时,服务器将用CopyBothResponse消息回复,然后开始向前端流式传输WAL。 + + + + 如果通过slot_name提供了复制槽名称, + 那么在复制进行期间会更新该复制槽,以便服务器知道哪些 WAL 段, + 以及在启用了 hot_standby_feedback 时,哪些事务 + 仍然被备库所需要。 + + + 如果客户端请求的时间线不是最新时间线,但属于服务器的历史,服务器会从请求的起点开始,流式传输该时间线上的所有 WAL,直到服务器切换到另一条时间线的位置。如果客户端请求的流式传输起点恰好位于旧时间线的末尾,服务器会立即返回 CommandComplete,而不进入 COPY 模式。 + + + 在非最新时间线上流式传输完全部 WAL 后,服务器会通过退出 COPY 模式来结束流式传输。当客户端也通过退出 COPY 模式来确认时,服务器会发送一个包含一行两列的结果集,指示该服务器历史中的下一条时间线。第一列是下一条时间线的 ID(类型为 int8),第二列是发生切换的 WAL 位置(类型为 text)。通常,切换位置就是所流式传输 WAL 的末尾,但也存在一些边界情况,服务器可能会先发送一些自己在提升前尚未重放的旧时间线 WAL。最后,服务器发送 CommandComplete 消息,然后准备接受新的命令。 + + WAL 数据通过一系列 CopyData 消息发送。(这样可以混合发送其他信息;尤其是服务器在开始流式传输后遇到故障时,可以发送 ErrorResponse 消息。)服务器发给客户端的每条 CopyData 消息,其有效载荷都包含一条具有下列格式之一的消息: + + + + + XLogData (B) + + + + + Byte1('w') + + 将该消息标识为 WAL 数据。 + + + + Int64 + + 本消息中 WAL 数据的起始位置。 + + + + Int64 + + 服务器上当前的 WAL 末尾位置。 + + + + Int64 + + 发送消息时服务器的系统时钟,以自 2000-01-01 午夜以来的微秒数表示。 + + + + Byten + + WAL 数据流的一个片段。 + 单条 WAL 记录绝不会被拆分到两条 XLogData 消息中。当 WAL 记录跨越 WAL 页边界,因而已经通过续接记录拆分时,可以在页边界处分开发送。换句话说,最初的主 WAL 记录及其续接记录可以在不同的 XLogData 消息中发送。 + + + + + + + + 主库保活消息 (B) + + + + + Byte1('k') + + 将该消息标识为发送端保活消息。 + + + + Int64 + + 服务器上当前的 WAL 末尾位置。 + + + + Int64 + + 发送消息时服务器的系统时钟,以自 2000-01-01 午夜以来的微秒数表示。 + + + + Byte1 + + 1 表示客户端应尽快回复此消息,以避免超时断开连接;否则为 0。 + + + + + + + + + + 接收进程可以随时使用以下消息格式之一回复发送端(同样放在 CopyData 消息的有效载荷中): + + + + + 备库状态更新 (F) + + + + + Byte1('r') + + 将该消息标识为接收端状态更新。 + + + + Int64 + + 备库已接收并写入磁盘的最后一个 WAL 字节的位置加 1。 + + + + Int64 + + 备库已刷盘的最后一个 WAL 字节的位置加 1。 + + + + Int64 + + 备库已应用的最后一个 WAL 字节的位置加 1。 + + + + Int64 + + 发送消息时客户端的系统时钟,以自 2000-01-01 午夜以来的微秒数表示。 + + + + Byte1 + + 如果为 1,表示客户端请求服务器立即回复此消息。可用它向服务器发送探测请求,以测试连接是否仍然正常。 + + + + + + + + + + + + + 热备反馈消息 (F) + + + + + Byte1('h') + + 将该消息标识为热备反馈消息。 + + + + Int64 + + 发送消息时客户端的系统时钟,以自 2000-01-01 午夜以来的微秒数表示。 + + + + Int32 + + 备库当前的 xmin。如果备库发送通知说此连接将不再发送热备反馈,则该值可能为 0。之后的非零消息可以重新启动反馈机制。 + + + + Int32 + + 备库当前的纪元。 + + + + + + + + + + + + + START_REPLICATION SLOT slot_name + LOGICAL XXX/XXX [ ( option_name + [ option_value ] [, ...] ) ] + + + + 指示服务器开始为逻辑复制流式传输 WAL,起始 WAL 位置为 XXX/XXX。服务器可以返回错误,例如请求的 WAL 段已被回收。成功时,服务器会响应一条 CopyBothResponse 消息,然后开始向前端流式传输 WAL。 + + + + CopyBothResponse 中承载的消息采用与 START_REPLICATION ... PHYSICAL + 文档中记载的相同格式。 + + + + 与所选复制槽关联的输出插件将用于处理流式输出。 + + + + + SLOT slot_name + + + + 要从中流式传输更改的复制槽名称。该参数是必需的,并且必须对应于使用 CREATE_REPLICATION_SLOTLOGICAL 模式下创建的现有逻辑复制槽。 + + + + + XXX/XXX + + + + 开始流式传输的WAL位置。 + + + + + option_name + + 传递给复制槽逻辑解码插件的选项名称。 + + + + option_value + + + + 指定选项相关的可选值,以字符串常量的形式表示。 + + + + + + + + + + DROP_REPLICATION_SLOT slot_name + 删除复制槽 + + + 删除复制槽,释放任何保留的服务器端资源。如果该槽当前正被一个活动连接使用,则此命令失败。 + + + slot_name + + + + 要删除的复制槽名称。 + + + + + + + + + + BASE_BACKUP [ LABEL 'label' ] [ PROGRESS ] [ FAST ] [ WAL ] [ NOWAIT ] [ MAX_RATE rate ] [ TABLESPACE_MAP ] BASE_BACKUP + + + 指示服务器开始流式传输基础备份。 + 在备份开始之前,系统将自动进入备份模式,并在备份完成后退出备份模式。 + 接受以下选项: + + + + LABEL 'label' + + + + 设置备份的标签。如果未指定,则将使用base backup作为备份标签。 + 标签的引号使用规则与打开的标准SQL字符串相同。 + + + + + + PROGRESS + + + 请求生成进度报告所需的信息。这将在每个表空间的首部发送一个近似大小, + 可用于计算流式传输的进度。这是通过在传输开始之前先枚举所有文件大小来计算的, + 可能会对性能产生负面影响。特别是,在流式传输数据之前可能需要更长的时间。 + 由于备份期间数据库文件可能会发生变化,因此大小仅为近似值, + 在估算与实际发送文件之间的这段时间里可能会增长或缩小。 + + + + + + FAST + + + 请求快速检查点。 + + + + + + WAL + + + 在备份中包含必要的 WAL 段。这会把开始备份到停止备份之间的所有文件放入基础目录 tar 文件内的pg_xlog目录中。 + + + + + NOWAIT + + 默认情况下,备份会等待最后一个必需的 WAL 段完成归档;如果未启用日志归档,则发出警告。指定 NOWAIT 会同时禁用等待和警告,由客户端负责确保所需日志可用。 + + + + + MAX_RATE rate + + + + 限制(节流)每单位时间从服务器传输到客户端的最大数据量。预期的单位是每秒千字节。 + 如果指定了此选项,则该值必须等于零,或者必须在32 kB到1 GB(含)的范围内。 + 如果传递零或未指定该选项,则不对传输施加任何限制。 + + + + + + TABLESPACE_MAP + + + + 在名为tablespace_map的文件中包含目录pg_tblspc中存在的符号链接的信息。 + 表空间映射文件包括目录pg_tblspc/中每个符号链接的名称及该符号链接的完整路径。 + + + + + + 备份开始时,服务器首先发送两个普通结果集,然后发送一个或多个 CopyResponse 结果。 + + 第一个普通结果集包含备份的起始位置,在一个包含两列的单行中。第一列包含以XLogRecPtr格式给出的起始位置,第二列包含相应的时间线ID。 + + + 第二个普通结果集中的每个表空间都有一行。 + 这一行中的字段是: + + + + spcoid (oid) + + + 表空间的OID,如果是基础目录则为null。 + + + + + spclocation (text) + + + 表空间目录的完整路径,如果是基础目录则为null。 + + + + + size (int8) + + 如果请求了进度报告,则为表空间的大致大小;否则为空值。 + + + + + 在第二个普通结果集之后,会发送一个或多个 CopyResponse 结果,其中一个用于主数据目录,其他结果分别用于 pg_defaultpg_global 之外的各个附加表空间。CopyResponse 结果中的数据是表空间内容的 tar 格式转储(遵循 POSIX 1003.1-2008 标准中规定的 ustar interchange format),但省略了标准规定的末尾两个全零块。tar 数据传输完成后,会发送最后一个普通结果集,其中包含备份的 WAL 结束位置,格式与起始位置相同。 + + + 数据目录和每个表空间的tar归档将包含目录中的所有文件,无论它们是PostgreSQL文件还是添加到同一目录的其他文件。唯一排除的文件是: + + + + + postmaster.pid + + + + + postmaster.opts + + + + PostgreSQL 服务器运行过程中创建的各种临时文件。 + + + + pg_xlog,包括子目录。如果备份包含WAL文件,则将包含pg_xlog的合成版本,但它只包含使备份可用所需的文件,而不包含其余内容。 + + + + + pg_replslot作为空目录复制。 + + + + + 除普通文件和目录之外的文件,例如符号链接和特殊设备文件,会被跳过。(pg_tblspc中的符号链接会被保留。) + + + + 如果服务器上的底层文件系统支持,将设置所有者、组和文件模式。 + + + + + + + + + + + + + 消息数据类型 + + + 本节描述了消息中使用的基本数据类型。 + + + + + + Intn(i) + + + + 以网络字节序(最高有效字节在前)表示的 n 位整数。 + 如果指定了 i,则表示其精确值;否则表示该值可变。例如:Int16、Int32(42)。 + + + + + + + Intn[k] + + + + 由 kn 位整数组成的数组,每个整数都按网络字节序排列。 + 数组长度 k 总是由消息中更早的某个字段确定。例如:Int16[M]。 + + + + + + + String(s) + + + + 一个以空字符结尾的字符串(C 风格字符串)。字符串没有特定的长度限制。 + 如果指定了 s,则表示其精确值;否则表示该值可变。 + 例如:String、String("user")。 + + + + + + 对后端返回的字符串长度,没有预定义的限制。 + 前端较好的编码策略是使用可扩展缓冲区,以便接收所有能放进内存的内容。 + 如果做不到这一点,就应读取完整字符串,并丢弃不适合固定大小缓冲区的尾随字符。 + + + + + + + + Byten(c) + + + + 精确 n 字节。如果字段宽度 n 不是常数, + 它总是可以由消息中更早的字段确定。如果指定了 c,则表示其精确值。 + 例如:Byte2、Byte1('\n')。 + + + + + + + +消息格式 + + + 本节描述每条消息的详细格式。每条消息都标记了可由前端(F)、后端(B)或双方(F&B)发送。 + 请注意,虽然每条消息开头都带有字节计数,但大多数消息格式都定义为无需参考该计数也能确定消息边界。这一设计最初是出于历史原因(早期已废弃的协议 v2 没有显式长度字段),同时也有助于有效性校验。 + + + + + + +AuthenticationOk (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(0) + +表示认证成功。 + + + + + + + + + + +AuthenticationKerberosV5 (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(2) + +表示需要 Kerberos V5 认证。 + + + + + + + + + +AuthenticationCleartextPassword (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(3) + +表示需要明文密码。 + + + + + + + + + +AuthenticationMD5Password (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(12) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(5) + +表示需要经过 MD5 加密的密码。 + + + +Byte4 + +加密密码时使用的盐。 + + + + + + + + + + +AuthenticationSCMCredential (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(6) + +表示需要 SCM 凭证消息。 + + + + + + + + + + +AuthenticationGSS (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(7) + +表示需要 GSSAPI 认证。 + + + + + + + + + + +AuthenticationSSPI (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(9) + +表示需要 SSPI 认证。 + + + + + + + + + + +AuthenticationGSSContinue (B) + + + + + +Byte1('R') + +将该消息标识为认证请求。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32(8) + +表示此消息包含 GSSAPI 或 SSPI 数据。 + + + +Byten + +GSSAPI 或 SSPI 认证数据。 + + + + + + + + + + + + + + + + +BackendKeyData (B) + + + + + +Byte1('K') + +将此消息标识为取消请求密钥数据。如果前端希望以后能够发送 CancelRequest 消息,就必须保存这些值。 + + + +Int32(12) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32 + +此后端的进程 ID。 + + + +Int32 + +此后端的密钥。 + + + + + + + + + + +Bind (F) + + + + + +Byte1('B') + +将该消息标识为 Bind 命令。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + +目标 portal 的名称(空字符串选择未命名的 portal)。 + + + +String + +源预备语句的名称(空字符串选择未命名的预备语句)。 + + + +Int16 + +后续参数格式码的数量(下文以 C 表示)。可以为零,表示没有参数,或者所有参数都使用默认格式(文本);也可以为一,此时指定的格式码应用于所有参数;还可以等于实际参数数量。 + + + +Int16[C] + +参数格式码。目前每个格式码必须为零(文本)或一(二进制)。 + + + +Int16 + +后续参数值的数量(可以为零)。必须与查询所需的参数数量一致。 + + +接下来,每个参数都有以下一对字段: + +Int32 + +参数值的长度,以字节为单位(不包括此长度字段本身)。可以为零。特殊值 -1 表示 NULL 参数值。为 NULL 时,后面不再有值的字节。 + + + +Byten + +参数值,格式由对应的格式码指明。n 为上述长度。 + + +最后一个参数之后是以下字段: + +Int16 + +后续结果列格式码的数量(下文以 R 表示)。可以为零,表示没有结果列,或者所有结果列都应使用默认格式(文本);也可以为一,此时指定的格式码应用于所有结果列(如果有);还可以等于查询实际的结果列数量。 + + + +Int16[R] + +结果列格式码。目前每个格式码必须为零(文本)或一(二进制)。 + + + + + + + + + +BindComplete (B) + + + + + +Byte1('2') + +将该消息标识为 Bind 完成指示。 + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + + +CancelRequest (F) + + + + + + + + Int32(16) + + + + 消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + Int32(80877102) + + + + 取消请求代码。此值的最高 16 位为 1234,最低 16 位为 5678。(为避免混淆,此代码不能与任何协议版本号相同。) + + + + + + Int32 + + + + 目标后端的进程 ID。 + + + + + + Int32 + + + + 目标后端的密钥。 + + + + + + + + + + + +Close (F) + + + + + +Byte1('C') + +将该消息标识为 Close 命令。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Byte1 + +S”表示关闭预备语句;“P”表示关闭 portal。 + + + +String + +要关闭的预备语句或 portal 的名称(空字符串选择未命名的预备语句或 portal)。 + + + + + + + + + +CloseComplete (B) + + + + + +Byte1('3') + +将该消息标识为 Close 完成指示。 + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +CommandComplete (B) + + + + + +Byte1('C') + +将该消息标识为命令完成响应。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + + 命令标签。通常是一个单词,用来标识已完成的 SQL 命令。 + + 对于 INSERT 命令,标签是 INSERT oid rows,其中 rows 是插入的行数。如果 rows 为 1 且目标表具有 OID,则 oid 是插入行的对象 ID;否则 oid 为 0。 + + + 对于DELETE命令,标签是DELETE rows, + 其中rows表示删除的行数。 + + + + 对于UPDATE命令,标签是UPDATE rows, + 其中rows是更新的行数。 + + + + 对于SELECTCREATE TABLE AS命令,标签是SELECT rows, + 其中rows是检索到的行数。 + + + + 对于MOVE命令,标签是MOVE rows, + 其中rows表示游标位置改变的行数。 + + + + 对于FETCH命令,标签是FETCH rows, + 其中rows是从游标中检索出的行数。 + + + 对于 COPY 命令,标签为 COPY rows,其中 rows 是复制的行数。(注意:行数仅出现在 PostgreSQL 8.2 及更高版本中。) + + + + + + + + + + + +CopyData (F & B) + + + + + + Byte1('d') + + + + 标识消息为COPY数据。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Byten + + + 数据是COPY数据流的一部分。来自后端的消息始终对应单个数据行, + 但来自前端的消息可能会任意划分数据流。 + + + + + + + + + + +CopyDone (F & B) + + + + + + + Byte1('c') + + + + 将消息标识为COPY完成指示符。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +CopyFail (F) + + + + + + + Byte1('f') + + + + 将消息标识为COPY失败指示器。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + + + 作为失败原因报告的错误消息。 + + + + + + + + + + + +CopyInResponse (B) + + + + + + + Byte1('G') + + +标识消息为开始复制输入的响应。前端此时必须发送复制输入数据(如果尚未准备好,应发送 CopyFail 消息)。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + Int8 + + + + 0表示整体COPY格式是文本的(行由换行符分隔,列由分隔符分隔等)。 + 1表示整体复制格式是二进制的(类似于DataRow格式)。 + 更多信息请参见。 + + + + +Int16 + + + 要复制的数据中的列数(以下用N表示)。 + + + + + + Int16[N] + + +各列使用的格式代码。目前每个代码必须为零(文本)或一(二进制)。如果整体复制格式为文本,则所有代码都必须为零。 + + + + + + + + + + +CopyOutResponse (B) + + + + + + + Byte1('H') + + +标识消息为开始复制输出的响应。该消息之后会发送复制输出数据。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + Int8 + + + + 0表示整体COPY格式是文本的(行由换行符分隔,列由分隔符分隔等)。 + 1表示整体复制格式是二进制的(类似于DataRow格式)。 + 更多信息请参见。 + + + + +Int16 + + + 要复制的数据中的列数(以下用N表示)。 + + + + + + Int16[N] + + +各列使用的格式代码。目前每个代码必须为零(文本)或一(二进制)。如果整体复制格式为文本,则所有代码都必须为零。 + + + + + + + + + + +CopyBothResponse (B) + + + + + + + Byte1('W') + + +标识消息为开始双向复制的响应。此消息仅用于流复制。 + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + Int8 + + + + 0表示整体COPY格式是文本的(行由换行符分隔,列由分隔符分隔等)。 + 1表示整体复制格式是二进制的(类似于DataRow格式)。 + 更多信息请参见。 + + + + +Int16 + + + 要复制的数据中的列数(以下用N表示)。 + + + + + + Int16[N] + + +各列使用的格式代码。目前每个代码必须为零(文本)或一(二进制)。如果整体复制格式为文本,则所有代码都必须为零。 + + + + + + + + + + +DataRow (B) + + + + + + Byte1('D') + + + + 标识消息为数据行。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int16 + + + 后面跟着的列值的数量(可能为零)。 + + + +接下来,每列都有以下两个字段: + +Int32 + +列值的长度,以字节为单位(不包括本长度字段自身)。可以为零。特殊值 -1 表示列值为 NULL,此时后面没有值字节。 + + + +Byten + + + 列的值,格式由相关的格式代码指示。 + n是上述长度。 + + + + + + + + + + + +Describe (F) + + + + + + + Byte1('D') + + + + 标识消息为描述命令。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Byte1 + + + 'S' 表示描述一个预备语句;或者 + 'P' 表示描述一个 portal。 + + + + +String + + + 要描述的预备语句或 portal 的名称(空字符串选择未命名的预备语句或 portal)。 + + + + + + + + + + +EmptyQueryResponse (B) + + + + + + + Byte1('I') + + +标识消息为对空查询字符串的响应。(此消息替代 CommandComplete。) + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +ErrorResponse (B) + + + + + + + Byte1('E') + + + + 将消息标识为错误。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + +消息体由一个或多个带标识的字段组成,最后以一个零字节终止。字段可以按任意顺序出现。每个字段包含以下内容: + +Byte1 + + + 一个用于标识字段类型的代码;如果为零,则这是消息终止符,后面没有字符串。 + 目前定义的字段类型列在中。 + 由于将来可能会添加更多的字段类型,前端应该静默地忽略未识别类型的字段。 + + + + +String + +字段值。 + + + + + + + + + + +Execute (F) + + + + + + + Byte1('E') + + + + 标识消息为一个执行命令。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + + + 要执行的 portal 的名称(空字符串选择未命名的 portal)。 + + + + +Int32 + +如果 portal 包含返回行的查询,则这是最多返回的行数(否则忽略此值)。零表示无限制 + + + + + + + + + +Flush (F) + + + + + + + Byte1('H') + + + + 将消息标识为Flush命令。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +FunctionCall (F) + + + + + + + Byte1('F') + + + + 标识消息为函数调用。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32 + +指定要调用的函数的对象 ID。 + + + +Int16 + +后续参数格式代码的数量(以下用 C 表示)。可以为零,表示没有参数,或所有参数都采用默认格式(文本);也可以为一,表示将指定的格式代码用于所有参数;还可以等于实际参数数量。 + + + +Int16[C] + + + 参数格式代码。每个目前必须是零(文本)或一(二进制)。 + + + + +Int16 + +指定传递给函数的参数数量。 + + +接下来,每个参数都有以下两个字段: + +Int32 + +参数值的长度,以字节为单位(不包括本长度字段自身)。可以为零。特殊值 -1 表示参数值为 NULL,此时后面没有值字节。 + + + +Byten + + + 参数的值,以相关格式代码指示的格式表示。 + n是上述长度。 + + + +最后一个参数之后还有以下字段: + +Int16 + +函数结果的格式代码。目前必须为零(文本)或一(二进制)。 + + + + + + + + + + +FunctionCallResponse (B) + + + + + + + Byte1('V') + + + + 标识消息为函数调用结果。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32 + +函数结果值的长度,以字节为单位(不包括本长度字段自身)。可以为零。特殊值 -1 表示函数结果为 NULL,此时后面没有值字节。 + + + +Byten + + + 函数结果的值,格式由相关的格式代码指示。 + n是上述长度。 + + + + + + + + + + + + +NegotiateProtocolVersion (B) + + + + + + + Byte1('v') + + + + 标识消息为协议版本协商消息。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32 + +对于客户端请求的协议主版本,服务器所支持的最新协议次版本。 + + + +Int32 + +服务器无法识别的协议选项数量。 + + +接下来,对于服务器无法识别的每个协议选项,都有以下内容: + +String + + + 选项名称。 + + + + + + + + + +NoData (B) + + + + + + + Byte1('n') + + + + 将消息标识为无数据指示器。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +NoticeResponse (B) + + + + + + + Byte1('N') + + + + 将消息标识为通知。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + +消息体由一个或多个带标识的字段组成,最后以一个零字节终止。字段可以按任意顺序出现。每个字段包含以下内容: + +Byte1 + + + 一个用于标识字段类型的代码;如果为零,则这是消息终止符,后面没有字符串。 + 目前定义的字段类型列在中。 + 由于将来可能会添加更多的字段类型,前端应该静默地忽略未识别类型的字段。 + + + + +String + +字段值。 + + + + + + + + + + +NotificationResponse (B) + + + + + + + Byte1('A') + + + + 标识消息为通知响应。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int32 + +发出通知的后端进程的进程 ID。 + + + +String + +发出该通知的通道名称。 + + + +String + +通知进程传来的载荷字符串。 + + + + + + + + + + +ParameterDescription (B) + + + + + + + Byte1('t') + + + + 标识消息为参数描述。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int16 + + + 语句使用的参数数量(可以为零)。 + + + +接下来,每个参数都有以下内容: + +Int32 + +指定参数数据类型的对象 ID。 + + + + + + + + + +ParameterStatus (B) + + + + + + + Byte1('S') + + + + 标识消息为运行时参数状态报告。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + +所报告的运行时参数的名称。 + + + +String + +参数的当前值。 + + + + + + + + + +Parse (F) + + + + + + + Byte1('P') + + + + 将消息标识为解析命令。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + +目标预备语句的名称(空字符串选择未命名的预备语句)。 + + + +String + +要解析的查询字符串。 + + + +Int16 + + + 指定的参数数据类型的数量(可以为零)。请注意,这不是查询字符串中可能出现的参数数量的指示, + 而是前端希望为其预先指定类型的参数数量。 + + + +接下来,每个参数都有以下内容: + +Int32 + +指定参数数据类型的对象 ID。此处填零等同于不指定类型。 + + + + + + + + + +ParseComplete (B) + + + + + + + Byte1('1') + + + + 将消息标识为解析完成指示器。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +PasswordMessage (F) + + + + + + + Byte1('p') + + + + 标识消息为密码响应。请注意,这也用于GSSAPI、SSPI和SASL响应消息。 + 可以从上下文中推断出确切的消息类型。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + + + 密码(如果需要,已加密)。 + + + + + + + + + + +PortalSuspended (B) + + + + + + + Byte1('s') + + + + 标识消息为 portal 挂起指示器。 + 请注意,仅当执行消息的行数限制达到时才会出现此消息。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +Query (F) + + + + + + + Byte1('Q') + + + + 标识消息为简单查询。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +String + + + 查询字符串本身。 + + + + + + + + + + + +ReadyForQuery (B) + + + + + + + Byte1('Z') + + + + 标识消息类型。ReadyForQuery在后端准备好进行新的查询周期时发送。 + + + + +Int32(5) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Byte1 + + + 当前后端事务状态指示器。 + 可能的值为'I',如果空闲(不在事务块中);'T',如果在事务块中; + 或'E',如果在失败的事务块中(查询将被拒绝,直到块结束)。 + + + + + + + + + + + +RowDescription (B) + + + + + + + Byte1('T') + + + + 标识消息为行描述。 + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + +Int16 + +指定一行中的字段数量(可以为零)。 + + +接下来,每个字段都有以下内容: + +String + + + 字段名称。 + + + + +Int32 + +如果能够确定该字段是某个特定表的列,则为该表的对象 ID;否则为零。 + + + +Int16 + +如果能够确定该字段是某个特定表的列,则为该列的属性编号;否则为零。 + + + +Int32 + +字段数据类型的对象 ID。 + + + +Int16 + + + + 数据类型大小(参见pg_type.typlen)。 + 注意,负值表示可变宽度类型。 + + + + +Int32 + + + + 类型修饰符(参见pg_attribute.atttypmod)。 + 修饰符的含义是特定于类型的。 + + + + +Int16 + +字段所使用的格式代码。目前为零(文本)或一(二进制)。对于 Describe 的语句变体所返回的 RowDescription,格式代码尚未确定,始终为零。 + + + + + + + + + + + + + + +SSLRequest (F) + + + + + +Int32(8) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + Int32(80877103) + + + + SSL请求代码。该值被选择为在最高的16位中包含1234, + 在最低的16位中包含5679。(为避免混淆,此代码 + 不得与任何协议版本号相同。) + + + + + + + + + + + +StartupMessage (F) + + + + + +Int32 + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + Int32(196608) + + +协议版本号。高 16 位为主版本号(此处描述的协议为 3);低 16 位为次版本号(此处描述的协议为 0)。 + + +协议版本号之后是一个或多个参数名与参数值字符串对。最后一个名称/值对之后必须有一个零字节作为终止符。参数可以按任意顺序出现。其中,user是必需的,其余均为可选。每个参数按以下方式指定: + +String + +参数名称。目前能够识别的名称如下: + + + user + + + + 要连接的数据库用户名称。必填项;没有默认值。 + + + + + + database + + + + 要连接的数据库。默认为用户名。 + + + + + + options + + + + 后端的命令行参数。(已弃用,建议设置单独的运行时参数。)此字符串中的空格被视为分隔参数,除非用反斜杠(\)转义;写\\表示字面反斜杠。 + + + +除上述参数外,还可以列出其他参数。以_pq_.开头的参数名称保留用于协议扩展,其余参数则作为运行时参数,在后端启动时设置。这些设置会在后端启动期间应用(在解析命令行参数之后,如果有的话),并作为会话默认值。 + + + +String + +参数值。 + + + + + + + + + + +Sync (F) + + + + + + + Byte1('S') + + + + 将消息标识为同步命令。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + +Terminate (F) + + + + + + + Byte1('X') + + + + 标识消息为终止。 + + + + +Int32(4) + +消息内容的长度,以字节为单位,包括此长度字段本身。 + + + + + + + + + + + + + + + +错误和通知消息域 + + + 本节描述了ErrorResponse和NoticeResponse消息中可能出现的字段。每个字段类型都有一个单字节的标识符。 + 请注意,任何给定的字段类型在消息中最多只能出现一次。 + + + + + + S + + + + 严重性: 字段内容为ERROR, FATAL, 或 + PANIC (在错误消息中), 或 + WARNING, NOTICE, DEBUG, + INFO, 或 LOG (在通知消息中), + 或者这些内容的本地化翻译。始终存在。 + + + + + + V + + + + 严重性:字段内容为 ERRORFATAL 或 + PANIC(在错误消息中),或 WARNINGNOTICEDEBUG、 + INFOLOG(在通知消息中)。 + 这与S字段相同,只是内容不会被本地化。仅在由PostgreSQL版本9.6 + 及更高版本生成的消息中存在。 + + + + + + C + + + + Code: 错误的SQLSTATE代码(参见)。不可本地化。始终存在。 + + + + + + M + + + + 消息: 主要的人类可读错误消息。 + 这应该准确但简洁(通常一行)。 + 总是存在。 + + + + + + D + + + + 详细信息:一个可选的次要错误消息,提供有关问题的更多详细信息。可能会跨多行。 + + + + + + H + + + + 提示: 关于问题应该怎么做的一个可选建议。 + 这意在与细节不同,它提供建议(可能不合适)而不是硬性事实。 + 可能会跨越多行。 + + + + + + P + + + + 位置:字段值是一个十进制ASCII整数,表示错误光标位置,作为原始查询字符串的索引。 + 第一个字符的索引为1,位置以字符而非字节计量。 + + + + + + p + + + + 内部位置:这与P字段的定义相同,但在光标位置指向内部生成的命令而不是客户端提交的命令时使用。 + 当此字段出现时,q字段将始终出现。 + + + + + +q + + + 内部查询: 一个失败的内部生成命令的文本。 + 例如,这可能是由PL/pgSQL函数发出的SQL查询。 + + + + + + W + + + + Where: 错误发生的上下文指示。 + 目前包括活动的过程语言函数和内部生成查询的调用堆栈回溯。 + 跟踪每行一个条目,最近的在前。 + + + + + + s + + + + Schema name: 如果错误与特定数据库对象相关联,则为包含该对象的模式的名称(如果有)。 + + + + + + t + + + + 表名: 如果错误与特定表相关联,则为表的名称。(有关表模式名称的名称,请参考模式名称字段。) + + + + + + c + + + + 列名: 如果错误与特定表列相关联,则为列的名称。(请参考模式和表名字段以识别表。) + + + + + + d + + + + 数据类型名称: 如果错误与特定数据类型相关联,则为数据类型的名称。 + (有关数据类型模式的名称,请参阅模式名称字段。) + + + + + + n + + + + 约束名称: 如果错误与特定约束相关联,则为约束的名称。请参考上面列出的字段,了解相关表或域。 + (为此,即使索引不是使用约束语法创建的,也将其视为约束。) + + + + + + F + + + + 文件: 报告错误的源代码位置的文件名。 + + + + + + L + + + + Line: 源代码位置的行号,报告错误的位置。 + + + + + + R + + + + Routine: 报告错误的源代码例程的名称。 + + + + + + + + + + 模式名称、表名称、列名称、数据类型名称和约束名称的字段仅针对有限数量的错误类型提供; + 请参阅。前端不应假设任何这些字段的存在就保证了另一个字段的存在。 + 核心错误源观察到上述相互关系,但用户定义的函数可能以其他方式使用这些字段。 + 同样地,客户端不应假设这些字段表示当前数据库中的当代对象。 + + + + + 客户端负责格式化显示的信息以满足其需求;特别是应根据需要换行。错误消息字段中出现的换行符应被视为段落分隔符,而不是换行符。 + + + + + + +自协议 2.0 以来的变更总结 + + + 本节提供一份简要的变更清单,供准备将现有客户端库更新到协议 3.0 的开发者参考。 + + +初始启动包采用灵活的字符串列表格式,取代了固定格式。注意,运行时参数的会话默认值现在可以直接在启动包中指定。(实际上,以前也能通过 options 字段实现,但由于 options 的宽度有限,且无法用引号保护值中的空白字符,这种方法并不稳妥。) + +现在,所有消息都在消息类型字节之后紧跟一个长度计数(启动包除外,它没有类型字节)。另请注意,PasswordMessage 现在也有类型字节。 + +ErrorResponse 和 NoticeResponse('E' 和 'N')消息现在包含多个字段,客户端代码可以利用这些字段组合出所需详细程度的错误消息。注意,各字段通常不会以换行符结束,而旧协议发送的单个字符串总是以换行符结束。 + +ReadyForQuery('Z')消息包含一个事务状态指示器。 + +BinaryRow 与 DataRow 消息类型不再有区别;单一的 DataRow 消息类型用于返回所有格式的数据。注意,DataRow 的布局已经改变,使其更容易解析。另外,二进制值的表示方式也已改变,不再直接取决于服务器内部的表示方式。 + +新增了扩展查询子协议,其中增加了前端消息类型 Parse、Bind、Execute、Describe、Close、Flush 和 Sync,以及后端消息类型 ParseComplete、BindComplete、PortalSuspended、ParameterDescription、NoData 和 CloseComplete。现有客户端不必关注这个子协议,但使用它可能有助于改善性能或功能。 + +COPY 数据现在封装在 CopyData 和 CopyDone 消息中。COPY 期间的错误恢复已有明确定义的方法。不再需要特殊的最后一行 \.COPY OUT 期间也不再发送它。(在 COPY IN 期间仍会将它识别为终止符,但这种用法已弃用,最终将被移除。)现已支持二进制 COPY。CopyInResponse 和 CopyOutResponse 消息包含了表示列数及各列格式的字段。 + +FunctionCall 和 FunctionCallResponse 消息的布局已经改变。FunctionCall 现在支持向函数传递 NULL 参数,也可以使用文本或二进制格式传递参数和取得结果。由于它不再提供对服务器内部数据表示的直接访问,因此也不再有理由将 FunctionCall 视为潜在的安全漏洞。 + +在连接启动期间,后端会为其认为客户端库关注的所有参数发送 ParameterStatus('S')消息。之后,只要这些参数中任何一个的当前值发生变化,就会发送一条 ParameterStatus 消息。 + +RowDescription('T')消息针对所描述行中的每一列,增加了表 OID 和列编号字段,并显示各列的格式代码。 + +后端不再生成 CursorResponse('P')消息。 + +NotificationResponse('A')消息增加了一个字符串字段,可以携带 NOTIFY 事件发送者传来的载荷字符串。 + +EmptyQueryResponse('I')消息以前包含一个空字符串参数,现在已将其移除。 + + + + diff --git a/zh/9.6/queries.sgml b/zh/9.6/queries.sgml new file mode 100644 index 00000000..27721148 --- /dev/null +++ b/zh/9.6/queries.sgml @@ -0,0 +1,1685 @@ + + + + 查询 + + + 查询 + + + + SELECT + + + + 前面的章节解释了如何创建表、如何用数据填充它们,以及如何操纵这些数据。现在我们终于可以讨论如何从数据库中检索数据了。 + + + + + 概述 + + 从数据库检索数据的过程或用于检索数据的命令称为查询。在 SQL 中,命令用于指定查询。SELECT命令的一般语法为: +WITH with_queries SELECT select_list FROM table_expression sort_specification +以下各节将详细描述选择列表、表表达式和排序说明。WITH查询是一项高级特性,因此放在最后介绍。 + + + 一种简单查询的形式如下: + +SELECT * FROM table1; + + 假设存在一个名为table1的表,则这条命令会从table1中检索所有行以及所有用户定义列。(具体的检索方式取决于客户端应用。例如,psql程序会在屏幕上显示一个 ASCII 形式的表格,而客户端库则会提供从查询结果中提取单个值的函数。)选择列表说明*表示表表达式所提供的全部列。选择列表也可以只选择部分可用列,或者利用这些列进行计算。例如,如果table1有名为abc的列(还可能有其他列),那么可以写出下面的查询: + +SELECT a, b + c FROM table1; + + (假设bc都是数值数据类型)。有关更多细节请参见。 + + + + FROM table1是一种简单的表表达式:它只读取一个表。通常,表表达式可以是由基本表、连接和子查询组成的复杂结构。不过,你也可以完全省略表表达式,把SELECT命令当成计算器来使用: + +SELECT 3 * 4; + + 如果选择列表中的表达式会返回变化的结果,这种用法就更有用了。例如,你可以这样调用函数: + +SELECT random(); + + + + + + + 表表达式 + + + 表表达式 + + + + 表表达式用于计算出一个表。表表达式包含一个FROM子句,后面可以根据需要跟上WHEREGROUP BYHAVING子句。最简单的表表达式只是引用磁盘上的一个表,即所谓的基本表;但也可以使用更复杂的表达式,以多种方式修改或组合基本表。 + + + + 表表达式中可选的WHEREGROUP BYHAVING子句指定了一个连续转换的流水线,这些转换作用于由FROM子句派生出的表。所有这些转换都会生成一个虚拟表,该表提供的各行会传递给选择列表,以计算查询的输出行。 + + + + <literal>FROM</literal>子句 + + 从逗号分隔的表引用列表所给出的一个或多个其他表推导出一个表。 +FROM table_reference , table_reference , ... +表引用可以是表名(可能带模式限定),也可以是子查询、JOIN结构等推导表,或它们的复杂组合。如果FROM子句中列出了多个表引用,这些表会进行交叉连接(即形成各表行的笛卡尔积,见下文)。FROM列表的结果是一个中间虚拟表,随后可以用WHEREGROUP BYHAVING子句对它进行变换,最终得到整个表表达式的结果。 + + + ONLY + + + + 当表引用命名的是表继承层次中的父表时,除非在表名前加上关键字ONLY,否则该表引用不仅会产生该表中的行,还会产生其所有后代表中的行。不过,这种引用只会产生该命名表中出现的列 — 子表中新增的列会被忽略。 + + + + 也可以不在表名前写ONLY,而是在表名后面写上*,显式指定要包含后代表。由于这种行为是默认行为(除非你更改了配置选项的设置),写*并无必要。不过,写*或许有助于强调将会搜索额外的表。 + + + + 连接表 + + + 连接 + + + + 一个连接表是根据特定的连接类型的规则从两个其它表(真实表或生成表)中派生的表。目前支持内连接、外连接和交叉连接。一个连接表的一般语法是: + +T1 join_type T2 join_condition + + 所有类型的连接都可以被链在一起或者嵌套:T1T2都可以是连接表。在JOIN子句周围可以使用圆括号来控制连接顺序。如果不使用圆括号,JOIN子句会从左至右嵌套。 + + + + 连接类型 + + + 交叉连接 + + + 连接 + 交叉 + + + + 交叉连接 + + + + + + +T1 CROSS JOIN T2 + + + + 对来自于T1T2的行的每一种可能的组合(即笛卡尔积),连接表将包含这样一行:它由所有T1里面的列后面跟着所有T2里面的列构成。如果两个表分别有 N 和 M 行,连接表将有 N * M 行。 + + + + FROM T1 CROSS JOIN T2等效于FROM T1 INNER JOIN T2 ON TRUE(见下文)。它也等效于FROM T1,T2。 + + + 当出现两个以上的表时,后一种等价关系并不严格成立,因为JOIN的绑定强于逗号。例如FROM T1 CROSS JOIN T2 INNER JOIN T3 ON conditionFROM T1,T2 INNER JOIN T3 ON condition并不完全相同,因为第一种情况中的condition可以引用T1,而第二种情况中则不行。 + + + + + + + + 限定连接 + + + 连接 + + + + + 外连接 + + + + + +T1 { INNER | { LEFT | RIGHT | FULL } OUTER } JOIN T2 ON boolean_expression +T1 { INNER | { LEFT | RIGHT | FULL } OUTER } JOIN T2 USING ( join column list ) +T1 NATURAL { INNER | { LEFT | RIGHT | FULL } OUTER } JOIN T2 + + + + INNEROUTER在所有形式中都是可选的。INNER是默认值;LEFTRIGHTFULL表示外连接。 + + + + 连接条件ONUSING子句中指定, 或者用关键字NATURAL隐含地指定。连接条件决定来自两个源表中的哪些行是匹配的,这些我们将在后文详细解释。 + + + 限定连接有以下几种类型: + + INNER JOIN + + + + 对于 T1 的每一行 R1,生成的连接表都有一行对应 T2 中的每一个满足和 R1 的连接条件的行。 + + + + + + LEFT OUTER JOIN + + 连接 + + + + + 左连接 + + + + + + 首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。因此,生成的连接表里为来自 T1 的每一行都至少包含一行。 + + + + + + RIGHT OUTER JOIN + + 连接 + + + + + 右连接 + + + + + + 首先,执行一次内连接。然后,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。因此,生成的连接表里为来自 T2 的每一行都至少包含一行。 + + + + + + FULL OUTER JOIN + + + + 首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。同样,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。 + + + + + + + + ON子句是最常见的连接条件的形式:它接收一个和WHERE子句里用的一样的布尔值表达式。 如果两个分别来自T1T2的行在ON表达式上运算的结果为真,那么它们就算是匹配的行。 + + + + USING是个缩写符号,它允许你利用特殊的情况:连接的两端都具有相同的连接列名。它接受共享列名的一个逗号分隔列表,并且为其中每一个共享列构造一个包含等值比较的连接条件。例如用USING (a, b)连接T1T2会产生连接条件ON T1.a = T2.a AND T1.b = T2.b。 + + + + 更进一步,JOIN USING的输出会废除冗余列:不需要把匹配上的列都打印出来,因为它们必须具有相等的值。不过JOIN ON会先产生来自T1的所有列,后面跟上所有来自T2的列;而JOIN USING会先为列出的每一个列对产生一个输出列,然后先跟上来自T1的剩余列,最后跟上来自T2的剩余列。 + + + + + 连接 + 自然 + + + 自然连接 + + 最后,NATURALUSING的一种缩写形式:它会形成一个USING列表,其中包含两个输入表中共同出现的所有列名。和USING一样,这些列在输出表中只出现一次。如果没有公共列名,NATURAL JOIN的行为就与JOIN ... ON TRUE相同,产生一个叉积连接。 + + + + + + 对于被连接的关系发生列变化的情况,USING相当安全,因为只有列出的列才会被合并。NATURAL的风险则大得多,因为只要任一关系的模式变更导致出现新的同名列,连接就会把这个新列也纳入合并。 + + + + + + + + 综合起来看,假设我们有表t1: + + num | name +-----+------ + 1 | a + 2 | b + 3 | c + + 和t2: + + num | value +-----+------- + 1 | xxx + 3 | yyy + 5 | zzz + + 然后我们用不同的连接方式可以获得各种结果: + +=> SELECT * FROM t1 CROSS JOIN t2; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 1 | a | 3 | yyy + 1 | a | 5 | zzz + 2 | b | 1 | xxx + 2 | b | 3 | yyy + 2 | b | 5 | zzz + 3 | c | 1 | xxx + 3 | c | 3 | yyy + 3 | c | 5 | zzz +(9 rows) + +=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 3 | c | 3 | yyy +(2 rows) + +=> SELECT * FROM t1 INNER JOIN t2 USING (num); + num | name | value +-----+------+------- + 1 | a | xxx + 3 | c | yyy +(2 rows) + +=> SELECT * FROM t1 NATURAL INNER JOIN t2; + num | name | value +-----+------+------- + 1 | a | xxx + 3 | c | yyy +(2 rows) + +=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 2 | b | | + 3 | c | 3 | yyy +(3 rows) + +=> SELECT * FROM t1 LEFT JOIN t2 USING (num); + num | name | value +-----+------+------- + 1 | a | xxx + 2 | b | + 3 | c | yyy +(3 rows) + +=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 3 | c | 3 | yyy + | | 5 | zzz +(3 rows) + +=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 2 | b | | + 3 | c | 3 | yyy + | | 5 | zzz +(4 rows) + + + + + 用ON指定的连接条件也可以包含与连接不直接相关的条件。这种功能可能对某些查询很有用,但是需要我们仔细想清楚。例如: + +=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx'; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx + 2 | b | | + 3 | c | | +(3 rows) + + 注意把限制放在WHERE子句中会产生不同的结果: + +=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx'; + num | name | num | value +-----+------+-----+------- + 1 | a | 1 | xxx +(1 row) + + 这是因为放在ON子句中的约束会在连接之前处理,而放在WHERE子句中的约束则会在连接之后处理。这对内连接无关紧要,但对外连接影响很大。 + + + + + 表和列别名 + + + 别名 + 在FROM子句中 + + + + 标签 + 别名 + + + + 你可以给表以及复杂的表引用指定一个临时名字,以便在查询的其余部分引用该派生表。这被称为表别名。 + + + + 要创建一个表别名,我们可以写: + +FROM table_reference AS alias + + 或者 + +FROM table_reference alias + + AS关键字只是可选的语法噪音。alias可以是任意标识符。 + + + + 表别名的典型应用是给长表名赋予比较短的标识符, 好让连接子句更易读。例如: + +SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num; + + + + 对于当前查询而言,别名成为该表引用的新名称—不允许在查询的其他地方通过原名称引用该表。因此,以下写法无效: +SELECT * FROM my_table AS m WHERE my_table.a > 5; -- wrong + + + + + 表别名主要用于简化符号,但是当把一个表连接到它自身时必须使用别名,例如: + +SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id; + + 此外,当表引用是子查询时也必须使用别名(参见)。 + + + + 圆括弧用于解决歧义。在下面的示例中,第一个语句将把别名b赋给my_table的第二个实例,但是第二个语句把别名赋给连接的结果: + +SELECT * FROM my_table AS a CROSS JOIN my_table AS b ... +SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ... + + + + + 另外一种给表指定别名的形式是给表的列赋予临时名字,就像给表本身指定别名一样: + +FROM table_reference AS alias ( column1 , column2 , ... ) + + 如果指定的列别名比表里实际的列少,那么剩下的列就没有被重命名。这种语法对于自连接或子查询特别有用。 + + + + 如果用这些形式中的任何一种给一个JOIN子句的输出附加了一个别名, 那么该别名就在JOIN的作用下隐去了其原始的名字。例如: + +SELECT a.* FROM my_table AS a JOIN your_table AS b ON ... + + 是合法 SQL,但是: + +SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c + + 是不合法的:表别名a在别名c外面是看不到的。 + + + + + + 子查询 + + + 子查询 + + + + 指定派生表的子查询必须用圆括号括起来,并且必须赋予一个表别名(参见)。例如: + +FROM (SELECT * FROM table1) AS alias_name + + + + + 这个示例等效于FROM table1 AS alias_name。更有趣的情况是在子查询里面有分组或聚合的时候, 子查询不能被简化为一个简单的连接。 + + + + 一个子查询也可以是一个VALUES列表: + +FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow')) + AS names(first, last) + + 同样,表别名是必须的。为VALUES列表中的列分配别名是可选的,但这是一个好习惯。更多信息可参见。 + + + + + 表函数 + + 表函数 + + + 函数 + 在FROM子句中 + + + + 表函数是那些生成行集合的函数,这些行可以由基础类型(标量类型)组成,也可以由复合数据类型(表行)组成。它们在查询的FROM子句中用法类似于表、视图或子查询。表函数返回的列可以像表、视图或子查询的列一样,出现在SELECTJOINWHERE子句中。 + + + + 也可以使用ROWS FROM语法把多个表函数组合起来,并以并行列的形式返回结果;这种情况下,结果行数等于返回行数最多的那个函数的结果行数,较小的结果会用空值填充到相同长度。 + + + +function_call WITH ORDINALITY AS table_alias (column_alias , ... ) +ROWS FROM( function_call , ... ) WITH ORDINALITY AS table_alias (column_alias , ... ) + + + + 如果指定了WITH ORDINALITY子句,一个额外的 + bigint类型的列将会被增加到函数的结果列中。这个列对 + 函数结果集的行进行编号,编号从 1 开始(这是对 SQL 标准语法 + UNNEST ... WITH ORDINALITY的一般化)。默认情 + 况下,序数列被称为ordinality,但也可以通过使用一个 + AS子句给它分配一个不同的列名。 + + + + 调用特殊的表函数UNNEST可以使用任意数量的数组参数, + 它会返回对应的列数,就好像在每一个参数上单独调用 + UNNEST)并且使用 + ROWS FROM结构把它们组合起来。 + + + +UNNEST( array_expression , ... ) WITH ORDINALITY AS table_alias (column_alias , ... ) + + + + 如果没有指定table_alias,该函数名将被用作 + 表名。在ROWS FROM()结构的情况中,会使用第一个函数名。 + + + + 如果没有提供列的别名,那么对于一个返回基础类型的函数,列名也与该函数 + 名相同。对于一个返回复合类型的函数,结果列会从该类型的属性得到名称。 + + + + 示例: + +CREATE TABLE foo (fooid int, foosubid int, fooname text); + +CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ + SELECT * FROM foo WHERE fooid = $1; +$$ LANGUAGE SQL; + +SELECT * FROM getfoo(1) AS t1; + +SELECT * FROM foo + WHERE foosubid IN ( + SELECT foosubid + FROM getfoo(foo.fooid) z + WHERE z.fooid = foo.fooid + ); + +CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); + +SELECT * FROM vw_getfoo; + + + + + 有时候,定义一个能够根据调用方式返回不同列集合的表函数很有用。为支持这一点,表函数可以被声明为返回不带OUT参数的伪类型record。当此类函数在查询中使用时,必须在查询本身中指定预期的行结构,这样系统才能知道如何分析和规划该查询。这种语法如下: + + + +function_call AS alias (column_definition , ... ) +function_call AS alias (column_definition , ... ) +ROWS FROM( ... function_call AS (column_definition , ... ) , ... ) + + + + 在不使用ROWS FROM()语法时, + column_definition列表会取代原本可附加到 + FROM项上的列别名列表,列定义中的名称就起到列别名的作用。 + 在使用ROWS FROM()语法时, + 可以为每一个成员函数单独附着一个 + column_definition列表;或者在只有一个成员 + 函数并且没有WITH ORDINALITY子句的情况下,可以在 + ROWS FROM()后面写一个 + column_definition列表来取代一个列别名列表。 + + + + 考虑下面的示例: + +SELECT * + FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc') + AS t1(proname name, prosrc text) + WHERE proname LIKE 'bytea%'; + + 函数(模块的一部分)执行远程查询。它被声明为返回record,因为它可能用于任意类型的查询。实际的列集必须在调用它的查询中指定,这样分析器才知道像*这样的写法应当扩展成什么。 + + + + 此示例使用ROWS FROM: + +SELECT * +FROM ROWS FROM + ( + json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]') + AS (a INTEGER, b TEXT), + generate_series(1, 3) + ) AS x (p, q, s) +ORDER BY p; + + p | q | s +-----+-----+--- + 40 | foo | 1 + 100 | bar | 2 + | | 3 + + 它把两个函数组合成一个FROM目标。json_to_recordset()被指定返回两列,第一列为integer,第二列为textgenerate_series()的结果则直接使用。ORDER BY子句会把列值按整数排序。 + + + + + <literal>LATERAL</literal>子查询 + + + LATERAL + 在FROM子句中 + + + + 可以在出现于FROM中的子查询前放置关键词LATERAL。这允许它们引用前面的FROM项提供的列(如果没有LATERAL,每一个子查询将被独立计算,并且因此不能被其他FROM项交叉引用)。 + + + + 出现在FROM中的表函数的前面也可以被放上关键词LATERAL,但对于函数该关键词是可选的,在任何情况下函数的参数都可以包含对前面的FROM项提供的列的引用。 + + + + LATERAL项既可以出现在FROM列表的顶层, + 也可以出现在JOIN树中。在后一种情况下,它还可以引用 + 它所处右侧JOIN左边的任何项。 + + + + 当FROM项包含LATERAL交叉引用时,求值过程如下:对于提供被引用列的FROM项中的每一行,或者对于多个FROM项共同提供这些列时对应的每一组行,都会用该行或该组行中的列值来计算LATERAL项。得到的结果行再像通常那样与生成它们的行做连接。对于源表中的每一行或每一组行,这一过程都会重复。 + + + + LATERAL的一个简单示例: + +SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss; + + 这不是非常有用,因为它和一种更简单的形式得到的结果完全一样: + +SELECT * FROM foo, bar WHERE bar.id = foo.bar_id; + + 在必须要使用交叉引用列来计算那些即将要被连接的行时,LATERAL是最有用的。一种常用的应用是为一个返回集合的函数提供一个参数值。例如,假设vertices(polygon)返回一个多边形的顶点集合,我们可以这样标识存储在一个表中的多边形中靠近的顶点: + +SELECT p1.id, p2.id, v1, v2 +FROM polygons p1, polygons p2, + LATERAL vertices(p1.poly) v1, + LATERAL vertices(p2.poly) v2 +WHERE (v1 <-> v2) < 10 AND p1.id != p2.id; + + 这个查询也可以被写成: + +SELECT p1.id, p2.id, v1, v2 +FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1, + polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2 +WHERE (v1 <-> v2) < 10 AND p1.id != p2.id; + + 或者写成其他几种等价的形式。(正如前面提到的,这个示例中的LATERAL关键字并非必需,但这里写出来是为了更清楚。) + + + + 把LEFT JOIN用于LATERAL子查询往往特别方便,这样即使LATERAL子查询没有为源行产生任何结果行,源行也仍会出现在结果中。例如,如果get_product_names()返回某个制造商生产的产品名称,而我们表中的某些制造商当前没有生产任何产品,就可以像下面这样找出它们: + +SELECT m.name +FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true +WHERE pname IS NULL; + + + + + + + <literal>WHERE</literal>子句 + + + WHERE + + + 的语法是: +WHERE search_condition +其中,search_condition可以是任意值表达式(见),只要其返回值的类型为boolean。 + + + + 在完成FROM子句的处理之后,生成的虚拟表中的每一行都会根据搜索条件进行检查。如果条件结果为真,该行就会保留在输出表中;否则(也就是说,如果结果为假或空)就会被丢弃。搜索条件通常至少会引用FROM子句生成的表中的某一列;这并非必须,但若不引用这些列,WHERE子句通常就没什么用了。 + + + + + + 内连接的连接条件既可以写在WHERE子句也可以写在JOIN子句里。例如,这些表表达式是等效的: + +FROM a, b WHERE a.id = b.id AND b.val > 5 + + 和: + +FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5 + + 或者可能还有: + +FROM a NATURAL JOIN b WHERE b.val > 5 + + 选择其中哪一种主要只是风格问题。FROM子句中的JOIN语法虽然属于 SQL 标准,但在其他 SQL 数据库管理系统中可能没有那么容易移植。对于外连接则没有选择:它们必须放在FROM子句中完成。外连接的ONUSING子句与WHERE条件并不等价,因为它除了会在最终结果中移除行之外,还会为不匹配的输入行增加结果行。 + + + + 下面是一些WHERE子句的例子: +SELECT ... FROM fdt WHERE c1 > 5 + +SELECT ... FROM fdt WHERE c1 IN (1, 2, 3) + +SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2) + +SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) + +SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100 + +SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1) + + fdt是由FROM子句推导得到的表。不满足WHERE子句搜索条件的行会从fdt中消除。注意,这里将标量子查询用作值表达式。与其他查询一样,子查询也可以使用复杂的表表达式。还要注意子查询中对fdt的引用方式。将c1限定为fdt.c1仅在c1也是子查询推导输入表中的列名时才有必要。不过,即使不必限定,限定列名仍能使含义更清晰。本例说明了外层查询的列命名作用域如何扩展到其内部查询。 + + + + + <literal>GROUP BY</literal>和<literal>HAVING</literal>子句 + + + GROUP BY + + + + 分组 + + + + 通过WHERE过滤之后,派生出来的输入表还可以使用GROUP BY子句进行分组,并通过HAVING子句排除某些分组行。 + + + +SELECT select_list + FROM ... + WHERE ... + GROUP BY grouping_column_reference , grouping_column_reference... + + + 用于把表中所有列出列的值均相同的行分到同一组。列的列出顺序没有影响。其效果是把具有相同值的每一组行合并为一个分组行,由它代表组内的所有行。这样可以消除输出中的冗余,或者计算应用于各组的聚合。例如: +=> SELECT * FROM test1; + x | y +---+--- + a | 3 + c | 2 + b | 5 + a | 1 +(4 rows) + +=> SELECT x FROM test1 GROUP BY x; + x +--- + a + b + c +(3 rows) + + + + + 在第二个查询里,我们不能写成SELECT * FROM test1 GROUP BY x, 因为列y里没有哪个值可以和每个组相关联起来。被分组的列可以在选择列表中引用是因为它们在每个组都有单一的值。 + + + + 通常,如果一个表被分了组,那么没有在GROUP BY中列出的列都不能被引用,除非在聚合表达式中被引用。 一个用聚合表达式的示例是: + +=> SELECT x, sum(y) FROM test1 GROUP BY x; + x | sum +---+----- + a | 4 + b | 5 + c | 2 +(3 rows) + + 这里的sum是一个聚合函数,它在整个组上计算出一个单一值。有关可用的聚合函数的更多信息可以在。 + + + + + + 不带聚合表达式的分组实际上是在计算某一列的非重复值集合。这也可以用DISTINCT子句实现(参阅)。 + + + + + 这里是另外一个示例:它计算每种产品的总销售额(而不是所有产品的总销售额): + +SELECT product_id, p.name, (sum(s.units) * p.price) AS sales + FROM products p LEFT JOIN sales s USING (product_id) + GROUP BY product_id, p.name, p.price; + + 在这个示例里,列product_idp.namep.price必须在GROUP BY子句里, 因为它们都在查询的选择列表里被引用到(但见下文)。列s.units不必在GROUP BY列表里,因为它只是在一个聚合表达式(sum(...))里使用,它代表一组产品的销售额。对于每种产品,这个查询都返回一个该产品的所有销售额的总和行。 + + + 函数依赖 + + + 如果 products 表的设计使得product_id是主键,那么在上面的示例中只按product_id分组就足够了,因为名称和价格都函数依赖于产品 ID,因此对于每个产品 ID 分组该返回哪个名称和价格值不会有歧义。 + + + + 在严格的 SQL 里,GROUP BY只能对源表的列进行分组,但PostgreSQL把这个扩展为也允许GROUP BY去根据选择列表中的列分组。也允许对值表达式进行分组,而不仅是简单的列名。 + + + + HAVING + + + + 如果一个表已经用GROUP BY子句分了组,而你只对其中某些组感兴趣,那么就可以使用HAVING子句。它很像WHERE子句,用于从结果中删除某些组。其语法是: + +SELECT select_list FROM ... WHERE ... GROUP BY ... HAVING boolean_expression + + 在HAVING子句中的表达式可以引用分组的表达式和未分组的表达式(后者必须涉及一个聚合函数)。 + + + + 示例: + +=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3; + x | sum +---+----- + a | 4 + b | 5 +(2 rows) + +=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c'; + x | sum +---+----- + a | 4 + b | 5 +(2 rows) + + + + + 再次,一个更现实的示例: + +SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit + FROM products p LEFT JOIN sales s USING (product_id) + WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks' + GROUP BY product_id, p.name, p.price, p.cost + HAVING sum(p.price * s.units) > 5000; + + 在上面的示例里,WHERE子句通过一个未分组的列来选择数据行(该表达式仅对最近四周内发生的销售为真),而HAVING子句则把输出限制为总销售收入超过 5000 的组。请注意,聚合表达式不必在查询的所有部分都完全相同。 + + + + 如果一个查询包含聚合函数调用,但没有GROUP BY子句,分组仍然会发生:结果是一个单一组行(如果该单一组行随后又被HAVING消除,则可能根本没有结果行)。如果查询包含HAVING子句,即使没有任何聚合函数调用或GROUP BY子句,情况也是如此。 + + + + + <literal>GROUPING SETS</literal>、<literal>CUBE</literal>和<literal>ROLLUP</literal> + + + GROUPING SETS + + + CUBE + + + ROLLUP + + + + 使用分组集的概念可以实现比上述更加复杂的分组操作。由 + FROMWHERE子句选出的数据被按照每一个指定 + 的分组集单独分组,按照简单GROUP BY子句对每一个分组计算 + 聚合,然后返回结果。例如: + +=> SELECT * FROM items_sold; + brand | size | sales +-------+------+------- + Foo | L | 10 + Foo | M | 20 + Bar | M | 15 + Bar | L | 5 +(4 rows) + +=> SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ()); + brand | size | sum +-------+------+----- + Foo | | 30 + Bar | | 20 + | L | 15 + | M | 35 + | | 50 +(5 rows) + + + + + GROUPING SETS的每一个子列表可以指定一个或者多个列或者表达式, + 它们将按照直接出现在GROUP BY子句中同样的方式被解释。一个空的 + 分组集表示所有的行都要被聚合到一个单一分组(即使没有输入行存在也会被输出) + 中,这就像前面所说的没有GROUP BY子句的聚合函数的情况一样。 + + + + 对于分组列或表达式没有出现在其中的分组集的结果行,对分组列或表达式的引用会 + 被空值所替代。要区分一个特定的输出行来自于哪个分组,请见 + 。 + + + + PostgreSQL 中提供了一种简化方法来指定两种常用类型的分组集。下面形式的子句 + +ROLLUP ( e1, e2, e3, ... ) + + 表示给定的表达式列表及其所有前缀(包括空列表),因此它等效于 + +GROUPING SETS ( + ( e1, e2, e3, ... ), + ... + ( e1, e2 ), + ( e1 ), + ( ) +) + + 这通常被用来分析历史数据,例如按部门、区和公司范围计算的总薪水。 + + + + 下面形式的子句 + +CUBE ( e1, e2, ... ) + + 表示给定的列表及其可能的子集(即幂集)。因此 + +CUBE ( a, b, c ) + + 等效于 + +GROUPING SETS ( + ( a, b, c ), + ( a, b ), + ( a, c ), + ( a ), + ( b, c ), + ( b ), + ( c ), + ( ) +) + + + + + CUBEROLLUP子句中的元素可以是表达式或者 + 圆括号中的元素子列表。在后一种情况下,为了生成各个分组集,这些子列表会被当作单一单元处理。例如: + +CUBE ( (a, b), (c, d) ) + + 等效于 + +GROUPING SETS ( + ( a, b, c, d ), + ( a, b ), + ( c, d ), + ( ) +) + + 并且 + +ROLLUP ( a, (b, c), d ) + + 等效于 + +GROUPING SETS ( + ( a, b, c, d ), + ( a, b, c ), + ( a ), + ( ) +) + + + + + CUBEROLLUP可以被直接用在 + GROUP BY子句中,也可以被嵌套在一个 + GROUPING SETS子句中。如果一个 + GROUPING SETS子句被嵌套在另一个同类子句中, + 效果和把内层子句的所有元素直接写在外层子句中一样。 + + + + 如果在一个GROUP BY子句中指定了多个分组项,那么最终的 + 分组集列表是这些项的笛卡尔积。例如: + +GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e)) + + 等效于 + +GROUP BY GROUPING SETS ( + (a, b, c, d), (a, b, c, e), + (a, b, d), (a, b, e), + (a, c, d), (a, c, e), + (a, d), (a, e) +) + + + + + + + 在表达式中,结构(a, b)通常会被识别为一个行构造器。在 + GROUP BY子句中,这不会在表达式的顶层应用,并且 + (a, b)会按照上面所说的被解析为一个表达式的列表。如果出于 + 某种原因你在分组表达式中需要一个行构造器,请使用 + ROW(a, b)。 + + + + + + 窗口函数处理 + + + 窗口函数 + 执行顺序 + + + + 如果查询包含任何窗口函数(见),这些函数将在任何分组、聚合和HAVING过滤被执行之后被计算。也就是说如果查询使用了任何聚合、GROUP BYHAVING,则窗口函数看到的行是分组行而不是来自于FROM/WHERE的原始表行。 + + + 当使用多个窗口函数时,窗口定义中具有语法等价的PARTITION BYORDER BY子句的所有窗口函数,都保证在数据的一次遍历中求值。因此,即使ORDER BY不能唯一确定排序顺序,它们看到的排序顺序也相同。不过,对于具有不同PARTITION BYORDER BY定义的函数,其求值不作保证。(在这种情况下,窗口函数的各次求值遍历之间通常需要排序步骤,而排序不保证保留那些在其ORDER BY看来等价的行的顺序。) + + + 目前,窗口函数总是要求输入数据预先排好序,因此查询输出总会按照某个窗口函数的PARTITION BY/ORDER BY子句排序。不过,不建议依赖这一点。如果你希望确保结果按特定方式排序,请显式使用顶层的ORDER BY子句。 + + + + + + + 选择列表 + + + SELECT + 选择列表 + + + + 如前面几节所示,SELECT命令中的表表达式会构造出一个中间虚拟表,其中可能涉及组合表和视图、消除行、分组等操作。这个表随后交由选择列表处理。选择列表决定中间表中的哪些会实际输出。 + + + + + 选择列表项 + + + * + + + + 最简单的选择列表是*,它会输出表表达式生成的所有列。否则,选择列表就是一个逗号分隔的值表达式列表(定义见)。例如,它可以是一个列名列表: + +SELECT a, b, c FROM ... + + 列名abc要么是FROM子句中所引用表的实际列名,要么是像所述为它们指定的别名。选择列表中可用的名字空间与WHERE子句相同;如果使用了分组,则与HAVING子句相同。 + + + + 如果超过一个表有同样的列名,那么你还必须给出表名字,如: + +SELECT tbl1.a, tbl2.a, tbl1.b FROM ... + + 在使用多个表时,要求一个特定表的所有列也是有用的: + +SELECT tbl1.*, tbl2.a FROM ... + + 更多有关table_name.*记号的内容请参考。 + + + + 如果在选择列表中使用任意值表达式,那么从概念上说,它会在返回表中增加一个新的虚拟列。该值表达式会对每个结果行计算一次,并用该行中的值替换其中的列引用。不过,选择列表中的表达式并不一定要引用FROM子句表表达式中的列;例如,它也可以是任意常量算术表达式。 + + + + + 列标签 + + + 别名 + 在选择列表中 + + + + 选择列表中的项可以被赋予名字,以供后续处理使用,例如在ORDER BY子句中引用,或者供客户端应用显示。例如: + +SELECT a AS value, b + c AS sum FROM ... + + + + + 如果没有使用AS指定输出列名,系统就会分配一个默认列名。对于简单的列引用,它就是被引用列的名字;对于函数调用,它就是函数的名字;对于复杂表达式,系统则会生成一个通用名称。 + + + AS关键字是可选的,但前提是新列名不能与任何PostgreSQL关键字相同(见)。为了避免意外使用关键字,可以用双引号括住列名。例如,VALUE是关键字,因此下面的写法无效: +SELECT a value, b + c AS sum FROM ... +但下面的写法有效: +SELECT a "value", b + c AS sum FROM ... +为防范将来可能新增的关键字,建议始终写出AS,或者用双引号括住输出列名。 + + + + + 输出列的命名和在FROM子句里的命名是不一样的 (参阅)。 它实际上允许你对同一个列命名两次,但是在选择列表中分配的名字是要传递下去的名字。 + + + + + + <literal>DISTINCT</literal> + + + DISTINCT + + + + 重复 + + + + 在处理完选择列表之后,结果表还可以选择性地删除重复行。我们可以直接在SELECT后面写上DISTINCT关键字来指定: + +SELECT DISTINCT select_list ... + + (如果不用DISTINCT,则可以用ALL关键字来显式指定保留所有行这一默认行为。) + + + + 空值 + 在 DISTINCT 中 + + + + 显然,如果两行至少在一个列值上不同,就认为它们是不同的。空值在这种比较中被认为是相等的。 + + + + 另外,我们还可以用任意表达式来判断什么行可以被认为是可区分的: + +SELECT DISTINCT ON (expression , expression ...) select_list ... + + 这里的expression是一个对所有行求值的任意值表达式。如果某一组行在这些表达式上的值都相等,那么它们就会被视为重复行,因此只保留该组中的第一行。请注意,某一组里的第一行是不可预测的,除非查询在足够多的列上进行了排序,以保证到达DISTINCT过滤器的行顺序是唯一的。(DISTINCT ON的处理发生在ORDER BY排序之后。) + + + + DISTINCT ON子句不是 SQL 标准的一部分,有时会被认为风格不佳,因为它的结果可能具有不确定性。通过审慎使用GROUP BY以及FROM中的子查询,可以避免使用这种结构,但它往往是最方便的替代方案。 + + + + + + + 组合查询 + + + UNION + + + INTERSECT + + + EXCEPT + + + 集合并 + + + 集合交 + + + 集合差 + + + 集合操作 + + + + 两个查询的结果可以用集合操作并、交、差进行组合。语法是 + +query1 UNION ALL query2 +query1 INTERSECT ALL query2 +query1 EXCEPT ALL query2 + + query1query2都是可以使用以上所有特性的查询。 + + + + UNION有效地把query2的结果附加到query1的结果上(不过我们不能保证这就是这些行实际被返回的顺序)。此外,它将删除结果中所有重复的行, 就象DISTINCT做的那样,除非你使用了UNION ALL。 + + + + INTERSECT返回那些同时存在于query1query2的结果中的行,除非声明了INTERSECT ALL, 否则所有重复行都被消除。 + + + + EXCEPT返回所有在query1结果中但不在query2结果中的行。(这有时被称为两个查询的。)同样,除非使用了EXCEPT ALL,否则重复行都会被消除。 + + + + 为了计算两个查询的并、交、差,这两个查询必须是并操作兼容的,也就意味着它们都返回同样数量的列, 并且对应的列有兼容的数据类型,如中描述的那样。 + + + + 集合操作可以组合使用,例如: + + query1 UNION query2 EXCEPT query3 + + 等价于: + + (query1 UNION query2) EXCEPT query3 + + 如这里所示,你可以使用括号控制求值顺序。如果没有括号,UNIONEXCEPT按从左到右结合,但INTERSECT的绑定强于这两个操作符。因此 + + query1 UNION query2 INTERSECT query3 + + 意思是 + + query1 UNION (query2 INTERSECT query3) + + 你也可以用括号包裹一个单独的query。如果query需要使用后续部分中讨论的任何子句(例如LIMIT),那么这一点非常重要。没有括号,将会得到语法错误,否则子句将被解析为适用于集合操作的输出,而不是它的输入之一。例如, + + SELECT a FROM b UNION SELECT x FROM y LIMIT 10 + + 是可以接受的,但它的意思是 + + (SELECT a FROM b UNION SELECT x FROM y) LIMIT 10 + + 不是 + + SELECT a FROM b UNION (SELECT x FROM y LIMIT 10) + + + + + + + 行排序 + + + 排序 + + + + ORDER BY + + + + 当一个查询生成输出表之后(也就是处理完选择列表之后),还可以选择对其排序。如果没有选择排序,行将按未指定的顺序返回。这时的实际顺序取决于扫描和连接计划类型以及磁盘上的物理顺序,但绝不能依赖这些因素。只有显式选择排序步骤,才能保证特定的输出顺序。 + + + + ORDER BY子句指定了排序顺序: + +SELECT select_list + FROM table_expression + ORDER BY sort_expression1 ASC | DESC NULLS { FIRST | LAST } + , sort_expression2 ASC | DESC NULLS { FIRST | LAST } ... + + 排序表达式可以是任何在查询选择列表中合法的表达式。例如: + +SELECT a, b FROM table1 ORDER BY a + b, c; + + 当指定了多个表达式时,后面的值将用于对那些在前面值上相等的行继续排序。每个表达式后面都可以选择性地写上ASCDESC关键字,以指定升序或降序排序方向。ASC是默认值。升序会把较小的值排在前面,而较小是由<操作符定义的。类似地,降序则由>操作符定义。 + + + 事实上,PostgreSQL会使用该表达式数据类型的默认 B-树操作符类来决定ASCDESC的排序顺序。按惯例,数据类型会被设置成由<>操作符来对应这种排序顺序,不过用户定义数据类型的设计者也可以选择不同的做法。 + + + + + + NULLS FIRSTNULLS LAST选项可用于决定空值在排序顺序中是位于非空值之前还是之后。默认情况下,空值排序时被认为大于任何非空值;也就是说,在DESC顺序下默认是NULLS FIRST,否则默认是NULLS LAST。 + + + + 注意顺序选项是对每一个排序列独立考虑的。例如ORDER BY x, y DESC表示ORDER BY x ASC, y DESC,而和ORDER BY x DESC, y DESC不同。 + + + 一个sort_expression也可以是输出列的列标签或编号,例如: +SELECT a + b AS sum, c FROM table1 ORDER BY sum; +SELECT a, max(b) FROM table1 GROUP BY a ORDER BY 1; +这两条查询都按第一个输出列排序。注意,输出列名必须单独出现,不能用于表达式中—例如,下面的写法是正确的: +SELECT a + b AS sum, c FROM table1 ORDER BY sum + c; -- wrong +这一限制是为了减少歧义。如果ORDER BY项是一个简单名称,既可能匹配输出列名,也可能匹配表表达式中的列,那么仍会有歧义。这种情况下使用输出列。只有在使用AS将输出列重命名为其他表列的名称时,才会造成这种混淆。 + + + ORDER BY可以被应用于UNIONINTERSECTEXCEPT组合的结果,但是在这种情况中它只被允许根据输出列名或编号排序,而不能根据表达式排序。 + + + + + + <literal>LIMIT</literal>和<literal>OFFSET</literal> + + + LIMIT + + + + OFFSET + + + + LIMITOFFSET允许仅检索查询其余部分所生成行的一部分: +SELECT select_list + FROM table_expression + ORDER BY ... + LIMIT { number | ALL } OFFSET number + + + + + 如果给出了一个限制计数,那么会返回数量不超过该限制的行(但可能更少些,因为查询本身可能生成的行数就比较少)。LIMIT ALL的效果和省略LIMIT子句一样,就像是LIMIT带有 NULL 参数一样。 + + + + OFFSET说明在开始返回结果行之前要跳过多少行。OFFSET 0的效果与省略OFFSET子句相同,OFFSET带有 NULL 参数时也是如此。 + + + + 如果OFFSETLIMIT都出现,那么在开始返回LIMIT行之前,会先跳过OFFSET行。 + + + + 如果使用LIMIT,那么用一个ORDER BY子句把结果行约束成一个唯一的顺序是很重要的。否则你就会拿到一个不可预料的该查询的行的子集。你要的可能是第十到第二十行,但以什么顺序的第十到第二十?除非你指定了ORDER BY,否则顺序是不知道的。 + + + + 查询优化器在生成查询计划时会考虑LIMIT,因此如果你给定LIMITOFFSET,那么你很可能收到不同的规划(产生不同的行顺序)。因此,使用不同的LIMIT/OFFSET值选择查询结果的不同子集将生成不一致的结果,除非你用ORDER BY强制一个可预测的顺序。这并非bug, 这是一个很自然的结果,因为 SQL 没有许诺把查询的结果按照任何特定的顺序发出,除非用了ORDER BY来约束顺序。 + + + + 被OFFSET子句跳过的行仍然需要在服务器内部计算;因此,较大的OFFSET可能效率不高。 + + + + + + <literal>VALUES</literal>列表 + + + VALUES + + + + VALUES提供了一种生成常量表的方法,可以在查询中使用,而不必在磁盘上实际创建并填充表。语法为: +VALUES ( expression [, ...] ) [, ...] +每个括号括住的表达式列表都会生成表中的一行。所有列表必须具有相同数量的元素(即表中的列数),并且各列表中对应项的数据类型必须兼容。为结果中各列分配的实际数据类型,使用与UNION相同的规则确定(见)。 + + + + 一个示例: + +VALUES (1, 'one'), (2, 'two'), (3, 'three'); + + + 将会返回一个有两列三行的表。它实际上等效于: + +SELECT 1 AS column1, 'one' AS column2 +UNION ALL +SELECT 2, 'two' +UNION ALL +SELECT 3, 'three'; + + + 在默认情况下,PostgreSQLcolumn1column2等名字分配给一个VALUES表的列。这些列名不是由SQL标准指定的,并且不同的数据库系统的做法也不同,因此通常最好使用表别名列表来重写这些默认的名字,像这样: + +=> SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter); + num | letter +-----+-------- + 1 | one + 2 | two + 3 | three +(3 rows) + + + + + 从语法上说,后面跟有表达式列表的VALUES列表被视为等同于 + +SELECT select_list FROM table_expression + + 一样,并且可以出现在SELECT能出现的任何地方。例如,你可以把它用作UNION的一部分,或者附加一个sort_specificationORDER BYLIMIT和/或OFFSET)给它。VALUES最常见的用途是作为一个INSERT命令的数据源,以及作为一个子查询。 + + + + 更多信息请见。 + + + + + + + <literal>WITH</literal>查询(公共表表达式) + + + WITH + 在 SELECT 中 + + + + 公共表表达式 + WITH + + + + WITH提供了一种为更大查询编写辅助语句的方法。这些语句通常被称为公共表表达式或CTE,可以视为仅在单个查询期间存在的临时表定义。WITH子句中的每个辅助语句都可以是SELECTINSERTUPDATEDELETE;而WITH子句本身则附加到一个主语句上,该主语句同样可以是SELECTINSERTUPDATEDELETE。 + + + + <literal>WITH</literal>中的<command>SELECT</command> + + SELECT用于WITH的基本价值是把复杂查询分解成更简单的部分。例如: +WITH regional_sales AS ( + SELECT region, SUM(amount) AS total_sales + FROM orders + GROUP BY region +), top_regions AS ( + SELECT region + FROM regional_sales + WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales) +) +SELECT region, + product, + SUM(quantity) AS product_units, + SUM(amount) AS product_sales +FROM orders +WHERE region IN (SELECT region FROM top_regions) +GROUP BY region, product; +这条查询只显示销售额最高的几个地区中各产品的销售总额。WITH子句定义了两个辅助语句,名为regional_sales和top_regions,其中,regional_sales的输出用于top_regions,而top_regions的输出用于主SELECT查询。本例也可以不用WITH来编写,但那样就需要两层嵌套的子SELECT。采用这种写法更容易理解一些。 + + 可选的RECURSIVE修饰符把WITH从单纯的语法便利变成一项能够完成标准 SQL 中原本无法完成之事的特性。使用RECURSIVE后,WITH查询可以引用自己的输出。一个非常简单的例子是以下查询,它计算 1 到 100 的整数之和: +WITH RECURSIVE t(n) AS ( + VALUES (1) + UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; +递归WITH查询的一般形式始终是一个非递归项,接着是UNION(或UNION ALL),然后是一个递归项,其中只有递归项才能包含对查询自身输出的引用。这样的查询按以下方式执行: + + + + 递归查询求值 + + + + + 计算非递归项。对UNION(但不对UNION ALL),抛弃重复行。把所有剩余的行包括在递归查询的结果中,并且也把它们放在一个临时的工作表中。 + + + + + + + 只要工作表不为空,重复下列步骤: + + + + + + 计算递归项,用当前工作表的内容替换递归自引用。对UNION(不是UNION ALL),抛弃重复行以及那些与之前结果行重复的行。将剩下的所有行包括在递归查询的结果中,并且也把它们放在一个临时的中间表中。 + + + + + + + 用中间表的内容替换工作表的内容,然后清空中间表。 + + + + + + + + + + 严格地说,这个过程是迭代而非递归,但RECURSIVE是 SQL 标准委员会选定的术语。 + + + + + 在上面的示例中,工作表在每一步都只有一行,并且在连续步骤中依次取值 1 到 100。到了第 100 步,由于WHERE子句不再产生输出,因此查询终止。 + + + 递归查询通常用于处理层次结构或树形结构数据。以下是一个有用的例子:只给定一个表示直接包含关系的表,查询找出产品的所有直接和间接子部件: +WITH RECURSIVE included_parts(sub_part, part, quantity) AS ( + SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product' + UNION ALL + SELECT p.sub_part, p.part, p.quantity + FROM included_parts pr, parts p + WHERE p.part = pr.sub_part +) +SELECT sub_part, SUM(quantity) as total_quantity +FROM included_parts +GROUP BY sub_part + + + + 使用递归查询时,必须确保查询的递归部分最终会不返回任何元组,否则查询会无限循环。有时,可以用UNION代替UNION ALL,通过丢弃与先前输出行重复的行来做到这一点。不过,循环往往并不涉及完全重复的输出行:可能需要只检查一个或几个字段,以判断是否曾到达同一点。处理这种情况的标准方法是计算一个包含已访问值的数组。例如,考虑以下查询,它搜索表graph,使用其中的link字段: +WITH RECURSIVE search_graph(id, link, data, depth) AS ( + SELECT g.id, g.link, g.data, 1 + FROM graph g + UNION ALL + SELECT g.id, g.link, g.data, sg.depth + 1 + FROM graph g, search_graph sg + WHERE g.id = sg.link +) +SELECT * FROM search_graph; +如果link关系包含循环,此查询就会循环。由于需要输出depth,仅把UNION ALL改为UNION并不能消除循环。我们需要判断在沿特定链接路径前进时,是否再次到达了同一行。为这个容易循环的查询添加pathcycle两列: +WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS ( + SELECT g.id, g.link, g.data, 1, + ARRAY[g.id], + false + FROM graph g + UNION ALL + SELECT g.id, g.link, g.data, sg.depth + 1, + path || g.id, + g.id = ANY(path) + FROM graph g, search_graph sg + WHERE g.id = sg.link AND NOT cycle +) +SELECT * FROM search_graph; +除了防止循环,数组值本身也常常很有用,因为它表示到达某一行所经过的路径 + + 在需要检查多个字段以识别循环的一般情况下,使用行数组。例如,如果需要比较字段f1f2: + + +WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS ( + SELECT g.id, g.link, g.data, 1, + ARRAY[ROW(g.f1, g.f2)], + false + FROM graph g + UNION ALL + SELECT g.id, g.link, g.data, sg.depth + 1, + path || ROW(g.f1, g.f2), + ROW(g.f1, g.f2) = ANY(path) + FROM graph g, search_graph sg + WHERE g.id = sg.link AND NOT cycle +) +SELECT * FROM search_graph; + + + + + + + 在通常只有一个字段需要检查即可识别环的情况下,可以省略ROW()语法。这样就能使用简单数组而不是复合类型数组,从而提高效率。 + + + + + 递归查询求值算法按广度优先搜索顺序产生输出。可以让外层查询按这样构造的路径列进行ORDER BY排序,以深度优先搜索顺序显示结果。 + + + 当不确定查询是否会循环时,一个有用的测试技巧是在父查询中加入LIMIT。例如,下面的查询将会无限循环,除非加上LIMIT: + + +WITH RECURSIVE t(n) AS ( + SELECT 1 + UNION ALL + SELECT n+1 FROM t +) +SELECT n FROM t LIMIT 100; +这一技巧之所以有效,是因为PostgreSQL的实现只会对WITH查询中父查询实际取出的那些行求值。不推荐在生产中使用这一技巧,因为其他系统可能有不同的行为。另外,如果让外层查询对递归查询结果排序或将其与其他表连接,这一技巧通常就不起作用,因为在这种情况下,外层查询通常还是会尝试取出WITH查询的全部输出。 + + WITH查询的一个有用特性是,在父查询每次执行时,它只求值一次,即使父查询或同级WITH查询对它有多次引用也是如此。因此,在多处需要的昂贵计算可以放在一个WITH查询中,以避免重复工作。另一个用途是防止带副作用的函数被意外求值多次。不过,另一方面,与普通子查询相比,优化器更难把父查询中的限制下推到WITH查询中。WITH查询通常会按书写的样子求值,不会抑制父查询稍后可能丢弃的行。(不过,如上所述,如果对该查询的引用只需要有限数量的行,求值可能会提前停止。) + + + 上面的示例仅显示了WITHSELECT一起使用, + 但它可以以相同的方式附加到INSERTUPDATEDELETE。 + 在每种情况下,它都等效于提供了可在主命令中引用的临时表。 + + + + + <literal>WITH</literal>中的数据修改语句 + + + 你可以在WITH中使用数据修改语句(INSERTUPDATEDELETE)。这样就可以在同一查询中执行多个不同的操作。 + 例如: + + +WITH moved_rows AS ( + DELETE FROM products + WHERE + "date" >= '2010-10-01' AND + "date" < '2010-11-01' + RETURNING * +) +INSERT INTO products_log +SELECT * FROM moved_rows; + + + 这个查询实际上将行从products移动到 + products_log。在WITH中的DELETE + 从products中删除指定的行,通过其RETURNING子句返回它们的 + 内容;然后主查询读取该输出并将其插入到 + products_log中。 + + + + 上述示例中有一个细节值得注意:WITH子句附加在INSERT上,而不是附加在INSERT内部的子SELECT上。这是必须的,因为数据修改语句只允许出现在附着于顶层语句的WITH子句中。不过,普通WITH的可见性规则仍然适用,因此仍然可以在该子SELECT中引用WITH语句的输出。 + + + + 正如上述示例所示,WITH中的数据修改语句通常带有RETURNING子句(见)。构成其他查询可引用临时表的,是RETURNING子句的输出,而不是数据修改语句的目标表。如果WITH中的数据修改语句缺少RETURNING子句,那么它就不会形成临时表,也无法被查询的其他部分引用。尽管如此,这样的语句仍然会被执行。下面是一个并不特别有用的示例: + + +WITH t AS ( + DELETE FROM foo +) +DELETE FROM bar; + + + 这个示例将从表foobar中移除所有行。被报告给客户端的受影响行的数目可能只包括从bar中移除的行。 + + + 数据修改语句不允许递归自引用。有时,可以通过引用递归WITH的输出来绕过这一限制,例如: +WITH RECURSIVE included_parts(sub_part, part) AS ( + SELECT sub_part, part FROM parts WHERE part = 'our_product' + UNION ALL + SELECT p.sub_part, p.part + FROM included_parts pr, parts p + WHERE p.part = pr.sub_part +) +DELETE FROM parts + WHERE part IN (SELECT part FROM included_parts); +此查询会删除某产品的所有直接和间接子部件。 + + + WITH中的数据修改语句只会执行一次,并且总会执行到完成,而不管主查询是否读取了它们的全部输出,甚至是否读取了任何输出。注意这与WITHSELECT的规则不同:正如前一小节所述,SELECT只会执行到主查询实际需要其输出为止。 + + + + WITH中的子语句彼此之间以及与主查询是并发执行的。因此,在WITH中使用数据修改语句时,实际更新发生的顺序是不可预知的。所有语句都使用同一个快照执行(参见),因此它们无法看见彼此对目标表产生的影响。这减轻了实际行更新顺序不可预知所带来的影响,也意味着RETURNING数据是在不同WITH子语句与主查询之间传递更改的唯一方式。例如,在 + + +WITH t AS ( + UPDATE products SET price = price * 1.05 + RETURNING * +) +SELECT * FROM products; + + + 外层SELECT返回的将是UPDATE操作发生之前的原始价格,而在 + + +WITH t AS ( + UPDATE products SET price = price * 1.05 + RETURNING * +) +SELECT * FROM t; + + + 外层SELECT将返回更新后的数据。 + + + + 在单个语句中试图更新同一行两次是不受支持的。只会发生一次修改,但很难(有时根本无法)可靠地预测究竟是哪一次。这同样适用于删除在同一语句中已经被更新过的行:只有更新会生效。因此,通常应避免在一个语句中两次修改同一行。特别要避免编写那些可能影响主语句或同级子语句所修改行的WITH子语句,因为这类语句的效果将不可预测。 + + + + 当前,用作WITH中数据修改语句目标的任何表不能有条件规则、ALSO规则或扩展到多个语句的INSTEAD规则。 + + + + + + + diff --git a/zh/9.6/query.sgml b/zh/9.6/query.sgml new file mode 100644 index 00000000..49e9dcf1 --- /dev/null +++ b/zh/9.6/query.sgml @@ -0,0 +1,667 @@ + + + + <acronym>SQL</acronym>语言 + + + 简介 + + + 本章概述如何使用SQL执行简单操作。 + 本教程仅供你入门之用,绝不是一份完整的SQL教程。 + 已有许多关于SQL的书籍,包括。 + 你应当知道,PostgreSQL的某些语言特性是对标准的扩展。 + + + + 在下面的示例中,我们假定你已经按照前一章所述创建了名为mydb的数据库, + 并且已经能够启动psql。 + + + + 本手册中的示例也可以在PostgreSQL源代码发行包的 + src/tutorial/目录中找到。(PostgreSQL + 的二进制发行包可能不提供这些文件。)要使用这些文件,请先切换到该目录并运行 + make: + + +$ cd .../src/tutorial +$ make + + + 这会创建脚本并编译包含用户定义函数和类型的 C 文件。然后,要开始本教程, + 请执行以下操作: + + +$ psql -s mydb + +... + +mydb=> \i basics.sql + + + \i命令会从指定文件中读取命令。psql的 + -s选项会让你进入单步模式,在将每条语句发送给服务器之前都会暂停。 + 本节使用的命令都位于文件basics.sql中。 + + + + + + 概念 + + + relational database + hierarchical database + object-oriented database + relation + table + + PostgreSQL是一种关系数据库管理系统 + (RDBMS)。这意味着它是一个用于管理存储在关系 + 中的数据的系统。关系本质上是的数学术语。如今,把数据存储在 + 表中的观念已经十分常见,似乎显而易见,但组织数据库的方式还有很多。类 Unix + 操作系统中的文件和目录就是层次数据库的一个例子。较新的发展则是面向对象数据库。 + + + + row + column + + 每个表都是一个有名字的集合。给定表中的每一行都有相同的一组 + 具名,并且每一列都具有特定的数据类型。虽然列在每一行中的 + 顺序是固定的,但一定要记住,SQL 并不以任何方式保证表内行的顺序(虽然可以为了显示 + 而对它们显式排序)。 + + + + database cluster + clusterof databasesdatabase cluster + + 表被归入数据库,而由单个PostgreSQL服务器实例管理的 + 数据库集合构成一个数据库集簇。 + + + + + + 创建一个新表 + + + CREATE TABLE + + + + 你可以通过指定表名以及各列的列名和类型来创建一个新表: + + +CREATE TABLE weather ( + city varchar(80), + temp_lo int, -- low temperature + temp_hi int, -- high temperature + prcp real, -- precipitation + date date +); + + + 你可以在psql中按这种换行格式输入该命令。 + psql会识别出该命令在分号出现之前尚未结束。 + + + + 在 SQL 命令中可以随意使用空白字符(即空格、制表符和换行符)。这意味着你可以按 + 与上面不同的方式对齐该命令,甚至把它全部写在一行上。两个连字符 + (--)表示注释。从它们开始直到行尾的内容 + 都会被忽略。SQL 对关键字和标识符不区分大小写,除非用双引号括起标识符以保留 + 大小写(上面的例子没有这么做)。 + + + + varchar(80)指定一种数据类型,它可以存储长度不超过 80 个字符的任意字符串。 + int是普通的整数类型。real是一种用于存储单精度浮点数的 + 类型。date应当不言自明。(是的,类型为date的列也叫 + date。这可能方便,也可能令人困惑 — 由你自己决定。) + + + + PostgreSQL支持标准的SQL类型 + intsmallintrealdouble + precisionchar(N)、 + varchar(N)date、 + timetimestampinterval,还支持 + 其他一些通用类型以及丰富的几何类型。PostgreSQL + 可以通过任意数量的用户定义数据类型进行定制。因此,类型名在语法中并不是关键字, + 只有在为了支持SQL标准中的特殊情况而必须如此时才是例外。 + + + + 第二个示例将存储城市及其关联的地理位置: + +CREATE TABLE cities ( + name varchar(80), + location point +); + + point类型就是PostgreSQL特有数据类型的一个例子。 + + + + + DROP TABLE + + + 最后还要提一句,如果你不再需要某个表,或者想用不同的定义重新创建它,可以使用 + 下面的命令把它移除: + +DROP TABLE tablename; + + + + + + + 向表中填充行 + + + INSERT + + + + INSERT语句用于向表中填充行: + + +INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27'); + + + 请注意,所有数据类型的输入格式都相当直观。那些并非简单数值的常量通常必须 + 用单引号(')括起,就像这个例子一样。date类型 + 实际上可接受的格式相当灵活,不过在本教程中,我们将坚持使用这里展示的这种无歧义格式。 + + + + point类型要求一个坐标对作为输入,如下所示: + +INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)'); + + + + + 到目前为止所用的语法要求你记住列的顺序。另一种语法允许你显式列出列: + +INSERT INTO weather (city, temp_lo, temp_hi, prcp, date) + VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29'); + + 如果愿意,你可以按不同顺序列出列,甚至省略某些列,例如降水量未知时: + +INSERT INTO weather (date, city, temp_hi, temp_lo) + VALUES ('1994-11-29', 'Hayward', 54, 37); + + 许多开发者认为,与依赖隐含顺序相比,显式列出列是一种更好的风格。 + + + + 请把上面展示的所有命令都输入一遍,这样你在下面各节中才有一些数据可供操作。 + + + + + COPY + 也可以使用COPY从纯文本文件装载大量数据。这通常更快,因为COPY命令针对这种用途进行了优化,不过灵活性不如INSERT。例如: +COPY weather FROM '/home/user/weather.txt'; +其中,源文件必须可以在运行后端进程的机器上访问,而不是客户端机器,因为后端进程直接读取该文件。有关COPY命令的更多信息,请参见。 + + + + + + 查询表 + + + query + SELECT + + 要从表中检索数据,就要查询该表。 + SQLSELECT语句就是用来做这件事的。 + 该语句分为选择列表(列出要返回的列)、表列表(列出从哪些表中检索数据)以及 + 可选的限定条件(指定限制条件的部分)。例如,要检索表 + weather的所有行,键入: + +SELECT * FROM weather; + + 这里*所有列的缩写。 + + + 虽然SELECT *对于即席查询很有用,但在生产代码中通常认为 + 这是糟糕的风格,因为给表增加一个列就会改变结果。 + + + 因此,使用下面的语句也会得到相同结果: + +SELECT city, temp_lo, temp_hi, prcp, date FROM weather; + + + 输出应该是: + + + city | temp_lo | temp_hi | prcp | date +---------------+---------+---------+------+------------ + San Francisco | 46 | 50 | 0.25 | 1994-11-27 + San Francisco | 43 | 57 | 0 | 1994-11-29 + Hayward | 37 | 54 | | 1994-11-29 +(3 rows) + + + + + 你可以在选择列表中编写表达式,而不仅仅是简单的列引用。例如,你可以这样做: + +SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather; + + 这样应该得到: + + city | temp_avg | date +---------------+----------+------------ + San Francisco | 48 | 1994-11-27 + San Francisco | 50 | 1994-11-29 + Hayward | 45 | 1994-11-29 +(3 rows) + + 注意这里AS子句如何用于重新命名输出列。 + (AS子句是可选的。) + + + + 查询可以被限定,方法是添加一个指定所需行的WHERE子句。 + WHERE子句包含一个布尔(真值)表达式,只有使该 + 布尔表达式为真的行才会被返回。在限定条件中可以使用常见的布尔操作符 + (ANDORNOT)。 + 例如,下面的查询检索 San Francisco 在雨天的天气记录: + + +SELECT * FROM weather + WHERE city = 'San Francisco' AND prcp > 0.0; + + 结果: + + city | temp_lo | temp_hi | prcp | date +---------------+---------+---------+------+------------ + San Francisco | 46 | 50 | 0.25 | 1994-11-27 +(1 row) + + + + + ORDER BY + + 你可以要求查询结果按排序后的顺序返回: + + +SELECT * FROM weather + ORDER BY city; + + + + city | temp_lo | temp_hi | prcp | date +---------------+---------+---------+------+------------ + Hayward | 37 | 54 | | 1994-11-29 + San Francisco | 43 | 57 | 0 | 1994-11-29 + San Francisco | 46 | 50 | 0.25 | 1994-11-27 + + + 在这个例子中,排序顺序没有完全指定,因此两个 San Francisco 行可能以任意次序出现。 + 但如果你使用下面的语句,就总会得到上面显示的结果: + + +SELECT * FROM weather + ORDER BY city, temp_lo; + + + + + DISTINCT + duplicate + + 你可以要求从查询结果中去除重复行: + + +SELECT DISTINCT city + FROM weather; + + + + city +--------------- + Hayward + San Francisco +(2 rows) + + + 这里同样要说明,结果行的顺序可能发生变化。你可以把DISTINCT + 和ORDER BY一起使用,以确保结果一致: + + + 在一些数据库系统中,包括较老版本的PostgreSQL, + DISTINCT的实现会自动对行排序,因此 + ORDER BY并非必需。但这并不是 SQL 标准的要求,而且当前的 + PostgreSQL并不保证 + DISTINCT会使行按顺序排列。 + + + + +SELECT DISTINCT city + FROM weather + ORDER BY city; + + + + + + + 表之间的连接 + + 连接 + + 到目前为止,我们的查询每次只访问一个表。查询可以同时访问多个表,也可以访问同一个表并同时处理其中的多行。一次访问同一个表或不同表中多行的查询称为连接查询。例如,假设要列出所有天气记录及其对应城市的位置。为此,需要比较city列在weather表各行中的值与name列在cities表所有行中的值,选出这些值匹配的行对。 + + 这只是一个概念模型。连接通常会以比真正比较每一个可能的行对更高效的方式执行, + 不过这对用户是不可见的。 + + 下面的查询可以完成这一点: +SELECT * + FROM weather, cities + WHERE city = name; + + + + city | temp_lo | temp_hi | prcp | date | name | location +---------------+---------+---------+------+------------+---------------+----------- + San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53) + San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53) +(2 rows) + + + + + 注意结果集的两点: + + + 没有与城市 Hayward 对应的结果行。这是因为cities表中 + 没有与 Hayward 匹配的项,所以连接会忽略weather表中 + 的不匹配行。我们很快就会看到如何修正这一点。 + + + + + 有两列包含城市名称。这是正确的,因为来自weathercities表的列列表被拼接在一起。不过,实际中通常不希望这样,因此可能更希望显式列出输出列,而不使用*: + +SELECT city, temp_lo, temp_hi, prcp, date, location + FROM weather, cities + WHERE city = name; + + + + + + + + 练习: + + 试着确定省略WHERE子句时,这条查询的含义。 + + + 由于所有列的名称各不相同,解析器自动确定了它们所属的表。如果两个表中有重复的列名,就需要限定列名,以说明所指的是哪一列,例如: +SELECT weather.city, weather.temp_lo, weather.temp_hi, + weather.prcp, weather.date, cities.location + FROM weather, cities + WHERE cities.name = weather.city; +通常认为,限定连接查询中的所有列名是一种良好风格,这样即使以后向某个表中添加了重复列名,查询也不会失败。 + + 目前所见的连接查询,也可以采用下面这种形式编写: +SELECT * + FROM weather INNER JOIN cities ON (weather.city = cities.name); +这种语法不如上面的语法常用,不过这里展示它是为了帮助理解后续主题。 + + + 连接外连接现在来看看如何把 Hayward 的记录找回来。希望查询扫描weather表,并为其中每一行查找匹配的cities行。如果找不到匹配行,希望用一些空值代替cities表中的列。这种查询称为外连接。(此前所见的连接都是内连接。)命令如下: +SELECT * + FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name); + + city | temp_lo | temp_hi | prcp | date | name | location +---------------+---------+---------+------+------------+---------------+----------- + Hayward | 37 | 54 | | 1994-11-29 | | + San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53) + San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53) +(3 rows) +这条查询称为左外连接,因为连接操作符左边的表,其每一行都至少在输出中出现一次,而右边的表只有与左表某行匹配的行才会输出。在输出某个没有右表匹配行的左表行时,右表的各列会用空值(null)代替。 + + + 练习: + + + 还有右外连接和全外连接。试着找出它们能做什么。 + + + + + 连接自连接 + 别名查询中的表名也可以把一个表与它自身连接,这称为自连接。例如,假设要找出处于其他天气记录温度范围内的所有天气记录。为此,需要比较temp_lotemp_hi两列在每个weather行中的值与temp_lotemp_hi两列在其他所有weather行中的值。可以使用下面的查询: +SELECT W1.city, W1.temp_lo AS low, W1.temp_hi AS high, + W2.city, W2.temp_lo AS low, W2.temp_hi AS high + FROM weather W1, weather W2 + WHERE W1.temp_lo < W2.temp_lo + AND W1.temp_hi > W2.temp_hi; + + city | low | high | city | low | high +---------------+-----+------+---------------+-----+------ + San Francisco | 43 | 57 | San Francisco | 46 | 50 + Hayward | 37 | 54 | San Francisco | 46 | 50 +(2 rows) +这里把 weather 表重新标记为W1和W2,以区分连接的左侧和右侧。在其他查询中也可以使用这种别名来减少输入,例如: +SELECT * + FROM weather w, cities c + WHERE w.city = c.name; +你会经常遇到这种缩写方式。 + + + + + 聚合函数 + + + aggregate function + + + + 与大多数其他关系数据库产品一样,PostgreSQL支持 + 聚合函数。聚合函数从多行输入中计算出单个结果。 + 例如,有一些聚合函数可在一组行上计算count(计数)、 + sum(和)、avg(平均值)、 + max(最大值)和min(最小值)。 + + + + 例如,我们可以用下面的语句找出所有记录中最低温度读数的最高值: + + +SELECT max(temp_lo) FROM weather; + + + + max +----- + 46 +(1 row) + + + + + subquery + + 如果我们想知道该读数出现在哪个城市(或哪些城市),可能会尝试: + + +SELECT city FROM weather WHERE temp_lo = max(temp_lo); -- WRONG + + + 但这样行不通,因为聚合函数max不能用于 + WHERE子句中(存在这个限制是因为WHERE + 子句决定哪些行会被纳入聚合计算;因此显然它必须在聚合函数计算之前求值。) + 不过,这类情况下通常可以把查询改写成能够得到所需结果的形式,这里我们通过使用 + 子查询来做到这一点: + + +SELECT city FROM weather + WHERE temp_lo = (SELECT max(temp_lo) FROM weather); + + + + city +--------------- + San Francisco +(1 row) + + + 这样做是可以的,因为子查询是一次独立的计算,它会与外层查询分开计算自己的聚合。 + + + + GROUP BY + HAVING聚合与GROUP BY子句结合使用也很有用。例如,可以通过下面的查询,得到每个城市所观测到的最低温度的最大值: +SELECT city, max(temp_lo) + FROM weather + GROUP BY city; + + + + city | max +---------------+----- + Hayward | 37 + San Francisco | 46 +(2 rows) +这会为每个城市输出一行。每个聚合结果都是针对与该城市匹配的表行计算的。这些分组行可以用以下子句过滤:HAVING: + + +SELECT city, max(temp_lo) + FROM weather + GROUP BY city + HAVING max(temp_lo) < 40; + + + + city | max +---------+----- + Hayward | 37 +(1 row) +这样就只会为所有temp_lo值均小于 40 的城市返回相同的结果。最后,如果只关心名称以S开头的城市,可以这样做: +SELECT city, max(temp_lo) + FROM weather + WHERE city LIKE 'S%' + GROUP BY city + HAVING max(temp_lo) < 40; + + + + + LIKE操作符执行模式匹配,详细说明见 + 。 + + + + + + + 理解聚合与SQLWHERE和 + HAVING子句之间的相互作用非常重要。 + WHEREHAVING的根本区别在于: + WHERE在分组和聚合计算之前选择输入行(因此它控制哪些行 + 进入聚合计算),而HAVING在分组和聚合计算之后选择分组行。 + 因此,WHERE子句中不能包含聚合函数;试图用聚合来决定哪些行 + 应该成为聚合的输入是没有意义的。另一方面,HAVING子句总是 + 包含聚合函数。(严格地说,也允许编写不使用聚合的HAVING子句, + 但那很少有用。同样的条件在WHERE阶段使用会更高效。) + + + + 在前面的例子中,我们可以在WHERE中应用城市名限制, + 因为它不需要聚合。这样比把限制放到HAVING中更高效, + 因为我们避免了对所有未通过WHERE检查的行进行分组和聚合计算。 + + + + + + 更新 + + + UPDATE + + + + 你可以使用UPDATE命令更新已有的行。假设你发现 11 月 28 日 + 之后的温度读数全部偏差了 2 度,那么就可以按下面的方式改正数据: + + +UPDATE weather + SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2 + WHERE date > '1994-11-28'; + + + + + 看看数据的新状态: + +SELECT * FROM weather; + + city | temp_lo | temp_hi | prcp | date +---------------+---------+---------+------+------------ + San Francisco | 46 | 50 | 0.25 | 1994-11-27 + San Francisco | 41 | 55 | 0 | 1994-11-29 + Hayward | 35 | 52 | | 1994-11-29 +(3 rows) + + + + + + 删除 + + + DELETE + + + + 可以使用DELETE命令从表中移除行。假设你不再关心 Hayward + 的天气,那么可以用下面的方法把这些行从表中删除: + +DELETE FROM weather WHERE city = 'Hayward'; + + + 所有属于 Hayward 的天气记录都会被删除。 + + +SELECT * FROM weather; + + + + city | temp_lo | temp_hi | prcp | date +---------------+---------+---------+------+------------ + San Francisco | 46 | 50 | 0.25 | 1994-11-27 + San Francisco | 41 | 55 | 0 | 1994-11-29 +(2 rows) + + + + + 对如下形式的语句必须保持警惕: + +DELETE FROM tablename; + + + 如果没有限定条件,DELETE将从指定表中删除所有 + 行,使其变为空表。系统在执行前不会请求你确认! + + + + diff --git a/zh/9.6/rangetypes.sgml b/zh/9.6/rangetypes.sgml new file mode 100644 index 00000000..92eca5d6 --- /dev/null +++ b/zh/9.6/rangetypes.sgml @@ -0,0 +1,342 @@ + + + + 范围类型 + + + range type + + + + 范围类型是表示某种元素类型的值范围的数据类型(该元素类型称为范围的子类型(subtype))。例如,timestamp的范围可用于表示会议室被预订的时间范围。在这种情况下,数据类型为tsrangetimestamp range的缩写),而timestamp就是其子类型。子类型必须具有全序,这样才能明确定义元素值是位于某个值范围之内、之前还是之后。 + + + + 范围类型很有用,因为它们能用单个范围值表示许多元素值,也能清晰地表达诸如范围重叠之类的概念。将时间和日期范围用于日程安排,是最清晰的例子;但价格区间、仪器的测量范围等场景也同样有用。 + + + + 内置范围类型 + + PostgreSQL 提供以下内置范围类型: + + int4rangeinteger 的范围 + + + int8rangebigint 的范围 + + + numrangenumeric 的范围 + + + tsrangetimestamp without time zone 的范围 + + + tstzrangetimestamp with time zone 的范围 + + + daterangedate 的范围 + + 此外,还可以定义自己的范围类型;参见了解更多信息。 + + + + + 示例 + + + +CREATE TABLE reservation (room int, during tsrange); +INSERT INTO reservation VALUES + (1108, '[2010-01-01 14:30, 2010-01-01 15:30)'); + +-- 包含关系 +SELECT int4range(10, 20) @> 3; + +-- 重叠 +SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0); + +-- 提取上界 +SELECT upper(int8range(15, 25)); + +-- 计算交集 +SELECT int4range(10, 20) * int4range(15, 25); + +-- 范围是否为空? +SELECT isempty(numrange(1, 5)); + + + 范围类型上的操作符和函数的完整列表见。 + + + + + + 包含界限与排除界限 + + + 每个非空范围都有两个界限:下界和上界。位于这两个值之间的所有点都包含在该范围内。包含界限表示边界点本身也包含在范围内,而排除界限则表示边界点不包含在范围内。 + + + + 在范围的文本形式中,包含下界用[表示,排除下界用(表示。同样,包含上界用]表示,排除上界用)表示。更多细节见。 + + + + 函数lower_incupper_inc分别测试范围值的下界和上界是否包含在内。 + + + + + + 无限(无界)范围 + + + 范围的下界可以省略,这意味着所有小于上界的值都包含在范围内,例如(,3]。同样,如果省略范围的上界,则所有大于下界的值都包含在范围内。如果上下界都被省略,则该元素类型的所有值都被认为处于该范围内。把缺失的界限指定为包含,会自动转换为排除,例如[,]会转换为(,)。你可以把这些缺失的值看作 +/-infinity,但它们是特殊的范围类型值,并且被认为超出了任何范围元素类型的 +/-infinity 值。 + + + + 具有infinity概念的元素类型可以将其用作显式界限值。例如,对于时间戳范围,[today,infinity)不包括特殊的timestampinfinity,而[today,infinity]则包括它,[today,)[today,]也一样。 + + + + 函数lower_infupper_inf分别测试范围的下界和上界是否为无限。 + + + + + 范围输入/输出 + + + 范围值的输入必须遵循下列模式之一: + +(lower-bound,upper-bound) +(lower-bound,upper-bound] +[lower-bound,upper-bound) +[lower-bound,upper-bound] +empty + + 如前所述,圆括号或方括号指示上下界是排除还是包含。注意最后一种模式是empty,它表示空范围(即不包含任何点的范围)。 + + + + lower-bound可以是子类型的合法输入字符串,也可以留空以表示没有下界。同样,upper-bound也可以是子类型的合法输入字符串,或者留空以表示没有上界。 + + + + 每个界限值都可以用"(双引号)字符括起来。如果界限值包含圆括号、方括号、逗号、双引号或反斜线,这样做就是必须的,因为否则这些字符会被视为范围语法的一部分。要在带引号的界限值中写入双引号或反斜线,需要在前面加一个反斜线。(另外,在双引号括起来的界限值中,两个连续的双引号表示一个双引号字符,这与 SQL 字符串字面量中单引号的规则类似。)或者,你也可以不使用引号,而是用反斜线转义所有本来会被当作范围语法的字符。另外,如果要把空字符串写成界限值,应写成"",因为什么都不写表示无限界限。 + + + + 范围值前后允许有空白,但圆括号或方括号之间的任何空白都会被视为下界或上界值的一部分。(取决于元素类型,这些空白可能有意义,也可能没有意义。) + + + + + + 这些规则与在复合类型字面量中书写字段值的规则非常相似。更多说明见。 + + + + + 示例: + +-- 包含 3,不包含 7,并包含中间的所有点 +SELECT '[3,7)'::int4range; + +-- 既不包含 3,也不包含 7,但包含中间的所有点 +SELECT '(3,7)'::int4range; + +-- 只包含单个点 4 +SELECT '[4,4]'::int4range; + +-- 不包含任何点(并将被规范化为 'empty') +SELECT '[4,4)'::int4range; + + + + + + 构造范围 + + + 每种范围类型都有一个与范围类型同名的构造函数。使用构造函数通常比书写范围字面量更方便,因为这样无需为界限值额外加引号。构造函数接受两个或三个参数。两个参数的形式构造标准形式的范围(下界包含,上界排除),而三个参数的形式则按第三个参数指定的界限形式构造范围。第三个参数必须是下列字符串之一: + ()、 + (]、 + [)或者 + []。 + 例如: + + +-- 完整形式为:下界、上界,以及指示界限包含性/排除性的文本参数。 +SELECT numrange(1.0, 14.0, '(]'); + +-- 如果省略第三个参数,则假定为 '[)'。 +SELECT numrange(1.0, 14.0); + +-- 虽然这里指定的是 '(]',但显示时该值会转换为规范形式,因为 int8range 是离散范围类型(见下文)。 +SELECT int8range(1, 14, '(]'); + +-- 任一界限使用 NULL 都会使该侧无界。 +SELECT numrange(NULL, 2.2); + + + + + + + 离散范围类型 + + + 离散范围是指其元素类型具有明确定义的步长,例如integerdate。在这类类型中,如果两个元素之间不存在合法值,就可以说它们是相邻的。这与连续范围形成对比:在连续范围中,两个给定值之间总是(或几乎总是)可以识别出其他元素值。例如,基于numeric的范围是连续的,基于timestamp的范围也是如此。(尽管timestamp的精度有限,因此理论上可以视为离散的,但通常最好仍将其视为连续的,因为步长通常并不是关注点。) + + + + 理解离散范围类型的另一种方式是:对每个元素值,都有一个明确的下一个上一个值。知道这一点后,就可以把原先给定的元素值替换成其下一个或上一个值,从而在范围界限的包含表示和排除表示之间相互转换。例如,在整数范围类型中,[4,8](3,9)表示相同的值集合;但对基于 numeric 的范围则并非如此。 + + + + 离散范围类型应当具有一个规范化函数,该函数知道元素类型所期望的步长。规范化函数负责将范围类型中的等价值转换成完全一致的表示形式,特别是对包含或排除界限保持一致。如果未指定规范化函数,那么格式不同的范围即使实际上表示的是同一组值,也总会被视为不相等。 + + + + 内置范围类型int4rangeint8rangedaterange都使用一种规范形式:包含下界而排除上界,也就是[)。不过,用户定义的范围类型也可以采用其他约定。 + + + + + 定义新的范围类型 + + + 用户可以定义自己的范围类型。最常见的原因,是希望对内置范围类型中未提供的子类型使用范围。例如,要定义一个子类型为float8的新范围类型: + + +CREATE TYPE floatrange AS RANGE ( + subtype = float8, + subtype_diff = float8mi +); + +SELECT '[1.234, 5.678]'::floatrange; + + + 因为float8没有有意义的步长,所以在这个示例中我们没有定义规范化函数。 + + + + 定义自己的范围类型还允许你指定不同的子类型 B-树操作符类或排序规则,从而改变用于判定哪些值落入给定范围的排序顺序。 + + + + 如果认为子类型的值是离散而非连续的,则CREATE TYPE命令应指定canonical函数。规范化函数接受一个输入的范围值,并必须返回一个等价的范围值,该值的界限和格式可能不同。对于表示同一组值的两个范围,例如整数范围[1, 7][1, 8),规范化输出必须相同。选择哪一种表示作为规范形式并不重要,只要不同格式的两个等价值总是会被映射为同一种格式的相同值即可。除了调整界限的包含/排除格式之外,如果期望的步长大于子类型能够存储的精度,规范化函数还可能对界限值进行舍入。例如,基于timestamp的范围类型可以定义为步长为一小时,在这种情况下,规范化函数需要把不是一小时整数倍的界限值舍入,或者直接抛出错误。 + + + + 此外,任何打算与 GiST 或 SP-GiST 索引一起使用的范围类型都应定义子类型差值函数,即subtype_diff函数。(没有subtype_diff时索引仍可工作,但效率很可能明显低于提供了差值函数的情况。)子类型差值函数接受两个子类型输入值,并返回它们的差值(即XY),结果表示为一个float8值。在上面的示例中,可以使用函数float8mi,它是常规float8减法操作符的底层实现;但对于其他子类型,则需要某种类型转换。此外,可能还需要仔细考虑如何把差异表示为数字。尽可能地,subtype_diff函数应与所选操作符类和排序规则所隐含的排序顺序一致;也就是说,只要其第一个参数按该排序顺序大于第二个参数,它的结果就应该为正。 + + + + 下面给出一个不那么简化的subtype_diff函数示例: + + + +CREATE FUNCTION time_subtype_diff(x time, y time) RETURNS float8 AS +'SELECT EXTRACT(EPOCH FROM (x - y))' LANGUAGE sql STRICT IMMUTABLE; + +CREATE TYPE timerange AS RANGE ( + subtype = time, + subtype_diff = time_subtype_diff +); + +SELECT '[11:10, 23:00]'::timerange; + + + + 关于创建范围类型的更多信息,见。 + + + + + 索引 + + + range type + indexes on + + + 可以为范围类型的表列创建 GiST 和 SP-GiST 索引。例如,创建 GiST 索引: +CREATE INDEX reservation_idx ON reservation USING GIST (during); +GiST 或 SP-GiST 索引可以加速涉及以下范围操作符的查询:=, + &&, + <@, + @>, + <<, + >>, + -|-, + &<&>(参见了解更多信息)。 + + + 此外,也可以为范围类型的表列创建 B-树和哈希索引。对于这些索引类型,基本上唯一有用的范围操作就是等值。系统为范围值定义了对应<>操作符的 B-树排序顺序,但这种顺序相当任意,在现实中通常并没有什么用处。范围类型的 B-树和哈希支持主要是为了允许在查询内部进行排序和哈希,而不是用于创建实际的索引。 + + + + + 范围上的约束 + + + range type + exclude + + + + 虽然UNIQUE是标量值的一种自然约束,但它通常并不适用于范围类型。相反,排他约束往往更合适(见CREATE TABLE ... CONSTRAINT ... EXCLUDE)。排他约束允许在范围类型上指定诸如不重叠之类的约束。例如: + + +CREATE TABLE reservation ( + during tsrange, + EXCLUDE USING GIST (during WITH &&) +); + + + 该约束会阻止表中同时存在任何重叠值: + + +INSERT INTO reservation VALUES + ('[2010-01-01 11:30, 2010-01-01 15:00)'); +INSERT 0 1 + +INSERT INTO reservation VALUES + ('[2010-01-01 14:45, 2010-01-01 15:45)'); +ERROR: conflicting key value violates exclusion constraint "reservation_during_excl" +DETAIL: Key (during)=(["2010-01-01 14:45:00","2010-01-01 15:45:00")) conflicts +with existing key (during)=(["2010-01-01 11:30:00","2010-01-01 15:00:00")). + + + + + 你可以使用btree_gist扩展在普通标量数据类型上定义排他约束,然后再将其与范围上的排他约束结合起来,以获得最大的灵活性。例如,在安装了btree_gist之后,下面的约束仅在会议室编号相等时才会拒绝重叠的范围: + + +CREATE EXTENSION btree_gist; +CREATE TABLE room_reservation ( + room text, + during tsrange, + EXCLUDE USING GIST (room WITH =, during WITH &&) +); + +INSERT INTO room_reservation VALUES + ('123A', '[2010-01-01 14:00, 2010-01-01 15:00)'); +INSERT 0 1 + +INSERT INTO room_reservation VALUES + ('123A', '[2010-01-01 14:30, 2010-01-01 15:30)'); +ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl" +DETAIL: Key (room, during)=(123A, ["2010-01-01 14:30:00","2010-01-01 15:30:00")) conflicts +with existing key (room, during)=(123A, ["2010-01-01 14:00:00","2010-01-01 15:00:00")). + +INSERT INTO room_reservation VALUES + ('123B', '[2010-01-01 14:30, 2010-01-01 15:30)'); +INSERT 0 1 + + + + diff --git a/zh/9.6/recovery-config.sgml b/zh/9.6/recovery-config.sgml new file mode 100644 index 00000000..30d265a7 --- /dev/null +++ b/zh/9.6/recovery-config.sgml @@ -0,0 +1,243 @@ + + + + 恢复配置 + + + 配置 + 恢复 + 备库 + + + 本章介绍 recovery.confrecovery.conf 文件中可用的设置。它们只在恢复期间生效。如果之后还要进行恢复,必须重新设置这些参数。恢复开始后,就不能再更改它们。 + + recovery.conf 中的设置使用 name = 'value' 格式,每行指定一个参数。井号(#)表示该行余下内容为注释。要在参数值中嵌入单引号,请写两个单引号('')。 + + 安装目录的 share/ 目录中提供了示例文件 share/recovery.conf.sample + + + + 归档恢复设置 + + + + restore_command (string) restore_command 恢复参数 + + + 用于获取 WAL 文件系列的一个已归档段的本地 shell 命令。这个参数是归档恢复所必需的,但是对于流复制是可选的。 + 在该字符串中的任何%f会被替换为从归档中获得的文件的名字,并且任何%p会被在服务器上的复制目标路径名替换(该路径名是相对于当前工作目录的,即集簇的数据目录)。 + 任何%r会被包含上一个可用重启点的文件的名字所替换。 + 在那些必须被保留用于使得一次恢复变成可重启的文件中,这个文件是其中最早的一个,因此这个信息可以被用来把归档截断为支持从当前恢复重启所需的最小值。 + %r通常只被温备配置(见)所使用。要嵌入一个真正的%字符,需要写成%%。 + + + + 很重要的一点是,该命令只有在成功时才返回一个为零的退出状态。 + 该命令会被询问不存在于归档中的文件名,当这样被询问时它必须返回非零。示例: + +restore_command = 'cp /mnt/server/archivedir/%f "%p"' +restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows + + 一个例外是如果该命令被一个信号(不是SIGTERM,它是数据库服务器关闭的一部分)或者一个 shell 错误(例如命令未找到)终止,则恢复将会中止并且服务器将不会启动。 + + + + + + archive_cleanup_command (string) archive_cleanup_command 恢复参数 + + + 这个可选参数指定了一个 shell 命令,它将在每一个重启点被执行。 + archive_cleanup_command的目的是提供一种清除不再被备库需要的旧的已归档 WAL 文件的机制。 + 任何%r会被替换为包含最后一个可用重启点的文件的名称。 + 那是使一次恢复变成可重启的所必须被保留的最早的文件,并且因此比%r更早的所有文件可以被安全地移除。 + 这个信息可以被用来把归档截断为支持从当前恢复重启所需的最小值。 + 对于单一备库配置,模块常常被用在archive_cleanup_command中,例如: +archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r' + 但是注意,如果多个备库正在从同一个归档目录中恢复,你将需要保证只有当所有服务器都不再需要这些 WAL 文件时才会删除它们。 + archive_cleanup_command通常被用于一种温备配置(见)中。 + 要在该命令中嵌入一个真正的%字符,需要写成%%。 + + + 如果该命令返回一个非零退出状态,则将会写出一个警告日志消息。 + 一个例外是如果该命令被一个信号或者一个 shell 错误(例如命令未找到)终止,则会抛出一个致命错误。 + + + + + + recovery_end_command (string) recovery_end_command 恢复参数 + + 此参数指定一个 shell 命令,在恢复结束时仅执行一次。此参数可选。recovery_end_command 用于提供复制或恢复后的清理机制。与 一样,所有 %r 都会被替换为包含最后一个有效重启点的文件的名称。 + + 如果该命令返回一个非零退出状态,则一个警告日志消息将被写出并且不管怎样该数据库将继续启动。 + 一个例外是如果该命令被一个信号或者 shell 错误(例如命令未找到)中止,该数据库将不会继续启动。 + + + + + + + + + + + 恢复目标设置 + + 默认情况下,恢复会一直进行到 WAL 日志末尾。可以使用以下参数指定更早的停止点。recovery_targetrecovery_target_namerecovery_target_timerecovery_target_xid 中最多只能使用一个;如果在配置文件中指定了多个,则使用最后一个条目。 + + + + recovery_target = 'immediate' recovery_target 恢复参数 + + + + 这个参数指定恢复应该在达到一个一致状态后尽快结束,即尽早结束。在从一个在线备份中恢复时,这意味着备份结束的那个点。 + + + + 在技术上,这是一个字符串参数,但是'immediate'是目前唯一允许的值。 + + + + + + recovery_target_name (string) recovery_target_name 恢复参数 + + + + 这个参数指定(pg_create_restore_point()所创建)的已命名的恢复点,恢复将进行到该恢复点。 + + + + + + recovery_target_time (timestamp) recovery_target_time 恢复参数 + + 此参数指定恢复要进行到的时间戳。精确的停止点还受 影响。 + + + + + recovery_target_xid (string) recovery_target_xid 恢复参数 + + 此参数指定恢复要进行到的事务 ID。请注意,虽然事务 ID 在事务开始时按顺序分配,但事务完成的顺序可能与 ID 的数值顺序不同。恢复的事务是那些在指定事务之前提交的事务,并可选择包括指定事务本身。精确的停止点还受 影响。 + + + + + + + 下列选项进一步指定恢复目标,并且影响到达目标时会发生什么: + + + + + recovery_target_inclusive (boolean) recovery_target_inclusive 恢复参数 + + 指定是在所设恢复目标之后立即停止(true),还是在其之前立即停止(false)。在指定 时适用。此设置控制恢复是否包含提交时间或事务 ID 分别恰好等于目标值的事务。默认值为 true + + + + + recovery_target_timeline (string) recovery_target_timeline 恢复参数 + + 指定恢复到某条特定时间线。默认沿着制作基础备份时的当前时间线恢复。设为 latest 时,会恢复到归档中找到的最新时间线,这对备库很有用。除此之外,只有在复杂的再次恢复场景中才需要设置此参数:需要返回的状态本身就是一次时间点恢复之后达到的状态。相关讨论参见 + + + + + recovery_target_action (enum) recovery_target_action 恢复参数 + + + 指定在达到恢复目标时服务器应该立刻采取的动作。默认动作是pause,这表示恢复将会被暂停。 + promote表示恢复处理将会结束并且服务器将开始接受连接。 + 最后,shutdown将在达到恢复目标之后停止服务器。 + + + 使用pause设置的目的是允许对数据库执行查询,以检查这个恢复目标是否为最合适的恢复位置。 + 暂停的状态可以使用pg_xlog_replay_resume()(见)继续,这会让恢复终结。 + 如果这个恢复目标不是想要的停止点,那么关闭服务器,将恢复目标设置改为一个稍后的目标并且重启以继续恢复。 + + + 要让实例在想要的重放点那里准备好,shutdown设置可以派上用场。 + 该实例将仍能重放更多 WAL 记录(并且事实上,下次启动时必须重新回放自上一个检查点以来的 WAL 记录)。 + + 注意,将 recovery_target_action 设为 shutdown 时,recovery.conf 不会被重命名,因此除非更改配置或手动删除 recovery.conf 文件,否则之后每次启动都会立即关闭。 + 如果未设置恢复目标,此设置不起作用。如果未启用 ,则 pause 的行为与 shutdown 相同。 + + + + + + + + + 备库设置 + + + + standby_mode (boolean) standby_mode 恢复参数 + + 指定是否将 PostgreSQL 服务器作为备库启动。如果此参数为 on,服务器到达已归档 WAL 的末尾时不会停止恢复,而会继续尝试使用 restore_command 获取新的 WAL 段,和/或按照 primary_conninfo 设置连接主库,以继续恢复。 + + + + primary_conninfo (string) primary_conninfo 恢复参数 + + 指定备库连接主库所用的连接字符串,格式见 。如果某个选项未在字符串中指定,就会检查相应的环境变量(参见 )。如果环境变量也未设置,则使用默认值。 + 连接字符串应指定主库的主机名(或地址);如果端口号与备库的默认端口不同,也应指定端口号。还应指定一个用户名,对应主库上具有适当权限的角色(参见 )。如果主库要求密码认证,还需要提供密码。密码可以放在 primary_conninfo 字符串中,也可以放在备库上单独的 ~/.pgpass 文件中(使用 replication 作为数据库名)。不要在 primary_conninfo 字符串中指定数据库名。 + 如果 standby_modeoff,此设置不起作用。 + + + + primary_slot_name (string) primary_slot_name 恢复参数 + + 可选地指定一个已存在的复制槽,在通过流复制连接主库时使用,以控制上游节点的资源移除(参见 )。如果未设置 primary_conninfo,此设置不起作用。 + + + + trigger_file (string) trigger_file 恢复参数 + + 指定一个触发文件,其出现会使备库结束恢复。即使未设置此值,也仍可以使用 pg_ctl promote 提升备库。如果 standby_modeoff,此设置不起作用。 + + + + + recovery_min_apply_delay (integer) + + recovery_min_apply_delay 恢复参数 + + + + + 默认情况下,备库会尽快恢复来自主库的 WAL 记录。保留一份延迟的数据副本可能很有用,因为它提供了纠正数据丢失错误的机会。此参数允许将恢复延迟一段固定时间;如果没有指定单位,则以毫秒计。例如,将此参数设置为 5min 时,只有当备库系统时间比主库报告的提交时间至少晚五分钟,备库才会重放各事务的提交。 + + + 服务器之间的复制延迟可能超过此参数的值,这种情况下不会增加延迟。注意,延迟根据主库写入的 WAL 时间戳与备库当前时间之差计算。网络延迟或级联复制配置导致的传输延迟,可能显著缩短实际等待时间。如果主库和备库的系统时钟不同步,恢复时可能比预期更早应用记录;但这通常不是主要问题,因为此参数的实用取值远大于服务器间常见的时间偏差。 + + + 延迟仅发生在事务提交的 WAL 记录上。其他记录会尽快重放;这不会造成问题,因为 MVCC 可见性规则确保在对应提交记录被应用之前,它们的效果不会可见。 + + + 恢复中的数据库达到一致状态后开始延迟,直到备库被提升或触发。此后,备库会结束恢复,不再等待。 + + + 此参数旨在用于流复制部署;不过,只要指定了此参数,它就会在所有情况下生效。使用此功能也会延迟 hot_standby_feedback,可能导致主库膨胀;同时使用两者时应谨慎。 + + + 当 synchronous_commit 设置为 remote_apply 时,同步复制会受到此设置影响;每个 COMMIT 都必须等待提交被应用。 + + + + + + + + + + + diff --git a/zh/9.6/ref/abort.sgml b/zh/9.6/ref/abort.sgml new file mode 100644 index 00000000..ffef76c6 --- /dev/null +++ b/zh/9.6/ref/abort.sgml @@ -0,0 +1,95 @@ + + + + + ABORT + + + + ABORT + 7 + SQL - 语言语句 + + + + ABORT + 中止当前事务 + + + + +ABORT [ WORK | TRANSACTION ] + + + + + 描述 + + + ABORT回滚当前事务,并丢弃该事务所做的全部更新。 + 这个命令在行为上与标准 SQL 命令 + 完全相同, + 只是出于历史原因而保留。 + + + + + 参数 + + + + WORK + TRANSACTION + + + 可选关键字,没有任何作用。 + + + + + + + + 注解 + + + 使用可成功地终止一个事务。 + + + + 在事务块之外发出ABORT会产生一条警告,除此之外没有效果。 + + + + + 示例 + + + 要中止所有更改: + +ABORT; + + + + + 兼容性 + + + 这个命令是PostgreSQL扩展,只是出于历史原因而保留。 + ROLLBACK是与之等效的标准 SQL 命令。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/allfiles.sgml b/zh/9.6/ref/allfiles.sgml new file mode 100644 index 00000000..77667bde --- /dev/null +++ b/zh/9.6/ref/allfiles.sgml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/ref/alter_aggregate.sgml b/zh/9.6/ref/alter_aggregate.sgml new file mode 100644 index 00000000..4b4b17fd --- /dev/null +++ b/zh/9.6/ref/alter_aggregate.sgml @@ -0,0 +1,183 @@ + + + + + ALTER AGGREGATE + + + + ALTER AGGREGATE + 7 + SQL - 语言语句 + + + + ALTER AGGREGATE + 更改一个聚合函数的定义 + + + + +ALTER AGGREGATE name ( aggregate_signature ) RENAME TO new_name +ALTER AGGREGATE name ( aggregate_signature ) + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER AGGREGATE name ( aggregate_signature ) SET SCHEMA new_schema + +其中aggregate_signature为: + +* | +[ argmode ] [ argname ] argtype [ , ... ] | +[ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + + + + + 描述 + + + ALTER AGGREGATE更改聚合函数的定义。 + + + 要使用ALTER AGGREGATE,必须拥有该聚合函数。要更改聚合函数的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在聚合函数所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建聚合函数完成的操作。不过,超级用户无论如何都可以更改任何聚合函数的所有权。) + + + + 参数 + + + + name + + + 现有聚合函数的名称(可以用模式限定)。 + + + + + + argmode + + + + 参数的模式:INVARIADIC。 + 如果省略,默认为IN。 + + + + + + argname + + + + 参数的名称。注意,ALTER AGGREGATE + 实际上并不关心参数名,因为确定聚合函数的标识只需要参数的数据类型。 + + + + + + argtype + + + 聚合函数作用于其上的输入数据类型。要引用零参数聚合函数,请在参数说明 + 列表的位置写上*。要引用有序集聚合函数,请在直接参数 + 说明和聚合参数说明之间写上ORDER BY。 + + + + + + new_name + + + 聚合函数的新名称。 + + + + + + new_owner + + + 聚合函数的新拥有者。 + + + + + + new_schema + + + 聚合函数的新模式。 + + + + + + + + 注解 + + + 引用有序集聚合的推荐语法,是像 + 中那样,在直接参数说明和聚合参数说明之间写上ORDER BY。 + 不过,省略ORDER BY,直接把两部分参数说明合并为一个列 + 表也同样可行。在这种简写形式中,如果直接参数列表和聚合参数列表中都使用 + 了VARIADIC "any",则只需写一次VARIADIC "any"。 + + + + + 示例 + + + 要把用于类型integer的聚合函数 + myavg重命名为my_average: + +ALTER AGGREGATE myavg(integer) RENAME TO my_average; + + + + + 要把用于类型integer的聚合函数 + myavg的拥有者改为joe: + +ALTER AGGREGATE myavg(integer) OWNER TO joe; + + + + + 把直接参数类型为float8、聚合参数类型为integer + 的有序集聚合mypercentile移动到 + 模式myschema中: + +ALTER AGGREGATE mypercentile(float8 ORDER BY integer) SET SCHEMA myschema; + + 这也同样可行: + +ALTER AGGREGATE mypercentile(float8, integer) SET SCHEMA myschema; + + + + + + 兼容性 + + + 在 SQL 标准中没有ALTER AGGREGATE语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_collation.sgml b/zh/9.6/ref/alter_collation.sgml new file mode 100644 index 00000000..ddf224c1 --- /dev/null +++ b/zh/9.6/ref/alter_collation.sgml @@ -0,0 +1,117 @@ + + + + + ALTER COLLATION + + + + ALTER COLLATION + 7 + SQL - 语言语句 + + + + ALTER COLLATION + 更改排序规则的定义 + + + + +ALTER COLLATION name RENAME TO new_name +ALTER COLLATION name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER COLLATION name SET SCHEMA new_schema + + + + + 描述 + + + ALTER COLLATION更改排序规则的定义。 + + + 要使用ALTER COLLATION,必须拥有该排序规则。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在排序规则所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建排序规则完成的操作。不过,超级用户无论如何都可以更改任何排序规则的所有权。) + + + + 参数 + + + + name + + + 一个现有排序规则的名称(可以是模式限定的)。 + + + + + + new_name + + + 排序规则的新名称。 + + + + + + new_owner + + + 排序规则的新拥有者。 + + + + + + new_schema + + + 排序规则的新模式。 + + + + + + + + + + 示例 + + + 要将排序规则de_DE重命名为german: + +ALTER COLLATION "de_DE" RENAME TO german; + + + + + 要将排序规则en_US的拥有者改为joe: + +ALTER COLLATION "en_US" OWNER TO joe; + + + + + 兼容性 + + + SQL 标准中没有ALTER COLLATION语句。 + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/alter_conversion.sgml b/zh/9.6/ref/alter_conversion.sgml new file mode 100644 index 00000000..18a32eba --- /dev/null +++ b/zh/9.6/ref/alter_conversion.sgml @@ -0,0 +1,115 @@ + + + + + ALTER CONVERSION + + + + ALTER CONVERSION + 7 + SQL - 语言语句 + + + + ALTER CONVERSION + 更改一个转换的定义 + + + + +ALTER CONVERSION name RENAME TO new_name +ALTER CONVERSION name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER CONVERSION name SET SCHEMA new_schema + + + + + 描述 + + + ALTER CONVERSION更改一个转换的定义。 + + + 要使用ALTER CONVERSION,必须拥有该转换。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在转换所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建转换完成的操作。不过,超级用户无论如何都可以更改任何转换的所有权。) + + + + 参数 + + + + name + + + 一个现有转换的名称(可以是模式限定的)。 + + + + + + new_name + + + 转换的新名称。 + + + + + + new_owner + + + 转换的新拥有者。 + + + + + + new_schema + + + 转换的新模式。 + + + + + + + + 示例 + + + 要把转换iso_8859_1_to_utf8重命名为latin1_to_unicode: + +ALTER CONVERSION iso_8859_1_to_utf8 RENAME TO latin1_to_unicode; + + + + + 要把转换iso_8859_1_to_utf8的拥有者改成joe: + +ALTER CONVERSION iso_8859_1_to_utf8 OWNER TO joe; + + + + + 兼容性 + + + 在 SQL 标准中没有ALTER CONVERSION语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_database.sgml b/zh/9.6/ref/alter_database.sgml new file mode 100644 index 00000000..c65ecfdd --- /dev/null +++ b/zh/9.6/ref/alter_database.sgml @@ -0,0 +1,194 @@ + + + + + ALTER DATABASE + + + + ALTER DATABASE + 7 + SQL - 语言语句 + + + + ALTER DATABASE + 更改一个数据库 + + + + +ALTER DATABASE name [ [ WITH ] option [ ... ] ] + +其中option可以是: + + ALLOW_CONNECTIONS allowconn + CONNECTION LIMIT connlimit + IS_TEMPLATE istemplate + +ALTER DATABASE name RENAME TO new_name + +ALTER DATABASE name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + +ALTER DATABASE name SET TABLESPACE new_tablespace + +ALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT } +ALTER DATABASE name SET configuration_parameter FROM CURRENT +ALTER DATABASE name RESET configuration_parameter +ALTER DATABASE name RESET ALL + + + + + 描述 + + + ALTER DATABASE更改一个数据库的属性。 + + + + 第一种形式更改某些数据库级设置(详见下文)。只有数据库拥有者或者超级用户才能更改这些设置。 + + + + 第二种形式更改数据库的名称。只有数据库拥有者或者超级用户可以重命名数据库;非超级用户拥有者还必须具有CREATEDB权限。当前连接的数据库不能被重命名(如果需要这样做,请连接到另一个数据库)。 + + + 第三种形式更改数据库的所有者。要更改所有者,必须拥有该数据库,并且还必须是新所有者角色的直接或间接成员,并且必须拥有CREATEDB权限。(请注意,超级用户会自动拥有所有这些权限。) + + + 第四种形式更改数据库的默认表空间。只有数据库拥有者或者超级用户可以这样做;你还必须对新表空间具有创建权限。该命令会将数据库旧默认表空间中的所有表和索引在物理上移动到新表空间中。对于该数据库而言,新默认表空间必须为空,并且不能有人连接到该数据库。位于非默认表空间中的表和索引不受影响。 + + + + 其余形式会更改某个PostgreSQL数据库的运行时配置变量的会话默认值。此后每当在该数据库中启动一个新会话时,指定的值就会成为会话默认值。数据库特定的默认值会覆盖postgresql.conf中的设置,或者从postgres命令行接收到的设置。只有数据库拥有者或者超级用户才能更改该数据库的会话默认值。某些变量不能以这种方式设置,或者只能由超级用户设置。 + + + + + 参数 + + + + name + + + 要修改其属性的数据库名称。 + + + + + + allowconn + + 如果为 false,则任何人都不能连接到该数据库。 + + + + + connlimit + + + 这个数据库允许多少并发连接。-1 表示没有限制。 + + + + + + istemplate + + + 如果为 true,则任何具有 CREATEDB 权限的用户都可以克隆该数据库;如果为 false,则只有超级用户或该数据库的拥有者可以克隆它。 + + + + + + new_name + + + 数据库的新名称。 + + + + + + new_owner + + + 数据库的新拥有者。 + + + + + + new_tablespace + + + 数据库的新默认表空间。 + + + + + + + configuration_parameter + value + + + 将此数据库在指定配置参数上的会话默认值设为给定值。如果 + valueDEFAULT,或者等效地使用了 + RESET,则数据库特定设置会被移除,因此新会话将继承系统范围的默认设置。使用 + RESET ALL可清除所有数据库特定设置。 + SET FROM CURRENT会把该参数在当前会话中的值保存为数据库特定值。 + + + + 关于允许的参数名和值的更多信息,见。 + + + + + + + + 注解 + + + 也可以把会话默认值绑定到特定角色,而不是数据库;见。如果发生冲突,角色特定设置会覆盖数据库特定设置。 + + + + + 示例 + + + 要在数据库test中默认禁用索引扫描: + + +ALTER DATABASE test SET enable_indexscan TO off; + + + + + 兼容性 + + + ALTER DATABASE语句是一个PostgreSQL扩展。 + + + + + 参见 + + + + + + + + + diff --git a/zh/9.6/ref/alter_default_privileges.sgml b/zh/9.6/ref/alter_default_privileges.sgml new file mode 100644 index 00000000..71cd9886 --- /dev/null +++ b/zh/9.6/ref/alter_default_privileges.sgml @@ -0,0 +1,196 @@ + + + + + ALTER DEFAULT PRIVILEGES + + + + ALTER DEFAULT PRIVILEGES + 7 + SQL - 语言语句 + + + + ALTER DEFAULT PRIVILEGES + 定义默认访问权限 + + + + +ALTER DEFAULT PRIVILEGES + [ FOR { ROLE | USER } target_role [, ...] ] + [ IN SCHEMA schema_name [, ...] ] + abbreviated_grant_or_revoke + +其中abbreviated_grant_or_revoke为以下之一: + +GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } + [, ...] | ALL [ PRIVILEGES ] } + ON TABLES + TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] + +GRANT { { USAGE | SELECT | UPDATE } + [, ...] | ALL [ PRIVILEGES ] } + ON SEQUENCES + TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] + +GRANT { EXECUTE | ALL [ PRIVILEGES ] } + ON FUNCTIONS + TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON TYPES + TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] + +REVOKE [ GRANT OPTION FOR ] + { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } + [, ...] | ALL [ PRIVILEGES ] } + ON TABLES + FROM { [ GROUP ] role_name | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { USAGE | SELECT | UPDATE } + [, ...] | ALL [ PRIVILEGES ] } + ON SEQUENCES + FROM { [ GROUP ] role_name | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { EXECUTE | ALL [ PRIVILEGES ] } + ON FUNCTIONS + FROM { [ GROUP ] role_name | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON TYPES + FROM { [ GROUP ] role_name | PUBLIC } [, ...] + [ CASCADE | RESTRICT ] + + + + + + 描述 + + ALTER DEFAULT PRIVILEGES允许设置将应用于将来创建的对象的权限。(它不影响已存在对象的权限。)目前只能更改表(包括视图和外部表)、序列、函数和类型(包括域)的权限。 + + + 虽然你可以修改自己的默认权限,也可以修改你所属角色的默认权限, + 但在创建对象时,新对象的权限只受当前角色的默认权限影响, + 不会从当前角色所属的任何角色继承。 + + + 下所述,任何对象类型的默认权限通常会向对象所有者授予所有可授予的权限,也可能向PUBLIC授予一些权限。不过,可以通过使用ALTER DEFAULT PRIVILEGES更改全局默认权限来改变这种行为。 + + 按模式指定的默认权限会添加到特定对象类型的全局默认权限中。这意味着,如果权限是全局授予的(无论是默认授予,还是根据未指定模式的先前ALTER DEFAULT PRIVILEGES命令授予),就不能按模式撤销这些权限。按模式执行REVOKE仅用于撤销先前按模式执行的GRANT的效果。 + + + 参数 + + + + target_role + + 当前角色所属的现有角色的名称。如果省略FOR ROLE,则假定为当前角色。 + + + + + schema_name + + + 一个现有模式的名称。如果指定,将修改以后在该模式中创建的对象的默认权限。 + 如果省略IN SCHEMA,则修改全局默认权限。 + + + + + + role_name + + + 要为其授予或撤销权限的现有角色的名称。此参数以及 + abbreviated_grant_or_revoke + 中的所有其他参数,均按中的描述工作, + 只不过这里设置的是整类对象的权限,而不是特定的具名对象。 + + + + + + + + + 注解 + + 使用\ddp命令获取现有默认权限分配的信息。权限值的含义与下对\dp的说明相同。 + + + 如果你希望删除一个其默认权限已被修改的角色, + 则必须先撤销对其默认权限所做的更改,或者使用DROP OWNED BY + 去除该角色的默认权限条目。 + + + + + 示例 + + + 为你后续在模式myschema中创建的所有表(以及视图)向所有人授予 SELECT 权限, + 并且也允许角色webuser对它们执行 INSERT: + + +ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLES TO PUBLIC; +ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLES TO webuser; + + + + + 撤销上述操作,使后续创建的表不再拥有任何超出正常情况的权限: + + +ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLES FROM PUBLIC; +ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLES FROM webuser; + + + + + 移除通常会在函数上授予的公共 EXECUTE 权限, + 使后续由角色admin创建的所有函数不再自动具有该权限: + +ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; + + 但请注意,你不能用仅限单个模式的命令实现这一效果。 + 下面这条命令不会产生作用,除非它是在撤销与之对应的GRANT: + +ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; + + 这是因为按模式设置的默认权限只能向全局设置增加权限, + 不能移除由全局设置授予的权限。 + + + + + 兼容性 + + + 在 SQL 标准中没有ALTER DEFAULT PRIVILEGES语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_domain.sgml b/zh/9.6/ref/alter_domain.sgml new file mode 100644 index 00000000..6592e0cf --- /dev/null +++ b/zh/9.6/ref/alter_domain.sgml @@ -0,0 +1,324 @@ + + + + + ALTER DOMAIN + + + + ALTER DOMAIN + 7 + SQL - 语言语句 + + + + ALTER DOMAIN + + 更改一个域的定义 + + + + + +ALTER DOMAIN name + { SET DEFAULT expression | DROP DEFAULT } +ALTER DOMAIN name + { SET | DROP } NOT NULL +ALTER DOMAIN name + ADD domain_constraint [ NOT VALID ] +ALTER DOMAIN name + DROP CONSTRAINT [ IF EXISTS ] constraint_name [ RESTRICT | CASCADE ] +ALTER DOMAIN name + RENAME CONSTRAINT constraint_name TO new_constraint_name +ALTER DOMAIN name + VALIDATE CONSTRAINT constraint_name +ALTER DOMAIN name + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER DOMAIN name + RENAME TO new_name +ALTER DOMAIN name + SET SCHEMA new_schema + + + + + 描述 + + + ALTER DOMAIN更改一个现有域的定义。有以下几种子形式: + + + + + SET/DROP DEFAULT + + + 这些形式为域设置或移除默认值。注意,默认值只适用于后续的 + INSERT命令;它们不会影响已存在于使用该域的表中的行。 + + + + + + SET/DROP NOT NULL + + + 这些形式更改域是被标记为允许 NULL 值还是拒绝 NULL 值。只有当使用该域的列中 + 不包含空值时,才能执行SET NOT NULL。 + + + + + + ADD domain_constraint [ NOT VALID ] + + 此形式使用与相同的语法向域添加新约束。向域添加新约束时,所有使用该域的列都会根据新添加的约束进行检查。可以使用NOT VALID选项添加新约束来抑制这些检查;之后可以使用ALTER DOMAIN ... VALIDATE CONSTRAINT验证该约束。新插入或更新的行始终根据所有约束进行检查,即使这些约束标记为NOT VALIDNOT VALID只能用于CHECK约束。 + + + + + DROP CONSTRAINT [ IF EXISTS ] + + + 这种形式删除域上的约束。如果指定了IF EXISTS而该约束不存在, + 则不会抛出错误,而是发出一条提示。 + + + + + + RENAME CONSTRAINT + + + 这种形式更改域上某个约束的名称。 + + + + + + VALIDATE CONSTRAINT + + + 这种形式验证一个先前以NOT VALID方式添加的约束,也就是说, + 它会核实该域类型的表列中的所有值都满足指定约束。 + + + + + + OWNER + + + 这种形式将域的拥有者更改为指定用户。 + + + + + + RENAME + + + 这种形式更改域的名称。 + + + + + + SET SCHEMA + + + 这种形式更改域所在的模式。与该域关联的任何约束也会一并移动到新模式中。 + + + + + + 要使用ALTER DOMAIN,必须拥有该域。要更改域的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在域所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建域完成的操作。不过,超级用户无论如何都可以更改任何域的所有权。) + + + + 参数 + + + + + name + + + 要修改的现有域的名称(可以是模式限定的)。 + + + + + + domain_constraint + + + 该域的新约束。 + + + + + + constraint_name + + + 要删除或重命名的现有约束名称。 + + + + + + NOT VALID + + + 不验证现有存储的数据是否满足该约束。 + + + + + + + CASCADE + + + 自动删除依赖于该约束的对象,以及进一步依赖于这些对象的所有对象 + (见)。 + + + + + + RESTRICT + + + 如果存在任何依赖对象,则拒绝删除该约束。这是默认行为。 + + + + + + new_name + + + 域的新名称。 + + + + + + new_constraint_name + + + 约束的新名称。 + + + + + + new_owner + + + 域的新拥有者的用户名。 + + + + + + new_schema + + + 域的新模式。 + + + + + + + + + + 注解 + + + 尽管ALTER DOMAIN ADD CONSTRAINT会尝试验证现有存储的数据 + 是否满足新约束,但这种检查并非万无一失,因为该命令无法看到那些 + 新插入或已更新但尚未提交的表行。如果存在并发操作可能插入不符合约束的数据的风险, + 正确做法是使用NOT VALID选项添加该约束,提交该命令,等待所有在此次 + 提交之前启动的事务结束,然后发出ALTER DOMAIN VALIDATE + CONSTRAINT来查找违反该约束的数据。这种方法是可靠的,因为一旦该约束被 + 提交,所有新事务都保证会对域类型的新值强制执行该约束。 + + + 目前,如果要验证的命名域或任何派生域被数据库中任意表的复合类型列使用,ALTER DOMAIN ADD CONSTRAINTALTER DOMAIN VALIDATE CONSTRAINTALTER DOMAIN SET NOT NULL都会失败。最终应改进这些命令,使其能够验证此类嵌套列上的新约束。 + + + + + 示例 + + + 向域添加一个NOT NULL约束: + +ALTER DOMAIN zipcode SET NOT NULL; + + 从域中移除一个NOT NULL约束: + +ALTER DOMAIN zipcode DROP NOT NULL; + + + + + 向域添加一个检查约束: + +ALTER DOMAIN zipcode ADD CONSTRAINT zipchk CHECK (char_length(VALUE) = 5); + + + + + 从域中移除一个检查约束: + +ALTER DOMAIN zipcode DROP CONSTRAINT zipchk; + + + + + 重命名域上的一个检查约束: + +ALTER DOMAIN zipcode RENAME CONSTRAINT zipchk TO zip_check; + + + + + 将域移动到另一个模式中: + +ALTER DOMAIN zipcode SET SCHEMA customers; + + + + + 兼容性 + + + ALTER DOMAIN符合SQL标准,但 + OWNERRENAME、 + SET SCHEMAVALIDATE CONSTRAINT + 这些变体是PostgreSQL扩展; + ADD CONSTRAINT变体中的NOT VALID子句也 + 是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_event_trigger.sgml b/zh/9.6/ref/alter_event_trigger.sgml new file mode 100644 index 00000000..73bcd87d --- /dev/null +++ b/zh/9.6/ref/alter_event_trigger.sgml @@ -0,0 +1,104 @@ + + + + + ALTER EVENT TRIGGER + + + + ALTER EVENT TRIGGER + 7 + SQL - 语言语句 + + + + ALTER EVENT TRIGGER + 更改事件触发器的定义 + + + + +ALTER EVENT TRIGGER name DISABLE +ALTER EVENT TRIGGER name ENABLE [ REPLICA | ALWAYS ] +ALTER EVENT TRIGGER name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER EVENT TRIGGER name RENAME TO new_name + + + + + 描述 + + + ALTER EVENT TRIGGER更改现有事件触发器 + 的属性。 + + + + 要修改事件触发器,你必须是超级用户。 + + + + + 参数 + + + + name + + + 要修改的现有事件触发器的名称。 + + + + + + new_owner + + + 该事件触发器的新所有者的用户名。 + + + + + + new_name + + + 该事件触发器的新名称。 + + + + + + DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER + + + 这些形式配置事件触发器的触发行为。被禁用的触发器仍为系统所知, + 但在其触发事件发生时不会执行。另见 + 。 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER EVENT TRIGGER语句。 + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/alter_extension.sgml b/zh/9.6/ref/alter_extension.sgml new file mode 100644 index 00000000..1f2448be --- /dev/null +++ b/zh/9.6/ref/alter_extension.sgml @@ -0,0 +1,295 @@ + + + + + ALTER EXTENSION + + + + ALTER EXTENSION + 7 + SQL - 语言语句 + + + + ALTER EXTENSION + + 更改扩展的定义 + + + + + +ALTER EXTENSION name UPDATE [ TO new_version ] +ALTER EXTENSION name SET SCHEMA new_schema +ALTER EXTENSION name ADD member_object +ALTER EXTENSION name DROP member_object + +其中member_object为: + + ACCESS METHOD object_name | + AGGREGATE aggregate_name ( aggregate_signature ) | + CAST (source_type AS target_type) | + COLLATION object_name | + CONVERSION object_name | + DOMAIN object_name | + EVENT TRIGGER object_name | + FOREIGN DATA WRAPPER object_name | + FOREIGN TABLE object_name | + FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + MATERIALIZED VIEW object_name | + OPERATOR operator_name (left_type, right_type) | + OPERATOR CLASS object_name USING index_method | + OPERATOR FAMILY object_name USING index_method | + [ PROCEDURAL ] LANGUAGE object_name | + SCHEMA object_name | + SEQUENCE object_name | + SERVER object_name | + TABLE object_name | + TEXT SEARCH CONFIGURATION object_name | + TEXT SEARCH DICTIONARY object_name | + TEXT SEARCH PARSER object_name | + TEXT SEARCH TEMPLATE object_name | + TRANSFORM FOR type_name LANGUAGE lang_name | + TYPE object_name | + VIEW object_name + +其中 aggregate_signature 是: + +* | +[ argmode ] [ argname ] argtype [ , ... ] | +[ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + + + + + 描述 + + + ALTER EXTENSION更改已安装扩展的定义。 + 其子形式如下: + + + + UPDATE + + + 这种形式将扩展更新到较新的版本。该扩展必须提供适当的更新 + 脚本(或一系列脚本),能够将当前已安装版本修改为所请求的版本。 + + + + + + SET SCHEMA + + + 这种形式将扩展的对象移入另一个模式。要使该命令成功,扩展 + 必须是可重定位的。 + + + + + + ADD member_object + + + 这种形式将一个现有对象加入扩展。这主要在扩展更新脚本中有用。 + 该对象随后将被视为扩展的成员;特别是,只能通过删除该扩展 + 来删除它。 + + + + + + DROP member_object + + + 这种形式从扩展中移除一个成员对象。这主要在扩展更新脚本中有用。 + 该对象不会被删除,而只是与扩展解除关联。 + + + + + + 有关这些操作的更多信息,见。 + + + + 要使用ALTER EXTENSION,你必须拥有该扩展。 + ADD/DROP形式还要求你拥有被添加/移除对象的所有权。 + + + + + 参数 + + + + + name + + + 一个已安装扩展的名称。 + + + + + + new_version + + + 扩展要更新到的新版本。它可以写成标识符或字符串常量。如果未指定, + ALTER EXTENSION UPDATE会尝试更新到该扩展控制文件中 + 标明的默认版本。 + + + + + + new_schema + + + 扩展的新模式。 + + + + + + object_name + aggregate_name + function_name + operator_name + + 要添加到扩展或从扩展中移除的对象名称。表、聚合、域、外部表、函数、操作符、操作符类、操作符族、序列、文本搜索对象、类型和视图的名称可以带模式限定。 + + + + + source_type + + + 类型转换的源数据类型的名称。 + + + + + + target_type + + + 类型转换的目标数据类型的名称。 + + + + + + argmode + + + 函数或聚合函数参数的模式:INOUTINOUTVARIADIC。如果省略,默认值为IN。请注意,ALTER EXTENSION实际上不会关注OUT参数,因为确定函数身份只需要输入参数。因此,列出ININOUTVARIADIC参数就足够了。 + + + + + argname + + + 函数或聚合函数参数的名称。请注意,ALTER EXTENSION实际上不会关注参数名称,因为确定函数身份只需要参数数据类型。 + + + + + argtype + + + 函数或聚合函数参数的数据类型。 + + + + + left_type + right_type + + 操作符参数的数据类型(可带模式限定)。对于前缀或后缀操作符缺少的参数,写作NONE + + + + + PROCEDURAL + + + + 这是一个噪声词。 + + + + + + type_name + + + + 该转换的数据类型的名称。 + + + + + + lang_name + + + + 该转换的语言的名称。 + + + + + + + + + 示例 + + + 将hstore扩展更新到版本 2.0: + +ALTER EXTENSION hstore UPDATE TO '2.0'; + + + + + 将hstore扩展的模式更改为utils: + +ALTER EXTENSION hstore SET SCHEMA utils; + + + + + 将现有函数添加到hstore扩展中: + +ALTER EXTENSION hstore ADD FUNCTION populate_record(anyelement, hstore); + + + + + 兼容性 + + + ALTER EXTENSION是一个PostgreSQL + 扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_foreign_data_wrapper.sgml b/zh/9.6/ref/alter_foreign_data_wrapper.sgml new file mode 100644 index 00000000..281da286 --- /dev/null +++ b/zh/9.6/ref/alter_foreign_data_wrapper.sgml @@ -0,0 +1,178 @@ + + + + + ALTER FOREIGN DATA WRAPPER + + + + ALTER FOREIGN DATA WRAPPER + 7 + SQL - 语言语句 + + + + ALTER FOREIGN DATA WRAPPER + 更改外部数据包装器的定义 + + + + +ALTER FOREIGN DATA WRAPPER name + [ HANDLER handler_function | NO HANDLER ] + [ VALIDATOR validator_function | NO VALIDATOR ] + [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) ] +ALTER FOREIGN DATA WRAPPER name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER FOREIGN DATA WRAPPER name RENAME TO new_name + + + + + 描述 + + + ALTER FOREIGN DATA WRAPPER更改外部数据包装器的 + 定义。该命令的第一种形式更改外部数据包装器的支持函数或通用选项 + (至少需要一个子句)。第二种形式更改外部数据包装器的拥有者。 + + + + 只有超级用户可以更改外部数据包装器。此外,只有超级用户才能成为外部 + 数据包装器的拥有者。 + + + + + 参数 + + + + name + + + 一个现有外部数据包装器的名称。 + + + + + + HANDLER handler_function + + + 为外部数据包装器指定一个新的处理器函数。 + + + + + + NO HANDLER + + + 用于指定该外部数据包装器不再具有一个处理器函数。 + + + 注意,使用没有处理器函数的外部数据包装器的外部表将无法访问。 + + + + + + VALIDATOR validator_function + + + 为外部数据包装器指定一个新的验证器函数。 + + + + 注意,该外部数据包装器本身或者依赖它的服务器、用户映射或外部表中 + 预先存在的选项,根据新的验证器可能是无效的。PostgreSQL + 不会检查这一点。用户必须在使用修改后的外部数据包装器之前自行确保 + 这些选项正确。不过,在此ALTER FOREIGN DATA + WRAPPER命令中指定的任何选项,都会使用新的验证器进行 + 检查。 + + + + + + NO VALIDATOR + + + 用于指定该外部数据包装器不再具有验证器函数。 + + + + + + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + + + 更改外部数据包装器的选项。ADDSET + 和DROP指定要执行的动作。如果未显式指定操作, + 则假定为ADD。选项名称必须唯一;如果存在验证器 + 函数,选项名称和值也会使用该外部数据包装器的验证器函数进行验证。 + + + + + + new_owner + + + 该外部数据包装器的新拥有者的用户名。 + + + + + + new_name + + + 该外部数据包装器的新名称。 + + + + + + + + 示例 + + 更改一个外部数据包装器dbi,添加选项foo,删除bar: + +ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar'); + + + + + 将外部数据包装器dbi的验证器更改为 + bob.myvalidator: + +ALTER FOREIGN DATA WRAPPER dbi VALIDATOR bob.myvalidator; + + + + + 兼容性 + + + ALTER FOREIGN DATA WRAPPER符合 ISO/IEC + 9075-9 (SQL/MED),但HANDLER、 + VALIDATOROWNER TO和 + RENAME子句属于扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_foreign_table.sgml b/zh/9.6/ref/alter_foreign_table.sgml new file mode 100644 index 00000000..0b037fb0 --- /dev/null +++ b/zh/9.6/ref/alter_foreign_table.sgml @@ -0,0 +1,480 @@ + + + + + ALTER FOREIGN TABLE + + + + ALTER FOREIGN TABLE + 7 + SQL - 语言语句 + + + + ALTER FOREIGN TABLE + 更改外部表的定义 + + + + +ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] name [ * ] + action [, ... ] +ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] name [ * ] + RENAME [ COLUMN ] column_name TO new_column_name +ALTER FOREIGN TABLE [ IF EXISTS ] name + RENAME TO new_name +ALTER FOREIGN TABLE [ IF EXISTS ] name + SET SCHEMA new_schema + +其中action为以下之一: + + ADD [ COLUMN ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ] + DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ] + ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] + ALTER [ COLUMN ] column_name SET DEFAULT expression + ALTER [ COLUMN ] column_name DROP DEFAULT + ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL + ALTER [ COLUMN ] column_name SET STATISTICS integer + ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] ) + ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) + ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } + ALTER [ COLUMN ] column_name OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) + ADD table_constraint [ NOT VALID ] + VALIDATE CONSTRAINT constraint_name + DROP CONSTRAINT [ IF EXISTS ] constraint_name [ RESTRICT | CASCADE ] + DISABLE TRIGGER [ trigger_name | ALL | USER ] + ENABLE TRIGGER [ trigger_name | ALL | USER ] + ENABLE REPLICA TRIGGER trigger_name + ENABLE ALWAYS TRIGGER trigger_name + SET WITH OIDS + SET WITHOUT OIDS + INHERIT parent_table + NO INHERIT parent_table + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) + + + + + 描述 + + + ALTER FOREIGN TABLE更改现有外部表的定义。有几种子形式: + + ADD COLUMN + + 该形式使用与相同的语法向外部表添加一个新列。与向普通表添加列的情况不同,底层存储不会发生任何变化;该操作只是声明现在可以通过外部表访问某个新列。 + + + + + DROP COLUMN [ IF EXISTS ] + + + 该形式从外部表中删除一列。如果该表之外的任何对象依赖于该列,例如视图, + 则需要指定CASCADE。如果指定了IF EXISTS + 而该列不存在,则不会抛出错误,而是改为发出一条提示。 + + + + + + SET DATA TYPE + + + 该形式更改外部表中一列的类型。同样,这不会影响任何底层存储: + 该操作只是更改PostgreSQL认为该列具有的类型。 + + + + + + SET/DROP DEFAULT + + + 这些形式为列设置或移除默认值。默认值只会应用于后续的 + INSERTUPDATE命令; + 它们不会导致表中已有的行发生变化。 + + + + + + SET/DROP NOT NULL + + + 把一列标记为允许或不允许空值。 + + + + + + SET STATISTICS + + + 该形式为后续 + 操作设置每列的统计信息收集目标。 + 详见的类似形式。 + + + + + + SET ( attribute_option = value [, ... ] ) + RESET ( attribute_option [, ... ] ) + + + 该形式设置或重置每个属性的选项。 + 详见的类似形式。 + + + + + + + SET STORAGE + + + + 该形式设置列的存储模式。 + 详见的类似形式。 + 注意,除非该表的外部数据包装器选择理会该设置,否则存储模式不会产生效果。 + + + + + + ADD table_constraint [ NOT VALID ] + + 该形式使用与相同的语法向外部表添加一个新约束。目前只支持CHECK约束。 + + 与向普通表添加约束的情况不同,不会采取任何措施来验证约束是否正确;相反,该操作只是声明外部表中的所有行都应被假定满足某个新条件。(参见中的讨论。)如果约束被标记为NOT VALID,则不会假定其成立,而只会记录下来供将来使用。 + + + + + VALIDATE CONSTRAINT + + + 该形式把先前被标记为NOT VALID的约束标记为有效。 + 不会采取任何措施来验证该约束,但未来的查询会假定它成立。 + + + + + + DROP CONSTRAINT [ IF EXISTS ] + + + 该形式删除外部表上指定的约束。如果指定了IF EXISTS + 而该约束不存在,则不会抛出错误,而是改为发出一条提示。 + + + + + + DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER + + 这些形式配置属于外部表的触发器的触发。更多详情参见的类似形式。 + + + + + SET WITH OIDS + + 该形式向表添加一个oid系统列(参见)。如果表已有 OID,则不执行任何操作。除非表的外部数据包装器支持 OID,否则该列只会读作零。 + + 请注意,这并不等同于ADD COLUMN oid oid;后者会添加一个碰巧命名为oid的普通列,而不是系统列。 + + + + + SET WITHOUT OIDS + + 该形式从表中移除oid系统列。这完全等同于DROP COLUMN oid RESTRICT,但如果表中已经没有oid列,则不会报错。 + + + + + INHERIT parent_table + + 该形式将目标外部表添加为指定父表的新子表。更多详情参见的类似形式。 + + + + + NO INHERIT parent_table + + + 该形式把目标外部表从指定父表的子表列表中移除。 + + + + + + OWNER + + + 该形式把外部表的拥有者更改为指定用户。 + + + + + + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + + + 更改外部表或其某一列的选项。ADD、SET + 和DROP指定要执行的动作。如果没有显式指定操作,则默认是 + ADD。不允许重复的选项名(不过表选项和列选项同名是可以的)。 + 选项名和值也会通过外部数据包装器库进行验证。 + + + + + + RENAME + + + RENAME形式更改外部表的名称,或者更改其中某一列的名称。 + + + + + + SET SCHEMA + + + 该形式把外部表移动到另一个模式中。 + + + + + + + + + 除了RENAMESET SCHEMA之外,所有动作都可以组合在一个包含多项修改的列表中一并应用。 + 例如,可以在一条命令中添加多个列和/或更改多个列的类型。 + + + + 如果命令写成ALTER FOREIGN TABLE IF EXISTS ...而外部表不存在, + 则不会抛出错误。这种情况下会发出一个提示。 + + + 要使用ALTER FOREIGN TABLE,必须拥有该表。要更改外部表的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在表所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建该表完成的操作。不过,超级用户无论如何都可以更改任何表的所有权。)要添加列或更改列类型,还必须在数据类型上拥有USAGE权限。 + + + + 参数 + + + + + name + + + 要修改的现有外部表的名称(可以带模式限定)。如果在表名前指定了 + ONLY,则只修改该表。如果未指定ONLY, + 则该表及其所有后代表(如果有)都会被修改。也可以在表名后指定 + *,显式表示包括后代表。 + + + + + + column_name + + + 新列或现有列的名称。 + + + + + + new_column_name + + + 现有列的新名称。 + + + + + + new_name + + + 表的新名称。 + + + + + + data_type + + + 新列的数据类型,或现有列的新数据类型。 + + + + + + table_constraint + + + 该外部表的新表约束。 + + + + + + constraint_name + + + 要删除的现有约束的名称。 + + + + + + CASCADE + + + 自动删除依赖于被删除列或约束的对象(例如引用该列的视图), + 以及所有进一步依赖于这些对象的对象(见)。 + + + + + + RESTRICT + + + 如果存在任何依赖对象,则拒绝删除该列或约束。这是默认行为。 + + + + + + trigger_name + + + 要禁用或启用的单个触发器的名称。 + + + + + + ALL + + + 禁用或启用属于该外部表的所有触发器。(如果其中任何触发器是内部生成的触发器, + 则需要超级用户权限。核心系统不会向外部表添加这类触发器,但附加代码可能会这么做。) + + + + + + USER + + + 禁用或启用属于该外部表的所有触发器,但内部生成的触发器除外。 + + + + + + parent_table + + + 要与该外部表关联或解除关联的父表。 + + + + + + new_owner + + + 该表的新拥有者的用户名。 + + + + + + new_schema + + + 该表将被移动到的模式名称。 + + + + + + + + 注解 + + + 关键字COLUMN只是噪声,可以省略。 + + + + 使用ADD COLUMNDROP COLUMN添加或移除列, + 添加NOT NULLCHECK约束, + 或使用SET DATA TYPE更改列类型时,不会检查与外部服务器的一致性。 + 用户有责任确保表定义与远端一致。 + + + + 关于有效参数的进一步说明,请参见 + 。 + + + + + 示例 + + + 要把一列标记为非空: + +ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL; + + + + 要更改外部表的选项: +ALTER FOREIGN TABLE myschema.distributors OPTIONS (ADD opt1 'value', SET opt2 'value2', DROP opt3 'value3'); + + + + + + 兼容性 + + + ADDDROPSET DATA TYPE + 形式符合 SQL 标准。其他形式是 + PostgreSQL对 SQL 标准的扩展。 + 另外,在单条ALTER FOREIGN TABLE命令中指定多个修改也是一种扩展。 + + + + ALTER FOREIGN TABLE DROP COLUMN可用于删除外部表的唯一一列, + 从而留下一个零列的表。这是 SQL 的一种扩展,因为 SQL 不允许零列外部表。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_function.sgml b/zh/9.6/ref/alter_function.sgml new file mode 100644 index 00000000..e60e2bcc --- /dev/null +++ b/zh/9.6/ref/alter_function.sgml @@ -0,0 +1,350 @@ + + + + + ALTER FUNCTION + + + + ALTER FUNCTION + 7 + SQL - 语言语句 + + + + ALTER FUNCTION + 更改函数的定义 + + + + +ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + action [ ... ] [ RESTRICT ] +ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + RENAME TO new_name +ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + SET SCHEMA new_schema +ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + DEPENDS ON EXTENSION extension_name + +其中action为以下之一: + + CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT + IMMUTABLE | STABLE | VOLATILE + [ NOT ] LEAKPROOF + [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER + PARALLEL { UNSAFE | RESTRICTED | SAFE } + COST execution_cost + ROWS result_rows + SET configuration_parameter { TO | = } { value | DEFAULT } + SET configuration_parameter FROM CURRENT + RESET configuration_parameter + RESET ALL + + + + + 描述 + + + ALTER FUNCTION更改函数的定义。 + + + 要使用ALTER FUNCTION,必须拥有该函数。要更改函数的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在函数所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建函数完成的操作。不过,超级用户无论如何都可以更改任何函数的所有权。) + + + + 参数 + + + + name + + + + 现有函数的名称(可以用模式限定)。 + + + + + + argmode + + + + + 参数的模式:INOUT、 + INOUTVARIADIC。如果省略, + 默认为IN。注意ALTER FUNCTION + 实际上并不关心任何OUT参数,因为确定函数身份只需 + 要输入参数。因此,只列出IN、 + INOUTVARIADIC参数就足够了。 + + + + + + argname + + + + + 参数的名称。注意ALTER FUNCTION实际上并不关心 + 参数名,因为确定函数身份只需要参数数据类型。 + + + + + + argtype + + + + + 函数参数的数据类型(如果有,可以用模式限定)。 + + + + + + new_name + + + + 该函数的新名称。 + + + + + + new_owner + + + + 该函数的新拥有者。注意,如果该函数被标记为 + SECURITY DEFINER,随后它将以新拥有者的身份执行。 + + + + + + new_schema + + + + 该函数的新模式。 + + + + + + extension_name + + 该函数所依赖的扩展名称。 + + + + + CALLED ON NULL INPUT + RETURNS NULL ON NULL INPUT + STRICT + + + + CALLED ON NULL INPUT将该函数改为在其部分 + 或全部参数为 null 时仍会被调用。 + RETURNS NULL ON NULL INPUT或 + STRICT将该函数改为只要任一参数为 null 就不调用, + 而是自动假定结果为 null。详见。 + + + + + + IMMUTABLE + STABLE + VOLATILE + + + + + 将该函数的易变性更改为指定的设置。详见 + 。 + + + + + + EXTERNAL SECURITY INVOKER + EXTERNAL SECURITY DEFINER + + + + + 更改该函数是否为安全性定义者。关键字EXTERNAL + 为了符合 SQL 标准而被忽略。关于这一能力的更多信息,参见 + 。 + + + + + + PARALLEL + + + + + 更改该函数是否被视为对并行执行安全。详见 + 。 + + + + + + LEAKPROOF + + + + 更改该函数是否被视为防泄漏的。关于这一能力的更多信息,参见 + 。 + + + + + + COST execution_cost + + + + + 更改该函数的估计执行代价。详见。 + + + + + + ROWS result_rows + + + + + 更改集合返回函数估计返回的行数。详见 + 。 + + + + + + configuration_parameter + value + + + + 添加或更改在调用该函数时要赋给某个配置参数的值。如果 + valueDEFAULT,或者 + 等效地使用了RESET,则会移除函数局部设置,这样 + 该函数就会使用其环境中现有的值执行。使用RESET ALL + 可以清除所有函数局部设置。SET FROM CURRENT + 会把执行ALTER FUNCTION时该参数的当前值保存为 + 进入函数时要应用的值。 + + + + 关于允许的参数名和值的更多信息,参见和 + 。 + + + + + + RESTRICT + + + + + 为符合 SQL 标准而被忽略。 + + + + + + + + + 示例 + + + 要将类型integer的函数sqrt + 重命名为square_root: + +ALTER FUNCTION sqrt(integer) RENAME TO square_root; + + + + + 要将类型integer的函数sqrt + 的拥有者改为joe: + +ALTER FUNCTION sqrt(integer) OWNER TO joe; + + + + + 要将类型integer的函数sqrt + 的模式改为maths: + +ALTER FUNCTION sqrt(integer) SET SCHEMA maths; + + + + + 要将类型integer的函数sqrt + 标记为依赖于扩展mathlib: + +ALTER FUNCTION sqrt(integer) DEPENDS ON EXTENSION mathlib; + + + + + 要调整自动为函数设置的搜索路径: + +ALTER FUNCTION check_password(text) SET search_path = admin, pg_temp; + + + + + 要禁用自动为函数设置的search_path: + +ALTER FUNCTION check_password(text) RESET search_path; + + 现在该函数将使用其调用者所使用的搜索路径执行。 + + + + + + 兼容性 + + + 该语句与 SQL 标准中的ALTER FUNCTION语句部分兼 + 容。标准允许修改函数的更多属性,但不提供重命名函数、将函数设为安全 + 性定义者、为函数附加配置参数值,或者更改函数的拥有者、模式或易变性 + 的能力。标准还要求使用RESTRICT关键字,而它在 + PostgreSQL中是可选的。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_group.sgml b/zh/9.6/ref/alter_group.sgml new file mode 100644 index 00000000..9a4cbe90 --- /dev/null +++ b/zh/9.6/ref/alter_group.sgml @@ -0,0 +1,119 @@ + + + + + ALTER GROUP + + + + ALTER GROUP + 7 + SQL - 语言语句 + + + + ALTER GROUP + 更改角色名称或成员资格 + + + + +ALTER GROUP role_specification ADD USER user_name [, ... ] +ALTER GROUP role_specification DROP USER user_name [, ... ] + +其中role_specification可以是: + + role_name + | CURRENT_USER + | SESSION_USER + +ALTER GROUP group_name RENAME TO new_name + + + + + 描述 + + + ALTER GROUP更改用户组的属性。 + 这是一个已废弃的命令,但出于向后兼容的考虑仍然接受它, + 因为组(以及用户)已经被更一般的角色概念所取代。 + + + 前两个变体将用户加入一个组,或将其从组中移除。(为此目的,任何角色都可以充当用户。)这些变体实际上等效于对名为的角色授予或撤销成员资格,因此更推荐使用 + + 第三个变体更改组的名称。这完全等同于使用重命名角色。 + + + + 参数 + + + + group_name + + + 要修改的组(角色)的名称。 + + + + + + user_name + + + 要加入该组或从该组中移除的用户(角色)。 + 这些用户必须已经存在;ALTER GROUP不会创建或删除用户。 + + + + + + new_name + + + 该组的新名称。 + + + + + + + + 示例 + + 向组中添加用户: + + +ALTER GROUP staff ADD USER karl, john; + + + 从组中移除用户: + + +ALTER GROUP workers DROP USER beth; + + + + + 兼容性 + + + SQL 标准中没有ALTER GROUP语句。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/alter_index.sgml b/zh/9.6/ref/alter_index.sgml new file mode 100644 index 00000000..a300f9b8 --- /dev/null +++ b/zh/9.6/ref/alter_index.sgml @@ -0,0 +1,214 @@ + + + + + ALTER INDEX + + + + ALTER INDEX + 7 + SQL - 语言语句 + + + + ALTER INDEX + 更改索引的定义 + + + + +ALTER INDEX [ IF EXISTS ] name RENAME TO new_name +ALTER INDEX [ IF EXISTS ] name SET TABLESPACE tablespace_name +ALTER INDEX name DEPENDS ON EXTENSION extension_name +ALTER INDEX [ IF EXISTS ] name SET ( storage_parameter [= value] [, ... ] ) +ALTER INDEX [ IF EXISTS ] name RESET ( storage_parameter [, ... ] ) +ALTER INDEX ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ] + SET TABLESPACE new_tablespace [ NOWAIT ] + + + + + 描述 + + + ALTER INDEX更改现有索引的定义。有几种子形式: + + + RENAME + + RENAME形式更改索引的名称。存储的数据不受影响。 + + + + + SET TABLESPACE + + 该形式将索引的表空间更改为指定的表空间,并将与索引关联的数据文件移动到新表空间。要更改索引的表空间,必须拥有该索引,并在新表空间上拥有CREATE权限。可以使用ALL IN TABLESPACE形式移动当前数据库中某个表空间内的所有索引;该形式会锁定所有要移动的索引,然后逐个移动。该形式还支持OWNED BY,只移动指定角色所拥有的索引。如果指定NOWAIT选项,而命令无法立即获取所需的全部锁,则会失败。请注意,该命令不会移动系统目录;如有需要,请改用ALTER DATABASE或显式调用ALTER INDEX。另请参见 + + + + + DEPENDS ON EXTENSION + + 该形式将索引标记为依赖扩展,因此扩展被删除时索引也会自动删除。 + + + + + SET ( storage_parameter [= value] [, ... ] ) + + 该形式更改索引的一个或多个索引方法特定存储参数。有关可用参数的详情,请参见。请注意,该命令不会立即修改索引内容;根据参数的不同,可能需要使用重建索引才能达到预期效果。 + + + + + RESET ( storage_parameter [, ... ] ) + + + 这种形式将一个或多个索引方法相关的存储参数重置为默认值。与 + SET一样,可能仍需要执行一次REINDEX + 才能让该索引被完全更新。 + + + + + + + + + + + 参数 + + + + + IF EXISTS + + + 如果该索引不存在,则不要抛出错误。这种情况下会发出一条提示。 + + + + + + name + + + 要修改的现有索引名称(可以是模式限定的)。 + + + + + + new_name + + + 该索引的新名称。 + + + + + + tablespace_name + + + 该索引将被移动到的表空间。 + + + + + + extension_name + + + 该索引所依赖的扩展的名称。 + + + + + + storage_parameter + + + 一个索引方法相关的存储参数的名称。 + + + + + + value + + + 一个索引方法相关的存储参数的新值。根据具体参数,它可能是数字或一个词。 + + + + + + + + + 注解 + + 这些操作也可以使用完成。实际上,ALTER INDEX只是ALTER TABLE中适用于索引的那些形式的别名。 + + + 以前曾有一种ALTER INDEX OWNER变体,但现在会被忽略 + (同时发出警告)。索引的拥有者不能不同于其所属表的拥有者。更改表的拥有者时, + 索引的拥有者也会自动更改。 + + + + 不允许更改系统目录索引的任何部分。 + + + + + 示例 + + 要重命名现有索引: + +ALTER INDEX distributors RENAME TO suppliers; + + + + + 要将索引移到另一个表空间: + +ALTER INDEX distributors SET TABLESPACE fasttablespace; + + + + + 要更改索引的填充因子(假设该索引方法支持fillfactor参数): + +ALTER INDEX distributors SET (fillfactor = 75); +REINDEX INDEX distributors; + + + + + + 兼容性 + + + ALTER INDEX是一种 + PostgreSQL扩展。 + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_language.sgml b/zh/9.6/ref/alter_language.sgml new file mode 100644 index 00000000..3c4d0994 --- /dev/null +++ b/zh/9.6/ref/alter_language.sgml @@ -0,0 +1,89 @@ + + + + + ALTER LANGUAGE + + + + ALTER LANGUAGE + 7 + SQL - 语言语句 + + + + ALTER LANGUAGE + 更改一种过程语言的定义 + + + + +ALTER [ PROCEDURAL ] LANGUAGE name RENAME TO new_name +ALTER [ PROCEDURAL ] LANGUAGE name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + + + + + 描述 + + + ALTER LANGUAGE更改一种过程语言的定义。 + 目前唯一的功能是重命名该语言或为其指定新的所有者。 + 要使用ALTER LANGUAGE,你必须是超级用户或者该语言的所有者。 + + + + + 参数 + + + + name + + + 语言的名称 + + + + + + new_name + + + 该语言的新名称 + + + + + + new_owner + + + 该语言的新所有者 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER LANGUAGE语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_large_object.sgml b/zh/9.6/ref/alter_large_object.sgml new file mode 100644 index 00000000..bb0c805c --- /dev/null +++ b/zh/9.6/ref/alter_large_object.sgml @@ -0,0 +1,78 @@ + + + + + ALTER LARGE OBJECT + + + + ALTER LARGE OBJECT + 7 + SQL - 语言语句 + + + + ALTER LARGE OBJECT + 更改一个大对象的定义 + + + + +ALTER LARGE OBJECT large_object_oid OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + + + + + 描述 + + + ALTER LARGE OBJECT更改一个大对象的定义。 + + + 要使用ALTER LARGE OBJECT,必须拥有该大对象。要更改所有者,还必须是新所有者角色的直接或间接成员。(不过,超级用户无论如何都可以更改任何大对象。)目前唯一的功能是指定新的所有者,因此两项限制始终适用。 + + + + 参数 + + + + large_object_oid + + + 要修改的大对象的 OID + + + + + + new_owner + + + 该大对象的新拥有者 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER LARGE OBJECT语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_materialized_view.sgml b/zh/9.6/ref/alter_materialized_view.sgml new file mode 100644 index 00000000..8d6c6e91 --- /dev/null +++ b/zh/9.6/ref/alter_materialized_view.sgml @@ -0,0 +1,163 @@ + + + + + ALTER MATERIALIZED VIEW + + + + ALTER MATERIALIZED VIEW + 7 + SQL - 语言语句 + + + + ALTER MATERIALIZED VIEW + 更改一个物化视图的定义 + + + + +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + action [, ... ] +ALTER MATERIALIZED VIEW name + DEPENDS ON EXTENSION extension_name +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + RENAME [ COLUMN ] column_name TO new_column_name +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + RENAME TO new_name +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + SET SCHEMA new_schema +ALTER MATERIALIZED VIEW ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ] + SET TABLESPACE new_tablespace [ NOWAIT ] + +其中action为以下之一: + + ALTER [ COLUMN ] column_name SET STATISTICS integer + ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] ) + ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) + ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } + CLUSTER ON index_name + SET WITHOUT CLUSTER + SET ( storage_parameter [= value] [, ... ] ) + RESET ( storage_parameter [, ... ] ) + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + + + + + 描述 + + + ALTER MATERIALIZED VIEW更改一个现有物化视图的 + 各种辅助属性。 + + + 要使用ALTER MATERIALIZED VIEW,必须拥有该物化视图。要更改物化视图的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在物化视图所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建物化视图完成的操作。不过,超级用户无论如何都可以更改任何视图的所有权。) + + DEPENDS ON EXTENSION形式将物化视图标记为依赖扩展,因此扩展被删除时物化视图会自动删除。 + + ALTER MATERIALIZED VIEW可用的语句子形式和操作是ALTER TABLE可用项的子集,用于物化视图时含义相同。详情请参见的说明。 + + + + 参数 + + + + + name + + + 一个现有物化视图的名称(可以是模式限定的)。 + + + + + + column_name + + + 新列或现有列的名称。 + + + + + + extension_name + + 物化视图所依赖的扩展名称。 + + + + + new_column_name + + + 现有列的新名称。 + + + + + + new_owner + + + 该物化视图的新拥有者的用户名。 + + + + + + new_name + + + 该物化视图的新名称。 + + + + + + new_schema + + + 该物化视图的新模式。 + + + + + + + + 示例 + + + 把物化视图foo重命名为 + bar: + +ALTER MATERIALIZED VIEW foo RENAME TO bar; + + + + + 兼容性 + + + ALTER MATERIALIZED VIEW是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_opclass.sgml b/zh/9.6/ref/alter_opclass.sgml new file mode 100644 index 00000000..f9a6e2ef --- /dev/null +++ b/zh/9.6/ref/alter_opclass.sgml @@ -0,0 +1,113 @@ + + + + + ALTER OPERATOR CLASS + + + + ALTER OPERATOR CLASS + 7 + SQL - 语言语句 + + + + ALTER OPERATOR CLASS + 更改一个操作符类的定义 + + + + +ALTER OPERATOR CLASS name USING index_method + RENAME TO new_name + +ALTER OPERATOR CLASS name USING index_method + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + +ALTER OPERATOR CLASS name USING index_method + SET SCHEMA new_schema + + + + + 描述 + + + ALTER OPERATOR CLASS更改一个操作符类的定义。 + + + 要使用ALTER OPERATOR CLASS,必须拥有该操作符类。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在操作符类所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建操作符类完成的操作。不过,超级用户无论如何都可以更改任何操作符类的所有权。) + + + + 参数 + + + + name + + + 一个现有操作符类的名称(可以是模式限定的)。 + + + + + + index_method + + + 该操作符类所对应的索引方法的名称。 + + + + + + new_name + + + 该操作符类的新名称。 + + + + + + new_owner + + + 该操作符类的新拥有者。 + + + + + + new_schema + + + 该操作符类的新模式。 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER OPERATOR CLASS语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_operator.sgml b/zh/9.6/ref/alter_operator.sgml new file mode 100644 index 00000000..c0dff2db --- /dev/null +++ b/zh/9.6/ref/alter_operator.sgml @@ -0,0 +1,134 @@ + + + + + ALTER OPERATOR + + + + ALTER OPERATOR + 7 + SQL - 语言语句 + + + + ALTER OPERATOR + 更改一个操作符的定义 + + + + +ALTER OPERATOR name ( { left_type | NONE } , { right_type | NONE } ) + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + +ALTER OPERATOR name ( { left_type | NONE } , { right_type | NONE } ) + SET SCHEMA new_schema + +ALTER OPERATOR name ( { left_type | NONE } , { right_type | NONE } ) + SET ( { RESTRICT = { res_proc | NONE } + | JOIN = { join_proc | NONE } + } [, ... ] ) + + + + + 描述 + + + ALTER OPERATOR更改一个操作符的定义。 + + + 要使用ALTER OPERATOR,必须拥有该操作符。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在操作符所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建操作符完成的操作。不过,超级用户无论如何都可以更改任何操作符的所有权。) + + + + 参数 + + + + name + + 现有操作符的名称(可带模式限定)。 + + + + + left_type + + 操作符左操作数的数据类型;如果操作符没有左操作数,写作NONE + + + + + right_type + + 操作符右操作数的数据类型;如果操作符没有右操作数,写作NONE + + + + + new_owner + + 操作符的新所有者。 + + + + + new_schema + + 操作符的新模式。 + + + + + res_proc + + 该操作符的限制性选择率估计函数;写作 NONE 可删除现有选择率估计函数。 + + + + + join_proc + + 该操作符的连接选择率估计函数;写作 NONE 可删除现有选择率估计函数。 + + + + + + + + 示例 + + 更改自定义操作符的拥有者,该操作符为a @@ b,类型为text: + +ALTER OPERATOR @@ (text, text) OWNER TO joe; + + + 更改自定义操作符的限制和连接选择率估计函数,操作符为a && b,类型为int[]: + +ALTER OPERATOR && (_int4, _int4) SET (RESTRICT = _int_contsel, JOIN = _int_contjoinsel); + + + + + + 兼容性 + + + 在 SQL 标准中没有ALTER OPERATOR语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_opfamily.sgml b/zh/9.6/ref/alter_opfamily.sgml new file mode 100644 index 00000000..026c3aa2 --- /dev/null +++ b/zh/9.6/ref/alter_opfamily.sgml @@ -0,0 +1,308 @@ + + + + + ALTER OPERATOR FAMILY + + + + ALTER OPERATOR FAMILY + 7 + SQL - 语言语句 + + + + ALTER OPERATOR FAMILY + 更改一个操作符族的定义 + + + + +ALTER OPERATOR FAMILY name USING index_method ADD + { OPERATOR strategy_number operator_name ( op_type, op_type ) + [ FOR SEARCH | FOR ORDER BY sort_family_name ] + | FUNCTION support_number [ ( op_type [ , op_type ] ) ] + function_name ( argument_type [, ...] ) + } [, ... ] + +ALTER OPERATOR FAMILY name USING index_method DROP + { OPERATOR strategy_number ( op_type [ , op_type ] ) + | FUNCTION support_number ( op_type [ , op_type ] ) + } [, ... ] + +ALTER OPERATOR FAMILY name USING index_method + RENAME TO new_name + +ALTER OPERATOR FAMILY name USING index_method + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + +ALTER OPERATOR FAMILY name USING index_method + SET SCHEMA new_schema + + + + + 描述 + + + ALTER OPERATOR FAMILY更改一个操作符族的定义。 + 你可以向该族添加操作符和支持函数、从该族中移除它们,或者更改该族的 + 名称或拥有者。 + + + + 使用ALTER OPERATOR FAMILY向一个族中添加操作符和 + 支持函数时,它们并不属于该族内任何特定的操作符类,而只是作为该族中的 + 松散成员存在。这表明这些操作符和函数与该族的语义兼容, + 但并不是任何特定索引正确工作所必需的。(那些确实必需的操作符和函数应当 + 声明为某个操作符类的一部分;参见。) + PostgreSQL允许随时从一个族中删除松散成员, + 但如果不删除整个类以及依赖它的任何索引,就不能删除操作符类的成员。 + 通常,单一数据类型的操作符和函数属于操作符类,因为支持该特定数据类型上的 + 索引需要它们,而跨数据类型的操作符和函数则作为该族中的松散成员。 + + + + 要使用ALTER OPERATOR FAMILY,你必须是超级用户。 + (之所以这样限制,是因为错误的操作符族定义可能会使服务器混乱,甚至导致 + 崩溃。) + + + + ALTER OPERATOR FAMILY目前不会检查操作符族定义 + 是否包括该索引方法所要求的全部操作符和函数,也不会检查这些操作符和函数 + 是否构成一个自洽的集合。定义一个合法的操作符族是用户的责任。 + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 一个现有操作符族的名称(可以是模式限定的)。 + + + + + + index_method + + + 该操作符族所对应的索引方法名称。 + + + + + + strategy_number + + + 与该操作符族关联的操作符的索引方法策略号。 + + + + + + operator_name + + + 与该操作符族关联的操作符名称(可以是模式限定的)。 + + + + + + op_type + + OPERATOR子句中,操作符的操作数数据类型,或使用NONE表示左一元或右一元操作符。与CREATE OPERATOR CLASS中的对应语法不同,必须始终指定操作数数据类型。 + + ADD FUNCTION子句中,如果函数要支持的操作数数据类型不同于函数的输入数据类型,则指定这些数据类型。对于 B-树 比较函数和哈希函数,不需要指定op_type,因为函数的输入数据类型始终是应使用的正确类型。对于 B-树 排序支持函数以及 GiST、SP-GiST 和 GIN 操作符类中的所有函数,则必须指定该函数要配合使用的操作数数据类型。 + + + 在DROP FUNCTION子句中,必须指定该函数意图支持的 + 操作数数据类型。 + + + + + + sort_family_name + + + 一个现有btree操作符族的名称(可以是模式限定的), + 它描述与某个排序操作符关联的排序顺序。 + + + + 如果既没有指定FOR SEARCH,也没有指定 + FOR ORDER BY,则默认值是FOR SEARCH。 + + + + + + support_number + + 与操作符族关联的函数的索引方法支持函数编号。 + + + + + function_name + + 作为操作符族索引方法支持函数的函数名称(可带模式限定)。 + + + + + argument_type + + + 该函数的参数数据类型。 + + + + + + new_name + + + 该操作符族的新名称。 + + + + + + new_owner + + + 该操作符族的新拥有者。 + + + + + + new_schema + + + 该操作符族所在的新模式。 + + + + + + + OPERATORFUNCTION子句可以以任何顺序出现。 + + + + + + 注解 + + + 注意,DROP语法只通过策略号或支持函数编号以及输入数据 + 类型来指定操作符族中的槽位,不会提及占用该槽位的操作符 + 或函数名称。另外,对于DROP FUNCTION,要指定的类型是 + 该函数意图支持的输入数据类型;对于 GiST、SP-GiST 和 GIN 索引,这些类型 + 可能与该函数实际的输入参数类型毫无关系。 + + + + 因为索引机制在使用函数之前不会检查函数的访问权限,将一个函数或者操作符 + 包括在一个操作符族中相当于在其上授予公共执行权限。这对操作符族中很有用 + 的这类函数来说通常不成问题。 + + + 不应由 SQL 函数定义这些操作符。SQL 函数很可能会被内联到调用查询中,从而阻止优化器识别该查询与索引匹配。 + + + 在PostgreSQL 8.4 之前,OPERATOR子句可以包含RECHECK选项。现在不再支持该选项,因为索引操作符是否有损会在运行时动态确定。这样可以高效处理操作符可能有损也可能无损的情况。 + + + + + 示例 + + + 下列示例命令向一个已经包含数据类型int4int2 + 的 B-树操作符类的操作符族中添加跨数据类型的操作符和支持函数。 + + + +ALTER OPERATOR FAMILY integer_ops USING btree ADD + + -- int4 对 int2 + OPERATOR 1 < (int4, int2) , + OPERATOR 2 <= (int4, int2) , + OPERATOR 3 = (int4, int2) , + OPERATOR 4 >= (int4, int2) , + OPERATOR 5 > (int4, int2) , + FUNCTION 1 btint42cmp(int4, int2) , + + -- int2 对 int4 + OPERATOR 1 < (int2, int4) , + OPERATOR 2 <= (int2, int4) , + OPERATOR 3 = (int2, int4) , + OPERATOR 4 >= (int2, int4) , + OPERATOR 5 > (int2, int4) , + FUNCTION 1 btint24cmp(int2, int4) ; + + + + 要再次移除这些条目: + + + +ALTER OPERATOR FAMILY integer_ops USING btree DROP + + -- int4 对 int2 + OPERATOR 1 (int4, int2) , + OPERATOR 2 (int4, int2) , + OPERATOR 3 (int4, int2) , + OPERATOR 4 (int4, int2) , + OPERATOR 5 (int4, int2) , + FUNCTION 1 (int4, int2) , + + -- int2 对 int4 + OPERATOR 1 (int2, int4) , + OPERATOR 2 (int2, int4) , + OPERATOR 3 (int2, int4) , + OPERATOR 4 (int2, int4) , + OPERATOR 5 (int2, int4) , + FUNCTION 1 (int2, int4) ; + + + + + 兼容性 + + + 在 SQL 标准中没有 + ALTER OPERATOR FAMILY语句。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/alter_policy.sgml b/zh/9.6/ref/alter_policy.sgml new file mode 100644 index 00000000..5aec73d3 --- /dev/null +++ b/zh/9.6/ref/alter_policy.sgml @@ -0,0 +1,133 @@ + + + + + ALTER POLICY + + + + ALTER POLICY + 7 + SQL - 语言语句 + + + + ALTER POLICY + 更改行级安全策略的定义 + + + + +ALTER POLICY name ON table_name RENAME TO new_name + +ALTER POLICY name ON table_name + [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ] + [ USING ( using_expression ) ] + [ WITH CHECK ( check_expression ) ] + + + + + 描述 + + + ALTER POLICY更改一条现有行级安全性策略的定义。 + + + + 要使用ALTER POLICY,你必须拥有该策略所适用的表。 + + + + 在ALTER POLICY的第二种形式中,角色列表、 + using_expression和 + check_expression在被指定时会分别独立替换。 + 省略这些子句中的任意一个时,策略中对应的部分保持不变。 + + + + + 参数 + + + + name + + + 要更改的现有策略名称。 + + + + + + table_name + + + 该策略所在表的名称(可选模式限定)。 + + + + + + new_name + + + 该策略的新名称。 + + + + + + role_name + + + 该策略适用的角色。可以一次指定多个角色。要将该策略应用于所有角色, + 可使用PUBLIC。 + + + + + + using_expression + + + 该策略的USING表达式。详见 + 。 + + + + + + check_expression + + + 该策略的WITH CHECK表达式。详见 + 。 + + + + + + + + + 兼容性 + + + ALTER POLICYPostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_role.sgml b/zh/9.6/ref/alter_role.sgml new file mode 100644 index 00000000..becdb1ee --- /dev/null +++ b/zh/9.6/ref/alter_role.sgml @@ -0,0 +1,280 @@ + + + + + ALTER ROLE + + + + ALTER ROLE + 7 + SQL - 语言语句 + + + + ALTER ROLE + 更改数据库角色 + + + + +ALTER ROLE role_specification [ WITH ] option [ ... ] + +其中option可以是: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + +ALTER ROLE name RENAME TO new_name + +ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter { TO | = } { value | DEFAULT } +ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter FROM CURRENT +ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET configuration_parameter +ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET ALL + +其中role_specification可以是: + + role_name + | CURRENT_USER + | SESSION_USER + + + + + 描述 + + + ALTER ROLE更改 + PostgreSQL角色的属性。 + + + 概要中列出的此命令的第一种形式可以更改中可指定的许多角色属性。(涵盖了所有可能的属性,但没有用于添加或移除成员资格的选项;请使用完成此操作。)命令中未提及的属性保留其以前的设置。数据库超级用户可以更改任何角色的任何这些设置。拥有CREATEROLE权限的角色可以更改除SUPERUSERREPLICATIONBYPASSRLS之外的任何这些设置,但只能针对非超级用户且非复制角色。普通角色只能更改自己的密码。 + + 第二种形式更改角色的名称。数据库超级用户可以重命名任何角色。拥有CREATEROLE权限的角色可以重命名非超级用户角色。当前会话用户不能被重命名。(如果需要这样做,请以其他用户连接。)由于MD5加密的密码使用角色名作为密码学盐,重命名角色会清除其密码(如果密码使用MD5加密)。 + + + 其余形式更改角色针对配置变量的会话默认值,可以针对所有数据库,也可以在指定 + IN DATABASE子句时仅针对指定数据库中的会话。如果指定的是 + ALL而不是角色名,就会更改所有角色的该项设置。将 + ALLIN DATABASE一起使用,实际上与命令 + ALTER DATABASE ... SET ...的效果相同。 + + + 当该角色随后启动新会话时,指定值将成为会话默认值,覆盖postgresql.conf中的设置或从postgres命令行接收的设置。这只发生在登录时;执行不会导致设置新的配置值。为所有数据库设置的设置会被附加到角色的数据库特定设置覆盖。特定数据库或特定角色的设置会覆盖为所有角色设置的设置。 + + 超级用户可以更改任何人的会话默认值。拥有CREATEROLE权限的角色可以更改非超级用户角色的默认值。普通角色只能为自己设置默认值。某些配置变量不能以这种方式设置,或者只能由超级用户执行命令来设置。只有超级用户可以更改所有数据库中所有角色的设置。 + + + + 参数 + + + + name + + + + 将要修改其属性的角色名称。 + + + + + + CURRENT_USER + + + + 修改当前用户,而不是显式标识的角色。 + + + + + + SESSION_USER + + + + 修改当前会话用户,而不是显式标识的角色。 + + + + + + SUPERUSER + NOSUPERUSER + CREATEDB + NOCREATEDB + CREATEROLE + NOCREATEROLE + INHERIT + NOINHERIT + LOGIN + NOLOGIN + REPLICATION + NOREPLICATION + BYPASSRLS + NOBYPASSRLS + CONNECTION LIMIT connlimit + PASSWORD password + ENCRYPTED + UNENCRYPTED + VALID UNTIL 'timestamp' + + 这些子句更改最初由设置的属性。更多信息请参见CREATE ROLE参考页。 + + + + + new_name + + + + 角色的新名称。 + + + + + + database_name + + + + 应在其中设置该配置变量的数据库名称。 + + + + + + configuration_parameter + value + + + 将该角色在指定配置参数上的会话默认值设为给定值。如果 + valueDEFAULT, + 或者等效地使用了RESET,则会移除角色特定变量设置, + 因此该角色在新会话中将继承系统范围的默认设置。使用 + RESET ALL可清除所有角色特定设置。 + SET FROM CURRENT会将该参数在当前会话中的值保存为角色特定值。 + 如果指定了IN DATABASE,则只会为给定角色和数据库 + 设置或移除该配置参数。 + + + 角色特定的变量设置只在登录时生效;不会处理角色特定的变量设置。 + + + 关于允许的参数名称和值详见和 + 。 + + + + + + + + 注解 + + 使用添加新角色,使用移除角色。 + + ALTER ROLE不能更改角色的成员资格。请使用完成此操作。 + + + 用这个命令指定未加密密码时必须加以注意。密码将以明文形式传输到服务器, + 并且也可能被记录在客户端命令历史或服务器日志中。 + 包含一个命令\password,可用于更改角色的密码而不暴露明文密码。 + + + + 也可以把会话默认值绑定到特定数据库,而不是角色;详见 + 。如果发生冲突,同时针对数据库和角色的设置 + 会覆盖角色特定设置,而角色特定设置又会覆盖数据库特定设置。 + + + + + 示例 + + + 更改角色的密码: + + +ALTER ROLE davide WITH PASSWORD 'hu8jmn3'; + + + + + 移除角色的密码: + + +ALTER ROLE davide WITH PASSWORD NULL; + + + + + 更改密码的失效日期,并指定该密码应在比 + UTC快 1 小时的时区中,于 2015 年 5 月 4 日中午失效: + +ALTER ROLE chris VALID UNTIL 'May 4 12:00:00 2015 +1'; + + + + + 使密码永久有效: + +ALTER ROLE fred VALID UNTIL 'infinity'; + + + + 授予角色创建其他角色和新数据库的能力: +ALTER ROLE miriam CREATEROLE CREATEDB; + + + + + 为角色指定 + 参数的非默认设置: + + +ALTER ROLE worker_bee SET maintenance_work_mem = 100000; + + + + + 为角色指定 + 参数的数据库特定的非默认设置: + + +ALTER ROLE fred IN DATABASE devel SET client_min_messages = DEBUG; + + + + + 兼容性 + + + ALTER ROLE语句是 + PostgreSQL的扩展。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/alter_rule.sgml b/zh/9.6/ref/alter_rule.sgml new file mode 100644 index 00000000..9bba9714 --- /dev/null +++ b/zh/9.6/ref/alter_rule.sgml @@ -0,0 +1,103 @@ + + + + + ALTER RULE + + + + ALTER RULE + 7 + SQL - 语言语句 + + + + ALTER RULE + 修改一条重写规则的定义 + + + + +ALTER RULE name ON table_name RENAME TO new_name + + + + + 描述 + + + ALTER RULE更改一条现有规则的属性。目前唯一可用的 + 操作是更改规则的名称。 + + + + 要使用ALTER RULE,你必须是该规则适用的表或视图 + 的拥有者。 + + + + + 参数 + + + + name + + + 要修改的现有规则的名称。 + + + + + + table_name + + + 该规则适用的表或视图名称(可以是模式限定的)。 + + + + + + new_name + + + 该规则的新名称。 + + + + + + + + 示例 + + + 要重命名一条现有规则: + +ALTER RULE notify_all ON emp RENAME TO notify_me; + + + + + 兼容性 + + + ALTER RULEPostgreSQL + 的一种语言扩展,整个查询重写系统也是如此。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_schema.sgml b/zh/9.6/ref/alter_schema.sgml new file mode 100644 index 00000000..274a89b7 --- /dev/null +++ b/zh/9.6/ref/alter_schema.sgml @@ -0,0 +1,89 @@ + + + + + ALTER SCHEMA + + + + ALTER SCHEMA + 7 + SQL - 语言语句 + + + + ALTER SCHEMA + 更改一个模式的定义 + + + + +ALTER SCHEMA name RENAME TO new_name +ALTER SCHEMA name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + + + + + 描述 + + + ALTER SCHEMA更改一个模式的定义。 + + + 要使用ALTER SCHEMA,必须拥有该模式。要重命名模式,还必须在数据库上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且必须在数据库上拥有CREATE权限。(请注意,超级用户会自动拥有所有这些权限。) + + + + 参数 + + + + name + + + 现有模式的名称。 + + + + + + new_name + + + 该模式的新名称。新名称不能以pg_开头,因为这类名称是为系统模式保留的。 + + + + + + new_owner + + + 该模式的新拥有者。 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER SCHEMA语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_sequence.sgml b/zh/9.6/ref/alter_sequence.sgml new file mode 100644 index 00000000..c85b0b5b --- /dev/null +++ b/zh/9.6/ref/alter_sequence.sgml @@ -0,0 +1,250 @@ + + + + + ALTER SEQUENCE + + + + ALTER SEQUENCE + 7 + SQL - 语言语句 + + + + ALTER SEQUENCE + + 更改序列发生器的定义 + + + + + +ALTER SEQUENCE [ IF EXISTS ] name [ INCREMENT [ BY ] increment ] + [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ] + [ START [ WITH ] start ] + [ RESTART [ [ WITH ] restart ] ] + [ CACHE cache ] [ [ NO ] CYCLE ] + [ OWNED BY { table_name.column_name | NONE } ] +ALTER SEQUENCE [ IF EXISTS ] name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER SEQUENCE [ IF EXISTS ] name RENAME TO new_name +ALTER SEQUENCE [ IF EXISTS ] name SET SCHEMA new_schema + + + + + 描述 + + + ALTER SEQUENCE更改现有序列发生器的参数。在 + ALTER SEQUENCE命令中未明确设置的参数,会保留其原有设置。 + + + 要使用ALTER SEQUENCE,必须拥有该序列。要更改序列的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在序列所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建序列完成的操作。不过,超级用户无论如何都可以更改任何序列的所有权。) + + + + 参数 + + + + + name + + + 要更改的序列名称(可以是模式限定的)。 + + + + + + IF EXISTS + + + 如果该序列不存在,则不要抛出错误。这种情况下会发出一条提示。 + + + + + + increment + + + 子句INCREMENT BY increment是可选的。正值会创建升序序列, + 负值会创建降序序列。如果未指定,则保留原有的增量值。 + + + + + + minvalue + NO MINVALUE + + + 可选子句MINVALUE minvalue确定序列可生成的最小值。 + 如果指定了NO MINVALUE,则升序序列和降序序列将分别使用默认值 1 和 -263-1。 + 如果两种选项都未指定,则保留当前最小值。 + + + + + + maxvalue + NO MAXVALUE + + + 可选子句MAXVALUE maxvalue确定序列的最大值。 + 如果指定了NO MAXVALUE,则升序序列和降序序列将分别使用默认值 263-1 和 -1。 + 如果两种选项都未指定,则保留当前最大值。 + + + + + + start + + + 可选子句START WITH start更改序列记录的起始值。 + 这不会影响当前序列值;它只是设置未来ALTER SEQUENCE RESTART命令将使用的值。 + + + + + + restart + + + 可选子句RESTART [ WITH restart ]更改序列的当前值。 + 这等价于以is_called = false调用setval函数: + 指定的值会由对nextval下一次调用返回。 + 写成不带restart值的RESTART,等价于提供 + 由CREATE SEQUENCE记录、或最近由ALTER SEQUENCE START WITH设置的起始值。 + + + + + + cache + + CACHE cache子句启用预分配序列号并将其存储在内存中,以便更快访问。最小值为 1(一次只能生成一个值,即不缓存)。如果未指定,则保留旧的缓存值。 + + + + + CYCLE + + 可选的CYCLE关键字可用于启用序列回绕:当递增或递减序列分别达到maxvalueminvalue时回绕。如果达到限制,下一个生成的数将分别为minvaluemaxvalue + + + + + NO CYCLE + + + 如果指定了可选关键字NO CYCLE,则当序列达到其最大值后,任何对 + nextval的调用都会返回错误。如果既未指定CYCLE也未指定 + NO CYCLE,则保留原有的循环行为。 + + + + + + OWNED BY table_name.column_name + OWNED BY NONE + + OWNED BY选项使序列与特定表列关联;如果该列(或其整个表)被删除,序列也会自动删除。如果指定此选项,该关联会替换序列此前指定的任何关联。指定的表必须与序列拥有相同的所有者,并且位于同一模式中。指定OWNED BY NONE会移除现有的关联,使序列成为独立的对象。 + + + + + new_owner + + 序列的新所有者的用户名。 + + + + + new_name + + + 该序列的新拥有者的用户名。 + + + + + + new_schema + + + 该序列的新名称。 + + + + + + + + + + 注解 + + + 为了避免阻塞从同一序列获取数字的并发事务,ALTER SEQUENCE对序列生成参数的更改永远不会被回滚;这些更改会立即生效且不可逆转。不过,OWNED BYOWNER TORENAME TOSET SCHEMA子句引起的是普通的目录更新,可以回滚。 + + + + ALTER SEQUENCE不会立即影响除当前后端之外、已经预分配(缓存)了序列值的其他后端中的 + nextval结果。它们会先用完所有缓存值,然后才会注意到序列生成参数已经改变。 + 当前后端则会立即受到影响。 + + + + ALTER SEQUENCE不会影响该序列的currval状态。 + (在PostgreSQL 8.3 之前,它有时会这样做。) + + + + 由于历史原因,序列也可以使用ALTER TABLE;但允许用于序列的 + ALTER TABLE变体仅限于与上述形式等价的那些。 + + + + + 示例 + + + 将一个名为serial的序列重启到 105: + +ALTER SEQUENCE serial RESTART WITH 105; + + + + + 兼容性 + + + ALTER SEQUENCE符合SQL标准,但 + START WITH、 + OWNED BYOWNER TORENAME TO和 + SET SCHEMA子句是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_server.sgml b/zh/9.6/ref/alter_server.sgml new file mode 100644 index 00000000..83edefb7 --- /dev/null +++ b/zh/9.6/ref/alter_server.sgml @@ -0,0 +1,134 @@ + + + + + ALTER SERVER + + + + ALTER SERVER + 7 + SQL - 语言语句 + + + + ALTER SERVER + 更改外部服务器的定义 + + + + +ALTER SERVER name [ VERSION 'new_version' ] + [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) ] +ALTER SERVER name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER SERVER name RENAME TO new_name + + + + + 描述 + + + ALTER SERVER更改外部服务器的定义。第一种形式 + 更改服务器的版本字符串或其通用选项(至少需要一个子句)。第二种形式 + 更改服务器的拥有者。 + + + 要更改服务器,必须是服务器的所有者。此外,要更改所有者,还必须拥有该服务器,是新所有者角色的直接或间接成员,并且必须在服务器的外部数据包装器上拥有USAGE权限。(请注意,超级用户会自动满足所有这些条件。) + + + + 参数 + + + + name + + + 一个现有服务器的名称。 + + + + + + new_version + + + 新服务器版本。 + + + + + + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + + + 更改服务器的选项。ADD、SET和 + DROP指定要执行的动作。如果未显式指定操作, + 则假定为ADD。选项名称必须唯一;名称和值也会使 + 用该服务器的外部数据包装器库进行验证。 + + + + + + new_owner + + + 该外部服务器的新拥有者的用户名。 + + + + + + new_name + + + 该外部服务器的新名称。 + + + + + + + + 示例 + + + 更改服务器foo,添加连接选项: + +ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb'); + + + + + 更改服务器foo,更改版本并修改 + host选项: + +ALTER SERVER foo VERSION '8.4' OPTIONS (SET host 'baz'); + + + + + 兼容性 + + + ALTER SERVER符合 ISO/IEC 9075-9 (SQL/MED)。 + OWNER TORENAME形式是 + PostgreSQL 扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_system.sgml b/zh/9.6/ref/alter_system.sgml new file mode 100644 index 00000000..e5991604 --- /dev/null +++ b/zh/9.6/ref/alter_system.sgml @@ -0,0 +1,127 @@ + + + + + ALTER SYSTEM + + + + ALTER SYSTEM + 7 + SQL - 语言语句 + + + + ALTER SYSTEM + 更改服务器配置参数 + + + + +ALTER SYSTEM SET configuration_parameter { TO | = } { value | 'value' | DEFAULT } + +ALTER SYSTEM RESET configuration_parameter +ALTER SYSTEM RESET ALL + + + + + 描述 + + + ALTER SYSTEM用于更改整个数据库集簇范围内的服 + 务器配置参数。与传统的手工编辑 + postgresql.conf文件相比,它可能更方便。 + ALTER SYSTEM会把给定的参数设置写入 + postgresql.auto.conf文件;除读取 + postgresql.conf之外,系统还会读取该文件。把参数设置为 + DEFAULT,或者使用RESET变体, + 会从postgresql.auto.conf文件中移除相应的配置 + 项。使用RESET ALL可以移除所有这类配置项。 + + + + 用ALTER SYSTEM设置的值,会在下一次重新加载服务 + 器配置后生效;对于那些只能在服务器启动时更改的参数,则会在下一次服 + 务器重启后生效。重新加载服务器配置可以通过调用 SQL 函数 + pg_reload_conf()、运行 + pg_ctl reload,或者向主服务器进程发送 + SIGHUP信号来触发。 + + + 只有超级用户可以使用ALTER SYSTEM。此外,由于此命令直接作用于文件系统且无法回滚,因此不允许在事务块或函数中使用。 + + + + 参数 + + + + configuration_parameter + + + 一个可设置的配置参数名称。可用参数见 + 。 + + + + + + value + + 参数的新值。根据具体参数的不同,可以适当地将值指定为字符串常量、标识符、数字或这些值的逗号分隔列表。可以写入DEFAULT,以指定从postgresql.auto.conf中移除参数及其值。 + + + + + + + 注解 + + 此命令不能用于设置,也不能用于设置postgresql.conf中不允许的参数(例如预设选项)。 + + + 关于设置这些参数的其他方法,见。 + + + + + 示例 + + + 设置wal_level: + +ALTER SYSTEM SET wal_level = replica; + + + + + 撤销该设置,恢复为postgresql.conf中生效的设置: + +ALTER SYSTEM RESET wal_level; + + + + + + 兼容性 + + + ALTER SYSTEM语句是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_table.sgml b/zh/9.6/ref/alter_table.sgml new file mode 100644 index 00000000..a8fade1f --- /dev/null +++ b/zh/9.6/ref/alter_table.sgml @@ -0,0 +1,764 @@ + + + + + ALTER TABLE + + + + ALTER TABLE + 7 + SQL - 语言语句 + + + + ALTER TABLE + 更改一个表的定义 + + + + +ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ] + action [, ... ] +ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ] + RENAME [ COLUMN ] column_name TO new_column_name +ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ] + RENAME CONSTRAINT constraint_name TO new_constraint_name +ALTER TABLE [ IF EXISTS ] name + RENAME TO new_name +ALTER TABLE [ IF EXISTS ] name + SET SCHEMA new_schema +ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ] + SET TABLESPACE new_tablespace [ NOWAIT ] +其中action为以下之一: + + ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ] + DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ] + ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ] + ALTER [ COLUMN ] column_name SET DEFAULT expression + ALTER [ COLUMN ] column_name DROP DEFAULT + ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL + ALTER [ COLUMN ] column_name SET STATISTICS integer + ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] ) + ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) + ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } + ADD table_constraint [ NOT VALID ] + ADD table_constraint_using_index + ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + VALIDATE CONSTRAINT constraint_name + DROP CONSTRAINT [ IF EXISTS ] constraint_name [ RESTRICT | CASCADE ] + DISABLE TRIGGER [ trigger_name | ALL | USER ] + ENABLE TRIGGER [ trigger_name | ALL | USER ] + ENABLE REPLICA TRIGGER trigger_name + ENABLE ALWAYS TRIGGER trigger_name + DISABLE RULE rewrite_rule_name + ENABLE RULE rewrite_rule_name + ENABLE REPLICA RULE rewrite_rule_name + ENABLE ALWAYS RULE rewrite_rule_name + DISABLE ROW LEVEL SECURITY + ENABLE ROW LEVEL SECURITY + FORCE ROW LEVEL SECURITY + NO FORCE ROW LEVEL SECURITY + CLUSTER ON index_name + SET WITHOUT CLUSTER + SET WITH OIDS + SET WITHOUT OIDS + SET TABLESPACE new_tablespace + SET { LOGGED | UNLOGGED } + SET ( storage_parameter [= value] [, ... ] ) + RESET ( storage_parameter [, ... ] ) + INHERIT parent_table + NO INHERIT parent_table + OF type_name + NOT OF + OWNER TO { new_owner | CURRENT_USER | SESSION_USER } + REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING } + +table_constraint_using_index为: + + [ CONSTRAINT constraint_name ] + { UNIQUE | PRIMARY KEY } USING INDEX index_name + [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + + + + + 描述 + + + ALTER TABLE更改现有表的定义。下面介绍几种子形式。请注意,每种子形式所需的锁级别可能不同。除非明确说明,否则会获取一个ACCESS EXCLUSIVE锁。当给出多个子命令时,获取的锁将是任一子命令所需的最严格锁。 + + ADD COLUMN [ IF NOT EXISTS ] + + 该形式使用与相同的语法向表添加新列。如果指定IF NOT EXISTS且已存在同名列,则不会抛出错误。 + + + + + DROP COLUMN [ IF EXISTS ] + + + 该形式从表中删除一列。涉及该列的索引和表约束也会自动删除。如果表外有任何对象依赖于该列,例如外键引用或视图,你就需要指定CASCADE。如果指定了IF EXISTS而该列不存在,则不会报错;此时会发出一条提示。 + + + + + + SET DATA TYPE + + 该形式更改表中某列的类型。涉及该列的索引和简单表约束会通过重新解析最初提供的表达式,自动转换为使用新列类型。可选的COLLATE子句为新列指定排序规则;如果省略,排序规则是新列类型的默认排序规则。可选的USING子句指定如何根据旧值计算新列值;如果省略,默认转换与从旧数据类型到新数据类型的赋值转换相同。如果旧类型到新类型之间不存在隐式转换或赋值转换,则必须提供USING子句。 + + + + + SET/DROP DEFAULT + + + 这些形式为列设置或移除默认值。默认值只会应用于后续的 + INSERTUPDATE命令; + 它们不会导致表中已有的行发生变化。 + + + + + + SET/DROP NOT NULL + + 这些形式更改列是否被标记为允许空值,或拒绝空值。只有当列不包含空值时,才能使用SET NOT NULL + + + + + SET STATISTICS + + 该形式为后续操作设置每列的统计信息收集目标。目标可以设置在 0 到 10000 范围内;也可以将其设置为 -1,以恢复使用系统默认的统计目标()。有关PostgreSQL查询规划器使用统计信息的更多信息,请参见 + + SET STATISTICS会获取一个SHARE UPDATE EXCLUSIVE锁。 + + + + + + SET ( attribute_option = value [, ... ] ) + RESET ( attribute_option [, ... ] ) + + 该形式设置或重置每属性选项。目前定义的每属性选项只有n_distinctn_distinct_inherited,它们会覆盖后续操作所作的不同值数量估计。n_distinct影响表本身的统计信息,而n_distinct_inherited影响为表及其继承子表收集的统计信息。当设置为正值时,ANALYZE会假定该列恰好包含指定数量的不同非空值。当设置为负值时(该值必须大于或等于 -1),ANALYZE会假定列中不同非空值的数量与表大小成线性关系;具体数量通过将估计的表大小乘以给定数值的绝对值计算。例如,-1 表示列中的所有值都不同,而 -0.5 表示平均每个值出现两次。当表大小随时间变化时,这可能很有用,因为只有在查询规划时才会执行与表中行数相乘的操作。指定 0 可恢复正常估计不同值数量。有关PostgreSQL查询规划器使用统计信息的更多信息,请参见 + + 更改每个属性的选项会获取一个SHARE UPDATE EXCLUSIVE锁。 + + + + + + SET STORAGE TOAST 每列存储设置 + + 该形式为列设置存储模式。这控制该列是内联保存还是保存在辅助TOAST表中,以及是否压缩数据。对于integer等定长值,必须使用PLAIN,数据以内联且未压缩的形式保存。MAIN用于内联的可压缩数据。EXTERNAL用于外部保存的未压缩数据,EXTENDED用于外部保存的压缩数据。对于支持非PLAIN存储的大多数数据类型,EXTENDED是默认值。使用EXTERNAL会使非常大的textbytea值上的子字符串操作运行得更快,但会增加存储空间。请注意,SET STORAGE本身不会更改表中的任何内容,它只会设置今后更新表时采用的策略。更多信息请参见 + + + + + ADD table_constraint [ NOT VALID ] + + 该形式使用与相同的约束语法向表添加新约束,另外还提供NOT VALID选项;目前该选项只允许用于外键和 CHECK 约束。 + + 通常,该形式会扫描表,以验证表中所有现有行都满足新约束。但如果使用NOT VALID选项,则会跳过这一可能耗时很长的扫描。后续插入或更新仍会强制执行该约束(也就是说,对于外键,除非被引用表中存在匹配行,否则操作会失败;对于 CHECK 约束,除非新行符合指定检查条件,否则操作会失败)。但是,在使用VALIDATE CONSTRAINT选项验证约束之前,数据库不会假定该约束对表中的所有行都成立。有关使用NOT VALID选项的更多信息,请参见下文 + + + 尽管大多数形式的ADD table_constraint需要ACCESS EXCLUSIVE锁,ADD FOREIGN KEY只需要SHARE ROW EXCLUSIVE锁。请注意,ADD FOREIGN KEY除了在声明约束的表上获取锁之外,还会在被引用的表上获取SHARE ROW EXCLUSIVE锁。 + + + + + + ADD table_constraint_using_index + + 该形式根据现有唯一索引向表添加新的PRIMARY KEYUNIQUE约束。索引的所有列都会包含在约束中。 + + 索引不能包含表达式列,也不能是部分索引。此外,它必须是使用默认排序方式的 B-树 索引。这些限制确保该索引等效于使用常规ADD PRIMARY KEYADD UNIQUE命令构建的索引。 + + 如果指定了PRIMARY KEY,且索引的列尚未标记为NOT NULL,则此命令会尝试对每个此类列执行ALTER COLUMN SET NOT NULL。这需要完整扫描表,以验证这些列不包含空值。在其他所有情况下,这都是快速操作。 + + 如果提供了约束名称,索引将被重命名以匹配约束名称。否则,约束将与索引同名。 + + 执行此命令后,索引将由约束拥有,其方式与常规ADD PRIMARY KEYADD UNIQUE命令构建索引时相同。特别是,删除约束也会使索引消失。 + + + 在需要添加新约束且不希望长时间阻塞表更新的情况下,使用现有索引添加约束可能很有用。为此,请使用CREATE INDEX CONCURRENTLY创建索引,然后使用此语法将其安装为正式约束。请参见下面的示例。 + + + + + + ALTER CONSTRAINT + + 该形式更改先前创建的约束的属性。目前只能更改外键约束。 + + + + + VALIDATE CONSTRAINT + + 该形式通过扫描表来验证先前以NOT VALID创建的外键或 CHECK 约束,确保不存在不满足约束的行。如果约束已经标记为有效,则不执行任何操作。(有关此命令用途的说明,请参见下文。) + 此命令获取一个SHARE UPDATE EXCLUSIVE锁。 + + + + + DROP CONSTRAINT [ IF EXISTS ] + + 该形式从表中删除指定的约束。如果指定IF EXISTS且约束不存在,则不会抛出错误;在这种情况下会发出通知。 + + + + + DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER + + 这些形式配置属于表的触发器的触发。已禁用的触发器仍为系统所知,但在触发事件发生时不会执行。对于延迟触发器,会在事件发生时检查启用状态,而不是在实际执行触发器函数时检查。可以按名称指定单个触发器,或指定表上的所有触发器,或仅指定用户触发器,从而禁用或启用触发器(最后一种选项不包括内部生成的约束触发器,例如用于实现外键约束或可延迟唯一性约束和排他约束的触发器)。禁用或启用内部生成的约束触发器需要超级用户权限;应谨慎操作,因为如果不执行触发器,当然无法保证约束的完整性。触发器触发机制还会受到配置变量的影响。简单启用的触发器会在复制角色为origin(默认值)或local时触发。配置为ENABLE REPLICA的触发器只会在会话处于replica模式时触发,配置为ENABLE ALWAYS的触发器则无论当前复制模式为何都会触发。 + 此命令获取一个SHARE ROW EXCLUSIVE锁。 + + + + + DISABLE/ENABLE [ REPLICA | ALWAYS ] RULE + + 这些形式配置属于表的重写规则的触发。已禁用的规则仍为系统所知,但在查询重写期间不会应用。其语义与禁用或启用触发器时相同。对于ON SELECT规则会忽略此配置;为了使视图即使在当前会话处于非默认复制角色时也能正常工作,这类规则始终会被应用。 + + + + + DISABLE/ENABLE ROW LEVEL SECURITY + + 这些形式控制表所属行安全策略的应用。如果启用且表不存在任何策略,则应用默认拒绝策略。请注意,即使禁用了表的行级安全性,表仍然可以存在策略;在这种情况下,这些策略不会被应用,并且会被忽略。另请参见 + + + + + NO FORCE/FORCE ROW LEVEL SECURITY + + 这些形式控制当用户是表所有者时表所属行安全策略的应用。如果启用,当用户是表所有者时会应用行级安全策略。如果禁用(默认设置),当用户是表所有者时不会应用行级安全性。另请参见 + + + + + CLUSTER ON + + 该形式为将来的操作选择默认索引。它实际上不会对表重新聚簇。 + 更改聚簇选项会获取一个SHARE UPDATE EXCLUSIVE锁。 + + + + + SET WITHOUT CLUSTER + + 该形式从表中移除最近使用的索引规范。这会影响将来的聚簇操作(这些操作未指定索引)。 + 更改聚簇选项会获取一个SHARE UPDATE EXCLUSIVE锁。 + + + + + SET WITH OIDS + + 该形式向表添加一个oid系统列(参见)。如果表已有 OID,则不执行任何操作。 + + 请注意,这并不等同于ADD COLUMN oid oid;后者会添加一个碰巧命名为oid的普通列,而不是系统列。 + + + + + SET WITHOUT OIDS + + 该形式从表中移除oid系统列。这完全等同于DROP COLUMN oid RESTRICT,但如果表中已经没有oid列,则不会报错。 + + + + + SET TABLESPACE + + 该形式将表的表空间更改为指定的表空间,并将与表关联的数据文件移动到新表空间。表上的索引(如果有)不会移动,但可以通过额外的SET TABLESPACE命令单独移动。可以使用ALL IN TABLESPACE形式移动当前数据库中某个表空间内的所有表;该形式会先锁定所有要移动的表,然后逐个移动。该形式还支持OWNED BY,只移动指定角色所拥有的表。如果指定NOWAIT选项,而命令无法立即获取所需的全部锁,则会失败。请注意,该命令不会移动系统目录;如有需要,请改用ALTER DATABASE或显式调用ALTER TABLEinformation_schema关系不被视为系统目录的一部分,因此会被移动。另请参见 + + + + + SET { LOGGED | UNLOGGED } + + 该形式将表从不记录 WAL 的表更改为记录 WAL 的表,或反之(参见)。不能将其应用于临时表。 + + + + + SET ( storage_parameter [= value] [, ... ] ) + + 该形式更改表的一个或多个存储参数。有关可用参数的详情,请参见。请注意,该命令不会立即修改表内容;根据参数的不同,可能需要重写表才能达到预期效果。可以使用VACUUM FULL、,或ALTER TABLE中会强制重写表的某种形式来完成重写。 + + 更改 fillfactor 和 autovacuum 存储参数时会获取SHARE UPDATE EXCLUSIVE锁。 + + + 虽然CREATE TABLE允许在WITH (storage_parameter)语法中指定OIDS,但ALTER TABLE不会将OIDS视为存储参数。应改用SET WITH OIDSSET WITHOUT OIDS形式来更改 OID 状态。 + + + + + + RESET ( storage_parameter [, ... ] ) + + + 该形式把一个或多个存储参数重置为默认值。与SET一样,可能仍需要进行表重写才能让整张表完全更新。 + + + + + + INHERIT parent_table + + 该形式将目标表添加为指定父表的新子表。之后,对父表执行的查询将包含目标表的记录。要作为子表添加,目标表必须已经包含与父表相同的所有列(也可以有额外的列)。列必须具有匹配的数据类型;如果父表中的列具有NOT NULL约束,则子表中的相应列也必须具有NOT NULL约束。 + + 父表的所有CHECK约束还必须在子表中有匹配的约束,但标记为不可继承的约束除外(即在父表中使用ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT创建的约束);这类约束会被忽略。所有匹配的子表约束都不能标记为不可继承。目前不考虑UNIQUEPRIMARY KEYFOREIGN KEY约束,但将来可能会改变。 + + + + + NO INHERIT parent_table + + + 该形式把目标表从指定父表的子表列表中移除。对父表的查询将不再包含来自目标表的记录。 + + + + + + OF type_name + + 该形式将表关联到一个复合类型,就像由CREATE TABLE OF创建该表一样。表的列名和类型列表必须与复合类型的完全匹配;是否存在oid系统列可以不同。该表不能继承任何其他表。这些限制确保CREATE TABLE OF允许等效的表定义。 + + + + + NOT OF + + + 该形式会解除类型化表与其类型之间的关联。 + + + + + + OWNER + + + 该形式把表、序列、视图、物化视图或外部表的所有者更改为指定用户。 + + + + + + REPLICA IDENTITY + + 该形式更改写入预写式日志以标识被更新或删除行的信息。除非正在使用逻辑复制,否则此选项没有效果。DEFAULT(非系统表的默认设置)记录主键列(如果有)的旧值。USING INDEX记录由指定索引覆盖的列的旧值,该索引必须是唯一的、非部分的、不可延迟的,并且只包含被标记为NOT NULL的列。FULL记录该行中所有列的旧值。NOTHING不记录关于旧行的任何信息(这是系统表的默认值)。在所有情况下,只有当至少一个将被记录的列在新旧两个版本的行之间存在差异时,才会记录旧值。 + + + + + + RENAME + + RENAME形式更改表(或索引、序列、视图、物化视图或外部表)的名称,表中单个列的名称,或表的约束名称。存储的数据不受影响。 + + + + + SET SCHEMA + + + 该形式把表移动到另一个模式。相关索引、约束以及由表列拥有的序列也会一并移动。 + + + + + + + + 对单个表执行操作的 ALTER TABLE 所有形式,除了RENAMESET SCHEMA之外,都可以组合成一个列表,一起应用多项更改。例如,可以在一条命令中添加多列和/或更改多列的类型。这对大表尤其有用,因为只需对表执行一次遍历。 + + 要使用ALTER TABLE,必须拥有该表。要更改表的模式或表空间,还必须在新模式或表空间上拥有CREATE权限。要将表作为父表的新子表添加,还必须拥有父表。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在表所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建表完成的操作。不过,超级用户无论如何都可以更改任何表的所有权。)要添加列、更改列类型或使用OF子句,还必须在数据类型上拥有USAGE权限。 + + + + 参数 + + + + + IF EXISTS + + 如果表不存在,则不抛出错误;在这种情况下会发出通知。 + + + + + name + + 要更改的现有表的名称(可带模式限定)。如果在表名之前指定ONLY,则只更改该表。如果未指定ONLY,则更改该表及其所有后代表(如果有)。也可以在表名后指定*,以明确表示包含后代表。 + + + + + column_name + + 新列或现有列的名称。 + + + + + new_column_name + + 现有列的新名称。 + + + + + new_name + + 表的新名称。 + + + + + data_type + + 新列的数据类型,或现有列的新数据类型。 + + + + + table_constraint + + 表的新约束。 + + + + + constraint_name + + 新约束或现有约束的名称。 + + + + + CASCADE + + 自动删除依赖于被删除列或约束的对象(例如引用该列的视图),并依次删除所有依赖于这些对象的对象(参见)。 + + + + + RESTRICT + + 如果存在任何依赖对象,则拒绝删除列或约束。这是默认行为。 + + + + + trigger_name + + 要禁用或启用的单个触发器的名称。 + + + + + ALL + + 禁用或启用属于表的所有触发器。(如果触发器中有内部生成的约束触发器,例如用于实现外键约束或可延迟唯一性约束和排他约束的触发器,则需要超级用户权限。) + + + + + USER + + 禁用或启用属于表的所有触发器,但内部生成的约束触发器除外,例如用于实现外键约束或可延迟唯一性约束和排他约束的触发器。 + + + + + index_name + + 现有索引的名称。 + + + + + storage_parameter + + 表存储参数的名称。 + + + + + value + + 表存储参数的新值。根据参数的不同,这可以是数字或单词。 + + + + + parent_table + + 要与该表关联或解除关联的父表。 + + + + + new_owner + + 表的新所有者的用户名。 + + + + + new_tablespace + + 表将被移动到的表空间名称。 + + + + + new_schema + + 表将被移动到的模式名称。 + + + + + + + + 注解 + + + 关键字COLUMN只是噪声,可以省略。 + + + 使用ADD COLUMN添加列时,表中的所有现有行都会使用该列的默认值初始化(如果未指定DEFAULT子句,则使用 NULL)。如果没有DEFAULT子句,这只是一次元数据更改,不需要立即更新表数据;添加的 NULL 值会在读取时提供。 + + 使用DEFAULT子句添加列,或更改现有列的类型,都需要重写整个表及其索引。作为更改现有列类型时的例外,如果USING子句不改变列内容,并且旧类型要么可以二进制强制转换为新类型,要么是新类型之上的无约束域,则不需要重写表;但受影响列上的任何索引仍必须重建。添加或移除系统oid列也需要重写整个表。对于大型表,重建表和/或索引可能需要相当长的时间,并且会暂时需要最多两倍的磁盘空间。 + + 添加CHECKNOT NULL约束需要扫描表,以验证现有行满足约束,但不需要重写表。 + + 允许在单条ALTER TABLE命令中指定多项更改,主要是因为这样可以将多次表扫描或重写合并为一次遍历。 + + + 扫描大表以验证新的外键、检查或非空约束可能需要很长时间,并且在ALTER TABLE ADD CONSTRAINT命令提交之前,会阻止对该表的其他更新。NOT VALID约束选项的主要目的,是减小添加约束对并发更新的影响。使用NOT VALID时,ADD CONSTRAINT命令不会扫描表,因此可以立即提交。之后可以发出VALIDATE CONSTRAINT命令,以验证现有行满足该约束。验证步骤不需要阻止并发更新,因为它知道其他事务会对它们插入或更新的行强制执行该约束;只需检查预先存在的行。因此,验证只会在被修改的表上获取SHARE UPDATE EXCLUSIVE锁。(如果约束是外键,则被该约束引用的表上还需要ROW SHARE锁。)除了改善并发性之外,在已知该表包含既有违规数据的情况下,NOT VALIDVALIDATE CONSTRAINT也很有用。一旦约束已经建立,就不能再插入新的违规数据,而现有问题则可以从容修正,直到VALIDATE CONSTRAINT最终成功。 + + + DROP COLUMN形式不会从物理上删除列,而只是使其对 SQL 操作不可见。表后续的插入和更新操作会为该列存储空值。因此,删除列的速度很快,但不会立即减少表在磁盘上的大小,因为被删除列占用的空间不会被回收。随着现有行被更新,空间会逐渐回收。(删除系统oid列时不适用这些说明;删除该列会立即重写表。) + + + 若要强制立即回收已删除列所占的空间,可以执行任何一种会导致整表重写的ALTER TABLE形式。这样会重建每一行,并用空值替换被删除的列。 + + + + 会重写表的ALTER TABLE形式对于 MVCC 来说并不安全。表重写完成后,如果并发事务使用的是在重写发生之前取得的快照,那么该表在这些并发事务看来会像一张空表。详见。 + + + + SET DATA TYPEUSING选项实际上可以指定任何涉及该行旧值的表达式;也就是说,它既可以引用正在转换的列,也可以引用其他列。这使得使用SET DATA TYPE语法完成非常通用的转换成为可能。正因为这种灵活性,USING表达式不会应用到列的默认值(如果有)上,因为其结果可能不是默认值所要求的常量表达式。这意味着,当从旧类型到新类型不存在隐式或赋值类型转换时,即便提供了USING子句,SET DATA TYPE也可能仍然无法转换默认值。在这种情况下,可以先用DROP DEFAULT删除默认值,执行ALTER TYPE,然后再用SET DEFAULT添加一个合适的新默认值。类似的考虑也适用于涉及该列的索引和约束。 + + + 如果表有任何后代表,则不允许只在父表中添加、重命名或更改列的类型,或重命名继承的约束,而不对后代表执行相同操作。也就是说,ALTER TABLE ONLY会被拒绝。这确保后代表始终拥有与父表匹配的列。 + + + 只有当某个后代表中的列既不是从其他父表继承而来,也从未有过该列的独立定义时,递归的DROP COLUMN操作才会移除该后代表中的此列。非递归的DROP COLUMN(即ALTER TABLE ONLY ... DROP COLUMN)永远不会移除任何后代列,而只会把它们标记为独立定义,而非继承得到。 + + + TRIGGERCLUSTEROWNERTABLESPACE操作永远不会递归到后代表;也就是说,它们的行为始终如同指定了ONLY。添加约束时,只有未标记为NO INHERITCHECK约束会递归。 + + 不允许更改系统目录表的任何部分。 + + + 有关有效参数的进一步说明,请参见中还有关于继承的更多信息。 + + + + + 示例 + + 要添加类型为varchar的列到表中: +ALTER TABLE distributors ADD COLUMN address varchar(30); + + + + + 要从表中删除一列: + +ALTER TABLE distributors DROP COLUMN address RESTRICT; + + + + + 要在一个操作中更改两个现有列的类型: + +ALTER TABLE distributors + ALTER COLUMN address TYPE varchar(80), + ALTER COLUMN name TYPE varchar(100); + + + + + 要把一个包含 Unix 时间戳的整数列改为 + timestamp with time zone,并通过USING子句完成转换: + +ALTER TABLE foo + ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone + USING + timestamp with time zone 'epoch' + foo_timestamp * interval '1 second'; + + + + + 如果该列带有一个不能自动转换为新数据类型的默认值表达式,也是同样的做法: + +ALTER TABLE foo + ALTER COLUMN foo_timestamp DROP DEFAULT, + ALTER COLUMN foo_timestamp TYPE timestamp with time zone + USING + timestamp with time zone 'epoch' + foo_timestamp * interval '1 second', + ALTER COLUMN foo_timestamp SET DEFAULT now(); + + + + + 要重命名一个现有列: + +ALTER TABLE distributors RENAME COLUMN address TO city; + + + + + 重命名一个现有的表: + +ALTER TABLE distributors RENAME TO suppliers; + + + + + 重命名一个现有的约束: + +ALTER TABLE distributors RENAME CONSTRAINT zipchk TO zip_check; + + + + + 为一列增加一个非空约束: + +ALTER TABLE distributors ALTER COLUMN street SET NOT NULL; + + 从一列移除一个非空约束: + +ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL; + + + + + 要向一个表及其所有后代添加一个检查约束: + +ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); + + + + + 要只向一个表本身添加检查约束,而不添加到其后代: + +ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5) NO INHERIT; + + (该检查约束也不会被未来的后代表继承。) + + + + 要从一个表及其所有后代移除一个检查约束: + +ALTER TABLE distributors DROP CONSTRAINT zipchk; + + + + + 只从一个表移除一个检查约束: + +ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk; + + (该检查约束在所有子表上仍然保留。) + + + + 为一个表增加一个外键约束: + +ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address); + + + + + 为一个表增加一个外键约束,并且尽量不要影响其他工作: + +ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address) NOT VALID; +ALTER TABLE distributors VALIDATE CONSTRAINT distfk; + + + + + 为一个表增加一个(多列)唯一约束: + +ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zipcode); + + + + + 为一个表增加一个自动命名的主键约束,注意一个表只能拥有一个主键: + +ALTER TABLE distributors ADD PRIMARY KEY (dist_id); + + + + + 把一个表移动到一个不同的表空间: + +ALTER TABLE distributors SET TABLESPACE fasttablespace; + + + + + 把一个表移动到一个不同的模式: + +ALTER TABLE myschema.distributors SET SCHEMA yourschema; + + + + + 重建一个主键约束,并且在重建索引期间不阻塞更新: + +CREATE UNIQUE INDEX CONCURRENTLY dist_id_temp_idx ON distributors (dist_id); +ALTER TABLE distributors DROP CONSTRAINT distributors_pkey, + ADD CONSTRAINT distributors_pkey PRIMARY KEY USING INDEX dist_id_temp_idx; + + + + + + 兼容性 + + 以下形式符合 SQL 标准:ADD(不带USING INDEX)、DROPSET DEFAULT以及SET DATA TYPE(不带USING)。其他形式是PostgreSQL对 SQL 标准的扩展。此外,在单条ALTER TABLE命令中指定多个操作的能力也是一种扩展。 + + + ALTER TABLE DROP COLUMN可以被用来删除一个表的唯一的 + 列,从而留下一个零列的表。这是一种 SQL 的扩展,SQL 中不允许零列的表。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/alter_tablespace.sgml b/zh/9.6/ref/alter_tablespace.sgml new file mode 100644 index 00000000..643f4637 --- /dev/null +++ b/zh/9.6/ref/alter_tablespace.sgml @@ -0,0 +1,117 @@ + + + + + ALTER TABLESPACE + + + + ALTER TABLESPACE + 7 + SQL - 语言语句 + + + + ALTER TABLESPACE + 更改一个表空间的定义 + + + + +ALTER TABLESPACE name RENAME TO new_name +ALTER TABLESPACE name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER TABLESPACE name SET ( tablespace_option = value [, ... ] ) +ALTER TABLESPACE name RESET ( tablespace_option [, ... ] ) + + + + + 描述 + + + ALTER TABLESPACE可以用于更改表空间的定义。 + + + 要更改表空间的定义,必须拥有该表空间。要更改所有者,还必须是新所有者角色的直接或间接成员。(请注意,超级用户会自动拥有这些权限。) + + + + + 参数 + + + + name + + + 现有表空间的名称。 + + + + + + new_name + + + 该表空间的新名称。新名称不能以pg_开头,因为这类名称保留给系统表空间使用。 + + + + + + new_owner + + + 该表空间的新拥有者。 + + + + + + tablespace_option + + 要设置或重置的表空间参数。目前可用的参数只有seq_page_costrandom_page_costeffective_io_concurrency。为特定表空间设置其中任一值,会覆盖规划器对读取该表空间中表的数据页成本的通常估计;该估计由同名配置参数确定(参见)。如果一个表空间位于比 I/O 子系统其余部分更快或更慢的磁盘上,这可能很有用。 + + + + + + + + 示例 + + + 将表空间index_space重命名为fast_raid: + +ALTER TABLESPACE index_space RENAME TO fast_raid; + + + + + 更改表空间index_space的拥有者: + +ALTER TABLESPACE index_space OWNER TO mary; + + + + + 兼容性 + + + 在 SQL 标准中没有 + ALTER TABLESPACE语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_trigger.sgml b/zh/9.6/ref/alter_trigger.sgml new file mode 100644 index 00000000..7384e01b --- /dev/null +++ b/zh/9.6/ref/alter_trigger.sgml @@ -0,0 +1,117 @@ + + + + + ALTER TRIGGER + + + + ALTER TRIGGER + 7 + SQL - 语言语句 + + + + ALTER TRIGGER + 更改触发器的定义 + + + + +ALTER TRIGGER name ON table_name RENAME TO new_name +ALTER TRIGGER name ON table_name DEPENDS ON EXTENSION extension_name + + + + + 描述 + + ALTER TRIGGER更改现有触发器的属性。RENAME子句更改给定触发器的名称,但不以其他方式更改触发器定义。DEPENDS ON EXTENSION子句将触发器标记为依赖扩展,因此扩展被删除时触发器也会自动删除。 + + + 若要更改触发器的属性,你必须拥有该触发器所作用的表。 + + + + + 参数 + + + + name + + + 要修改的现有触发器的名称。 + + + + + + table_name + + + 该触发器所作用的表的名称。 + + + + + + new_name + + + 该触发器的新名称。 + + + + + + extension_name + + 触发器所依赖的扩展名称。 + + + + + + + 注解 + + 临时启用或禁用触发器的能力由提供,而不是由ALTER TRIGGER提供,因为ALTER TRIGGER没有便捷的方式来表达一次启用或禁用表中所有触发器的选项。 + + + + 示例 + + + 要重命名一个现有触发器: + +ALTER TRIGGER emp_stamp ON emp RENAME TO emp_track_chgs; + + + + 要将一个触发器标记为依赖于某个扩展: + +ALTER TRIGGER emp_stamp ON emp DEPENDS ON EXTENSION emplib; + + + + + 兼容性 + + + ALTER TRIGGER是 + PostgreSQL 对 SQL 标准的扩展。 + + + + + 参见 + + + + + + diff --git a/zh/9.6/ref/alter_tsconfig.sgml b/zh/9.6/ref/alter_tsconfig.sgml new file mode 100644 index 00000000..c19cfa68 --- /dev/null +++ b/zh/9.6/ref/alter_tsconfig.sgml @@ -0,0 +1,195 @@ + + + + + ALTER TEXT SEARCH CONFIGURATION + + + + ALTER TEXT SEARCH CONFIGURATION + 7 + SQL - 语言语句 + + + + ALTER TEXT SEARCH CONFIGURATION + 更改一个文本搜索配置的定义 + + + + +ALTER TEXT SEARCH CONFIGURATION name + ADD MAPPING FOR token_type [, ... ] WITH dictionary_name [, ... ] +ALTER TEXT SEARCH CONFIGURATION name + ALTER MAPPING FOR token_type [, ... ] WITH dictionary_name [, ... ] +ALTER TEXT SEARCH CONFIGURATION name + ALTER MAPPING REPLACE old_dictionary WITH new_dictionary +ALTER TEXT SEARCH CONFIGURATION name + ALTER MAPPING FOR token_type [, ... ] REPLACE old_dictionary WITH new_dictionary +ALTER TEXT SEARCH CONFIGURATION name + DROP MAPPING [ IF EXISTS ] FOR token_type [, ... ] +ALTER TEXT SEARCH CONFIGURATION name RENAME TO new_name +ALTER TEXT SEARCH CONFIGURATION name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER TEXT SEARCH CONFIGURATION name SET SCHEMA new_schema + + + + + + 描述 + + + + + ALTER TEXT SEARCH CONFIGURATION + 更改一个文本搜索配置的定义。你可以修改其从记号类型到字典的映射, + 或者更改该配置的名称或拥有者。 + + + + + + 要使用ALTER TEXT SEARCH CONFIGURATION, + 你必须是该配置的拥有者。 + + + + + + 参数 + + + + name + + + + 一个现有文本搜索配置的名称(可以是模式限定的)。 + + + + + + + token_type + + + + 由该配置的解析器发出的记号类型的名称。 + + + + + + + dictionary_name + + + + 用于指定记号类型的文本搜索字典名称。如果列出了多个字典, + 将按指定顺序依次查阅它们。 + + + + + + + old_dictionary + + + + 在映射中要替换的文本搜索字典的名称。 + + + + + + + new_dictionary + + + + 被用来替代old_dictionary + 的文本搜索字典的名称。 + + + + + + + new_name + + + + 该文本搜索配置的新名称。 + + + + + + + new_owner + + + + 该文本搜索配置的新拥有者。 + + + + + + + new_schema + + + + 该文本搜索配置的新模式。 + + + + + + + ADD MAPPING FOR形式为指定的词元类型安装要查询的字典列表;如果任何词元类型已经存在映射,则会报错。ALTER MAPPING FOR形式执行相同操作,但会先移除这些词元类型的现有映射。ALTER MAPPING REPLACE形式在任何出现旧字典的地方用new_dictionary替换old_dictionary。当出现FOR时,只对指定的词元类型执行此操作;不出现时,则对该配置的所有映射执行。DROP MAPPING形式移除指定词元类型的所有字典,使这些类型的词元被文本搜索配置忽略。如果词元类型没有映射,则会报错,除非出现IF EXISTS + + + + + 示例 + + 以下示例在my_config中使用english的任何地方,都将english字典替换为swedish字典。 + + +ALTER TEXT SEARCH CONFIGURATION my_config + ALTER MAPPING REPLACE english WITH swedish; + + + + + + 兼容性 + + + + + 在 SQL 标准中没有 + ALTER TEXT SEARCH CONFIGURATION + 语句。 + + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_tsdictionary.sgml b/zh/9.6/ref/alter_tsdictionary.sgml new file mode 100644 index 00000000..1356e2fd --- /dev/null +++ b/zh/9.6/ref/alter_tsdictionary.sgml @@ -0,0 +1,201 @@ + + + + + ALTER TEXT SEARCH DICTIONARY + + + + ALTER TEXT SEARCH DICTIONARY + 7 + SQL - 语言语句 + + + + ALTER TEXT SEARCH DICTIONARY + 更改一个文本搜索字典的定义 + + + + +ALTER TEXT SEARCH DICTIONARY name ( + option [ = value ] [, ... ] +) +ALTER TEXT SEARCH DICTIONARY name RENAME TO new_name +ALTER TEXT SEARCH DICTIONARY name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER TEXT SEARCH DICTIONARY name SET SCHEMA new_schema + + + + + + 描述 + + + + + ALTER TEXT SEARCH DICTIONARY更改文本搜索字典的 + 定义。你可以更改该字典的模板相关选项,也可以更改该字典的名称或拥有者。 + + + + + + 要使用ALTER TEXT SEARCH DICTIONARY,你必须是该字典 + 的拥有者。 + + + + + + 参数 + + + + name + + + + 一个现有文本搜索字典的名称(可以是模式限定的)。 + + + + + + + option + + + + 要为此字典设置的模板相关选项的名称。 + + + + + + + value + + + + 模板相关选项要使用的新值。如果省略等号和值,则会从该字典中移除 + 该选项之前的设置,从而允许使用默认值。 + + + + + + + new_name + + + + 该文本搜索字典的新名称。 + + + + + + + new_owner + + + + 该文本搜索字典的新拥有者。 + + + + + + + new_schema + + + + 该文本搜索字典的新模式。 + + + + + + + + 模板相关选项可以以任意顺序出现。 + + + + + + 示例 + + + + + 下面的示例命令更改了一个基于 Snowball 的字典的停用词列表。其他参数 + 保持不变。 + + + + + +ALTER TEXT SEARCH DICTIONARY my_dict ( StopWords = newrussian ); + + + + + + 下面的示例命令将语言选项更改为dutch,并完全移除 + 了停用词选项。 + + + + + +ALTER TEXT SEARCH DICTIONARY my_dict ( language = dutch, StopWords ); + + + + + + 下面的示例命令更新了该字典的定义,但实际上并没有做 + 任何更改。 + + +ALTER TEXT SEARCH DICTIONARY my_dict ( dummy ); + + + (之所以可行,是因为选项移除代码在不存在该选项时也不会报错。) + 这种技巧在修改该字典的配置文件时很有用:ALTER + 会强制现有数据库会话重新读取配置文件,而如果它们先前已经读取过这 + 些文件,本来是不会再次读取的。 + + + + + + + 兼容性 + + + + + 在 SQL 标准中没有 + ALTER TEXT SEARCH DICTIONARY语句。 + + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_tsparser.sgml b/zh/9.6/ref/alter_tsparser.sgml new file mode 100644 index 00000000..e1026b50 --- /dev/null +++ b/zh/9.6/ref/alter_tsparser.sgml @@ -0,0 +1,91 @@ + + + + + ALTER TEXT SEARCH PARSER + + + + ALTER TEXT SEARCH PARSER + 7 + SQL - 语言语句 + + + + ALTER TEXT SEARCH PARSER + 更改一个全文检索解析器的定义 + + + + +ALTER TEXT SEARCH PARSER name RENAME TO new_name +ALTER TEXT SEARCH PARSER name SET SCHEMA new_schema + + + + + 描述 + + + ALTER TEXT SEARCH PARSER更改全文检索解析器的定义。 + 当前唯一支持的功能是更改该解析器的名称。 + + + + 要使用ALTER TEXT SEARCH PARSER,你必须是超级用户。 + + + + + 参数 + + + + name + + + 一个现有全文检索解析器的名称(可以是模式限定的)。 + + + + + + new_name + + + 该全文检索解析器的新名称。 + + + + + + new_schema + + + 该全文检索解析器的新模式。 + + + + + + + + 兼容性 + + + SQL 标准中没有ALTER TEXT SEARCH PARSER语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_tstemplate.sgml b/zh/9.6/ref/alter_tstemplate.sgml new file mode 100644 index 00000000..e2604fd1 --- /dev/null +++ b/zh/9.6/ref/alter_tstemplate.sgml @@ -0,0 +1,91 @@ + + + + + ALTER TEXT SEARCH TEMPLATE + + + + ALTER TEXT SEARCH TEMPLATE + 7 + SQL - 语言语句 + + + + ALTER TEXT SEARCH TEMPLATE + 更改一个文本搜索模板的定义 + + + + +ALTER TEXT SEARCH TEMPLATE name RENAME TO new_name +ALTER TEXT SEARCH TEMPLATE name SET SCHEMA new_schema + + + + + 描述 + + + ALTER TEXT SEARCH TEMPLATE更改文本搜索模板的定义。 + 当前唯一支持的功能是更改该模板的名称。 + + + + 要使用 ALTER TEXT SEARCH TEMPLATE,你必须是超级用户。 + + + + + 参数 + + + + name + + + 一个现有文本搜索模板的名称(可以是模式限定的)。 + + + + + + new_name + + + 该文本搜索模板的新名称。 + + + + + + new_schema + + + 该文本搜索模板的新模式。 + + + + + + + + 兼容性 + + + SQL 标准中没有 ALTER TEXT SEARCH TEMPLATE 语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_type.sgml b/zh/9.6/ref/alter_type.sgml new file mode 100644 index 00000000..ab9a159c --- /dev/null +++ b/zh/9.6/ref/alter_type.sgml @@ -0,0 +1,302 @@ + + + + + ALTER TYPE + + + + ALTER TYPE + 7 + SQL - 语言语句 + + + + ALTER TYPE + + 更改类型的定义 + + + + + +ALTER TYPE name action [, ... ] +ALTER TYPE name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER TYPE name RENAME ATTRIBUTE attribute_name TO new_attribute_name [ CASCADE | RESTRICT ] +ALTER TYPE name RENAME TO new_name +ALTER TYPE name SET SCHEMA new_schema +ALTER TYPE name ADD VALUE [ IF NOT EXISTS ] new_enum_value [ { BEFORE | AFTER } existing_enum_value ] + +其中action为以下之一: + + ADD ATTRIBUTE attribute_name data_type [ COLLATE collation ] [ CASCADE | RESTRICT ] + DROP ATTRIBUTE [ IF EXISTS ] attribute_name [ CASCADE | RESTRICT ] + ALTER ATTRIBUTE attribute_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ CASCADE | RESTRICT ] + + + + + 描述 + + + ALTER TYPE更改现有类型的定义。有几种子形式: + + ADD ATTRIBUTE + + 该形式使用与相同的语法向复合类型添加新属性。 + + + + + DROP ATTRIBUTE [ IF EXISTS ] + + + 这种形式从复合类型中删除一个属性。 + 如果指定了IF EXISTS而该属性不存在,则不会抛出错误, + 而是发出一条提示。 + + + + + + SET DATA TYPE + + + 这种形式更改复合类型中某个属性的数据类型。 + + + + + + OWNER + + + 这种形式更改类型的拥有者。 + + + + + + RENAME + + 该形式更改类型的名称或复合类型中单个属性的名称。 + + + + + SET SCHEMA + + + 这种形式将类型移动到另一个模式中。 + + + + + + ADD VALUE [ IF NOT EXISTS ] [ BEFORE | AFTER ] + + + 这种形式向枚举类型中添加一个新值。可以用 + BEFOREAFTER + 指定该新值在枚举排序中的位置,即位于某个现有值之前或之后。 + 否则,新项会被添加到值列表的末尾。 + + + 如果指定了 IF NOT EXISTS,而该类型已经包含该新值, + 则不会报错:系统会发出一条提示,但不会执行任何其他操作。 + 否则,如果该新值已存在,就会报错。 + + + + + + CASCADE + + + 自动将该操作传播到正在修改的类型的类型化表及其后代。 + + + + + + RESTRICT + + + 如果正在修改的类型是某个类型化表的类型,则拒绝该操作。 + 这是默认行为。 + + + + + + + + ADD ATTRIBUTEDROP + ATTRIBUTEALTER ATTRIBUTE 操作 + 可以组合成一个包含多项修改的列表,以便并行应用。 + 例如,可以在一条命令中添加多个属性和/或更改多个属性的数据类型。 + + + 要使用ALTER TYPE,必须拥有该类型。要更改类型的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在类型所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建类型完成的操作。不过,超级用户无论如何都可以更改任何类型的所有权。)要添加属性或更改属性类型,还必须在该属性的数据类型上拥有USAGE权限。 + + + + 参数 + + + + + name + + + 要修改的现有类型的名称(可以是模式限定的)。 + + + + + + new_name + + + 该类型的新名称。 + + + + + + new_owner + + + 该类型新的拥有者的用户名。 + + + + + + new_schema + + + 该类型的新模式。 + + + + + + attribute_name + + + 要添加、修改或删除的属性名称。 + + + + + + new_attribute_name + + + 要重命名的属性的新名称。 + + + + + + data_type + + + 要添加的属性的数据类型,或者要修改的属性的新类型。 + + + + + + new_enum_value + + + 要添加到枚举类型值列表中的新值。 + 与所有枚举字面量一样,它必须加引号。 + + + + + + existing_enum_value + + + 一个现有枚举值;在枚举类型的排序顺序中,新值会紧邻该值, + 被添加到它之前或之后。与所有枚举字面量一样,它必须加引号。 + + + + + + + + + + 注解 + + ALTER TYPE ... ADD VALUE(向枚举类型添加新值的形式)不能在事务块内执行。 + + 涉及新增枚举值的比较有时会比只涉及枚举类型原有成员的比较慢。通常只有在使用BEFOREAFTER将新值的排序位置设置在列表末尾以外时,才会发生这种情况。但是,即使新值添加在末尾,有时也会发生这种情况(如果自枚举类型最初创建以来 OID 计数器发生了回绕)。速度下降通常并不明显;但如果这确实重要,可以通过删除并重新创建枚举类型,或转储并重新加载数据库,恢复最佳性能。 + + + + 示例 + + + 要重命名一个数据类型: + +ALTER TYPE electronic_mail RENAME TO email; + + + + + 要将类型email的拥有者改为 + joe: + +ALTER TYPE email OWNER TO joe; + + + + + 要将类型email所在的模式改为 + customers: + +ALTER TYPE email SET SCHEMA customers; + + + + 向类型添加新属性: +ALTER TYPE compfoo ADD ATTRIBUTE f3 int; + + + + + 要在枚举类型的特定排序位置添加一个新值: + +ALTER TYPE colors ADD VALUE 'orange' AFTER 'red'; + + + + + + 兼容性 + + + 添加和删除属性的这些变体属于 SQL 标准;其他变体则是 + PostgreSQL 扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/alter_user.sgml b/zh/9.6/ref/alter_user.sgml new file mode 100644 index 00000000..d37af7d9 --- /dev/null +++ b/zh/9.6/ref/alter_user.sgml @@ -0,0 +1,80 @@ + + + + + ALTER USER + + + + ALTER USER + 7 + SQL - 语言语句 + + + + ALTER USER + 更改数据库角色 + + + + +ALTER USER role_specification [ WITH ] option [ ... ] + +其中option可以是: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + +ALTER USER name RENAME TO new_name + +ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter { TO | = } { value | DEFAULT } +ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter FROM CURRENT +ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] RESET configuration_parameter +ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] RESET ALL + +其中role_specification可以是: + + role_name + | CURRENT_USER + | SESSION_USER + + + + + 描述 + + + ALTER USER现为 + 的别名。 + + + + + 兼容性 + + + ALTER USER语句是 + PostgreSQL的一种扩展。 + SQL 标准将用户的定义交由具体实现决定。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/alter_user_mapping.sgml b/zh/9.6/ref/alter_user_mapping.sgml new file mode 100644 index 00000000..be12eafb --- /dev/null +++ b/zh/9.6/ref/alter_user_mapping.sgml @@ -0,0 +1,112 @@ + + + + + ALTER USER MAPPING + + + + ALTER USER MAPPING + 7 + SQL - 语言语句 + + + + ALTER USER MAPPING + 更改用户映射的定义 + + + + +ALTER USER MAPPING FOR { user_name | USER | CURRENT_USER | SESSION_USER | PUBLIC } + SERVER server_name + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + + + + + 描述 + + + ALTER USER MAPPING更改用户映射的定义。 + + + + 外部服务器的拥有者可以为该服务器上的任何用户修改用户映射。 + 此外,如果某个用户已被授予该服务器上的USAGE权限, + 那么该用户也可以修改其自己用户名对应的用户映射。 + + + + + 参数 + + + + user_name + + 映射的用户名。CURRENT_USERUSER与当前用户名称匹配。PUBLIC用于匹配系统中当前及未来的所有用户名。 + + + + + server_name + + + 该用户映射所属服务器的名称。 + + + + + + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + + + 更改该用户映射的选项。新选项会覆盖先前指定的任何选项。 + ADDSETDROP + 指定要执行的动作。如果未显式指定操作,则假定为ADD。 + 选项名必须唯一;这些选项还会由该服务器的外部数据包装器进行验证。 + + + + + + + + 示例 + + + 更改用户映射bob、服务器foo的密码: + +ALTER USER MAPPING FOR bob SERVER foo OPTIONS (SET password 'public'); + + + + + + 兼容性 + + + ALTER USER MAPPING符合 ISO/IEC 9075-9 + (SQL/MED)。这里有一个细微的语法问题:该标准省略了FOR + 关键字。由于CREATE USER MAPPING和 + DROP USER MAPPING都在类似位置使用 + FOR,而 IBM DB2(另一个主要的 SQL/MED 实现)也要求在 + ALTER USER MAPPING中使用它,因此为了保持一致性和互操作 + 性,PostgreSQL 在这里偏离了标准。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/alter_view.sgml b/zh/9.6/ref/alter_view.sgml new file mode 100644 index 00000000..72dcdf7f --- /dev/null +++ b/zh/9.6/ref/alter_view.sgml @@ -0,0 +1,183 @@ + + + + + ALTER VIEW + + + + ALTER VIEW + 7 + SQL - 语言语句 + + + + ALTER VIEW + 更改视图的定义 + + + + +ALTER VIEW [ IF EXISTS ] name ALTER [ COLUMN ] column_name SET DEFAULT expression +ALTER VIEW [ IF EXISTS ] name ALTER [ COLUMN ] column_name DROP DEFAULT +ALTER VIEW [ IF EXISTS ] name OWNER TO { new_owner | CURRENT_USER | SESSION_USER } +ALTER VIEW [ IF EXISTS ] name RENAME TO new_name +ALTER VIEW [ IF EXISTS ] name SET SCHEMA new_schema +ALTER VIEW [ IF EXISTS ] name SET ( view_option_name [= view_option_value] [, ... ] ) +ALTER VIEW [ IF EXISTS ] name RESET ( view_option_name [, ... ] ) + + + + + 描述 + + + ALTER VIEW更改视图的各种辅助属性。 + (如果要修改视图的定义查询,请使用 + CREATE OR REPLACE VIEW。) + + + 要使用ALTER VIEW,必须拥有该视图。要更改视图的模式,还必须在新模式上拥有CREATE权限。要更改所有者,还必须是新所有者角色的直接或间接成员,并且该角色必须在视图所在模式上拥有CREATE权限。(这些限制确保更改所有者不会执行任何无法通过删除并重新创建视图完成的操作。不过,超级用户无论如何都可以更改任何视图的所有权。) + + + + 参数 + + + + name + + + 现有视图的名称(可以带模式限定)。 + + + + + + IF EXISTS + + + 如果视图不存在,则不抛出错误,而是发出一条提示。 + + + + + + SET/DROP DEFAULT + + + 这些形式用于设置或移除列的默认值。对于任何以该视图为目标的 + INSERTUPDATE 命令, + 视图列的默认值都会在应用该视图上的任何规则或触发器之前被代入。 + 因此,视图的默认值将优先于底层关系中的任何默认值。 + + + + + + new_owner + + + 视图新拥有者的用户名。 + + + + + + new_name + + + 该视图的新名称。 + + + + + + new_schema + + + 该视图的新模式。 + + + + + + SET ( view_option_name [= view_option_value] [, ... ] ) + RESET ( view_option_name [, ... ] ) + + 设置或重置视图选项。目前支持的选项有: + + check_option (string) + + + 更改视图的检查选项。该值必须为 local + 或 cascaded。 + + + + + security_barrier (boolean) + + 更改视图的 security-barrier 属性。该值必须是布尔值,例如truefalse + + + + + + + + + + + 注解 + + + 出于历史原因,视图也可以使用 ALTER TABLE; + 但允许用于视图的 ALTER TABLE 变体,仅限于与上面所示 + 形式等价的那些。 + + + + + 示例 + + + 将视图 foo 重命名为 + bar: + +ALTER VIEW foo RENAME TO bar; + + + + + 要为一个可更新视图附加默认列值: + +CREATE TABLE base_table (id int, ts timestamptz); +CREATE VIEW a_view AS SELECT * FROM base_table; +ALTER VIEW a_view ALTER COLUMN ts SET DEFAULT now(); +INSERT INTO base_table(id) VALUES(1); -- ts will receive a NULL +INSERT INTO a_view(id) VALUES(2); -- ts will receive the current time + + + + + 兼容性 + + + ALTER VIEWPostgreSQL + 对 SQL 标准的扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/analyze.sgml b/zh/9.6/ref/analyze.sgml new file mode 100644 index 00000000..d489b8a7 --- /dev/null +++ b/zh/9.6/ref/analyze.sgml @@ -0,0 +1,176 @@ + + + + + ANALYZE + + + + ANALYZE + 7 + SQL - 语言语句 + + + + ANALYZE + 收集数据库的统计信息 + + + + +ANALYZE [ VERBOSE ] [ table_name [ ( column_name [, ...] ) ] ] + + + + + 描述 + + + ANALYZE收集数据库中各表内容的统计信息,并将结果存储到pg_statistic + 系统目录中。随后,查询规划器会使用这些统计信息来帮助确定查询的最高效执行计划。 + + + 如果不带参数,ANALYZE 会检查当前数据库中的每个表。带有参数时,ANALYZE 只检查该表。还可以提供列名列表,这时只会收集这些列的统计信息。 + + + + 参数 + + + + VERBOSE + + 启用进度消息显示。 + + + + + table_name + + 要分析的特定表的名称(可带模式限定)。如果省略,则分析当前数据库中的所有普通表(但不包括外部表)。 + + + + + column_name + + 要分析的特定列的名称。默认为所有列。 + + + + + + + 输出 + + + 指定VERBOSE时,ANALYZE会输出进度消息, + 指示当前正在处理哪个表,同时还会打印这些表的各种统计信息。 + + + + + 注解 + + + 要分析一个表,通常调用者必须是该表的拥有者或超级用户。 + 不过,数据库拥有者可以分析其数据库中的所有表,但共享系统目录除外。 + (对共享系统目录的这一限制意味着,真正意义上的全数据库 + ANALYZE 只能由超级用户执行。) + ANALYZE 会跳过调用用户无权分析的任何表。 + + + + 只有在显式选中时才会分析外部表。并非所有外部数据包装器都支持ANALYZE。 + 如果该表的包装器不支持ANALYZE,命令会打印一条警告且不执行任何操作。 + + + + 在默认的PostgreSQL配置中,自动清理守护进程 + (见)会在表首次装载数据时,以及在常规运行过程中数据发生变化时, + 自动分析这些表。当自动清理被禁用时,最好定期运行ANALYZE, + 或者在对表内容做出重大更改之后立即运行。准确的统计信息有助于规划器选择最合适的查询计划, + 从而提高查询处理速度。对于以读取为主的数据库,一个常见策略是在每天使用率较低的时段运行一次 + ANALYZE。 + (如果更新活动很频繁,这样做仍然不够。) + + + ANALYZE只需获取目标表上的读锁,因此可以与该表上的其他活动并行运行。 + + ANALYZE收集的统计信息通常包括每列中某些高频值的列表,以及显示每列大致数据分布的直方图。如果ANALYZE认为它们没有意义(例如,唯一键列中没有高频值),或者列的数据类型不支持相应的操作符,则可能省略其中一项或两项。更多统计信息见 + + + 对于大型表,ANALYZE会对表内容进行随机采样,而不是检查每一行。 + 这使得即使是很大的表,也能在较短时间内完成分析。不过要注意,这些统计信息只是近似值, + 并且即使实际表内容没有变化,每次运行ANALYZE时统计信息也会略有变化。 + 这可能导致中显示的规划器估算代价略有变化。 + 在少数情况下,这种非确定性会导致规划器在运行ANALYZE之后改用不同的查询计划。 + 要避免这种情况,可以按下文所述提高ANALYZE收集的统计信息量。 + + + + 可以通过调整配置变量来控制分析程度, + 也可以针对单个列使用ALTER TABLE ... ALTER COLUMN ... SET + STATISTICS设置每列的统计信息目标(参见)。目标值会设置高频值列表中的最大条目数, + 以及直方图中的最大桶数。默认目标值是 100,但可以把它调高或调低,以在规划器估算精度、 + ANALYZE耗费的时间以及pg_statistic占用的空间之间作出权衡。 + 特别地,将统计信息目标设置为零会禁用该列的统计信息收集。对于那些从不出现在查询 + WHEREGROUP BYORDER BY子句中的列, + 这样做可能很有用,因为规划器不会用到这些列的统计信息。 + + + + 被分析列中最大的统计信息目标决定了为了生成统计信息而需要采样的表行数。 + 增加该目标会导致执行ANALYZE所需的时间和空间按比例增加。 + + + + ANALYZE估算的值之一是每一列中出现的非重复值数量。由于只检查了部分行, + 即使使用可能的最大统计信息目标,这种估计有时也可能相当不精确。如果这种不精确导致查询计划不佳, + 就可以手工确定一个更精确的值,然后用 + ALTER TABLE ... ALTER COLUMN ... SET (n_distinct = ...) + 把该值设置进去(参见)。 + + + + 如果被分析的表有一个或多个子表,ANALYZE会两次收集统计信息: + 一次只针对父表中的行,另一次针对父表及其所有子表中的行。规划遍历整个继承树的查询时, + 需要第二组统计信息。不过,自动清理守护进程在决定是否为该表触发自动分析时, + 只会考虑对父表本身的插入或更新。如果该表很少被插入或更新,那么除非手工运行 + ANALYZE,否则继承统计信息就不会保持最新。 + + + + 如果某些子表是外部数据包装器不支持ANALYZE的外部表, + 那么在收集继承统计信息时会忽略这些子表。 + + + + 如果被分析的表完全为空,ANALYZE将不会为该表记 + 录新的统计信息。任何现有统计信息都会被保留。 + + + + + 兼容性 + + + SQL 标准中没有ANALYZE语句。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/begin.sgml b/zh/9.6/ref/begin.sgml new file mode 100644 index 00000000..b3fc5a6b --- /dev/null +++ b/zh/9.6/ref/begin.sgml @@ -0,0 +1,135 @@ + + + + + BEGIN + + + + BEGIN + 7 + SQL - 语言语句 + + + + BEGIN + 开始一个事务块 + + + + +BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ] + +其中 transaction_mode 是下列之一: + + ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } + READ WRITE | READ ONLY + [ NOT ] DEFERRABLE + + + + + 描述 + + + BEGIN启动一个事务块,也就是说,在 + BEGIN命令之后的所有语句都会在同一个事务中执行, + 直到显式给出或 + 为止。 + 默认情况下(不使用BEGIN), + PostgreSQL自动提交模式执行事务, + 也就是说,每条语句都在自己的事务中执行,并在语句结束时隐式提交 + (如果执行成功,否则回滚)。 + + + + 在事务块中执行语句通常会更快,因为事务的启动和提交需要大量的 CPU 和磁盘活动。 + 在进行多个相关更改时,把多条语句放在一个事务中执行也有助于保证一致性: + 其他会话将无法看到那些相关更新尚未全部完成时的中间状态。 + + + 如果指定了隔离级别、读写模式或可延迟模式,新事务就会具有这些特征,就像执行了一样。 + + + + 参数 + + + + WORK + TRANSACTION + + + 可选关键字,没有任何作用。 + + + + + + + 关于本语句其他参数含义的信息,参见。 + + + + + 注解 + + BEGIN具有相同功能。 + + 使用 来结束事务块。 + + + 如果在已经处于事务块中时发出BEGIN,将会产生一条警告消息。 + 事务状态不会受影响。 + 若要在事务块中实现事务嵌套,请使用保存点 + (参见)。 + + + + 出于向后兼容的考虑,连续多个transaction_modes + 之间的逗号可以省略。 + + + + + 示例 + + + 开始一个事务块: + + +BEGIN; + + + + + 兼容性 + + BEGINPostgreSQL的一种语言扩展。它等价于 SQL 标准命令,后者的参考页中包含更多兼容性信息。 + + + DEFERRABLE + transaction_mode + 是PostgreSQL的一种语言扩展。 + + + + 另外,在嵌入式 SQL 中,关键字BEGIN用于不同的目的。 + 在移植数据库应用程序时,应当谨慎处理事务语义。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/checkpoint.sgml b/zh/9.6/ref/checkpoint.sgml new file mode 100644 index 00000000..deefc6c7 --- /dev/null +++ b/zh/9.6/ref/checkpoint.sgml @@ -0,0 +1,63 @@ + + + + + CHECKPOINT + + + + CHECKPOINT + 7 + SQL - 语言语句 + + + + CHECKPOINT + 强制执行一次事务日志检查点 + + + + +CHECKPOINT + + + + + 描述 + + + 检查点是事务日志序列中的一个位置,到这一位置时,所有数据文件都已更新,以反映日志中的信息。 + 所有数据文件都会被刷写到磁盘。 + 有关检查点期间会发生什么的更多细节,参见。 + + + + CHECKPOINT命令会在发出该命令时强制立即执行检查点, + 而不是等待系统安排的常规检查点 + (由中的设置控制)。 + CHECKPOINT并非设计用于正常运行期间。 + + + + 如果在恢复期间执行CHECKPOINT命令, + 它将强制执行一个重启点(见), + 而不是写入一个新的检查点。 + + + + 只有超级用户才能调用CHECKPOINT。 + + + + + 兼容性 + + + CHECKPOINT命令是 + PostgreSQL的一种语言扩展。 + + + diff --git a/zh/9.6/ref/close.sgml b/zh/9.6/ref/close.sgml new file mode 100644 index 00000000..2bd31368 --- /dev/null +++ b/zh/9.6/ref/close.sgml @@ -0,0 +1,124 @@ + + + + + CLOSE + + + + cursor + CLOSE + + + + CLOSE + 7 + SQL - 语言语句 + + + + CLOSE + 关闭一个游标 + + + + +CLOSE { name | ALL } + + + + + 描述 + + + CLOSE释放与一个打开的游标相关联的资源。 + 游标关闭后,不允许再对其执行任何后续操作。 + 当不再需要游标时,应将其关闭。 + + + + 每个不可保持的打开游标都会在事务通过COMMIT或 + ROLLBACK结束时被隐式关闭。 + 可保持游标会在创建它的事务通过ROLLBACK中止时被隐式关闭。 + 如果创建它的事务成功提交,则该可保持游标会一直保持打开状态,直到显式执行 + CLOSE,或者客户端断开连接。 + + + + + 参数 + + + + name + + + 要关闭的打开游标的名称。 + + + + + + ALL + + + 关闭所有打开的游标。 + + + + + + + + + 注解 + + + PostgreSQL没有显式的OPEN游标语句; + 游标在声明时即被视为打开。 + 请使用语句来声明游标。 + + + + 可以通过查询pg_cursors + 系统视图查看所有可用游标。 + + + + 如果在某个保存点之后关闭了游标,而该保存点后来又被回滚, + 则CLOSE不会被回滚;也就是说,该游标仍然保持关闭状态。 + + + + + 示例 + + + 关闭游标liahona: + +CLOSE liahona; + + + + + 兼容性 + + + CLOSE完全符合 SQL 标准。 + CLOSE ALLPostgreSQL的一种扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/cluster.sgml b/zh/9.6/ref/cluster.sgml new file mode 100644 index 00000000..5fe1ac6b --- /dev/null +++ b/zh/9.6/ref/cluster.sgml @@ -0,0 +1,178 @@ + + + + + CLUSTER + + + + CLUSTER + 7 + SQL - 语言语句 + + + + CLUSTER + 按照一个索引对表进行聚簇 + + + + +CLUSTER [VERBOSE] table_name [ USING index_name ] +CLUSTER [VERBOSE] + + + + + 描述 + + + CLUSTER 指示 PostgreSQL + 按照 index_name + 指定的索引,对 table_name + 指定的表进行聚簇。该索引必须已经定义在 + table_name 上。 + + + + 当一个表被聚簇时,它会根据索引信息在物理上重新排序。聚簇是一次性 + 操作:之后如果表再被更新,这些更改不会再次被聚簇。也就是说,系 + 统不会试图按照索引顺序存储新行或更新后的行。(如果需要,可以定期 + 再次执行该命令来重新聚簇。此外,将表的 fillfactor + 存储参数设置为小于 100%,有助于在更新期间保持聚簇顺序,因为如果 + 页面上有足够空间,更新后的行会保留在同一页面中。) + + + + 当一个表被聚簇时,PostgreSQL + 会记住该表是按哪个索引聚簇的。形式 + CLUSTER table_name + 会使用之前相同的索引对表重新聚簇。你也可以使用 + 的 + CLUSTERSET WITHOUT CLUSTER + 形式,设置未来聚簇操作要使用的索引,或者清除任何先前的设置。 + + + + 不带任何参数的 CLUSTER 会对当前数据库中所有此前已聚簇且属于调用用户的表重新执行聚簇;如果由超级用户调用,则会对所有此前已聚簇的表重新执行聚簇。 + 这种形式的 CLUSTER 不能在事务块内执行。 + + + + 当对一个表执行聚簇时,会在该表上获取 ACCESS + EXCLUSIVE 锁。这会阻止任何其他数据库操作(包括读和写) + 在 CLUSTER 完成前访问该表。 + + + + + 参数 + + + + table_name + + + 表的名称(可能是模式限定的)。 + + + + + + index_name + + + 索引的名称。 + + + + + + VERBOSE + + 在每个表被聚簇时打印进度报告。 + + + + + + + 注解 + + 当你在表中随机访问单行时,数据在表中的实际顺序并不重要。但是,如果访问某些数据比其他数据更频繁,并且存在将这些数据放在一起的索引,使用CLUSTER就会有益。如果要从表中获取一段索引值范围,或者获取有多行匹配的单个索引值,CLUSTER会有帮助,因为一旦索引确定第一个匹配行所在的表页,其他匹配行很可能已在同一个表页中,从而减少磁盘访问并加快查询。 + + + CLUSTER 可以通过在指定索引上进行索引扫描,或者 + (如果该索引是 B-树)先执行顺序扫描再排序,来对表重新排序。它会 + 根据规划器代价参数和可用的统计信息,尝试选择速度更快的方法。 + + + 使用索引扫描时,会创建一个表的临时副本,其中表数据按索引顺序排列。还会为表上的每个索引创建临时副本。因此,所需的磁盘空闲空间至少应等于表大小与索引大小之和。 + + 使用顺序扫描加排序时,还会创建临时排序文件,因此临时空间需求峰值可达表大小的两倍再加上索引大小。这种方法通常比索引扫描更快,但如果无法接受其磁盘空间需求,可以临时将设置为off来禁用这种选择。 + + 建议在聚簇之前将设置为一个合理的大值(但不超过可专门用于CLUSTER操作的内存量)。 + + + 因为规划器会记录有关表中数据顺序的统计信息,建议在新近聚簇过的表上 + 运行。 + 否则,规划器可能会选择很差的查询计划。 + + + + 因为 CLUSTER 会记住哪些索引已被设为聚簇索引, + 你可以第一次先手工聚簇需要聚簇的表,然后设置一个定期运行的维护脚 + 本,执行不带任何参数的 CLUSTER,这样这些表就会 + 被周期性地重新聚簇。 + + + + + + 示例 + + + 按照索引 employees_ind 对表 + employees 进行聚簇: + +CLUSTER employees USING employees_ind; + + + + + 使用之前用过的同一个索引对 employees 表进行聚簇: + +CLUSTER employees; + + + + + 对数据库中此前已聚簇过的所有表执行聚簇: + +CLUSTER; + + + + + 兼容性 + + + SQL 标准中没有 CLUSTER 语句。 + + + 语法 +CLUSTER index_name ON table_name + 也受支持,以兼容 8.3 之前的 PostgreSQL 版本。 + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/clusterdb.sgml b/zh/9.6/ref/clusterdb.sgml new file mode 100644 index 00000000..1d209893 --- /dev/null +++ b/zh/9.6/ref/clusterdb.sgml @@ -0,0 +1,295 @@ + + + + + clusterdb + + + + clusterdb + 1 + 应用程序 + + + + clusterdb + 聚簇一个PostgreSQL数据库 + + + + + clusterdb + connection-option + + + + + + + + + table + + + + dbname + + + + clusterdb + connection-option + + + + + + + + 描述 + + + clusterdb是一个用于重新聚簇PostgreSQL + 数据库中各表的工具。它会找出此前已经聚簇过的表,并按照上次使用的同一索引再次对其进行聚簇。 + 从未聚簇过的表不会受到影响。 + + + + clusterdb是 SQL 命令的一个包装器。 + 使用该工具聚簇数据库,与使用其他访问服务器的方法聚簇数据库,并没有实质区别。 + + + + + + + 选项 + + + clusterdb接受以下命令行参数: + + + + + + 聚簇所有数据库。 + + + + + + + + + + 当未使用 / 时,指定要聚簇的数据库名。 + 如果未指定,则从环境变量 PGDATABASE 读取数据库名。 + 如果该环境变量也未设置,则使用为该连接指定的用户名作为数据库名。 + dbname可以是一个连接字符串。 + 如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 回显clusterdb生成并发送到服务器的命令。 + + + + + + + + + + 不显示进度消息。 + + + + + + + + + 仅对table执行聚簇。可以通过多次指定选项来聚簇多个表。 + + + + + + + + + 在处理过程中打印详细信息。 + + + + + + + + + + 打印clusterdb的版本并退出。 + + + + + + + + + + 显示有关clusterdb命令行参数的帮助信息,并退出。 + + + + + + + + + clusterdb还接受以下用于连接参数的命令行参数: + + + + + + 指定运行服务器的机器的主机名。如果该值以斜杠开头,则它将被用作 Unix 域套接字的目录。 + + + + + + + + + + 指定服务器监听连接的 TCP 端口,或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 用于连接的用户名。 + + + + + + + + + + 绝不发出密码提示。如果服务器要求密码认证,而又无法通过 .pgpass + 文件等其他方式获得密码,则连接尝试将失败。这个选项在批处理作业和脚本中很有用,因为这些场景下没有用户可以输入密码。 + + + + + + + + + + 强制clusterdb在连接到数据库之前提示输入密码。 + + + + 这个选项并非必不可少,因为如果服务器要求密码认证,clusterdb会自动提示输入密码。不过,clusterdb会浪费一次连接尝试,才知道服务器需要密码。在某些情况下,为了避免这次额外的连接尝试,提前指定是值得的。 + + + + + + + + + 当使用 / 时,连接到该数据库以收集要聚簇的数据库列表。 + 如果未指定,则使用postgres数据库;如果它不存在,则使用template1。 + 这里也可以是一个连接字符串。 + 如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + 此外,在连接其他数据库时,除数据库名本身外的连接字符串参数也会被重用。 + + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 该工具和大多数其他PostgreSQL工具一样,也使用 + libpq支持的环境变量(见)。 + + + + + + + 诊断 + + + 如果遇到问题,请参见中 + 关于潜在问题和错误消息的讨论。数据库服务器必须在目标主机上运行。此外, + libpq前端库所使用的任何默认连接设置和环境变量都会生效。 + + + + + + + 示例 + + + 要聚簇数据库test: + +$ clusterdb test + + + + + 要聚簇名为xyzzy的数据库中的单个表foo: + +$ clusterdb --table foo xyzzy + + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/comment.sgml b/zh/9.6/ref/comment.sgml new file mode 100644 index 00000000..791f75db --- /dev/null +++ b/zh/9.6/ref/comment.sgml @@ -0,0 +1,315 @@ + + + + + COMMENT + + + + COMMENT + 7 + SQL - 语言语句 + + + + COMMENT + 定义或修改对象的注释 + + + + +COMMENT ON +{ + ACCESS METHOD object_name | + AGGREGATE aggregate_name ( aggregate_signature ) | + CAST (source_type AS target_type) | + COLLATION object_name | + COLUMN relation_name.column_name | + CONSTRAINT constraint_name ON table_name | + CONSTRAINT constraint_name ON DOMAIN domain_name | + CONVERSION object_name | + DATABASE object_name | + DOMAIN object_name | + EXTENSION object_name | + EVENT TRIGGER object_name | + FOREIGN DATA WRAPPER object_name | + FOREIGN TABLE object_name | + FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + INDEX object_name | + LARGE OBJECT large_object_oid | + MATERIALIZED VIEW object_name | + OPERATOR operator_name (left_type, right_type) | + OPERATOR CLASS object_name USING index_method | + OPERATOR FAMILY object_name USING index_method | + POLICY policy_name ON table_name | + [ PROCEDURAL ] LANGUAGE object_name | + ROLE object_name | + RULE rule_name ON table_name | + SCHEMA object_name | + SEQUENCE object_name | + SERVER object_name | + TABLE object_name | + TABLESPACE object_name | + TEXT SEARCH CONFIGURATION object_name | + TEXT SEARCH DICTIONARY object_name | + TEXT SEARCH PARSER object_name | + TEXT SEARCH TEMPLATE object_name | + TRANSFORM FOR type_name LANGUAGE lang_name | + TRIGGER trigger_name ON table_name | + TYPE object_name | + VIEW object_name +} IS 'text' + +其中aggregate_signature为: + +* | +[ argmode ] [ argname ] argtype [ , ... ] | +[ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + + + + + 描述 + + COMMENT存储数据库对象的注释。 + + 每个对象只存储一个注释字符串,因此要修改注释,请对同一对象发出新的COMMENT命令。要移除注释,请用NULL代替文本字符串。对象删除时,其注释也会自动删除。 + + 对于大多数对象类型,只有对象的拥有者才能设置注释。角色没有拥有者,因此COMMENT ON ROLE的规则是:要为超级用户角色添加注释,必须是超级用户;要为非超级用户角色添加注释,必须拥有CREATEROLE权限。同样,访问方法也没有拥有者;要为访问方法添加注释,必须是超级用户。当然,超级用户可以为任何对象添加注释。 + + + 可以使用psql\d + 系列命令查看这些注释。其他用于检索注释的用户界面,也可以建立在 + psql使用的同一组内置函数之上,即 + obj_descriptioncol_description + 和shobj_description + (见)。 + + + + + 参数 + + + + object_name + relation_name.column_name + aggregate_name + constraint_name + function_name + operator_name + policy_name + rule_name + trigger_name + + 要添加注释的对象名称。表、聚合、排序规则、转换、域、外部表、函数、索引、操作符、操作符类、操作符族、序列、文本检索对象、类型和视图的名称可以带模式限定。为列添加注释时,relation_name必须引用表、视图、复合类型或外部表。 + + + + + table_name + domain_name + + + + 当为约束、触发器、规则或策略创建注释时,这些参数指定定义该对象的表或域的名称。 + + + + + + source_type + + + + 类型转换的源数据类型的名称。 + + + + + + target_type + + + + 类型转换的目标数据类型的名称。 + + + + + + argmode + + + 函数或聚合函数参数的模式:IN、 + OUTINOUTVARIADIC。 + 如果省略,默认值是IN。注意COMMENT + 实际上并不关心OUT参数,因为确定函数标识只需要输入参数。 + 因此,列出ININOUTVARIADIC + 参数就足够了。 + + + + + + argname + + + 函数或聚合函数参数的名称。注意COMMENT + 实际上并不关心参数名称,因为确定函数标识只需要参数数据类型。 + + + + + + argtype + + + 函数或聚合函数参数的数据类型。 + + + + + + large_object_oid + + + + 大对象的 OID。 + + + + + + left_type + right_type + + 操作符参数的数据类型(可带模式限定)。对于前缀或后缀操作符缺失的参数,请写NONE + + + + + PROCEDURAL + + + + 这是一个噪声词。 + + + + + + type_name + + + + + 该转换所对应的数据类型名称。 + + + + + + lang_name + + + + + 该转换所用语言的名称。 + + + + + + text + + 新的注释,以字符串字面量形式书写;也可以写 NULL 来移除该注释。 + + + + + + + + + 注解 + + + 目前没有用于查看注释的安全机制:任何连接到某个数据库的用户都可以看到 + 该数据库中所有对象的注释。对于数据库、角色和表空间这类共享对象, + 注释是全局存储的,因此连接到集簇中任一数据库的任何用户都可以看到 + 共享对象的全部注释。因此,不要在注释中放入与安全密切相关的信息。 + + + + + 示例 + + + 为表mytable附加一条注释: + + +COMMENT ON TABLE mytable IS 'This is my table.'; + + + 再将其移除: + + +COMMENT ON TABLE mytable IS NULL; + + + + 更多示例: +COMMENT ON ACCESS METHOD rtree IS 'R-Tree access method'; +COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance'; +COMMENT ON CAST (text AS int4) IS 'Allow casts from text to int4'; +COMMENT ON COLLATION "fr_CA" IS 'Canadian French'; +COMMENT ON COLUMN my_table.my_column IS 'Employee ID number'; +COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8'; +COMMENT ON CONSTRAINT bar_col_cons ON bar IS 'Constrains column col'; +COMMENT ON CONSTRAINT dom_col_constr ON DOMAIN dom IS 'Constrains col of domain'; +COMMENT ON DATABASE my_database IS 'Development Database'; +COMMENT ON DOMAIN my_domain IS 'Email Address Domain'; +COMMENT ON EXTENSION hstore IS 'implements the hstore data type'; +COMMENT ON FOREIGN DATA WRAPPER mywrapper IS 'my foreign data wrapper'; +COMMENT ON FOREIGN TABLE my_foreign_table IS 'Employee Information in other database'; +COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral'; +COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID'; +COMMENT ON LANGUAGE plpython IS 'Python support for stored procedures'; +COMMENT ON LARGE OBJECT 346344 IS 'Planning document'; +COMMENT ON MATERIALIZED VIEW my_matview IS 'Summary of order history'; +COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts'; +COMMENT ON OPERATOR - (NONE, integer) IS 'Unary minus'; +COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees'; +COMMENT ON OPERATOR FAMILY integer_ops USING btree IS 'all integer operators for btrees'; +COMMENT ON POLICY my_policy ON mytable IS 'Filter rows by users'; +COMMENT ON ROLE my_role IS 'Administration group for finance tables'; +COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records'; +COMMENT ON SCHEMA my_schema IS 'Departmental data'; +COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys'; +COMMENT ON SERVER myserver IS 'my foreign server'; +COMMENT ON TABLE my_schema.my_table IS 'Employee Information'; +COMMENT ON TABLESPACE my_tablespace IS 'Tablespace for indexes'; +COMMENT ON TEXT SEARCH CONFIGURATION my_config IS 'Special word filtering'; +COMMENT ON TEXT SEARCH DICTIONARY swedish IS 'Snowball stemmer for Swedish language'; +COMMENT ON TEXT SEARCH PARSER my_parser IS 'Splits text into words'; +COMMENT ON TEXT SEARCH TEMPLATE snowball IS 'Snowball stemmer'; +COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'Transform between hstore and Python dict'; +COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for RI'; +COMMENT ON TYPE complex IS 'Complex number data type'; +COMMENT ON VIEW my_view IS 'View of departmental costs'; + + + + + + 兼容性 + + + SQL 标准中没有COMMENT命令。 + + + diff --git a/zh/9.6/ref/commit.sgml b/zh/9.6/ref/commit.sgml new file mode 100644 index 00000000..0e4ea87c --- /dev/null +++ b/zh/9.6/ref/commit.sgml @@ -0,0 +1,86 @@ + + + + + COMMIT + + + + COMMIT + 7 + SQL - 语言语句 + + + + COMMIT + 提交当前事务 + + + + +COMMIT [ WORK | TRANSACTION ] + + + + + 描述 + + + COMMIT提交当前事务。 + 该事务所做的所有更改都会对其他会话可见, + 并且如果发生崩溃,也能保证其持久性。 + + + + + 参数 + + + + WORK + TRANSACTION + + + 可选关键字,没有任何作用。 + + + + + + + + 注解 + + 使用 中止事务。 + + 在事务之外发出 COMMIT 不会造成危害,但会产生一条警告消息。 + + + + 示例 + + + 提交当前事务,并使所有更改永久生效: + +COMMIT; + + + + + 兼容性 + + SQL 标准只规定了 COMMITCOMMIT WORK 这两种形式。在其他方面,此命令完全符合标准。 + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/commit_prepared.sgml b/zh/9.6/ref/commit_prepared.sgml new file mode 100644 index 00000000..9c51f29a --- /dev/null +++ b/zh/9.6/ref/commit_prepared.sgml @@ -0,0 +1,101 @@ + + + + + COMMIT PREPARED + + + + COMMIT PREPARED + 7 + SQL - 语言语句 + + + + COMMIT PREPARED + 提交一个先前为两阶段提交而预备的事务 + + + + +COMMIT PREPARED transaction_id + + + + + 描述 + + + COMMIT PREPARED提交一个处于预备状态的事务。 + + + + + 参数 + + + + transaction_id + + + 要提交的事务的事务标识符。 + + + + + + + + 注解 + + + 要提交预备事务,执行者必须是最初执行该事务的同一用户,或者是超级用户; + 但不必处在执行该事务的同一会话中。 + + + + 这个命令不能在事务块内执行。 + 该预备事务会被立即提交。 + + + + 当前所有处于预备状态的事务都列在 + pg_prepared_xacts + 系统视图中。 + + + + + 示例 + + 提交事务标识符为foobar的事务: + + +COMMIT PREPARED 'foobar'; + + + + + + 兼容性 + + + COMMIT PREPARED是 + PostgreSQL扩展。它旨在供外部事务管理系统使用, + 其中有些系统已被标准覆盖(例如 X/Open XA),但这些系统的 SQL 侧并未标准化。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/copy.sgml b/zh/9.6/ref/copy.sgml new file mode 100644 index 00000000..a3fc38ca --- /dev/null +++ b/zh/9.6/ref/copy.sgml @@ -0,0 +1,818 @@ + + + + + COPY + + + + COPY + 7 + SQL - 语言语句 + + + + COPY + 在文件和表之间复制数据 + + + + +COPY table_name [ ( column_name [, ...] ) ] + FROM { 'filename' | PROGRAM 'command' | STDIN } + [ [ WITH ] ( option [, ...] ) ] + +COPY { table_name [ ( column_name [, ...] ) ] | ( query ) } + TO { 'filename' | PROGRAM 'command' | STDOUT } + [ [ WITH ] ( option [, ...] ) ] + +其中option为以下之一: + + FORMAT format_name + OIDS [ boolean ] + FREEZE [ boolean ] + DELIMITER 'delimiter_character' + NULL 'null_string' + HEADER [ boolean ] + QUOTE 'quote_character' + ESCAPE 'escape_character' + FORCE_QUOTE { ( column_name [, ...] ) | * } + FORCE_NOT_NULL ( column_name [, ...] ) + FORCE_NULL ( column_name [, ...] ) + ENCODING 'encoding_name' + + + + + 描述 + + + COPY在 + PostgreSQL表与标准文件系统文件之间 + 传输数据。COPY TO将表的内容复制 + 文件,而COPY FROM + 则将数据文件复制到表中(追加到表中已有的数据之 + 后)。COPY TO也可以复制 + SELECT查询的结果。 + + + + 如果指定了列列表,COPY TO只会将指定列中的数据复制到文件。 + 对于COPY FROM,文件中的每个字段会按顺序插入到指定列中。 + 未在COPY FROM列列表中指定的表列将接收其默认值。 + + + + 带文件名的COPY会指示 + PostgreSQL服务器直接从文件读取 + 或向文件写入。该文件必须可由 + PostgreSQL用户(服务器运行时使用的用户 ID) + 访问,并且其名称必须从服务器的视角指定。当指定 + PROGRAM时,服务器会执行给定的命令,并从该程序的标准 + 输出读取,或者向该程序的标准输入写入。该命令必须从服务器的视角指定,并 + 且必须可由PostgreSQL用户执行。指定 + STDINSTDOUT时,数据通过客 + 户端与服务器之间的连接传输。 + + + + + 参数 + + + + table_name + + + + 一个现有表的名称(可以是模式限定的)。 + + + + + + column_name + + + 要复制的可选列列表。如果没有指定列列表,则会复制该表所有列。 + + + + + + query + + 其结果将被复制的命令。注意,查询外层必须带圆括号。 + + 对于INSERTUPDATE和 + DELETE查询,必须提供 + RETURNING子句,并且目标关系不能有条件规则,也不能有 + ALSO规则,也不能有扩展为多个语句的 + INSTEAD规则。 + + + + + + filename + + 输入或输出文件的路径名。输入文件名可以是绝对路径或相对路径,但输出文件名必须是绝对路径。Windows 用户可能需要使用E''字符串,并将路径名中的反斜线写成两个。 + + + + + PROGRAM + + 要执行的命令。在COPY FROM中,从该命令的标准输出读取输入;在COPY TO中,将输出写入该命令的标准输入。 + 注意,该命令由 shell 调用,因此如果需要向 shell 命令传递来自不可信来源的参数,必须谨慎移除或转义对 shell 可能有特殊意义的字符。出于安全考虑,最好使用固定命令字符串,或至少避免在其中传入任何用户输入。 + + + + + STDIN + + 指定输入来自客户端应用程序。 + + + + + STDOUT + + 指定输出发送到客户端应用程序。 + + + + + boolean + + 指定所选选项是否开启。可以写TRUEON1来启用选项,写FALSEOFF0来禁用它。也可以省略boolean值,此时假定为TRUE + + + + + FORMAT + + 选择要读取或写入的数据格式:textcsv(逗号分隔值)或binary。默认为text + + + + + OIDS + + 指定复制每一行的 OID。(如果为没有 OID 的表指定了 OIDS,或者复制的是一个 query,就会报错。) + + + + + FREEZE + + 请求以行已冻结的状态复制数据,就像运行过VACUUM FREEZE命令之后一样。这是用于初始数据装载的性能选项。只有当正在装载的表是在当前子事务中创建或截断的、没有打开的游标且该事务不持有更早的快照时,行才会被冻结。 + 注意,一旦数据成功装载,所有其他会话将立即能够看到这些数据。这违反了正常的 MVCC 可见性规则,指定此选项的用户应注意可能由此引发的问题。 + + + + + DELIMITER + + 指定文件中每行内分隔列的字符。文本格式默认为制表符,CSV格式默认为逗号。它必须是单个单字节字符。使用binary格式时不允许此选项。 + + + + + NULL + + + 指定表示一个空值的字符串。文本格式中默认是 + \N(反斜线-N),CSV格式中默认 + 是一个未加引用的空串。在你不想区分空值和空串的情况下,即使在文本 + 格式中你也可能更喜欢空串。使用binary格式时不允许这 + 个选项。 + + + + + + 在使用COPY FROM时,任何匹配此字符串的 + 数据项都会被存储为空值,因此应确保这里使用的字符串与 + COPY TO时使用的相同。 + + + + + + + + HEADER + + 指定文件包含一个标题行,其中列出文件中每一列的名称。输出时,第一行包含表中的列名;输入时,第一行会被忽略。只有使用 CSV 格式时才允许此选项。 + + + + + QUOTE + + + 指定在对数据值加引号时使用的引用字符。默认是双引号。 + 这必须是一个单一的单字节字符。只有使用 + CSV格式时才允许这个选项。 + + + + + + ESCAPE + + + 指定在与QUOTE值匹配的数据字符之前应出现 + 的字符。默认值与QUOTE值相同(这样当引用字符 + 出现在数据中时,就会被双写)。这必须是一个单一的单字节字符。 + 只有使用CSV格式时才允许这个选项。 + + + + + + FORCE_QUOTE + + + + 强制对每个指定列中的所有非NULL值使用引号。 + NULL输出永远不会加引号。如果指定了*, + 则所有列中的非NULL值都会加引号。此选项仅允许用于 + COPY TO,且只能在使用CSV格式时使用。 + + + + + + FORCE_NOT_NULL + + + 不要将指定列的值与空值串进行匹配。在空值串就是空串的默认情况下, + 这意味着空串将被读作长度为零的字符串而不是空值(即使它们没有 + 被引用)。 + 只有在COPY FROM中使用 + CSV格式时才允许这个选项。 + + + + + + FORCE_NULL + + + 将指定列的值与空值串匹配,即使它已经被加上引号;如果找到 + 匹配,就将该值设为NULL。在空值串就是空串的默认 + 情况下,这会把一个带引号的空串转换为 NULL。 + 只有在COPY FROM中使用 + CSV格式时才允许这个选项。 + + + + + + ENCODING + + + 指定文件采用encoding_name编码。如果省略此选项, + 将使用当前客户端编码。详见下文注解。 + + + + + + + + + + 输出 + + + 成功完成时,COPY命令会返回形如 + +COPY count + + 的命令标签。count为复制的行数。 + + + + + + 只有当命令不是COPY ... TO STDOUT,或不是等效的 + psql元命令\copy ... to stdout时, + psql才会打印这个命令标签。这是为了避免将 + 命令标签与刚刚输出的数据混淆。 + + + + + + 注解 + + + COPY只能用于普通表,不能用于视图。但可以写 + COPY (SELECT * FROM viewname) TO ...。 + + + + COPY只处理指定的表本身;它不会从子表复制数据 + 或向子表复制数据。因此,例如COPY table TO显示的数据与 + SELECT * FROM ONLY table相同。但COPY + (SELECT * FROM table) TO ...可用于导出 + 继承层次结构中的所有数据。 + + + + 你必须对COPY TO读取其值的表具有 + SELECT权限,并对COPY FROM + 插入其值的表具有INSERT权限。对于命令中列出的列, + 具有列级权限即可。 + + + + 如果对表启用了行级安全,相关的SELECT策略将应用于 + COPY table TO + 语句。目前,对启用了行级安全的表不支持COPY FROM。 + 请改用等效的INSERT语句。 + + + + COPY命令中指定的文件由服务器而非客户端应用直接读取或写入。 + 因此,这些文件必须位于数据库服务器所在机器上,或者可由数据库服务器访问, + 而不是仅由客户端访问。它们必须可由PostgreSQL用户 + (服务器运行时使用的用户 ID)访问,并且对该用户可读或可写。同样, + 使用PROGRAM指定的命令也是由服务器而非客户端应用直接执行, + 因而必须可由PostgreSQL用户执行。只有数据库超级用户才允许使用指定文件名或命令的COPY,因为这允许读取或写入服务器有权访问的任何文件。 + + + 不要将COPY与 + psql指令 + \copy + 混淆。\copy会调用 + COPY FROM STDINCOPY TO + STDOUT,然后在psql客户端可访问的 + 文件中读取或存储数据。因此,使用\copy时, + 文件的可访问性和访问权限取决于客户端而不是服务器。 + + + + 建议在COPY中使用的文件名始终指定为绝对路径。 + 对于COPY TO,服务器会强制这一点;但对于 + COPY FROM,你仍可选择从使用相对路径指定的文件中读取。 + 该路径将相对于服务器进程的工作目录(通常是集簇的数据目录)而非客户端的工作目录进行解释。 + + + + 使用PROGRAM执行命令可能会受到操作系统 + 的访问控制机制(如 SELinux)的限制。 + + + + COPY FROM将调用目标表上的任何触发器 + 和检查约束。但是它不会调用规则。 + + + + COPY的输入和输出会受 + DateStyle影响。为确保数据能移植到其他可能使用非默认 + DateStyle设置的PostgreSQL + 安装中,使用COPY TO前应将 + DateStyle设置为ISO。同样也建议避免在 + IntervalStyle设置为sql_standard时转储 + 数据,因为负的 interval 值可能会被采用不同 + IntervalStyle设置的服务器误解。 + + + + 即使数据会被服务器直接从一个文件读取或者写入一个文件而不通过 + 客户端,输入数据也会被根据ENCODING选项或者当前 + 客户端编码解释,并且输出数据会被根据ENCODING或 + 者当前客户端编码进行编码。 + + + COPY 会在遇到第一个错误时停止操作。对于 COPY TO,这应当不会导致问题;但对于 COPY FROM,目标表此时已经接收了前面的行。这些行不可见也不可访问,但仍占用磁盘空间。如果在一次大型复制操作已经进行很久后才失败,可能会浪费大量磁盘空间。可以调用 VACUUM 回收这些浪费的空间。 + + + FORCE_NULLFORCE_NOT_NULL可以同时 + 用于同一列。这会把带引号的空值串转换为空值,并把不带引号的空值串 + 转换为空串。 + + + + + + 文件格式 + + + 文本格式 + + + 在使用text格式时,读取或写入的是一个文本文件, + 其中表中的每一行对应文件中的一行。每行中的列由分隔符字符隔开。 + 列值本身是由各属性数据类型的输出函数生成、或可被其输入函数接受的 + 字符串。对于为空值的列,会使用指定的空值串代替。 + 如果输入文件中的任何一行包含的列数多于或少于预期, + COPY FROM就会报错。 + 如果指定了 OIDS,OID 会作为第一列读取或写入,位于用户数据列之前。 + + 数据结束可以表示为只包含反斜线加点号(\.)的一行。从文件读取时,不需要数据结束标记,因为文件结束已足够;只有在使用 3.0 之前版本的客户端协议、向客户端应用程序复制数据或从其复制数据时,才需要该标记。 + + + 在COPY数据中,可以使用反斜线字符(\) + 来转义那些原本可能被当作行或列分隔符的数据字符。特别是, + 如果下列字符作为列值的一部分出现,那么它们前面必须 + 加一个反斜线:反斜线本身、换行、回车以及当前分隔符字符。 + + + + COPY TO输出指定的空值串时不会添加任何反斜线; + 相反,COPY FROM会在去除反斜线之前先将输入 + 与空值串进行匹配。因此,像\N这样的空值串不会与实际的 + 数据值\N混淆,因为后者会表示为\\N。 + + + + COPY FROM识别下列特殊的反斜线序列: + + + + + + 序列 + 表示 + + + + + + \b + 退格 (ASCII 8) + + + \f + 换页 (ASCII 12) + + + \n + 新行 (ASCII 10) + + + \r + 回车 (ASCII 13) + + + \t + 制表 (ASCII 9) + + + \v + 纵向制表 (ASCII 11) + + + \digits + 反斜线后跟一到三个八进制数字表示该数字代码对应的字节 + + + \xdigits + 反斜线加x后跟一到两个十六进制数字表示该数字代码对应的字节 + + + + + + 目前,COPY TO从不会输出八进制或十六进制数字反斜线 + 序列,但对这些控制字符确实会使用上表列出的其他序列。 + + + + 任何上表中未提到的其他反斜线字符都将表示其自身。不过,要注意不要 + 不必要地添加反斜线,因为那可能意外地产生与数据结束标记 + (\.)或空值串(默认是\N)匹配的字符串。 + 这些字符串会在进行任何其他反斜线处理之前先被识别出来。 + + + + 强烈建议生成COPY数据的应用将数据中的换行和回车分别 + 转换为\n\r序列。目前,仍然可以用 + 反斜线加回车表示数据回车,用反斜线加换行表示数据换行。不过, + 未来版本可能不再接受这些表示方式。如果COPY文件在不同机器之间 + 传输(例如从 Unix 到 Windows,或反之),这些表示方式也非常容易被破坏。 + + + + 所有反斜线序列都在编码转换后进行解释。 + 用八进制和十六进制数字反斜线序列指定的字节必须在数据库编码中形成有效字符。 + + + + COPY TO会用 Unix 风格的换行( + \n)结束每一行。运行在 Microsoft Windows + 上的服务器则会输出回车/换行(\r\n),但这只适用于 + 复制到服务器文件的COPY;为保证跨平台一致性, + COPY TO STDOUT总是发送\n, + 与服务器平台无关。COPY FROM能够处理以换行、回车 + 或回车/换行结束的行。为减少本应是数据的未加反斜线新行或回车带来的风险, + 如果输入中的行结束符并不一致,COPY FROM将会报错。 + + + + + CSV 格式 + + + 这种格式选项用于导入和导出许多其他程序(如电子表格)使用的 + 逗号分隔值(CSV)文件格式。不同于 + PostgreSQL标准文本格式使用的转义规则, + 它会生成并识别通用的 CSV转义机制。 + + + + 每条记录中的值由DELIMITER字符分隔。如果某个值包含 + 分隔符字符、QUOTE字符、NULL字符串、 + 回车或换行字符,那么整个值都会以前后各一个QUOTE字符包围, + 并且该值内每次出现QUOTE字符或ESCAPE + 字符之前都会加上转义字符。对于指定列中的非NULL值输出, + 还可以使用FORCE_QUOTE来强制加引号。 + + + + CSV格式没有标准方式区分NULL值和空字符串。 + PostgreSQLCOPY通过引号来处理 + 这一区别。NULL会按照NULL参数字符串输出, + 且不会被加引号;而与NULL参数字符串匹配的非NULL + 值会被加引号。例如,在默认设置下,NULL会写成一个未加引号的 + 空字符串,而空字符串数据值会写成双引号包围的形式("")。 + 读取值时遵循类似规则。你可以使用FORCE_NOT_NULL来阻止 + 对指定列进行NULL输入比较。也可以使用FORCE_NULL + 将带引号的空值串数据值转换为NULL。 + + + 因为反斜线在 CSV 格式中不是特殊字符,数据结束标记 \. 也可能作为数据值出现。为避免误解,当 \. 数据值作为一行中的唯一字段出现时,输出时会自动给它加上引号;输入时,如果它带有引号,就不会被解释为数据结束标记。如果正在装载由其他应用程序创建的文件,其中只有一个不带引号的列,而且该列可能包含值 \.,那么可能需要在输入文件中给该值加上引号。 + + + + 在CSV格式中,所有字符都有意义。被空白字符或 + DELIMITER之外其他字符包围的带引号值,会把这些字符 + 也包含进值中。如果你导入的数据来自某个会用空白把CSV + 行填充到固定宽度的系统,这可能导致错误。出现这种情况时, + 你可能需要在将数据导入PostgreSQL之前, + 先预处理CSV文件以移除尾随空白。 + + + + + CSV 格式既能识别也能生成这样的 CSV 文件:其中带引号的值包含内嵌的回车和换行。因此,这类文件不像文本格式文件那样严格地一行对应表中的一行。 + + + + + 很多程序会生成奇怪、甚至近乎反常的CSV文件,因此这种文件格式更像一种约定而非标准。 + 因而你可能会遇到无法用这种机制导入的文件,而COPY也可能生成其他程序无法处理的文件。 + + + + + + + 二进制格式 + + + binary格式选项会使所有数据以二进制格式而不是文本格式 + 存储或读取。它比文本和CSV格式稍快一些,但二进制格式文件在不同的 + 机器架构和PostgreSQL版本之间的可移植性较差。 + 此外,二进制格式与数据类型高度相关。例如,不能从 + smallint列输出二进制数据再读入到integer列中, + 尽管这种做法在文本格式下是可行的。 + + + + binary文件格式由文件头、零个或多个包含 + 行数据的元组以及一个文件尾构成。头部和数据都以网络字节序表示。 + + + + + + 7.4 之前的PostgreSQL版本 + 使用一种不同的二进制文件格式。 + + + + + 文件头 + + 文件头由 15 字节的固定字段组成,后面跟着一个可变长度的头部扩展区域。固定字段如下: + + 签名 + + +11 字节序列PGCOPY\n\377\r\n\0 — 注意, +零字节是签名中必不可少的一部分。(该签名的设计目的是便于识别那些在 +不具备 8 位透明性的传输过程中遭到破坏的文件。行尾转换过滤器、 +零字节丢失、高位丢失或奇偶校验变化等情况都会改变该签名。) + + + + + + 标志域 + + 这是一个 32 位整数位掩码,用于表示文件格式的重要属性。位编号从 0(LSB)到 31(MSB)。注意,此字段与文件格式中的所有整数字段一样,采用网络字节序存储(最高有效字节在前)。位 16-31 保留用于表示文件格式的关键问题;如果读取程序发现这个范围内有非预期的位被置位,就应中止。位 0-15 保留用于表示向后兼容的格式问题;读取程序应直接忽略这个范围内非预期的置位。目前仅定义了一个标志位,其余位必须为零: + + 位 16 + + + 如果为 1,则数据中包含 OID;如果为 0,则不包含。 + + + + + + + + 头部扩展区长度 + + +32 位整数,表示头部剩余部分的长度(以字节计),不包括该字段本身。 +当前该值为零,因此其后紧接着第一个元组。未来对这种格式的更改 +可能允许在头部中包含额外数据。如果读取程序不知道如何处理头部 +扩展区数据,应静默跳过它。 + + + + + + + +头部扩展区被设想为包含一系列可自我标识的块。标志域并不用于告诉 +读取程序扩展区中包含哪些内容。头部扩展内容的具体设计留待后续版本决定。 + + + + 这种设计既允许向后兼容的头部新增(增加头部扩展块,或设置低位标志位), + 也允许不向后兼容的更改(设置高位标志位来表明这类更改,并在需要时向扩展区 + 增加支持数据)。 + + + + + 元组 + +每个元组都以一个 16 位整数计数开头,用于表示该元组中的字段数。(目前, +一个表中的所有元组都应有相同的计数,但这未必永远如此。)随后,对元组中的 +每个字段,都会有一个 32 位长度字,后跟该字段数据的相应字节数。(长度字不 +包括其本身,且可以为零。)特殊情况下,-1 表示一个 NULL 字段值;在 NULL +情况下,后面不会跟随任何值字节。 + + + +字段之间没有对齐填充或任何其他额外数据。 + + + +当前,二进制格式文件中的所有数据值都假定为二进制格式(格式代码一)。 +可以预见,未来的扩展可能会增加一个允许为各列分别指定格式代码的头部字段。 + + + +要确定实际元组数据应采用的二进制格式,你应该参考 +PostgreSQL源码,特别是各列 +数据类型对应的*send*recv函数(这些函数通常可 +以在源码分发包的src/backend/utils/adt/目录中找到)。 + + + 如果文件中包含 OID,则 OID 字段紧跟在字段计数值之后。它是一个普通字段,只是不计入字段数。特别是,它带有一个长度字段 — 这样就能较容易地处理 4 字节或 8 字节的 OID,也允许在将来确有需要时把 OID 表示为空值。 + + + + + 文件尾 + + + 文件尾由一个值为 -1 的 16 位整数构成。这很容易与元组的字段计数字区分开来。 + + + + 如果字段计数字既不是 -1 也不是预期的列数,读取程序应报告错误。 + 这提供了一项额外检查,以防与数据失去同步。 + + + + + + + + 示例 + + + 下面的示例使用竖线(|)作为字段分隔符将一个表复制到客户端: + +COPY country TO STDOUT (DELIMITER '|'); + + + + + 要将文件中的数据复制到country表中: + +COPY country FROM '/usr1/proj/bray/sql/country_data'; + + + + + 只把名称以 'A' 开头的国家复制到一个文件中: + +COPY (SELECT * FROM country WHERE country_name LIKE 'A%') TO '/usr1/proj/bray/sql/a_list_countries.copy'; + + + + + 要复制到压缩文件中,可以将输出通过管道送入外部压缩程序: + +COPY country TO PROGRAM 'gzip > /usr1/proj/bray/sql/country_data.gz'; + + + + + 下面给出适合从STDIN复制到表中的示例数据: + +AF AFGHANISTAN +AL ALBANIA +DZ ALGERIA +ZM ZAMBIA +ZW ZIMBABWE + + 注意每一行中的空白实际上是一个制表符。 + + + + 下面是用二进制格式输出的相同数据。该数据是用 Unix 工具 + od -c过滤后显示的。该表具有三列, + 第一列类型是char(2),第二列类型是text, + 第三列类型是integer。所有行在第三列都是空值。 + +0000000 P G C O P Y \n 377 \r \n \0 \0 \0 \0 \0 \0 +0000020 \0 \0 \0 \0 003 \0 \0 \0 002 A F \0 \0 \0 013 A +0000040 F G H A N I S T A N 377 377 377 377 \0 003 +0000060 \0 \0 \0 002 A L \0 \0 \0 007 A L B A N I +0000100 A 377 377 377 377 \0 003 \0 \0 \0 002 D Z \0 \0 \0 +0000120 007 A L G E R I A 377 377 377 377 \0 003 \0 \0 +0000140 \0 002 Z M \0 \0 \0 006 Z A M B I A 377 377 +0000160 377 377 \0 003 \0 \0 \0 002 Z W \0 \0 \0 \b Z I +0000200 M B A B W E 377 377 377 377 377 377 + + + + + 兼容性 + + + SQL 标准中没有COPY语句。 + + + 以下语法曾在PostgreSQL9.0 之前的版本中使用,目前仍受支持: +COPY table_name [ ( column_name [, ...] ) ] + FROM { 'filename' | STDIN } + [ [ WITH ] + [ BINARY ] + [ OIDS ] + [ DELIMITER [ AS ] 'delimiter_character' ] + [ NULL [ AS ] 'null string' ] + [ CSV [ HEADER ] + [ QUOTE [ AS ] 'quote_character' ] + [ ESCAPE [ AS ] 'escape_character' ] + [ FORCE NOT NULL column_name [, ...] ] ] ] + +COPY { table_name [ ( column_name [, ...] ) ] | ( query ) } + TO { 'filename' | STDOUT } + [ [ WITH ] + [ BINARY ] + [ OIDS ] + [ DELIMITER [ AS ] 'delimiter_character' ] + [ NULL [ AS ] 'null string' ] + [ CSV [ HEADER ] + [ QUOTE [ AS ] 'quote_character' ] + [ ESCAPE [ AS ] 'escape_character' ] + [ FORCE QUOTE { column_name [, ...] | * } ] ] ] +注意,在这种语法中,BINARYCSV被当作独立的关键字,而不是FORMAT选项的参数。 + + 以下语法曾在PostgreSQL7.3 之前的版本中使用,目前仍受支持: +COPY [ BINARY ] table_name [ WITH OIDS ] + FROM { 'filename' | STDIN } + [ [USING] DELIMITERS 'delimiter_character' ] + [ WITH NULL AS 'null_string' ] + +COPY [ BINARY ] table_name [ WITH OIDS ] + TO { 'filename' | STDOUT } + [ [USING] DELIMITERS 'delimiter_character' ] + [ WITH NULL AS 'null_string' ] + + + diff --git a/zh/9.6/ref/create_access_method.sgml b/zh/9.6/ref/create_access_method.sgml new file mode 100644 index 00000000..6f6ded6c --- /dev/null +++ b/zh/9.6/ref/create_access_method.sgml @@ -0,0 +1,104 @@ + + + + + CREATE ACCESS METHOD + + + + CREATE ACCESS METHOD + 7 + SQL - 语言语句 + + + + CREATE ACCESS METHOD + 定义一种新的访问方法 + + + + +CREATE ACCESS METHOD name + TYPE access_method_type + HANDLER handler_function + + + + + 描述 + + + CREATE ACCESS METHOD创建一种新的访问方法。 + + + + 访问方法的名称在数据库中必须唯一。 + + + + 只有超级用户可以定义新的访问方法。 + + + + + 参数 + + + + name + + + 要创建的访问方法的名称。 + + + + + + access_method_type + + 此子句指定要定义的访问方法类型。目前仅支持 INDEX + + + + + handler_function + + handler_function 是一个先前注册的函数的名称(可能带有模式限定),该函数表示访问方法。处理函数必须声明为接受一个 internal 类型的单一参数,其返回类型取决于访问方法的类型;对于 INDEX 访问方法,它必须是 index_am_handler。处理函数必须实现的 C 级 API 会根据访问方法的类型而有所不同。索引访问方法 API 的描述见 + + + + + + + 示例 + + + 创建索引访问方法heptree,其处理器函数为heptree_handler: + +CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler; + + + + + + 兼容性 + + + CREATE ACCESS METHOD是一种PostgreSQL扩展。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/create_aggregate.sgml b/zh/9.6/ref/create_aggregate.sgml new file mode 100644 index 00000000..ed2c96bc --- /dev/null +++ b/zh/9.6/ref/create_aggregate.sgml @@ -0,0 +1,586 @@ + + + + + CREATE AGGREGATE + + + + CREATE AGGREGATE + 7 + SQL - 语言语句 + + + + CREATE AGGREGATE + 定义一个新的聚合函数 + + + + +CREATE AGGREGATE name ( [ argmode ] [ argname ] arg_data_type [ , ... ] ) ( + SFUNC = sfunc, + STYPE = state_data_type + [ , SSPACE = state_data_size ] + [ , FINALFUNC = ffunc ] + [ , FINALFUNC_EXTRA ] + [ , COMBINEFUNC = combinefunc ] + [ , SERIALFUNC = serialfunc ] + [ , DESERIALFUNC = deserialfunc ] + [ , INITCOND = initial_condition ] + [ , MSFUNC = msfunc ] + [ , MINVFUNC = minvfunc ] + [ , MSTYPE = mstate_data_type ] + [ , MSSPACE = mstate_data_size ] + [ , MFINALFUNC = mffunc ] + [ , MFINALFUNC_EXTRA ] + [ , MINITCOND = minitial_condition ] + [ , SORTOP = sort_operator ] + [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] +) + +CREATE AGGREGATE name ( [ [ argmode ] [ argname ] arg_data_type [ , ... ] ] + ORDER BY [ argmode ] [ argname ] arg_data_type [ , ... ] ) ( + SFUNC = sfunc, + STYPE = state_data_type + [ , SSPACE = state_data_size ] + [ , FINALFUNC = ffunc ] + [ , FINALFUNC_EXTRA ] + [ , INITCOND = initial_condition ] + [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] + [ , HYPOTHETICAL ] +) + +或旧式语法 + +CREATE AGGREGATE name ( + BASETYPE = base_type, + SFUNC = sfunc, + STYPE = state_data_type + [ , SSPACE = state_data_size ] + [ , FINALFUNC = ffunc ] + [ , FINALFUNC_EXTRA ] + [ , COMBINEFUNC = combinefunc ] + [ , SERIALFUNC = serialfunc ] + [ , DESERIALFUNC = deserialfunc ] + [ , INITCOND = initial_condition ] + [ , MSFUNC = msfunc ] + [ , MINVFUNC = minvfunc ] + [ , MSTYPE = mstate_data_type ] + [ , MSSPACE = mstate_data_size ] + [ , MFINALFUNC = mffunc ] + [ , MFINALFUNC_EXTRA ] + [ , MINITCOND = minitial_condition ] + [ , SORTOP = sort_operator ] +) + + + + + 描述 + + CREATE AGGREGATE 定义一个新的聚合函数。系统自带了一些基本且常用的聚合函数;它们在 中有文档说明。如果需要定义新类型或需要一个尚未提供的聚合函数,则可以使用 CREATE AGGREGATE 来实现所需功能。 + + + 如果给出了一个模式名(例如CREATE AGGREGATE + myschema.myagg ...),则该聚合函数会在指定模式中创建。否则 + 会在当前模式中创建。 + + + + 聚合函数由其名称和输入数据类型来标识。如果同一模式中的两个聚合作用于不 + 同的输入类型,它们可以具有相同的名称。聚合的名称和输入数据类型还必须不 + 同于同一模式中每个普通函数的名称和输入数据类型。这种行为与普通函数名的 + 重载完全相同(见)。 + + + 简单聚合函数由一个或两个普通函数构成:状态转换函数sfunc,以及一个可选的最终计算函数ffunc。它们的使用方式如下: +sfunc( internal-state, next-data-values ) ---> next-internal-state +ffunc( internal-state ) ---> aggregate-value + + + + + PostgreSQL会创建一个数据类型为 + stype的临时变量,用来保存 + 聚合的当前内部状态。对于每个输入行,都会先计算聚合参数值,然后以当前状 + 态值和新的参数值调用状态转移函数,从而计算新的内部状态值。等到所有行都 + 处理完毕后,再调用一次最终函数来计算聚合的返回值。如果没有最终函数,则 + 原样返回结束时的状态值。 + + + + 聚合函数可以提供一个初始条件,即内部状态值的初始值。它在数据库中以 + text类型的值指定和存储,但它必须是该状态值数据类型常量的 + 合法外部表示。如果未提供,则状态值初始为空值。 + + + + 如果状态转移函数被声明为strict,则不能用空输入调用它。 + 采用这种转移函数时,聚合执行的行为如下。任何输入值为空的行都会被忽略 + (不会调用该函数,并保留先前的状态值)。如果初始状态值为空,则在第一个 + 所有输入值都非空的行上,第一个参数值会取代状态值,并在之后每一个所有输 + 入值都非空的行上调用状态转移函数。这对于实现max之 + 类的聚合很方便。注意,只有当 + state_data_type + 与第一个 + arg_data_type相同时,这种行为 + 才可用。当这两种类型不同时,必须提供非空初始条件,或者使用非 strict + 状态转移函数。 + + + + 如果状态转移函数不是 strict,则它会无条件地在每个输入行上被调用,并且 + 必须自行处理空输入和空状态值。这使聚合作者能够完全控制聚合对空值的处理 + 方式。 + + + + 如果最终函数被声明为strict,那么当结束状态值为空时不会调 + 用它;而是自动返回空结果。(这当然只是 strict 函数的正常行为。)无论如何, + 最终函数都可以选择返回空值。例如,avg的最终函数在发 + 现输入行数为零时会返回空值。 + + + + 有时将最终函数声明为不仅接收状态值,还接收与聚合输入值相对应的额外参数 + 会很有用。这样做的主要原因是,如果最终函数是多态的,状态值的数据类型不 + 足以确定结果类型。这些额外参数总是以 NULL 传递(因此在使用 + FINALFUNC_EXTRA选项时,最终函数不能声明为 strict),但它 + 们仍然是有效参数。例如,最终函数可以利用 + get_fn_expr_argtype来识别当前调用中的实际参数类型。 + + + 聚合函数可选择性地支持 移动聚合模式,如 所述。这需要指定 MSFUNCMINVFUNCMSTYPE 参数,并可选地指定 MSSPACEMFINALFUNCMFINALFUNC_EXTRAMINITCOND 参数。除了 MINVFUNC 之外,这些参数的工作方式与对应的无 M 的简单聚合参数相同;它们定义了包含逆向转换函数的聚合的独立实现。 + + + 参数列表中带有ORDER BY的语法会创建一种称为 + 有序集聚合的特殊聚合类型;如果指定了 + HYPOTHETICAL,则会创建 + 假想集聚合。这些聚合以依赖顺序的方式对一组已 + 排序的值进行操作,因此指定输入排序顺序是调用中必不可少的一部分。它们 + 还可以有直接参数,即每次聚合只求值一次而不是对 + 每个输入行求值一次的参数。假想集聚合是有序集聚合的一个子类,其中要求 + 某些直接参数在数量和数据类型上与被聚合的参数列匹配。这使得这些直接参 + 数的值可以作为一条额外的假想行添加到聚合输入行的集合中。 + + + 聚合函数可选择性地支持 部分聚合,如 所述。这需要指定 COMBINEFUNC 参数。如果 state_data_typeinternal,通常也应提供 SERIALFUNCDESERIALFUNC 参数,以便支持并行聚合。请注意,聚合函数还必须标记为 PARALLEL SAFE 才能启用并行聚合。 + + + 行为类似于MINMAX的聚合,有 + 时可以通过查阅索引而不是扫描每个输入行来优化。如果该聚合可以这样优化, + 请通过指定一个排序操作符来表明。基本要求是,该 + 聚合必须返回该操作符所诱导的排序顺序中的第一个元素;换句话说: + +SELECT agg(col) FROM tab; + + 必须等价于: + +SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; + + 进一步的假设是,该聚合忽略空输入,并且当且仅当不存在非空输入时返回空 + 结果。通常,某种数据类型的<操作符是 + MIN的合适排序操作符,而>是 + MAX的合适排序操作符。注意,除非指定的操作符是 + B-树索引操作符类中小于大于策略成员,否 + 则这种优化实际上永远不会生效。 + + + + 要能够创建聚合函数,你必须在参数类型、状态类型和返回类型上拥有 + USAGE权限,并在支持函数上拥有 + EXECUTE权限。 + + + + + 参数 + + + + name + + + + 要创建的聚合函数的名称(可带模式限定)。 + + + + + + argmode + + + + + 参数的模式:INVARIADIC。 + (聚合函数不支持OUT参数。)如果省略,默认为 + IN。只有最后一个参数可以标记为 + VARIADIC。 + + + + + + argname + + + + + 参数的名称。目前这仅对文档有用。如果省略,则该参数没有名称。 + + + + + + arg_data_type + + + + 该聚合函数所操作的输入数据类型。要创建零参数聚合函数,在参数说明列表 + 的位置写*。(此类聚合的一个示例是 + count(*)。) + + + + + + base_type + + + + 在CREATE AGGREGATE的旧语法中,输入数据类型通过 + basetype参数指定,而不是写在聚合名称旁边。注意, + 这种语法只允许一个输入参数。要用这种语法定义零参数聚合函数,应将 + basetype指定为"ANY"(不是 + *)。有序集聚合不能用旧语法定义。 + + + + + + sfunc + + 每次处理输入行时调用的状态转换函数的名称。对于一个普通的 N 参数聚合函数,sfunc 必须接受 N+1 个参数,第一个参数类型为 state_data_type,其余参数与聚合声明的输入数据类型匹配。该函数必须返回 state_data_type 类型的值。此函数接收当前状态值和当前输入数据值,返回下一个状态值。 + + + 对于有序集(包括假想集)聚合,状态转移函数只接收当前状态值和聚合参数, + 不接收直接参数。除此之外它与普通情况相同。 + + + + + + state_data_type + + + + 聚合状态值的数据类型。 + + + + + + state_data_size + + 聚合状态值的大致平均大小(以字节为单位)。如果省略此参数或其值为零,则会基于 state_data_type 使用默认估计值。规划器使用此值来估算分组聚合查询所需的内存。规划器仅在估计哈希表可容纳于 时才会考虑使用哈希聚合;因此,此参数的较大值会抑制哈希聚合的使用。 + + + + + ffunc + + 在遍历所有输入行后调用以计算聚合结果的最终函数的名称。对于普通聚合,此函数必须接受一个 state_data_type 类型的单一参数。聚合的返回数据类型定义为该函数的返回类型。如果未指定 ffunc,则将结束状态值用作聚合结果,返回类型为 state_data_type + + + 对于有序集(包括假想集)聚合,最终函数不仅接收最终状态值,还会接收所 + 有直接参数的值。 + + + + 如果指定了FINALFUNC_EXTRA,则除了最终状态值和任何 + 直接参数之外,最终函数还会接收额外的 NULL 值,它们对应于该聚合的常规 + (被聚合的)参数。这主要用于在定义多态聚合时能够正确解析聚合的结果类 + 型。 + + + + + + combinefunc + + + + 可以选择指定combinefunc + 函数,以使聚合函数支持部分聚合。如果提供了它,则 + combinefunc必须把两个 + state_data_type值合并起 + 来;这两个值各自包含对某个输入值子集聚合得到的结果,并产生一个新的 + state_data_type,表示同 + 时对这两组输入进行聚合的结果。可以把这个函数看作一种 + sfunc:不同之处在于,它 + 不是处理单个输入行并把它加入运行聚合状态,而是把另一个聚合状态加入运 + 行状态。 + + + + combinefunc必须声明为接 + 收两个state_data_type + 参数,并返回一个state_data_type + 值。该函数也可以选择声明为strict。在这种情况下,只要 + 任一输入状态为空,就不会调用该函数;另一个状态会被视为正确结果。 + + + + 对于state_data_type为 + internal的聚合函数, + combinefunc不能声明为 + strict。在这种情况下, + combinefunc必须确保正 + 确处理空状态,并且返回的状态被正确存储在聚合内存上下文中。 + + + + + + serialfunc + + + + state_data_type为 + internal的聚合函数,只有在具备一个 + serialfunc函数时才能参与 + 并行聚合;该函数必须把聚合状态序列化为一个bytea值,以传 + 输给另一个进程。该函数必须接收一个internal类型参数并返回 + bytea类型结果。还需要相应的 + deserialfunc。 + + + + + + deserialfunc + + + + 将先前序列化的聚合状态反序列化回 + state_data_type。该函数 + 必须接收bytea和internal两个参数,并产生一 + 个internal类型的结果。(注意:第二个internal + 参数未被使用,但出于类型安全的原因必须存在。) + + + + + + initial_condition + + 状态值的初始设置。这必须是 state_data_type 数据类型所接受形式的字符串常量。如果未指定,状态值将从 null 开始。 + + + + + msfunc + + + + 在移动聚合模式下,对每个输入行调用的前向状态转移函数名称。它与常规转 + 移函数完全相同,只是其第一个参数和结果都是 + mstate_data_type类型,这可能与 + state_data_type不同。 + + + + + + minvfunc + + + + 在移动聚合模式中使用的逆向状态转移函数名称。该函数的参数和结果类型与 + msfunc相同,但它不是把值添加到当前聚合状 + 态中,而是从中移除一个值。逆向状态转移函数必须与前向状态转移函数具有相同 + 的 strict 属性。 + + + + + + mstate_data_type + + + + 使用移动聚合模式时,聚合状态值的数据类型。 + + + + + + mstate_data_size + + + + 使用移动聚合模式时,聚合状态值的近似平均大小(以字节为单位)。其作用 + 与state_data_size相同。 + + + + + + mffunc + + + + 在使用移动聚合模式时,在遍历完所有输入行后调用、用于计算聚合结果的最 + 终函数名称。其工作方式与ffunc相同,只是其第一 + 个参数的类型是mstate_data_type,而额外的哑 + 参数通过写MFINALFUNC_EXTRA来指定。由 + mffunc或 + mstate_data_type决定的聚合结果类型,必须 + 与该聚合常规实现决定的结果类型一致。 + + + + + + minitial_condition + + + + 使用移动聚合模式时,状态值的初始设置。其作用与 + initial_condition相同。 + + + + + + sort_operator + + + + 与类MIN或类MAX聚合关联的排 + 序操作符。这只是一个操作符名称(可带模式限定)。假定该操作符与该聚合 + 具有相同的输入数据类型(该聚合必须是单参数普通聚合)。 + + + + + + PARALLEL + + PARALLEL SAFEPARALLEL RESTRICTEDPARALLEL UNSAFE 的含义与 中的相同。如果聚合函数被标记为 PARALLEL UNSAFE(这是默认值!)或 PARALLEL RESTRICTED,则不会考虑对其进行并行化处理。请注意,规划器不会检查聚合函数支持函数的并行安全性标记,只检查聚合函数本身的标记。 + + + + + HYPOTHETICAL + + + + 仅用于有序集聚合。该标志指定应按假想集聚合的要求处理聚合参数:也就是, + 最后几个直接参数必须与聚合(WITHIN GROUP)参数的 + 数据类型匹配。HYPOTHETICAL标志不会影响运行时行为, + 只影响在解析时对聚合参数的数据类型和排序规则的确定。 + + + + + + + CREATE AGGREGATE的参数可以按任意顺序书写,而不只 + 是按上面展示的顺序。 + + + + + 注解 + + + 在指定支持函数名的参数中,如果需要可以写模式名,例如 + SFUNC = public.sum。但是不要在那里写参数类型 + — 支持函数的参数类型由其他参数决定。 + + + + 如果一个聚合支持移动聚合模式,那么当它被用作具有移动帧起点的窗口的窗 + 口函数时(即帧起点模式不是UNBOUNDED PRECEDING), + 就能提高计算效率。从概念上讲,当前向状态转移函数在输入值从底部进入窗口帧 + 时把它们加入聚合状态,逆向状态转移函数则在这些值从顶部离开窗口帧时再次将 + 其移除。因此,移除值时,总是按它们被加入时的相同顺序移除。每当调用逆 + 向状态转移函数时,它收到的就是最早被加入但尚未被移除的参数值。逆向状态转移函 + 数可以假定,在它移除最旧的那一行之后,当前状态中至少还会保留一行。(若 + 非如此,窗口函数机制就会简单地重新开始一次全新的聚合,而不是使用逆向 + 状态转移函数。) + + + + 用于移动聚合模式的前向状态转移函数不允许返回 NULL 作为新的状态值。如果逆 + 向状态转移函数返回 NULL,就表示该逆向函数无法针对这个特定输入逆转状态计 + 算,因此会从当前帧起始位置开始重新计算聚合。这一约定使得移动聚合模式 + 可用于这样一些场景:在少数不常见的情况下,难以从运行状态值中逆向消除 + 对应输入的影响。 + + + 如果没有提供移动聚合实现,聚合仍然可以与移动帧一起使用,但每当帧起点移动时,PostgreSQL都会重新计算整个聚合。注意,无论聚合是否支持移动聚合模式,PostgreSQL都能在不重新计算的情况下处理移动的帧结束位置;做法是继续把新值添加到聚合状态中。这里假定最终函数不会损坏聚合的状态值,这样即使已经针对一组帧边界得到了聚合结果值,聚合也能继续进行。 + + + 有序集聚合的语法允许对最后一个直接参数和最后一个聚合 + (WITHIN GROUP)参数都指定 + VARIADIC。但是,当前实现从两个方面限制了 + VARIADIC的使用。第一,有序集聚合只能使用 + VARIADIC "any",不能使用其他可变参数数组类型。第 + 二,如果最后一个直接参数是VARIADIC "any",则只能有 + 一个聚合参数,并且它也必须是VARIADIC "any"。 + (在系统目录使用的表示中,这两个参数会合并为单个 + VARIADIC "any"项,因为pg_proc + 无法表示具有多个VARIADIC参数的函数。)如果该聚合是 + 假想集聚合,则与VARIADIC "any"参数匹配的直接参数就 + 是假想参数;其前面的任何参数都表示额外的直接参数,它们不受必须与聚合 + 参数匹配的约束。 + + + + 当前,有序集聚合无需支持移动聚合模式,因为它们不能被用作窗口函数。 + + + + 部分(包括并行)聚合当前不支持有序集聚合。此外,对于包含 + DISTINCTORDER BY子句的聚合调 + 用,也永远不会使用部分聚合,因为在部分聚合期间无法支持这些语义。 + + + + + + 示例 + + + 见。 + + + + + + 兼容性 + + + CREATE AGGREGATE是 + PostgreSQL的语言扩展。SQL 标准不提供用户 + 定义聚合函数。 + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_cast.sgml b/zh/9.6/ref/create_cast.sgml new file mode 100644 index 00000000..f91a5e47 --- /dev/null +++ b/zh/9.6/ref/create_cast.sgml @@ -0,0 +1,388 @@ + + + + + CREATE CAST + + + + CREATE CAST + 7 + SQL - 语言语句 + + + + CREATE CAST + 定义一种新的类型转换 + + + + + +CREATE CAST (source_type AS target_type) + WITH FUNCTION function_name [ (argument_type [, ...]) ] + [ AS ASSIGNMENT | AS IMPLICIT ] + +CREATE CAST (source_type AS target_type) + WITHOUT FUNCTION + [ AS ASSIGNMENT | AS IMPLICIT ] + +CREATE CAST (source_type AS target_type) + WITH INOUT + [ AS ASSIGNMENT | AS IMPLICIT ] + + + + + + 描述 + + + CREATE CAST定义一种新的类型转换。 + 类型转换规定如何在两种数据类型之间执行转换。例如, + +SELECT CAST(42 AS float8); + + 通过调用一个预先指定的函数(此处是 + float8(int4))把整型常量 42 转换成 + float8类型。(如果没有定义合适的类型转换, + 转换就会失败。) + + + + 两种类型可以是二进制可强制转换的,这意味着无需调用任 + 何函数,就可以免费执行转换。这要求对应的值使用相同的内部 + 表示。例如,textvarchar这两种类型在两个方向上 + 都是二进制可强制转换的。二进制可强制转换性不一定是对称关系。例如,在当前实现 + 中,从xmltext的类型转换可以免费执行,但反方向 + 则需要一个至少执行语法检查的函数。(双向都二进制可强制转换的两种类型也称 + 为二进制兼容。) + + + + 使用WITH INOUT语法,你可以把一种类型转换定义为 + 基于 I/O 的类型转换。基于 I/O 的类型转换通过调用源 + 数据类型的输出函数,并将得到的字符串传给目标数据类型的输入函数来执 + 行。在许多常见情况下,这项特性避免了为转换单独编写类型转换函数的必 + 要。基于 I/O 的类型转换与常规的基于函数的类型转换行为相同,只是实现方 + 式不同。 + + + + 默认情况下,只有显式请求类型转换时才会调用一种类型转换,也就是显式使 + 用CAST(x AS + typename)或 + x::typename + 这种构造。 + + + + 如果一种类型转换被标记为AS ASSIGNMENT,那么在把值赋 + 给目标数据类型的列时就可以隐式调用它。例如,假设foo.f1 + 是一个text类型的列,那么如果从integer到 + text的类型转换被标记为AS ASSIGNMENT, + 则: + +INSERT INTO foo (f1) VALUES (42); + + 就会被允许,否则不会。(我们通常用术语赋值类型转换 + 来描述这种类型转换。) + + + + 如果一种类型转换被标记为AS IMPLICIT,那么无论是在赋 + 值上下文中还是在表达式内部,都可以在任何上下文中隐式调用它。(我们通 + 常用术语隐式类型转换来描述这种类型转换。)例如, + 考虑这个查询: + +SELECT 2 + 4.0; + + 解析器最初分别把这两个常量标记为integer和 + numeric类型。系统目录中没有integer + + numeric操作符,但有一个 + numeric + numeric操作符。因 + 此,如果存在一种从integernumeric的可用类型转 + 换,并且被标记为AS IMPLICIT — 实际上确实如此 + — 该查询就会成功。解析器将应用该隐式类型转换,并把该查询解析为如 + 同它被写成了: + +SELECT CAST ( 2 AS numeric ) + 4.0; + + + + + 现在,系统目录还提供了一种从numericinteger的 + 类型转换。如果该类型转换被标记为AS IMPLICIT — + 实际上并没有 — 那么解析器就必须在上面的解释方式和另一种方案之间作 + 出选择:把numeric常量转换成integer,然后应用 + integer + integer操作符。由于 + 它不知道该偏向哪一种选择,就会放弃并将该查询判定为有歧义。两种类型转 + 换中只有一种是隐式的,正是借此我们让解析器倾向于把混合了 + numericinteger的表达式解析为 + numeric;系统对此并没有内置知识。 + + + + 把类型转换标记为隐式时应当保持保守。过多的隐式类型转换路径可能导致 + PostgreSQL对命令作出令人意外的解释,或 + 者因为存在多种可能的解释而根本无法解析命令。一个好的经验法则是,只有 + 对同一一般类型分类中且能保留信息的类型间转换,才让它可以被隐式调用。 + 例如,从int2int4的类型转换可以合理地设为隐 + 式,但从float8int4的类型转换大概应仅限赋值 + 使用。跨类型分类的类型转换,例如从textint4, + 最好只允许显式调用。 + + + + + + 有时出于可用性或标准兼容性的原因,有必要在一组类型之间提供多种隐式类 + 型转换,这会带来像上面那样无法避免的歧义。解析器有一种基于 + 类型分类首选类型的后备启 + 发式规则,在这种情况下有助于提供期望的行为。详见 + 。 + + + + + 要能够创建一种类型转换,你必须拥有源数据类型或目标数据类型之一,并且 + 对另一种类型具有USAGE权限。要创建一种二进制强制 + 转换,你必须是超级用户。(之所以有这一限制,是因为错误的二进制强 + 制转换很容易使服务器崩溃。) + + + + + 参数 + + + + source_type + + + + + 该类型转换的源数据类型的名称。 + + + + + + target_type + + + + + 该类型转换的目标数据类型的名称。 + + + + + + function_name(argument_type [, ...]) + + + + + 用于执行该类型转换的函数。函数名可以用模式限定;如果没有,则会在模 + 式搜索路径中查找该函数。该函数的结果数据类型必须与类型转换的目标类 + 型一致。其参数见下文。 + + + + + + WITHOUT FUNCTION + + + + + 表示源类型对目标类型是二进制可强制转换的,因此执行该类型转换不需要函 + 数。 + + + + + + WITH INOUT + + + + + 表示该类型转换是一种基于 I/O 的类型转换,其执行方式是调用源数据类型 + 的输出函数,并将得到的字符串传给目标数据类型的输入函数。 + + + + + + AS ASSIGNMENT + + + + + 表示该类型转换可以在赋值上下文中隐式调用。 + + + + + + AS IMPLICIT + + + + + 表示该类型转换可以在任何上下文中隐式调用。 + + + + + + + 类型转换实现函数可以有一到三个参数。第一个参数类型必须与源类型相同, + 或者可以由源类型进行二进制强制转换得到。第二个参数(如果有)必须是 + integer类型;它接收与目标类型关联的类型修饰符,如果没有 + 则为-1。第三个参数(如果有)必须是boolean + 类型;如果该类型转换是显式类型转换,它接收true,否则 + 接收false。(奇怪的是,SQL 标准在某些情况下要求显式类 + 型转换和隐式类型转换具有不同的行为。这个参数是为必须实现这类类型转换 + 的函数提供的。不建议你把自己的数据类型设计成需要关心这一点。) + + + + 类型转换函数的返回类型必须与类型转换的目标类型相同,或者对该目标类型 + 是二进制可强制转换的。 + + + + 通常,类型转换的源数据类型和目标数据类型必须不同。不过,如果它具有一 + 个接受多个参数的类型转换实现函数,则允许声明源类型和目标类型相同的类 + 型转换。这用于在系统目录中表示特定类型的长度强制函数。所命名的函数用 + 于将该类型的值强制为其第二个参数给定的类型修饰符值。 + + + + 当一种类型转换的源类型和目标类型不同,且其函数接受多个参数时,它支持 + 在一个步骤中同时完成从一种类型到另一种类型的转换并应用长度强制。如果 + 没有这样的条目,对使用类型修饰符的类型进行强制就需要两个类型转换步 + 骤:先在数据类型之间进行转换,再应用该修饰符。 + + + + 当前,到域类型或从域类型的类型转换都没有效果。到域或从域的类型转换 + 都会使用与其底层类型关联的类型转换。 + + + + + + 注解 + + 使用 删除用户定义的转换。 + + + 请记住,如果你希望能够双向转换类型,就需要在两个方向上分别显式声明类 + 型转换。 + + + + cast + I/O conversion + + + + 通常没有必要在用户定义类型与标准字符串类型(text、 + varcharchar(n), + 以及被定义为属于字符串分类的用户定义类型)之间创建类型转换。 + PostgreSQL会自动为此提供基于 I/O 的类型转 + 换。转换到字符串类型的自动类型转换被视为赋值类型转换,而从字符串类型 + 出发的自动类型转换则只允许显式调用。你可以声明自己的类型转换来替代自 + 动类型转换,从而覆盖这种行为,但通常这样做的唯一原因,是希望该转换比 + 标准的仅赋值或仅显式设置更容易调用。另一种可能的原因是,你希望该转换 + 的行为不同于该类型的 I/O 函数;但这已经足够反常,你应该三思这是不是一 + 个好主意。(确实有少数内置类型在转换行为上有所不同,大多是由于 SQL 标 + 准的要求。) + + + + 虽然这不是强制要求,但仍建议你继续遵循这种老惯例,即按目标数据类型为 + 类型转换实现函数命名。许多用户已经习惯于用函数风格的记法进行类型转 + 换,也就是typename(x)。 + 这种记法实际上无非就是调用类型转换实现函数;它不会被特别当作类型转换 + 处理。如果你的转换函数没有按这种惯例命名,那么用户会感到意外。由于 + PostgreSQL允许同一函数名按不同参数类型重 + 载,因此让来自不同类型的多个转换函数都使用目标类型的名称并不存在困 + 难。 + + + + + + 实际上,前一段说得过于简单了:有两种情况下,即使一个函数调用形式没有 + 匹配到实际存在的函数,也会被当作类型转换请求。如果函数调用 + name(x)不能与任何现有函数精确匹配, + 但name是一个数据类型名,并且 + pg_cast为从x的类型到该类型提供了 + 二进制强制转换,那么该调用会被解释为二进制强制转换。 + 作出这一例外,是为了让二进制强制转换即使没有任何函数,也能使 + 用函数语法调用。同样,如果没有pg_cast项,但该 + 类型转换的目标或源是字符串类型,则该调用会被解释为基于 I/O 的类型转 + 换。这一例外允许基于 I/O 的类型转换使用函数语法调用。 + + + + + + + 还有一个例外中的例外:从复合类型到字符串类型的基于 I/O 的类型转换不能 + 使用函数语法调用,而必须写成显式类型转换语法(CAST + 或::记法)。增加这一例外,是因为在引入自动提供的基于 + I/O 的类型转换之后,如果原意是函数调用或列引用,就太容易意外地触发这 + 种类型转换了。 + + + + + + + + 示例 + + + 要使用函数int4(bigint)创建一种从类型 + bigint到类型int4的赋值类型转换: + +CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT; + + (在系统中这种类型转换已经被预定义。) + + + + + + 兼容性 + + + CREATE CAST命令符合SQL标 + 准,不过 SQL 没有对二进制可强制转换的类型或实现函数的额外参数作出规定。 + AS IMPLICIT也是 + PostgreSQL的扩展。 + + + + + + + 另见 + + + , + , + + + + + diff --git a/zh/9.6/ref/create_collation.sgml b/zh/9.6/ref/create_collation.sgml new file mode 100644 index 00000000..f1489c62 --- /dev/null +++ b/zh/9.6/ref/create_collation.sgml @@ -0,0 +1,146 @@ + + + + + CREATE COLLATION + + + + CREATE COLLATION + 7 + SQL - 语言语句 + + + + CREATE COLLATION + 定义一种新排序规则 + + + + +CREATE COLLATION name ( + [ LOCALE = locale, ] + [ LC_COLLATE = lc_collate, ] + [ LC_CTYPE = lc_ctype ] +) +CREATE COLLATION name FROM existing_collation + + + + + 描述 + + + CREATE COLLATION使用指定的操作系统区域设 + 置,或者通过复制现有排序规则,定义一个新的排序规则。 + + + + 要创建排序规则,你必须拥有目标模式上的 + CREATE权限。 + + + + + + 参数 + + + + name + + + 排序规则的名称。排序规则名称可以带有模式限定。如果没有,排序规则将在当前模式中定义。排序规则名称在该模式中必须唯一。(系统目录中可能包含其他编码的同名排序规则,但如果数据库编码不匹配,则这些排序规则将被忽略。) + + + + + locale + + + 这是同时设置 LC_COLLATELC_CTYPE 的快捷方式。如果指定了此选项,则不能指定这两个参数中的任何一个。 + + + + + lc_collate + + + LC_COLLATE 区域类别使用指定的操作系统区域设置。该区域设置必须适用于当前数据库编码。(有关精确规则,请参见。) + + + + + lc_ctype + + + LC_CTYPE 区域类别使用指定的操作系统区域设置。该区域设置必须适用于当前数据库编码。(有关精确规则,请参见。) + + + + + existing_collation + + + + 要复制的现有排序规则的名称。新的排序规则将具有与现有排序规则相同 + 的属性,但它是一个独立对象。 + + + + + + + + + 注解 + + + 使用DROP COLLATION可移除用户定义的排序规则。 + + + + 关于 PostgreSQL 中排序规则支持的更多信息, + 见。 + + + + + 示例 + + 创建排序规则,使用操作系统区域设置fr_FR.utf8(假设当前数据库编码为UTF8): + +CREATE COLLATION french (LOCALE = 'fr_FR.utf8'); + + + + 从现有排序规则创建排序规则: +CREATE COLLATION german FROM "de_DE"; +这在应用程序中使用与操作系统无关的排序规则名称时非常方便。 + + + + + 兼容性 + + + 在 SQL 标准中有一个CREATE COLLATION + 语句,但它仅限于复制现有排序规则。创建新排序规则的语法是 + PostgreSQL扩展。 + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_conversion.sgml b/zh/9.6/ref/create_conversion.sgml new file mode 100644 index 00000000..33f27e07 --- /dev/null +++ b/zh/9.6/ref/create_conversion.sgml @@ -0,0 +1,156 @@ + + + + + CREATE CONVERSION + + + + CREATE CONVERSION + 7 + SQL - 语言语句 + + + + CREATE CONVERSION + 定义一个新的编码转换 + + + + +CREATE [ DEFAULT ] CONVERSION name + FOR source_encoding TO dest_encoding FROM function_name + + + + + 描述 + + CREATE CONVERSION 定义字符集编码之间的新转换。此外,被标记为 DEFAULT 的转换可用于客户端与服务器之间的自动编码转换。为此目的,必须定义两个转换:从编码 A 到 B 以及 从编码 B 到 A。 + + + 要创建一个转换,你必须对该函数具有EXECUTE权限, + 并在目标模式上具有CREATE权限。 + + + + + + 参数 + + + + DEFAULT + + + + DEFAULT子句表示该转换是这一特定源编码到目标编码的默认 + 转换。对于某个编码对,在一个模式中应该只有一个默认转换。 + + + + + + name + + + + 转换的名称。转换名可以是模式限定的;如果不是,则该转换定义在 + 当前模式中。转换名在一个模式中必须唯一。 + + + + + + source_encoding + + + + 源编码名称。 + + + + + + dest_encoding + + + + 目标编码名称。 + + + + + + function_name + + + + 用于执行该转换的函数。函数名可以是模式限定的;如果不是,则会在 + 搜索路径中查找该函数。 + + + 该函数必须具有以下签名: +conv_proc( + integer, -- source encoding ID + integer, -- destination encoding ID + cstring, -- source string (null terminated C string) + internal, -- destination (fill with a null terminated C string) + integer -- source string length +) RETURNS void; + + + + + + + + 注解 + + + 使用DROP CONVERSION可以删除用户定义的转换。 + + + + 创建转换所要求的权限在未来版本中可能会更改。 + + + + + 示例 + + + 使用myfunc创建从编码UTF8到 + LATIN1的转换: + +CREATE CONVERSION myconv FOR 'UTF8' TO 'LATIN1' FROM myfunc; + + + + + + 兼容性 + + + CREATE CONVERSION是 + PostgreSQL的一个扩展。SQL 标准中 + 没有CREATE CONVERSION语句,但有一个在目的和语法上都非常相似的 + CREATE TRANSLATION语句。 + + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/create_database.sgml b/zh/9.6/ref/create_database.sgml new file mode 100644 index 00000000..d05caa19 --- /dev/null +++ b/zh/9.6/ref/create_database.sgml @@ -0,0 +1,256 @@ + + + + + CREATE DATABASE + + + + CREATE DATABASE + 7 + SQL - 语言语句 + + + + CREATE DATABASE + 创建一个新数据库 + + + + +CREATE DATABASE name + [ [ WITH ] [ OWNER [=] user_name ] + [ TEMPLATE [=] template ] + [ ENCODING [=] encoding ] + [ LC_COLLATE [=] lc_collate ] + [ LC_CTYPE [=] lc_ctype ] + [ TABLESPACE [=] tablespace_name ] + [ ALLOW_CONNECTIONS [=] allowconn ] + [ CONNECTION LIMIT [=] connlimit ] + [ IS_TEMPLATE [=] istemplate ] ] + + + + + 描述 + + + CREATE DATABASE 创建一个新的 PostgreSQL 数据库。 + + + + 要创建数据库,你必须是超级用户,或者拥有特殊的 + CREATEDB 权限。见 。 + + + + 默认情况下,新数据库通过克隆标准系统数据库 + template1 来创建。可以通过写成 TEMPLATE + name 指定其他模板。特别是, + 写成 TEMPLATE template0 时,可以创建一个全新的数据库, + 它只包含你的 PostgreSQL 版本预定义的标准对象。 + 如果你希望避免复制任何可能已添加到 template1 中的站点本地附加对象, + 这会很有用。 + + + + + 参数 + + + + name + + + 要创建的数据库名称。 + + + + + user_name + + 新数据库所有者的角色名称,或使用 DEFAULT 以使用默认值(即执行命令的用户)。要创建由其他角色拥有的数据库,你必须是该角色的直接或间接成员,或为超级用户。 + + + + template + + + 用于创建新数据库的模板名称,或者指定 DEFAULT + 以使用默认模板(template1)。 + + + + + encoding + + + 新数据库要使用的字符集编码。可指定字符串常量(例如 + 'SQL_ASCII')、整数编码编号,或者指定 + DEFAULT 以使用默认编码(即模板数据库的编码)。 + PostgreSQL 服务器支持的字符集见 + 。其他限制见下文。 + + + + + lc_collate + + 新数据库中使用的排序规则(LC_COLLATE)。这会影响字符串的排序顺序,例如在带有 ORDER BY 的查询中,以及文本列索引中使用的顺序。默认值是使用模板数据库的排序规则。请参见下文的其他限制。 + + + + lc_ctype + + 新数据库中使用的字符分类(LC_CTYPE)。这会影响字符的分类,例如小写、大写和数字。默认值是使用模板数据库的字符分类。请参见下文的其他限制。 + + + + tablespace_name + + + 将与新数据库关联的表空间名称,或者指定 DEFAULT + 以使用模板数据库的表空间。该表空间将成为在此数据库中创建对象时使用的默认表空间。详见 + 。 + + + + + + allowconn + + + 如果为 false,则任何人都不能连接到该数据库。默认值为 true,即允许连接 + (但仍受其他机制限制,例如 + GRANT/REVOKE CONNECT)。 + + + + + + connlimit + + + 可对该数据库建立的并发连接数。-1(默认值)表示不受限制。 + + + + + + istemplate + + + 如果为 true,则任何具有 CREATEDB 权限的用户都可以克隆该数据库;如果为 false(默认值),则只有超级用户或该数据库的拥有者可以克隆它。 + + + + + + + 可选参数可以按任意顺序书写,不一定要按照上面的顺序。 + + + + + 注解 + + + CREATE DATABASE 不能在事务块内执行。 + + + + 形如 could not initialize database directory 的错误, + 多半与数据目录权限不足、磁盘已满或其他文件系统问题有关。 + + + 使用 删除数据库。 + + + 程序 是这个命令的一个包装器程序,为方便使用而提供。 + + + 数据库级配置参数(通过 设置)和数据库级权限(通过 设置)不会从模板数据库复制。 + + + 尽管可以通过把某个数据库名指定为模板,从而复制它而不是复制 + template1,但这(至少目前)并不打算作为一种通用的 + COPY DATABASE 功能。主要限制是,在复制模板数据库期间,不能有任何其他会话连接到该数据库。 + CREATE DATABASE 在启动时如果发现存在任何其他连接,就会失败;否则,在 + CREATE DATABASE 完成之前,将阻止对模板数据库建立新的连接。详见 + 。 + + + + 为新数据库指定的字符集编码必须与所选区域设置(LC_COLLATE + 和 LC_CTYPE)兼容。如果区域设置为 C + (或等价的 POSIX),则允许所有编码;但对于其他区域设置,只有一种编码能够正常工作。(不过,在 Windows 上,UTF-8 编码可与任何区域设置一起使用。) + CREATE DATABASE 允许超级用户不考虑区域设置而指定 + SQL_ASCII 编码,但这种选择已弃用;如果数据库中存储了与该区域设置不兼容编码的数据,字符串函数的行为可能会出错。 + + + + 编码和区域设置必须与模板数据库的设置一致,除非使用 + template0 作为模板。这是因为其他数据库可能包含与指定编码不匹配的数据,或者包含排序顺序会受 + LC_COLLATELC_CTYPE 影响的索引。复制这样的数据会导致数据库在新设置下被视为损坏。不过,已知 + template0 不包含任何会受此影响的数据或索引。 + + + + CONNECTION LIMIT 选项只是近似地被强制执行;如果两个新会话几乎同时启动,而该数据库只剩下一个连接,则两者都可能失败。此外,该限制对超级用户或后台工作进程无效。 + + + + + 示例 + + + 要创建一个新数据库: + + +CREATE DATABASE lusiadas; + + + + + 要创建一个由用户 salesapp 拥有、默认表空间为 + salesspace 的数据库 sales: + + +CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace; + + + + + 要创建支持 ISO-8859-1 字符集的数据库music: + + +CREATE DATABASE music ENCODING 'LATIN1' TEMPLATE template0; + + + 在这个示例中,只有当template1的编码不是 ISO-8859-1 时,才需要TEMPLATE template0子句。 + 注意,更改编码可能还需要选择新的LC_COLLATELC_CTYPE设置。 + + + + + + 兼容性 + + + SQL 标准中没有 CREATE DATABASE 语句。数据库相当于目录,而目录的创建由实现定义。 + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/create_domain.sgml b/zh/9.6/ref/create_domain.sgml new file mode 100644 index 00000000..b7b6ce86 --- /dev/null +++ b/zh/9.6/ref/create_domain.sgml @@ -0,0 +1,245 @@ + + + + + CREATE DOMAIN + + + + CREATE DOMAIN + 7 + SQL - 语言语句 + + + + CREATE DOMAIN + 定义一个新域 + + + + +CREATE DOMAIN name [ AS ] data_type + [ COLLATE collation ] + [ DEFAULT expression ] + [ constraint [ ... ] ] + +其中 constraint 为: + +[ CONSTRAINT constraint_name ] +{ NOT NULL | NULL | CHECK (expression) } + + + + + 描述 + + + CREATE DOMAIN创建一个新域。域本质上是一种带有可选 + 约束(即对允许值集合的限制)的数据类型。定义域的用户将成为其拥有者。 + + + + 如果给定了模式名(例如CREATE DOMAIN + myschema.mydomain ...),则该域会在指定模式中创建。否则它会 + 在当前模式中创建。域名在其所在模式中的现有类型和域之间必须唯一。 + + + + 域适合把字段上的常见约束抽象到单一位置进行维护。例如,若有多个表都 + 包含电子邮件地址列,并且都需要同一个 CHECK 约束来验证地址语法, + 那么定义一个域会比在每个表上分别设置该约束更合适。 + + + + 要创建域,你必须对其底层类型拥有USAGE权限。 + + + + + 参数 + + + + name + + + 要创建的域名(可选地带模式限定)。 + + + + + + data_type + + + 该域的底层数据类型。它可以包含数组说明符。 + + + + + + collation + + + 该域的可选排序规则。如果未指定排序规则,则使用底层数据类型的默认排序规则。如果指定了COLLATE,则底层 + 类型必须是一种支持排序规则的数据类型。 + + + + + + DEFAULT expression + + + + DEFAULT子句为该域数据类型的列指定默认值。 + 该值可以是任意不含变量的表达式(但不允许子查询)。默认表达式的 + 数据类型必须与该域的数据类型匹配。如果未指定默认值,则默认值为 + 空值。 + + + + 默认表达式会在任何未为该列指定值的插入操作中使用。如果为某个 + 特定列定义了默认值,它就会覆盖与该域关联的任何默认值。反过来, + 域默认值又会覆盖与底层数据类型关联的任何默认值。 + + + + + + CONSTRAINT constraint_name + + + 约束的可选名称。如果未指定,系统会生成一个名称。 + + + + + + NOT NULL + + + 该域的值不允许为空值(但见下文注解)。 + + + + + + NULL + + + 该域的值允许为空值。这是默认行为。 + + + + 该子句仅用于与非标准 SQL 数据库兼容。不鼓励在新应用中使用它。 + + + + + + CHECK (expression) + + CHECK子句指定该域的值必须满足的完整性 + 约束或测试。每个约束都必须是一个产生布尔结果的表达式。它应使用 + 关键字VALUE来引用被测试的值。求值结果为 TRUE + 或 UNKNOWN 的表达式会通过检查。如果表达式产生 FALSE 结果,就会 + 报告错误,并且不允许将该值转换成该域类型。 + + + + 当前,CHECK表达式不能包含子查询,也不能引用 + VALUE之外的其他变量。 + + + + 当一个域有多个CHECK约束时,会按名称的字母顺序 + 测试它们。(9.5 之前的PostgreSQL版本 + 并不保证CHECK约束遵循任何特定的触发顺序。) + + + + + + + + 注解 + + + 域约束,特别是NOT NULL,会在把值转换成域类型时 + 进行检查。即便存在这样的约束,一个名义上属于该域类型的列也仍可能 + 读出为空值。例如,在外连接查询中,如果该域列位于外连接中可为空的 + 一侧,就可能发生这种情况。一个更微妙的例子是: + +INSERT INTO tab (domcol) VALUES ((SELECT domcol FROM tab WHERE false)); + + 这个空标量子 SELECT 会产生一个空值,该空值被视为域类型的值,因此 + 不会再对其执行进一步的约束检查,插入也会成功。 + + + + 由于 SQL 普遍假定空值是每种数据类型的合法值,因此很难彻底避免这类 + 问题。因此,最佳实践是把域约束设计为允许空值,然后在需要时对该域 + 类型的列应用列级NOT NULL约束,而不是直接对域 + 类型应用这种约束。 + + + + PostgreSQL假定CHECK + 约束的条件是不可变的,也就是说,对于相同的输入值,它们总会给出相同 + 的结果。正是基于这一假设,系统只会在值首次被转换为域类型时检查 + CHECK约束,而不会在其他时候检查。(这与表 + CHECK约束的处理方式基本相同,如所述。) + + + 一个常见的破坏此假设的方式是,在 CHECK 表达式中引用用户定义的函数,然后更改该函数的行为。PostgreSQL 不禁止此类操作,但它不会察觉到存储的域类型值现在违反了 CHECK 约束。这将导致后续的数据库转储和重新加载失败。处理此类更改的推荐方法是先删除约束(使用 ALTER DOMAIN),调整函数定义,然后重新添加约束,从而对存储的数据重新进行检查。 + + + + 示例 + + + 这个示例创建us_postal_code数据类型,然后在一个表定义 + 中使用该类型。这里使用正则表达式测试来验证该值看起来是否为一个合法 + 的美国邮政编码: + + +CREATE DOMAIN us_postal_code AS TEXT +CHECK( + VALUE ~ '^\d{5}$' +OR VALUE ~ '^\d{5}-\d{4}$' +); + +CREATE TABLE us_snail_addy ( + address_id SERIAL PRIMARY KEY, + street1 TEXT NOT NULL, + street2 TEXT, + street3 TEXT, + city TEXT NOT NULL, + postal us_postal_code NOT NULL +); + + + + + 兼容性 + + + 命令CREATE DOMAIN符合 SQL 标准。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_event_trigger.sgml b/zh/9.6/ref/create_event_trigger.sgml new file mode 100644 index 00000000..034073d1 --- /dev/null +++ b/zh/9.6/ref/create_event_trigger.sgml @@ -0,0 +1,166 @@ + + + + + CREATE EVENT TRIGGER + + + + CREATE EVENT TRIGGER + 7 + SQL - 语言语句 + + + + CREATE EVENT TRIGGER + 定义一个新的事件触发器 + + + + +CREATE EVENT TRIGGER name + ON event + [ WHEN filter_variable IN (filter_value [, ... ]) [ AND ... ] ] + EXECUTE PROCEDURE function_name() + + + + + + 描述 + + + + + CREATE EVENT TRIGGER创建一个新的事件触发器。 + 每当指定的事件发生,并且与该触发器关联的WHEN条件(如果有)得到 + 满足时,就会执行该触发器函数。有关事件触发器的一般性介绍,见 + 。创建事件触发器的用户将成为其所有者。 + + + + + + 参数 + + + + name + + + + 要赋予新触发器的名称。该名称在数据库内必须唯一。 + + + + + + + event + + + + 触发对给定函数调用的事件名称。有关事件名称的更多信息,见 + 。 + + + + + + + filter_variable + + + + 用于过滤事件的变量名称。这样可以将触发器限定为只在原本支持的部分情形下触发。当前唯一支持的 + filter_variable + 是TAG。 + + + + + + + filter_value + + + + 与相关filter_variable + 关联的一组值;当其取值属于该列表时,触发器就会被触发。对于 + TAG,这表示一组命令标签(例如 + 'DROP FUNCTION')。 + + + + + + + function_name + + + 由用户提供的函数,声明为不接受参数并返回类型 + event_trigger。 + + + + + + + + + 注解 + + + 只有超级用户能创建事件触发器。 + + + 事件触发器在单用户模式下被禁用(参见 )。如果错误的事件触发器导致数据库严重失效,以至于无法删除触发器,可以重启到单用户模式,然后就能执行删除操作。 + + + + 示例 + + 禁止执行任何DDL命令: +CREATE OR REPLACE FUNCTION abort_any_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$; + +CREATE EVENT TRIGGER abort_ddl ON ddl_command_start + EXECUTE PROCEDURE abort_any_command(); + + + + + + 兼容性 + + + + + 在 SQL 标准中没有 + CREATE EVENT TRIGGER语句。 + + + + + + + + 参见 + + + + + + + + + diff --git a/zh/9.6/ref/create_extension.sgml b/zh/9.6/ref/create_extension.sgml new file mode 100644 index 00000000..7d4ace7f --- /dev/null +++ b/zh/9.6/ref/create_extension.sgml @@ -0,0 +1,223 @@ + + + + + CREATE EXTENSION + + + + CREATE EXTENSION + 7 + SQL - 语言语句 + + + + CREATE EXTENSION + 安装一个扩展 + + + + +CREATE EXTENSION [ IF NOT EXISTS ] extension_name + [ WITH ] [ SCHEMA schema_name ] + [ VERSION version ] + [ FROM old_version ] + [ CASCADE ] + + + + + 描述 + + + CREATE EXTENSION将一个新扩展装载到当前数据库中。 + 同名扩展不能已经被装载。 + + + + 装载一个扩展本质上就是运行该扩展的脚本文件。该脚本通常会创建新的 + SQL 对象,例如函数、数据类型、操作符以及索引支持 + 方法。CREATE EXTENSION还会记录所有已创建对象 + 的标识,这样在发出DROP EXTENSION时就可以将 + 它们一并删除。 + + + 加载扩展需要与创建其组件对象所需的权限相同。对于大多数扩展,这意味着需要超级用户或数据库所有者权限。运行 CREATE EXTENSION 的用户将成为扩展的所有者,用于后续的权限检查,以及扩展脚本创建的任何对象的所有者。 + + + + + 参数 + + + + IF NOT EXISTS + + + 如果已存在同名扩展,则不要抛出错误。这种情况下会发出一条提示。 + 注意,这并不保证现有扩展与根据当前可用脚本文件本应创建出的扩展 + 有任何相似之处。 + + + + + + extension_name + + + 要安装的扩展名称。PostgreSQL将根据 + SHAREDIR/extension/extension_name.control + 文件中的详细信息创建该扩展。 + + + + + + schema_name + + + 如果该扩展允许其内容被重定位,则这是安装该扩展所含对象的模式名称。 + 指定的模式必须已经存在。如果未指定,并且扩展控制文件中也未指定 + 模式,则使用当前默认的对象创建模式。 + + + + 如果扩展在其控制文件中指定了 schema 参数, + 就不能用 SCHEMA 子句覆盖该模式。通常,如果给出 + 了 SCHEMA 子句,且它与扩展的 + schema 参数冲突,就会报错。不过,如果同时给出 + CASCADE 子句,则发生冲突时会忽略 + schema_name。给定的 + schema_name 将用于 + 安装任何必需且其控制文件中未指定 schema 的扩展。 + + + + 请记住,扩展本身不被视为属于任何模式:扩展的名称是非限定名, + 并且必须在整个数据库范围内唯一。但是,属于该扩展的对象可以位于 + 模式中。 + + + + + + version + + + 要安装的扩展版本。它可以写成标识符,也可以写成字符串常量。 + 默认版本由扩展控制文件指定。 + + + + + + old_version + + FROM old_version 必须在且仅在尝试安装一个替换 旧式模块的扩展时指定,该模块仅是一组未打包为扩展的对象集合。此选项会导致 CREATE EXTENSION 运行一个替代安装脚本,将现有对象吸收进扩展中,而不是创建新对象。请确保 SCHEMA 指定的是包含这些现有对象的模式。 + + old_version的值由扩展作者决定,如果存在多个可升级为扩展的旧式模块版本,该值可能会有所不同。对于PostgreSQL 9.1 之前版本提供的标准附加模块,将模块更新为扩展形式时应使用unpackaged作为old_version的值。 + + + + + CASCADE + + + 自动安装该扩展所依赖、但尚未安装的任何扩展。它们的依赖也会以同样 + 的方式递归自动安装。如果给出了 SCHEMA 子句, + 它将适用于所有以这种方式安装的扩展。该语句的其他选项不会应用于 + 自动安装的扩展;特别是,这些扩展总是选择其默认版本。 + + + + + + + + 注解 + + + 在使用 CREATE EXTENSION 将扩展装载到数据库之前, + 必须先安装好该扩展的支持文件。关于安装 + PostgreSQL 随附扩展的信息,可见 + 额外提供的模块。 + + + + 当前可供装载的扩展可从系统视图 + pg_available_extensions + 或 + pg_available_extension_versions + 中查出。 + + + + + 以超级用户身份安装扩展,意味着必须相信扩展作者以安全的方式编写了 + 扩展安装脚本。恶意用户要创建特洛伊木马对象并不算太难;这类对象可能 + 会在之后执行编写粗心的扩展脚本时实施攻击,使该用户获得超级用户权限。 + 不过,只有当特洛伊木马对象在脚本执行期间位于 + search_path 中时,它们才会构成危险;这意味着它们 + 位于扩展的安装目标模式中,或位于它所依赖的某个扩展的目标模式中。 + 因此,处理那些脚本尚未经过仔细审查的扩展时,一个经验法则是: + 只把它们安装到从未向任何不受信任用户授予、而且今后也不会授予 + CREATE 权限的模式中。它们所依赖的任何扩展也应如此。 + + + + 一般认为,PostgreSQL 随附的扩展能够抵御 + 这类安装时攻击,但少数依赖其他扩展的扩展除外。正如这些扩展的文档所 + 述,它们应当安装到安全模式中,或者安装到与其所依赖扩展相同的模式中, + 或者同时满足这两点。 + + + + + 关于编写新扩展的信息,见 。 + + + + + 示例 + + + 将 hstore 扩展安装到当前数据库中,并把 + 其对象放在 addons 模式中: + +CREATE EXTENSION hstore SCHEMA addons; + + 实现同样效果的另一种方式是: + +SET search_path = addons; +CREATE EXTENSION hstore; + + + + 将 PostgreSQL 9.1 之前版本中安装的hstore更新为扩展形式: +CREATE EXTENSION hstore SCHEMA public FROM unpackaged; +请注意指定安装现有hstore对象时所用的模式。 + + + + 兼容性 + + + CREATE EXTENSION 是 + PostgreSQL 的扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_foreign_data_wrapper.sgml b/zh/9.6/ref/create_foreign_data_wrapper.sgml new file mode 100644 index 00000000..4092e11b --- /dev/null +++ b/zh/9.6/ref/create_foreign_data_wrapper.sgml @@ -0,0 +1,172 @@ + + + + + CREATE FOREIGN DATA WRAPPER + + + + CREATE FOREIGN DATA WRAPPER + 7 + SQL - 语言语句 + + + + CREATE FOREIGN DATA WRAPPER + 定义一个新的外部数据包装器 + + + + +CREATE FOREIGN DATA WRAPPER name + [ HANDLER handler_function | NO HANDLER ] + [ VALIDATOR validator_function | NO VALIDATOR ] + [ OPTIONS ( option 'value' [, ... ] ) ] + + + + + 描述 + + + CREATE FOREIGN DATA WRAPPER创建一个新的外部数据包装器。 + 定义该外部数据包装器的用户将成为它的拥有者。 + + + + 外部数据包装器名称在数据库内必须唯一。 + + + + 只有超级用户能够创建外部数据包装器。 + + + + + 参数 + + + + name + + + 要创建的外部数据包装器的名称。 + + + + + + HANDLER handler_function + + handler_function + 是一个预先注册的函数的名称,该函数将被调用以取得外部表所需的执行函数。 + 该处理器函数不能接受任何参数,并且其返回类型必须是 + fdw_handler。 + + + + 可以创建没有处理器函数的外部数据包装器,但使用这种包装器的外部表 + 只能被声明,不能被访问。 + + + + + + VALIDATOR validator_function + + validator_function + 是一个预先注册的函数的名称,该函数将被调用以检查提供给该外部数据 + 包装器的通用选项,以及用于外部服务器、用户映射和使用该外部数据包装器 + 的外部表的选项。如果未指定验证器函数或者指定了 + NO VALIDATOR,则在创建时不会检查这些选项。 + (外部数据包装器可能会在运行时忽略无效的选项声明,或者拒绝它们, + 这取决于其实现。)验证器函数必须接受两个参数:一个参数的类型为 + text[],其中包含系统目录中存储的选项数组;另一个参数的 + 类型为oid,其值是包含这些选项的系统目录 + 的 OID。返回类型会被忽略;该函数应当使用 + ereport(ERROR)报告无效选项。 + + + + + + OPTIONS ( option 'value' [, ... ] ) + + + 这个子句为新的外部数据包装器指定选项。允许的选项名和值都依赖于 + 各个外部数据包装器,并通过该外部数据包装器的验证器函数进行验证。 + 选项名必须唯一。 + + + + + + + + 注解 + + + PostgreSQL的外部数据功能仍在积极开发中。 + 查询优化能力还很初级,而且这项工作在很大程度上也留给了包装器本身。 + 因此,未来在性能方面仍有相当大的改进空间。 + + + + + 示例 + + + 创建一个无用的外部数据包装器dummy: + +CREATE FOREIGN DATA WRAPPER dummy; + + + + + 用处理器函数file_fdw_handler创建一个外部数据包装器 + file: + +CREATE FOREIGN DATA WRAPPER file HANDLER file_fdw_handler; + + + + + 用一些选项创建一个外部数据包装器mywrapper: + +CREATE FOREIGN DATA WRAPPER mywrapper + OPTIONS (debug 'true'); + + + + + 兼容性 + + + CREATE FOREIGN DATA WRAPPER符合 ISO/IEC + 9075-9 (SQL/MED),但HANDLER和 + VALIDATOR子句属于扩展,而标准中的 + LIBRARYLANGUAGE子句 + 在PostgreSQL中尚未实现。 + + + + 不过需要注意,SQL/MED 功能整体上尚未完全符合该标准。 + + + + + 另见 + + + + + + + + + + + diff --git a/zh/9.6/ref/create_foreign_table.sgml b/zh/9.6/ref/create_foreign_table.sgml new file mode 100644 index 00000000..3e2f22cc --- /dev/null +++ b/zh/9.6/ref/create_foreign_table.sgml @@ -0,0 +1,240 @@ + + + + + CREATE FOREIGN TABLE + + + + CREATE FOREIGN TABLE + 7 + SQL - 语言语句 + + + + CREATE FOREIGN TABLE + 定义一个新外部表 + + + + +CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name ( [ + { column_name data_type [ OPTIONS ( option 'value' [, ... ] ) ] [ COLLATE collation ] [ column_constraint [ ... ] ] + | table_constraint } + [, ... ] +] ) +[ INHERITS ( parent_table [, ... ] ) ] + SERVER server_name +[ OPTIONS ( option 'value' [, ... ] ) ] + +其中 column_constraint 是: + +[ CONSTRAINT constraint_name ] +{ NOT NULL | + NULL | + CHECK ( expression ) [ NO INHERIT ] | + DEFAULT default_expr } + +table_constraint 是: + +[ CONSTRAINT constraint_name ] +CHECK ( expression ) [ NO INHERIT ] + + + + + 描述 + + + CREATE FOREIGN TABLE将在当前数据库中创建一个新的外部表。该表归发出该命令的用户所有。 + + + + 如果给出了模式名(例如 CREATE FOREIGN TABLE myschema.mytable ...),则表将在指定模式中创建。 + 否则,它将在当前模式中创建。 + 外部表名必须与同一模式中任何其他关系(表、序列、索引、视图、物化视图或外部表)的名称不同。 + + + + CREATE FOREIGN TABLE还会自动创建一种数据类型,用以表示与该外部表一行对应的复合类型。因此,外部表名不能与同一模式中任何已有数据类型同名。 + + + + 要创建外部表,必须拥有该外部服务器上的USAGE权限,以及表中所用所有列类型上的USAGE权限。 + + + + + 参数 + + + + + IF NOT EXISTS + + + 已经存在同名关系时不要抛出错误。这种情况下会发出一个提示。注意, + 已存在的关系不保证与原本将要创建的关系有任何相似之处。 + + + + + + table_name + + + 要创建的表名(可选地带有模式限定)。 + + + + + + column_name + + + 要在新表中创建的列名。 + + + + + + data_type + + + 该列的数据类型,可以包含数组说明符。有关PostgreSQL支持的数据类型的更多信息,参见。 + + + + + + COLLATE collation + + COLLATE子句为列指定排序规则(该列必须属于支持排序规则的数据类型)。如果未指定,则使用列数据类型的默认排序规则。 + + + + + INHERITS ( parent_table [, ... ] ) + + 可选的 INHERITS 子句指定一个表列表,新创建的外部表将自动继承这些表的所有列。父表可以是普通表或外部表。有关更多详细信息,请参阅 的类似形式。 + + + + + CONSTRAINT constraint_name + + 用于列或表约束的可选名称。如果违反约束,约束名称将出现在错误消息中,因此可以使用像 col must be positive 这样的约束名称向客户端应用程序传达有用的约束信息。(包含空格的约束名称需要使用双引号。)如果未指定约束名称,系统将自动生成名称。 + + + + + NOT NULL + + 该列不允许包含空值。 + + + + + NULL + + 该列允许包含空值。这是默认行为。 + + 此子句仅用于与非标准 SQL 数据库兼容。在新应用程序中不建议使用。 + + + + + CHECK ( expression ) [ NO INHERIT ] + + CHECK子句指定一个产生布尔结果的表达式,该表达式期望外部表中的每一行都满足;也就是说,对于外部表中的所有行,该表达式应产生 TRUE 或 UNKNOWN,而绝不能产生 FALSE。作为列约束指定的检查约束应仅引用该列的值,而出现在表约束中的表达式可以引用多个列。 + + 目前,CHECK表达式不能包含子查询,也不能引用除当前行的列以外的变量。可以引用系统列tableoid,但不能引用任何其他系统列。 + + 标记为NO INHERIT的约束不会传播到子表。 + + + + + DEFAULT default_expr + + DEFAULT子句为其所在列定义对应的列分配一个默认数据值。该值可以是任何不包含变量的表达式(不允许包含子查询或对当前表中其他列的交叉引用)。默认表达式的数据类型必须与列的数据类型匹配。 + + 在任何未为该列指定值的插入操作中,将使用默认表达式。如果列没有默认值,则默认值为 null。 + + + + + server_name + + 要用于外部表的现有外部服务器的名称。有关定义服务器的详细信息,请参阅 + + + + + OPTIONS ( option 'value' [, ...] ) + + 与新外部表或其某一列关联的选项。允许的选项名称和值取决于每个外部数据包装器,并通过外部数据包装器的验证函数进行验证。不允许重复的选项名称(尽管表选项和列选项具有相同名称是可以接受的)。 + + + + + + + + + 注解 + + + 核心PostgreSQL系统不会强制执行外部表上的约束(例如CHECKNOT NULL子句),而且大多数外部数据包装器也不会尝试强制执行它们;也就是说,这些约束只是被假定为真。因为这种强制执行只会适用于通过外部表插入或更新的行,而不会适用于通过其他方式修改的行,例如直接在远程服务器上修改的行,所以这样做意义不大。相反,附加到外部表上的约束应当表示由远程服务器强制执行的约束。 + + + + 某些专用的外部数据包装器可能是其所访问数据的唯一访问机制,在这种情况下,由外部数据包装器自身执行约束检查也许是合适的。但除非其文档明确说明,否则不应假定某个包装器会这样做。 + + + 尽管 PostgreSQL 不会尝试对外部表强制执行约束,但它会假设这些约束在查询优化目的上是正确的。如果外部表中存在不满足声明约束的行,则对该表的查询可能会产生错误结果。确保约束定义与实际情况一致是用户的职责。 + + + + 示例 + + + 创建通过服务器film_server访问的外部表films: + + +CREATE FOREIGN TABLE films ( + code char(5) NOT NULL, + title varchar(40) NOT NULL, + did integer NOT NULL, + date_prod date, + kind varchar(10), + len interval hour to minute +) +SERVER film_server; + + + + + + 兼容性 + + CREATE FOREIGN TABLE 命令在很大程度上符合 SQL 标准;然而,与 CREATE TABLE 类似,允许使用 NULL 约束和零列外部表。指定列默认值的能力也是 PostgreSQL 的扩展。以 PostgreSQL 定义的形式存在的表继承是非标准的。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/create_function.sgml b/zh/9.6/ref/create_function.sgml new file mode 100644 index 00000000..ef7eadc2 --- /dev/null +++ b/zh/9.6/ref/create_function.sgml @@ -0,0 +1,568 @@ + + + + + CREATE FUNCTION + + + + CREATE FUNCTION + 7 + SQL - 语言语句 + + + + CREATE FUNCTION + 定义一个新函数 + + + + +CREATE [ OR REPLACE ] FUNCTION + name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] ) + [ RETURNS rettype + | RETURNS TABLE ( column_name column_type [, ...] ) ] + { LANGUAGE lang_name + | TRANSFORM { FOR TYPE type_name } [, ... ] + | WINDOW + | { IMMUTABLE | STABLE | VOLATILE } + | [ NOT ] LEAKPROOF + | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } + | { [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER } + | PARALLEL { UNSAFE | RESTRICTED | SAFE } + | COST execution_cost + | ROWS result_rows + | SET configuration_parameter { TO value | = value | FROM CURRENT } + | AS 'definition' + | AS 'obj_file', 'link_symbol' + } ... + [ WITH ( attribute [, ...] ) ] + + + + + 描述 + + + CREATE FUNCTION定义一个新函数。CREATE OR REPLACE FUNCTION将创建一个新函数,或者替换现有定义。要定义函数,用户必须具有该语言上的USAGE权限。 + + + 如果包含模式名,则函数将在指定的模式中创建。否则将在当前模式中创建。新函数的名称不能与同一模式中具有相同输入参数类型的任何现有函数匹配。然而,不同参数类型的函数可以共享一个名称(这称为 重载)。 + + + 要替换一个现有函数的当前定义,可以使用CREATE OR REPLACE FUNCTION。但不能用这种方式更改函数的名称或者参数类型(如果尝试这样做,实际上就会创建一个新的不同函数)。此外,CREATE OR REPLACE FUNCTION也不允许更改现有函数的返回类型。要做到这一点,必须删除该函数并重新创建。(使用OUT参数时,这意味着除非删除该函数,否则不能更改任何OUT参数的类型。) + + + + 当CREATE OR REPLACE FUNCTION被用来替换一个现有函数时,该函数的拥有权和权限不会改变。所有其他的函数属性会按照该命令中指定的或者隐含的值赋值。必须拥有(包括成为拥有角色的成员)该函数才能替换它。 + + + + 如果删除函数后再重新创建,新函数就不再是旧函数的同一实体;你将必须删除引用旧函数的现有规则、视图、触发器等。使用CREATE OR REPLACE FUNCTION可以在不破坏引用该函数的对象的情况下更改函数定义。此外,ALTER FUNCTION还可用于更改现有函数的大多数辅助属性。 + + + + 创建该函数的用户将成为该函数的拥有者。 + + + + 要创建一个函数,你必须拥有参数类型和返回类型上的USAGE权限。 + + + + + 参数 + + + + + name + + + + + 要创建的函数名称(可以被模式限定)。 + + + + + + argmode + + + + + 参数的模式可以是:INOUTINOUT或者VARIADIC。如果省略,则默认为IN。只有OUT参数可以跟在VARIADIC参数之后。此外,OUTINOUT参数不能与RETURNS TABLE记法一起使用。 + + + + + + argname + + + + + 参数的名称。某些语言(包括 SQL 和 PL/pgSQL)允许在函数体中使用该名称。对于其他语言,就函数本身而言,输入参数的名称只是额外文档;但你可以在调用函数时使用输入参数名来提高可读性(见)。无论如何,输出参数的名称很重要,因为它定义了结果行类型中的列名。(如果省略输出参数的名称,系统将选择一个默认列名。) + + + + + + argtype + + + + + 该函数参数(如果有)的数据类型(可以是模式限定的)。参数类型可以是基础类型、复合类型或者域类型,也可以引用一个表列的类型。 + + + + 根据实现语言的不同,也可能允许指定诸如cstring这样的伪类型。伪类型表示实际参数类型要么没有被完整指定,要么不属于普通 SQL 数据类型集合。 + + + + 写成table_name.column_name%TYPE即可引用一个列的类型。使用这种特性有时有助于让函数独立于表定义的变化。 + + + + + + default_expr + + + + + 如果未指定该参数,则用作默认值的表达式。该表达式必须能被强制转换为该参数的类型。只有输入参数(包括INOUT)才能有默认值。所有跟在具有默认值参数之后的输入参数也都必须有默认值。 + + + + + + rettype + + + + + 该函数的返回数据类型(可以是模式限定的)。返回类型可以是基础类型、复合类型或者域类型,也可以引用一个表列的类型。根据实现语言的不同,也可能允许指定诸如cstring这样的伪类型。如果函数不应该返回值,请把返回类型指定为void。 + + + + 当存在OUTINOUT参数时,可以省略RETURNS子句。如果写出该子句,它必须与输出参数所隐含的结果类型一致:如果有多个输出参数,则为RECORD;如果只有一个输出参数,则为该输出参数的类型。 + + + + SETOF修饰符表示该函数将返回一组项,而不是单个项。 + + + + 写成table_name.column_name%TYPE即可引用一个列的类型。 + + + + + + column_name + + + + + RETURNS TABLE语法中输出列的名称。这实际上是声明一个具名OUT参数的另一种方式,只不过RETURNS TABLE还隐含了RETURNS SETOF。 + + + + + + column_type + + + + + RETURNS TABLE语法中的输出列的数据类型。 + + + + + + lang_name + + + 实现该函数所用语言的名称。它可以是sqlcinternal,也可以是用户定义的过程语言的名称,例如plpgsql。使用单引号括起名称已被弃用,并且要求大小写匹配。 + + + + + TRANSFORM { FOR TYPE type_name } [, ... ] } + + + + + 列出对该函数调用时应应用的转换。转换在 SQL 类型和语言相关的数据类型之间进行变换,详见。过程语言实现通常把有关内置类型的知识硬编码在代码中,因此那些不需要列举在这里。如果一种过程语言实现不知道如何处理某种类型且没有提供转换,它将回退到默认的数据类型转换行为,但这取决于具体实现。 + + + + + + WINDOW + + + + WINDOW表示该函数是窗口函数而不是普通函数。目前这只对用 C 编写的函数有用。在替换现有函数定义时,不能更改WINDOW属性。 + + + + + + IMMUTABLE + STABLE + VOLATILE + + + + + 这些属性会告诉查询优化器该函数的行为。最多只能指定其中一个。如果这些属性都没有出现,则默认假定为VOLATILE。 + + + IMMUTABLE表示该函数不能修改数据库,并且在给定相同参数值时总会返回相同结果;也就是说,它不会执行数据库查找,也不会以其他方式使用未直接出现在其参数列表中的信息。如果给出此选项,任何使用全常量参数对该函数的调用都可以立即替换为该函数值。 + + + STABLE表示该函数不能修改数据库,并且在一次表扫描内,对于相同参数值会一致地返回相同结果,但其结果可能在不同 SQL 语句之间发生变化。这适用于结果依赖于数据库查找、参数变量(例如当前时区)等的函数。(对于希望查询由当前命令修改过的行的AFTER触发器,这样做并不合适。)另请注意,current_timestamp函数族也属于稳定函数,因为它们的值在一个事务内不会变化。 + + + VOLATILE表示该函数的值即使在一次表扫描内也可能发生变化,因此无法进行任何优化。从这个意义上说,真正不稳定的数据库函数相对较少;一些例子是random()currval()timeofday()。但请注意,任何有副作用的函数都必须归类为不稳定,即使其结果相当可预测,也必须如此,以防其调用被优化掉;例如setval()。 + + + + 更多细节见。 + + + + + + LEAKPROOF + + + + LEAKPROOF表示该函数没有副作用。除返回值外,它不会泄露其参数的任何信息。例如,对某些参数值会抛出错误而对另一些不会,或者在错误消息中包含参数值的函数,都不是防泄漏的。这会影响系统如何执行针对使用security_barrier选项创建的视图或启用了行级安全的表的查询。为了防止数据被无意暴露,系统会先强制执行安全策略和安全屏障视图中的条件,再执行查询本身中包含非防泄漏函数的用户提供条件。被标记为防泄漏的函数和操作符被视为可信,因此可以在安全策略和安全屏障视图的条件之前执行。此外,不接受参数的函数,或者没有从安全屏障视图或表中接收到任何参数的函数,即使未标记为防泄漏,也可以在安全条件之前执行。参见。此选项只能由超级用户设置。 + + + + + + CALLED ON NULL INPUT + RETURNS NULL ON NULL INPUT + STRICT + + + + CALLED ON NULL INPUT(默认)表示当某些参数为空值时,仍会正常调用该函数。如果有需要,则由函数作者负责检查空值并作出适当响应。 + + + RETURNS NULL ON NULL INPUTSTRICT表示只要任一参数为空值,该函数总是返回空值。如果指定了这个参数,那么在参数中出现空值时不会执行该函数,而是自动假定结果为空值。 + + + + + + EXTERNAL SECURITY INVOKER + EXTERNAL SECURITY DEFINER + + + SECURITY INVOKER表示该函数将以调用它的用户的权限执行。这是默认设置。SECURITY DEFINER指定该函数将以创建它的用户的权限执行。 + + + 为了符合 SQL,允许使用关键字EXTERNAL。但它是可选的,因为与 SQL 不同,这个特性适用于所有函数,而不仅仅是外部函数。 + + + + + + PARALLEL + + + PARALLEL UNSAFE表示该函数不能在并行模式中执行;SQL 语句中只要出现这类函数就会强制使用串行执行计划。这是默认选项。PARALLEL RESTRICTED表示该函数可以在并行模式中执行,但只能在并行组领导者进程中执行。PARALLEL SAFE表示该函数可以在并行模式下不受限制地执行,包括在并行工作进程中执行。 + + + 如果函数修改任何数据库状态,或者通过使用子事务等方式改变事务,或者访问序列或尝试对设置进行持久更改(例如使用setval),则应将其标记为并行不安全。如果函数访问临时表、客户端连接状态、游标、预备语句或其他系统无法在并行模式下同步的后端本地状态,则应将其标记为并行受限(例如,setseed只能由组领导者执行,因为其他进程所做的更改不会反映在领导者中)。一般来说,如果函数在受限或不安全时被标记为安全,或者实际上不安全时被标记为受限,那么在并行查询中使用它可能会抛出错误或产生错误结果。C 语言函数如果被错误标记,理论上可能表现出完全未定义的行为,因为系统无法保护自己免受任意 C 代码的影响,但在最可能的情况下,其结果不会比任何其他函数更糟。如果不确定,函数应标记为UNSAFE,这是默认值。 + + + + + execution_cost + + + + + 一个正数,给出该函数的估计执行代价,单位为。如果该函数返回一个集合,则这是每个返回行的代价。如果未指定代价,则对 C 语言和内部函数假定为 1 个单位,对其他所有语言的函数假定为 100 个单位。较大的值会让规划器尽量避免对该函数进行不必要的频繁求值。 + + + + + + result_rows + + + + + 一个正数,给出规划器应预计该函数返回的行数。只有函数被声明为返回集合时才允许使用此项。默认假定为 1000 行。 + + + + + + configuration_parameter + value + + + + SET子句会在进入函数时将指定的配置参数设为给定值,并在函数退出时恢复为先前的值。SET FROM CURRENT会把执行CREATE FUNCTION时该参数的当前值保存下来,作为进入函数时要应用的值。 + + + + 如果函数附带了SET子句,那么在函数内针对同一变量执行的SET LOCAL命令,其效果会被限制在该函数内部:函数退出时,配置参数先前的值仍会被恢复。不过,普通的SET命令(不带LOCAL)会覆盖SET子句,就像它会覆盖先前的SET LOCAL命令一样:这类命令的效果会在函数退出后继续保持,除非当前事务被回滚。 + + + + 关于允许的参数名和值的更多信息,见。 + + + + + + definition + + + + + 一个定义该函数的字符串常量,其含义取决于所用语言。它可以是一个内部函数名称、一个对象文件的路径、一个 SQL 命令,或者用一种过程语言编写的文本。 + + + + 使用美元引用(见)来书写函数定义字符串通常会更有帮助,而不是使用普通的单引号语法。如果没有美元引用,函数定义中的任何单引号或者反斜线都必须用双写来转义。 + + + + + + + obj_file, link_symbol + + + 当 C 语言源代码中的函数名称与 SQL 函数名称不同时,这种形式的AS子句用于可动态加载的 C 语言函数。字符串obj_file是包含该动态可加载对象的文件名,而字符串link_symbol是函数的链接符号,即 C 语言源代码中函数的名称。如果省略链接符号,则假定它与正在定义的 SQL 函数名称相同。所有函数的 C 名称都必须不同,因此必须为重载的 C 函数指定不同的 C 名称(例如将参数类型作为 C 名称的一部分)。 + + + 当重复的CREATE FUNCTION调用引用同一个对象文件时,该文件在每个会话中只装载一次。要卸载并重新装载该文件(例如在开发期间),请启动一个新会话。 + + + + + + + attribute + + + 用于指定函数可选信息的历史方式。以下属性可以出现在此处: + + isStrict + + 等同于STRICTRETURNS NULL ON NULL INPUT + + + + + isCachable + + isCachableIMMUTABLE的已废弃等价写法;出于向后兼容的原因,目前仍接受它。 + + + + 属性名称不区分大小写。 + + + + + + + 有关编写函数的详细信息,请参阅。 + + + + + + + 重载 + + + PostgreSQL允许函数重载;也就是说,只要输入参数类型不同,同一个名称就可以用于多个不同的函数。无论你是否使用这一能力,在某些用户不信任其他用户的数据库中调用函数时,都需要采取安全预防措施;参见。 + + + + 如果两个函数具有相同的名称和输入参数类型,它们被认为相同(不考虑任何OUT参数)。因此这些声明会冲突: + +CREATE FUNCTION foo(int) ... +CREATE FUNCTION foo(int, out text) ... + + + + + 参数类型列表不同的函数在创建时不会被视为冲突,但如果提供了默认值,则在使用时可能发生冲突。例如,考虑下面这些声明: + +CREATE FUNCTION foo(int) ... +CREATE FUNCTION foo(int, int default 42) ... + + 调用foo(10)会失败,因为系统无法确定应该调用哪个函数。 + + + + + + + 注解 + + + 允许使用完整的SQL类型语法来声明函数参数和返回值。不过,CREATE FUNCTION会丢弃带圆括号的类型修饰符(例如numeric类型的精度字段)。因此,CREATE FUNCTION foo (varchar(10)) ...CREATE FUNCTION foo (varchar) ...完全等同。 + + + + 在用CREATE OR REPLACE FUNCTION替换现有函数时,更改参数名会受到限制。不能更改已经分配给任何输入参数的名称(但可以给先前没有名称的参数补上名称)。如果输出参数多于一个,也不能更改输出参数的名称,因为那会改变描述函数结果的匿名复合类型的列名。这些限制是为了确保函数被替换时,已有的函数调用不会停止工作。 + + + + 如果一个函数被声明为带有VARIADIC参数的STRICT函数,则严格性检查测试的是可变参数数组作为一个整体是否非空。如果该数组包含空值元素,仍会调用该函数。 + + + + + + 示例 + + 下面给出一些简单示例,帮助你开始使用。有关更多信息和示例,请参见。 + +CREATE FUNCTION add(integer, integer) RETURNS integer + AS 'select $1 + $2;' + LANGUAGE SQL + IMMUTABLE + RETURNS NULL ON NULL INPUT; + + + + + 在PL/pgSQL中,使用参数名把一个整数加 1: + +CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$ + BEGIN + RETURN i + 1; + END; +$$ LANGUAGE plpgsql; + + + + + 返回一个包含多个输出参数的记录: + +CREATE FUNCTION dup(in int, out f1 int, out f2 text) + AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ + LANGUAGE SQL; + +SELECT * FROM dup(42); + + 你也可以用一个显式命名的复合类型,更详细地表达同样的意思: + +CREATE TYPE dup_result AS (f1 int, f2 text); + +CREATE FUNCTION dup(int) RETURNS dup_result + AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ + LANGUAGE SQL; + +SELECT * FROM dup(42); + + 另一种返回多列的方法是使用TABLE函数: + +CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text) + AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ + LANGUAGE SQL; + +SELECT * FROM dup(42); + + 不过,TABLE函数与前面的示例不同,因为它实际返回的是一记录,而不只是单条记录。 + + + + + 安全地编写 <literal>SECURITY DEFINER</literal>函数 + + + search_path 配置参数 + 用于保护函数 + + + + 因为SECURITY DEFINER函数要以创建它的用户的权限执行,所以必须小心确保该函数不会被滥用。出于安全考虑,应将设置为排除任何可被不受信任用户写入的模式。这可以防止恶意用户创建对象(例如表、函数和操作符)来遮蔽该函数原本打算使用的对象。在这方面尤其重要的是临时表模式;默认情况下它最先被搜索,而且通常任何人都可写。一个安全的安排是强制把临时模式放到搜索顺序的最后。要做到这一点,应把pg_temppg_temp用于保护函数写成search_path中的最后一项。下面这个函数展示了安全用法: + + +CREATE FUNCTION check_password(uname TEXT, pass TEXT) +RETURNS BOOLEAN AS $$ +DECLARE passed BOOLEAN; +BEGIN + SELECT (pwd = $2) INTO passed + FROM pwds + WHERE username = $1; + + RETURN passed; +END; +$$ LANGUAGE plpgsql + SECURITY DEFINER + -- 设置一个安全的 search_path:受信的模式,然后是 'pg_temp'。 + SET search_path = admin, pg_temp; + + + 这个函数的意图是访问admin.pwds表。但如果没有SET子句,或者SET子句只提到admin,那么该函数就可能因为有人创建一个名为pwds的临时表而被利用。 + + + PostgreSQL 8.3 版本之前,SET子句尚不可用,因此较旧的函数可能包含相当复杂的逻辑来保存、设置和恢复search_path。使用SET子句更容易实现此目的。 + + 还需注意,默认情况下,新创建的函数会将执行权限授予PUBLIC(有关更多信息,请参见)。通常,你会希望将安全定义器函数的使用限制为某些用户。为此,必须撤销默认的PUBLIC权限,然后有选择地授予执行权限。为避免新函数在一段时间内对所有人可访问,应在同一个事务中创建函数并设置权限。例如: + + +BEGIN; +CREATE FUNCTION check_password(uname TEXT, pass TEXT) ... SECURITY DEFINER; +REVOKE ALL ON FUNCTION check_password(uname TEXT, pass TEXT) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION check_password(uname TEXT, pass TEXT) TO admins; +COMMIT; + + + + + + 兼容性 + + CREATE FUNCTION命令在 SQL:1999 及更高版本中定义。PostgreSQL版本与其相似,但并不完全兼容。这些属性和可用的不同语言都不具备可移植性。 + + + 为与某些其他数据库系统兼容,argmode可以写在argname之前或之后,但只有前一种写法符合标准。 + + + + 对于参数默认值,SQL 标准只规定了带有DEFAULT关键字的语法。带=的语法用于 T-SQL 和 Firebird。 + + + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_group.sgml b/zh/9.6/ref/create_group.sgml new file mode 100644 index 00000000..d69a1658 --- /dev/null +++ b/zh/9.6/ref/create_group.sgml @@ -0,0 +1,70 @@ + + + + + CREATE GROUP + + + + CREATE GROUP + 7 + SQL - 语言语句 + + + + CREATE GROUP + 定义一个新的数据库角色 + + + + +CREATE GROUP name [ [ WITH ] option [ ... ] ] + +其中option可以是: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + | IN ROLE role_name [, ...] + | IN GROUP role_name [, ...] + | ROLE role_name [, ...] + | ADMIN role_name [, ...] + | USER role_name [, ...] + | SYSID uid + + + + + 描述 + + + CREATE GROUP 现在是 的别名。 + + + + + 兼容性 + + + SQL 标准中没有 CREATE GROUP 语句。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/create_index.sgml b/zh/9.6/ref/create_index.sgml new file mode 100644 index 00000000..348a07a6 --- /dev/null +++ b/zh/9.6/ref/create_index.sgml @@ -0,0 +1,551 @@ + + + + + CREATE INDEX + + + + CREATE INDEX + 7 + SQL - 语言语句 + + + + CREATE INDEX + 定义一个新索引 + + + + +CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON table_name [ USING method ] + ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) + [ WITH ( storage_parameter [= value] [, ... ] ) ] + [ TABLESPACE tablespace_name ] + [ WHERE predicate ] + + + + + + 描述 + + + CREATE INDEX在指定关系的指定列上构建一个索引, + 该关系可以是表或物化视图。索引主要用于提升数据库性能 + (但使用不当也可能导致性能下降)。 + + + + 索引的键字段指定为列名,或者指定为写在圆括号中的表达式。 + 如果索引方法支持多列索引,则可以指定多个字段。 + + + + 索引字段可以是根据表行中一个或多个列值计算得到的表达式。 + 该特性可用于根据基础数据的某种变换来快速访问数据。例如, + 在upper(col)上计算的索引可让子句 + WHERE upper(col) = 'JIM'使用索引。 + + + + PostgreSQL提供了索引方法 + B-树、hash、GiST、SP-GiST、GIN 以及 BRIN。用户也可以定义自己的索引 + 方法,但这相当复杂。 + + + + 当WHERE子句存在时,会创建一个 + 部分索引。部分索引只包含表中一部分行的索引项, + 通常这一部分比表的其余部分更适合建立索引。例如,如果一个表同时包含 + 已开票和未开票订单,而未开票订单只占整个表的一小部分,但这一部分又经常 + 被访问,就可以只对这部分创建索引来提升性能。另一种可能 + 的应用是将WHEREUNIQUE结合使用, + 以便在表的一个子集上强制唯一性。更多讨论请见 + 。 + + + + WHERE子句中使用的表达式只能引用底层表的列,但 + 它可以使用所有列,而不仅仅是被索引的列。当前, + WHERE中也禁止使用子查询和聚合表达式。同样的 + 限制也适用于作为表达式的索引字段。 + + + + 所有在索引定义中使用的函数和操作符都必须是不可变的, + 也就是说,它们的结果只能依赖其参数,而不能受任何外部因素影响 + (例如另一个表的内容或当前时间)。这种限制确保索引的行为定义明确。 + 要在索引表达式或WHERE子句中使用用户定义的函数, + 记得在创建该函数时将其标记为不可变。 + + + + + 参数 + + + + UNIQUE + + + 使系统在创建索引时(如果数据已经存在)以及每次添加数据时, + 检查表中的重复值。任何会导致重复项的插入或更新操作都会报错。 + + + + + + CONCURRENTLY + + 使用此选项时,PostgreSQL将在构建索引时不获取任何会阻止对表进行并发插入、更新或删除的锁;而标准索引构建会阻塞对表的写入(但不会阻塞读取),直到构建完成。使用此选项时有几个注意事项需要了解—请参见 + + 对于临时表,CREATE INDEX始终是非并发的, + 因为没有其他会话可以访问它们,而且非并发创建索引的代价更低。 + + + + + + IF NOT EXISTS + + + 如果同名关系已存在,则不抛出错误,而是发出一个提示。注意, + 现有索引并不保证与本应创建的索引有任何相似之处。指定 + IF NOT EXISTS时,必须提供索引名。 + + + + + + name + + 要创建的索引名称。此处不能包含模式名称;索引始终创建在其父表所在的同一模式中。如果省略名称,PostgreSQL会根据父表名称和被索引的列名选择合适的名称。 + + + + + table_name + + + 要建立索引的表名(可以是模式限定名)。 + + + + + + method + + + 要使用的索引方法的名称。选择包括btreehash、 + gistspgistgin和 + brin。 + 默认方法是btree。 + + + + + + column_name + + + 一个表列的名称。 + + + + + + expression + + + 一个基于表中一个或多个列的表达式。通常必须像语法中所示那样写在 + 外围圆括号中。不过,如果该表达式是函数调用形式,则可以省略圆括号。 + + + + + + collation + + + 将用于该索引的排序规则名称。默认情况下,索引使用被索引列声明的 + 排序规则,或者被索引表达式的结果排序规则。对于涉及使用非默认排序 + 规则表达式的查询,使用非默认排序规则的索引可能会很有用。 + + + + + + opclass + + + 一个操作符类的名称。详见下文。 + + + + + + ASC + + + 指定升序排序(默认)。 + + + + + + DESC + + 指定降序排序。 + + + + + NULLS FIRST + + + 指定把空值排序在非空值前面。在指定DESC时, + 这是默认行为。 + + + + + + NULLS LAST + + + 指定把空值排序在非空值后面。在没有指定DESC时, + 这是默认行为。 + + + + + + storage_parameter + + 索引方法专用存储参数的名称。有关详细信息,请参见 + + + + + tablespace_name + + + 在其中创建索引的表空间。如果未指定,将查阅 + ;对于临时表上的索引,则查阅 + 。 + + + + + + predicate + + 部分索引的约束表达式。 + + + + + + + 索引存储参数 + + + 可选的WITH子句为索引指定存储参数。每一种 + 索引方法都有其各自允许的存储参数集合。 + B-树、hash、GiST 和 SP-GiST 索引方法都接受以下参数: + + + + + fillfactor + + 索引的填充因子是一个百分比,用于确定索引方法将尝试把索引页填充到多满。对于 B-树,在初始构建索引期间,以及向右扩展索引(添加新的最大键值)时,叶页都会填充到该百分比。如果之后页面变得完全填满,就会进行拆分,导致索引效率逐渐下降。B-树 使用默认填充因子 90,但可以选择 10 到 100 之间的任意整数值。如果表是静态的,填充因子 100 最适合将索引的物理大小降至最低;但对于频繁更新的表,较小的填充因子更适合减少页面拆分的需要。其他索引方法以不同但大致类似的方式使用填充因子;默认填充因子因方法而异。 + + + + + + GiST 索引还接受以下参数: + + + + + buffering + + 确定是否使用中描述的缓冲构建技术来构建索引。设置为OFF时禁用,设置为ON时启用,设置为AUTO时初始禁用,但一旦索引大小达到,就会动态启用。默认值为AUTO + + + + + + GIN 索引接受不同的参数: + + + + + fastupdate + + 此设置控制中描述的快速更新技术的使用。这是一个布尔参数:ON启用快速更新,OFF禁用快速更新。(如中所述,允许使用ONOFF的其他拼写形式。)默认值为ON + + + + + 通过ALTER INDEX关闭fastupdate + 会阻止后续插入进入待处理索引项列表,但这本身不会刷新现有条目。 + 之后可能需要对该表执行VACUUM,或调用 + gin_clean_pending_list函数,以确保待处理列表被清空。 + + + + + + + + gin_pending_list_limit + + + + 自定义参数。 + 该值以千字节为单位。 + + + + + + + BRIN 索引接受一个不同的参数: + + + + + pages_per_range + + + + 定义每个BRIN索引项对应的一个块范围由多少个表块组成 + (详见)。默认值为128。 + + + + + + + + 并发构建索引 + + + 索引 + 并发构建 + + + + 创建索引可能会干扰数据库的正常运行。通常 + PostgreSQL会锁住要建立索引的表,阻止其写入, + 并通过一次扫描完成整个索引构建。其他事务仍可读取该表,但如果它们试图 + 在表中插入、更新或删除行,就会阻塞直到索引构建完成。如果系统是在线生 + 产数据库,这可能产生严重影响。对非常大的表建立索引可能需要很多小时, + 即便是较小的表,索引构建也可能在一段对生产系统而言不可接受的时间内阻 + 止写入者操作。 + + + + PostgreSQL支持在不阻止写入的情况下构建索引。 + 这种方法通过在CREATE INDEX中指定 + CONCURRENTLY选项来启用。使用该选项时, + PostgreSQL必须对该表执行两次扫描,此外还 + 必须等待所有现有、可能修改或使用该索引的事务结束。因此,这种方法比标 + 准索引构建需要更多总工作量,完成时间也明显更长。不过,由于它允许在构 + 建索引期间继续进行正常操作,所以这种方法适合在生产环境中新增索引。当 + 然,创建索引带来的额外 CPU 和 I/O 负载也可能拖慢其他操作。 + + + 在并发索引构建中,索引实际上会在一个事务中录入系统目录,然后在另外两个事务中执行两次表扫描。每次表扫描之前,索引构建都必须等待已修改该表的现有事务结束。第二次扫描之后,索引构建必须等待所有持有早于第二次扫描的快照(参见)的事务结束,其中包括其他表上并发索引构建任一阶段使用的事务。最后,索引才可以标记为可用,CREATE INDEX命令随之结束。不过即便如此,索引也可能无法立即用于查询:在最坏情况下,只要还存在早于索引构建开始的事务,就不能使用它。 + + 如果在扫描表时出现问题,例如死锁或唯一索引中的唯一性冲突,CREATE INDEX命令将失败,但会留下一个无效索引。由于该索引可能不完整,查询时会忽略它;但是,它仍会带来更新开销。该psql + \d命令会将此类索引报告为INVALID: + + +postgres=# \d tab + Table "public.tab" + Column | Type | Modifiers +--------+---------+----------- + col | integer | +Indexes: + "idx" btree (col) INVALID +在这种情况下,建议的恢复方法是删除索引,然后重新尝试执行CREATE INDEX CONCURRENTLY。(另一种可能性是重建索引,使用REINDEX。但是,由于REINDEX不支持并发构建,此选项不太有吸引力。) + + + 并发构建唯一索引时的另一项注意事项是,在第二次表扫描开始时,唯一性约 + 束就已经开始对其他事务生效了。这意味着在该索引可供使用之前,其他查询 + 就可能报告约束违规,甚至在索引构建最终失败的情况下也是如此。另外,如 + 果第二次扫描确实失败了,那个无效索引之后仍会继续强制 + 执行其唯一性约束。 + + + + 也支持并发构建表达式索引和部分索引。计算这些表达式时发生的错误, + 可能导致与上文所述唯一性约束违规类似的行为。 + + + 普通索引构建允许同一表上的其他普通索引构建并行进行,但一张表上一次只能进行一个并发索引构建。在这两种情况下,期间都不允许对该表进行其他类型的模式修改。另一个区别是,普通CREATE INDEX命令可以在事务块中执行,而CREATE INDEX CONCURRENTLY不能。 + + + + + 注解 + + + 关于索引何时能被使用、何时不被使用以及什么情况下它们有用的信息请 + 见。 + + + + + 哈希索引操作目前不会写入 WAL 日志,因此如果数据库崩溃时还有未写入 + 的更改,哈希索引可能需要用REINDEX重建。此外, + 在初始基础备份之后,对哈希索引的更改不会通过流复制或基于文件的 + 复制进行复制,因此之后使用这些索引的查询会得到错误的答案。哈希 + 索引在时间点恢复期间也不能被正确恢复。由于这些原因,目前不鼓励 + 使用哈希索引。 + + + + 目前,只有 B-树、GiST、GIN 和 BRIN 索引方法支持多列索引。默认最多可指定 32 个字段。(构建PostgreSQL时可以更改此限制。)目前只有 B-树 支持唯一索引。 + + 索引的每一列都可以指定一个操作符类。操作符类标识索引用于该列的操作符。例如,四字节整数上的 B-树 索引会使用int4_ops类;此操作符类包含四字节整数的比较函数。实际上,列数据类型的默认操作符类通常已足够。操作符类存在的主要原因是,某些数据类型可能有多个有意义的排序方式。例如,我们可能希望按绝对值或实部对复数数据类型排序。我们可以为该数据类型定义两个操作符类,并在创建索引时选择合适的类。有关操作符类的更多信息,请参见 + + + 对于支持有序扫描的索引方法(当前只有 B-树),可以指定可选子句 + ASCDESCNULLS FIRST + 和/或NULLS LAST来修改索引的排序顺序。由于有序索引可 + 以向前或向后扫描,因此创建单列DESC索引通常并无用处 + — 常规索引已经提供了这种排序顺序。这些选项的价值在于可以创建与混合 + 排序查询所要求顺序相匹配的多列索引,例如 + SELECT ... ORDER BY x ASC, y DESC。如果需要在依靠 + 索引避免排序步骤的查询中支持空值排在低位,而不是默认的 + 空值排在高位行为,那么NULLS选项就很有用。 + + + + 系统定期收集表中所有列的统计信息。新创建的非表达式索引可以立即使用 + 这些统计数据来确定索引的有用性。对于新的表达式索引,需要运行 + ANALYZE + 或等待自动清理守护进程分析表以 + 生成这些索引的统计信息。 + + + + 对于大多数索引方法,索引的创建速度取决于 + 的设置。较大的值将会减少 + 索引创建所需的时间,当然不要把它设置得超过实际可用的内存量(那会迫使 + 机器进行交换)。 + + + 使用删除索引。 + + + 早期版本的PostgreSQL还提供过一种 R-tree + 索引方法。该方法已经被移除,因为它相对于 GiST 方法并无明显优势。如果 + 指定了USING rtreeCREATE INDEX + 会将其解释为USING gist,以简化旧数据库向 GiST 的转换。 + + + + + 示例 + + 创建 B-树索引,列为title,所在表为films: + +CREATE UNIQUE INDEX title_idx ON films (title); + + + + + 要在表达式lower(title)上创建一个索引,以便高效执行 + 不区分大小写的搜索: + +CREATE INDEX ON films ((lower(title))); + + (在这个示例中,索引名称被省略,因此系统会选择一个名字, + 通常为films_lower_idx。) + + + + 要创建一个具有非默认排序规则的索引: + +CREATE INDEX title_idx_german ON films (title COLLATE "de_DE"); + + + + + 要创建一个具有非默认空值排序顺序的索引: + +CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST); + + + + + 要创建一个具有非默认填充因子的索引: + +CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70); + + + + + 要创建一个禁用快速更新的GIN索引: + +CREATE INDEX gin_idx ON documents_table USING GIN (locations) WITH (fastupdate = off); + + + + + 要在表films的列code上创建一个索引, + 并让该索引驻留在表空间indexspace中: + +CREATE INDEX code_idx ON films (code) TABLESPACE indexspace; + + + + + 要在点属性上创建一个 GiST 索引,以便能够在转换函数的结果上高效地使用 + box 操作符: + +CREATE INDEX pointloc + ON points USING gist (box(location,location)); +SELECT * FROM points + WHERE box(location,location) && '(0,0),(1,1)'::box; + + + + + 要在不阻止对表执行写操作的情况下创建索引: + +CREATE INDEX CONCURRENTLY sales_quantity_index ON sales_table (quantity); + + + + + + + 兼容性 + + + CREATE INDEX是 + PostgreSQL的语言扩展。SQL 标准中没有关于 + 索引的规定。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_language.sgml b/zh/9.6/ref/create_language.sgml new file mode 100644 index 00000000..face7c46 --- /dev/null +++ b/zh/9.6/ref/create_language.sgml @@ -0,0 +1,241 @@ + + + + + CREATE LANGUAGE + + + + CREATE LANGUAGE + 7 + SQL - 语言语句 + + + + CREATE LANGUAGE + 定义一种新的过程语言 + + + + +CREATE [ OR REPLACE ] [ PROCEDURAL ] LANGUAGE name +CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name + HANDLER call_handler [ INLINE inline_handler ] [ VALIDATOR valfunction ] + + + + + 描述 + + CREATE LANGUAGEPostgreSQL数据库中注册一种新的过程语言。随后,可以用这种新语言定义函数和触发器过程。 + + + PostgreSQL 9.1 开始,大多数过程语言都已变成扩展,因此应使用而不是CREATE LANGUAGE来安装。现在直接使用CREATE LANGUAGE应仅限于扩展安装脚本。如果数据库中有一个语言(例如升级的结果),可以使用CREATE EXTENSION langname FROM unpackaged将其转换为扩展。 + + + + CREATE LANGUAGE实际上是将该语言名称与负责执行 + 用这种语言编写的函数的处理器函数关联起来。关于语言处理器的更多信息, + 参见。 + + + + CREATE LANGUAGE命令有两种形式。第一种形式中,用户只提供所需语言的名称,PostgreSQL服务器会查询pg_pltemplate系统目录,以确定正确的参数。第二种形式中,用户在提供语言名称的同时也提供语言参数。第二种形式可用来创建未在pg_pltemplate中定义的语言,但这种做法被认为已经过时。 + + + + 当服务器在pg_pltemplate目录中找到给定语言名称的条目时,即使命令包含语言参数,也会使用目录中的数据。这种行为简化了旧转储文件的装载,因为它们很可能包含关于语言支持函数的过时信息。 + + + + 通常,用户必须具有PostgreSQL超级用户权限才能注册新语言。不过,如果语言列在pg_pltemplate目录中,并且标记为允许数据库所有者创建(tmpldbacreate为真),数据库所有者就可以在该数据库中注册这种新语言。默认情况下,数据库所有者可以创建受信任的语言,但超级用户可以通过修改pg_pltemplate的内容来调整这一点。语言的创建者成为其所有者,以后可以删除它、重命名它或将它分配给新的所有者。 + + + + CREATE OR REPLACE LANGUAGE会创建新语言,或替换现有定义。如果语言已经存在,则会根据指定值或从pg_pltemplate获取的值更新其参数,但不会改变语言的所有权和权限设置,并且假定用该语言编写的任何现有函数仍然有效。除了满足创建语言的常规权限要求外,用户还必须是超级用户或现有语言的所有者。REPLACE主要用于确保语言存在。如果语言具有pg_pltemplate条目,则REPLACE实际上不会改变现有定义的任何内容,除非出现少见的情况:该pg_pltemplate条目在语言创建后被修改过。 + + + + + 参数 + + + + TRUSTED + + + TRUSTED指定该语言不会让用户获得其原本 + 无权访问的数据访问能力。如果在注册该语言时省略这个关键字,只有 + 拥有PostgreSQL超级用户权限的用户 + 才能使用该语言创建新函数。 + + + + + + PROCEDURAL + + + + 这是一个噪声词。 + + + + + + name + + + + 新过程语言的名称。该名称不能与数据库中其他语言的名称重复。 + + + + 为了向后兼容,名称可以用单引号括起来。 + + + + + + HANDLER call_handler + + + call_handler + 是一个先前注册的函数名称,它会被调用来执行该过程语言中的函数。 + 过程语言的调用处理器必须使用某种编译型语言(例如 C)编写,并采用 + 版本 1 调用约定。它必须在PostgreSQL + 中注册为一个不带参数且返回language_handler类型的 + 函数。该返回类型是一种占位符类型,仅用于把 + 该函数标识为调用处理器。 + + + + + + INLINE inline_handler + + + inline_handler + 是一个先前注册的函数名称,它会被调用来执行该语言中的匿名代码块 + (命令)。 + 如果没有指定inline_handler + 函数,则该语言不支持匿名代码块。该处理器函数必须接受一个 + internal类型的参数,该参数将是 + DO命令的内部表示,并且它通常返回 + void。处理器的返回值会被忽略。 + + + + + + VALIDATOR valfunction + + + valfunction + 是一个先前注册的函数名称,在创建该语言中的新函数时会调用它来验证 + 该新函数。如果没有指定验证器函数,那么新函数在创建时不会被检查。 + 验证器函数必须接受一个oid类型的参数,它将是待创建 + 函数的 OID,并且它通常返回void。 + + + + 验证器函数通常会检查函数体的语法正确性,但它也可以检查函数的其他 + 属性,例如该语言是否支持某些参数类型。要报告错误,验证器函数 + 应使用ereport()函数。函数的返回值会被忽略。 + + + + + + + TRUSTED选项和支持函数名称会被忽略,前提是服务器在pg_pltemplate中有指定语言名称的条目。 + + + + + 注解 + + + 程序是对CREATE LANGUAGE命令的一个简单包装。它使从 shell 命令行安装过程语言变得更加容易。 + + + + 使用删除过程语言,或者更好的方法是使用程序。 + + + + 系统目录pg_language(见)记录当前已安装语言的信息。此外, + createlang有一个可以列出已安装语言的选项。 + + + + 要用某种过程语言创建函数,用户必须拥有该语言的 + USAGE权限。默认情况下,对于受信任的语言, + USAGE会授予PUBLIC(即所有人)。 + 如果需要,也可以撤销这项授权。 + + + + 过程语言是各个数据库本地的。不过,可以将某种语言安装到 + template1数据库中,这样它就会在之后创建的所有 + 数据库中自动可用。 + + + + 如果服务器在pg_pltemplate中没有该语言的条目,则调用处理器函数、内联处理器函数(如果有)和验证器函数(如果有)必须已经存在。但如果有条目,则这些函数不必事先存在;如果数据库中不存在它们,就会自动定义它们。(如果安装中没有实现该语言的共享库,这可能导致CREATE LANGUAGE失败。) + + + + 在 7.3 之前的PostgreSQL版本中,必须将处理器函数声明为返回占位符类型opaque,而不是language_handler。为了支持装载旧转储文件,CREATE LANGUAGE会接受声明为返回opaque的函数,但会发出提示,并将函数声明的返回类型改为language_handler。 + + + + + 示例 + + + 创建任何标准过程语言的首选方式很简单: + +CREATE LANGUAGE plperl; + + + + + 对于 pg_pltemplate 目录中未知的语言,需要类似以下的命令序列: + +CREATE FUNCTION plsample_call_handler() RETURNS language_handler + AS '$libdir/plsample' + LANGUAGE C; +CREATE LANGUAGE plsample + HANDLER plsample_call_handler; + + + + + 兼容性 + + + CREATE LANGUAGE是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_materialized_view.sgml b/zh/9.6/ref/create_materialized_view.sgml new file mode 100644 index 00000000..a593dbd8 --- /dev/null +++ b/zh/9.6/ref/create_materialized_view.sgml @@ -0,0 +1,164 @@ + + + + + CREATE MATERIALIZED VIEW + + + + CREATE MATERIALIZED VIEW + 7 + SQL - 语言语句 + + + + CREATE MATERIALIZED VIEW + 定义一个新物化视图 + + + + +CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] table_name + [ (column_name [, ...] ) ] + [ WITH ( storage_parameter [= value] [, ... ] ) ] + [ TABLESPACE tablespace_name ] + AS query + [ WITH [ NO ] DATA ] + + + + + 描述 + + + CREATE MATERIALIZED VIEW定义一个基于查询的物化 + 视图。在发出该命令时,会执行该查询并用其结果填充该视图(除非使用了 + WITH NO DATA),之后可以使用 + REFRESH MATERIALIZED VIEW来刷新它。 + + + + CREATE MATERIALIZED VIEW类似于 + CREATE TABLE AS,不过它还会记住用于初始化该视图的 + 查询,以便后续按需刷新。物化视图具有很多与表相同的属性,但不支持临 + 时物化视图,也不支持自动生成 OID。 + + + + + 参数 + + + + IF NOT EXISTS + + + + 如果已存在同名物化视图,则不要抛出错误。在这种情况下会发出一条通 + 知。请注意,不能保证现有的物化视图与原本会创建出来的那个物化视图 + 有任何相似之处。 + + + + + + + table_name + + + + 要创建的物化视图名称(可以带模式限定)。该名称必须与同一模式中的 + 任何其他关系(表、序列、索引、视图、物化视图或外部表)的名称不同。 + + + + + + + column_name + + + + 新物化视图中的列名。如果没有提供列名,则采用查询输出列的名称。 + + + + + + + WITH ( storage_parameter [= value] [, ... ] ) + + + 该子句为新物化视图指定可选的存储参数;更多信息见。凡是CREATE + TABLE支持的参数,CREATE MATERIALIZED + VIEW也同样支持,但OIDS除外。更多信息见。 + + + + + + TABLESPACE tablespace_name + + + tablespace_name是创建新物化视图所在表空间的名称。如果未指定,则参考。 + + + + + + query + + + 一个TABLE或命令。该查询将在一种安全受限的操作中运行。特别是,调用那些自身会创建临时表的函数将会失败。 + + + + + + WITH [ NO ] DATA + + + + 该子句指定是否在创建时填充物化视图。如果不填充,则该物化视图会被 + 标记为不可扫描,在使用REFRESH MATERIALIZED VIEW + 之前不能查询。 + + + + + + + + + + + 兼容性 + + + + + CREATE MATERIALIZED VIEW是一种 + PostgreSQL扩展。 + + + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_opclass.sgml b/zh/9.6/ref/create_opclass.sgml new file mode 100644 index 00000000..b88ea4a7 --- /dev/null +++ b/zh/9.6/ref/create_opclass.sgml @@ -0,0 +1,283 @@ + + + + + CREATE OPERATOR CLASS + + + + CREATE OPERATOR CLASS + 7 + SQL - 语言语句 + + + + CREATE OPERATOR CLASS + 定义一个新的操作符类 + + + + +CREATE OPERATOR CLASS name [ DEFAULT ] FOR TYPE data_type + USING index_method [ FAMILY family_name ] AS + { OPERATOR strategy_number operator_name [ ( op_type, op_type ) ] [ FOR SEARCH | FOR ORDER BY sort_family_name ] + | FUNCTION support_number [ ( op_type [ , op_type ] ) ] function_name ( argument_type [, ...] ) + | STORAGE storage_type + } [, ... ] + + + + + 描述 + + + CREATE OPERATOR CLASS创建一个新的操作符类。操作符类定义特定数据类型如何用于索引。它指定某些操作符为该数据类型和索引方法承担特定角色或策略。它还指定为索引列选择该操作符类时,索引方法要使用的支持函数。在创建操作符类之前,必须先定义它使用的所有操作符和函数。 + + + + 如果给出了一个模式名称,那么该操作符类会被创建在指定模式中。否则,它 + 会被创建在当前模式中。同一模式中的两个操作符类只有在被用于不同的索引 + 方法时才可以具有相同的名称。 + + + + 定义操作符类的用户将成为其拥有者。目前,创建用户必须是超级用户。 + (之所以有此限制,是因为错误的操作符类定义可能会使服务器混乱,甚 + 至崩溃。) + + + + CREATE OPERATOR CLASS当前不会检查操作符 + 类定义是否包括该索引方法所要求的所有操作符和函数,也不会检查这些操作符 + 和函数是否构成一个自洽的集合。定义一个合法的操作符类是用户的责任。 + + + + 相关的操作符类可以分组为操作符族。要把一个新的 + 操作符类加入现有操作符族,可以在CREATE OPERATOR + CLASS中指定FAMILY选项。如果没有这个选项, + 新类会被放入一个与其同名的操作符族中(如果该操作符族尚不存在,就会创 + 建它)。 + + + + 进一步的信息可参考。 + + + + + 参数 + + + + name + + + 要创建的操作符类的名称。该名称可以被模式限定。 + + + + + + DEFAULT + + + 如果出现,该操作符类将成为其数据类型的默认操作符类。对于某个 + 特定的数据类型和索引方法,最多只能有一个默认操作符类。 + + + + + + data_type + + + 该操作符类对应的列数据类型。 + + + + + + index_method + + + 该操作符类对应的索引方法名称。 + + + + + + family_name + + + 要把该操作符类加入其中的现有操作符族名称。如果没有指定, + 则使用一个与该操作符类同名的操作符族(如果它还不存在则创建)。 + + + + + + strategy_number + + + 与该操作符类关联的操作符在此索引方法中的策略号。 + + + + + + operator_name + + + 与该操作符类关联的操作符名称(可以是模式限定的)。 + + + + + + op_type + + + 在OPERATOR子句中,表示该操作符的操作数数据类型, + 或者用NONE表示一个左一元或右一元操作符。在通常情况下,如 + 果操作数数据类型与操作符类的数据类型相同,则可以省略操作数数据类型。 + + + + 在FUNCTION子句中,表示该函数意图支持的操作数数据类型;如果它不同于函数的输入数据类型(对于 B-树比较函数和哈希函数),或者不同于该类的数据类型(对于 B-树排序支持函数,以及 GiST、SP-GiST、GIN 和 BRIN 操作符类中的所有函数),则需要在这里指定。上述默认值都是正确的,因此无需将op_type指定在FUNCTION子句中;唯一的例外是某个 B-树排序支持函数打算支持跨数据类型比较的情形。 + + + + + + sort_family_name + + + 一个现有btree操作符族的名称(可以是模式限定的), + 它描述与一种排序操作符相关联的排序顺序。 + + + + 如果既没有指定FOR SEARCH,也没有指定 + FOR ORDER BY,则默认值是FOR SEARCH。 + + + + + + support_number + + + 与操作符类关联的函数在索引方法中的支持函数编号。 + + + + + + function_name + + + 作为该操作符类的索引方法支持函数的函数名称(可带模式限定)。 + + + + + + argument_type + + + 该函数的参数数据类型。 + + + + + + storage_type + + + 实际存储在索引中的数据类型。通常它与列数据类型相同,但是有些 + 索引方法(目前是 GiST、GIN 和 BRIN)允许它们不同。 + 除非索引方法允许使用不同类型,否则必须省略STORAGE子句。 + + + + + + + + OPERATORFUNCTIONSTORAGE + 子句可以以任何顺序出现。 + + + + + 注解 + + + 因为索引机制在使用函数之前不会检查函数的访问权限,将一个函数或者操作符包括在 + 一个操作符类中,相当于授予其公共执行权限。对适合放入操作符类的那类 + 函数而言,这通常不是问题。 + + + + 操作符不应该由 SQL 函数定义。SQL 函数很有可能会被内联到调用查询中,这 + 会妨碍优化器识别该查询匹配一个索引。 + + + + 在PostgreSQL 8.4 之前,OPERATOR子句可以包含RECHECK选项。现在不再支持该选项,因为索引操作符是否有损会在运行时动态确定。这样可以高效处理操作符可能有损也可能无损的情况。 + + + + + 示例 + + + 下面的示例为数据类型_int4int4数组) + 定义了一个 GiST 索引操作符类。完整示例见 + 模块。 + + + +CREATE OPERATOR CLASS gist__int_ops + DEFAULT FOR TYPE _int4 USING gist AS + OPERATOR 3 &&, + OPERATOR 6 = (anyarray, anyarray), + OPERATOR 7 @>, + OPERATOR 8 <@, + OPERATOR 20 @@ (_int4, query_int), + FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal), + FUNCTION 2 g_int_union (internal, internal), + FUNCTION 3 g_int_compress (internal), + FUNCTION 4 g_int_decompress (internal), + FUNCTION 5 g_int_penalty (internal, internal, internal), + FUNCTION 6 g_int_picksplit (internal, internal), + FUNCTION 7 g_int_same (_int4, _int4, internal); + + + + + 兼容性 + + + CREATE OPERATOR CLASS是一种 + PostgreSQL扩展。在 SQL 标准中没有 + CREATE OPERATOR CLASS语句。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/create_operator.sgml b/zh/9.6/ref/create_operator.sgml new file mode 100644 index 00000000..acced1fa --- /dev/null +++ b/zh/9.6/ref/create_operator.sgml @@ -0,0 +1,287 @@ + + + + + CREATE OPERATOR + + + + CREATE OPERATOR + 7 + SQL - 语言语句 + + + + CREATE OPERATOR + 定义一个新的操作符 + + + + +CREATE OPERATOR name ( + PROCEDURE = function_name + [, LEFTARG = left_type ] [, RIGHTARG = right_type ] + [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ] + [, RESTRICT = res_proc ] [, JOIN = join_proc ] + [, HASHES ] [, MERGES ] +) + + + + + 描述 + + + CREATE OPERATOR定义一个新的操作符 + name。定义该操作符 + 的用户将成为其拥有者。如果给出了模式名,该操作符将在指定的模式中创建; + 否则将在当前模式中创建。 + + + + 操作符名称是一个字符序列,最多包含 NAMEDATALEN-1 个字符(默认为 63 个),字符取自以下列表: + + ++ - * / < > = ~ ! @ # % ^ & | ` ? + + + 名称的选择有以下几项限制: + + + + --/*不能出现在操作符名称的 + 任何位置,因为它们会被视为注释的开始。 + + + + + 多字符操作符名称不能以+- + 结尾,除非该名称中还至少包含下列字符之一: + +~ ! @ # % ^ & | ` ? + + 例如,@-是允许的操作符名称,而 + *-则不允许。这一限制使 + PostgreSQL能够在不要求记号之间必须有空格 + 的情况下解析符合 SQL 规范的命令。 + + + + + 使用=>作为操作符名称的做法已被弃用。未来版本可能完全禁止这种用法。 + + + + + + + 输入!=时会被映射为<>, + 因此这两个名称始终等效。 + + + + 必须至少定义LEFTARG和RIGHTARG中的一个。对于二元操作符,必须同时定义两者。对于右一元操作符,只应定义LEFTARG;对于左一元操作符,只应定义RIGHTARG。 + + + + + 右一元操作符(也称后缀操作符)已被弃用,将在PostgreSQL 14 中移除。 + + + + + function_name过程必须事先已用CREATE + FUNCTION定义,并且其定义必须接受正确数量(一个或两个)的指定类型参数。 + + + + 其他子句用于指定可选的操作符优化属性。其含义详见 + 。 + + + + 要创建操作符,你必须在参数类型和返回类型上具有 + USAGE权限,并在底层函数上具有 + EXECUTE权限。如果指定了交换子或求反器操作符, + 你还必须拥有这些操作符。 + + + + + 参数 + + + + name + + + 要定义的操作符名称。允许使用的字符请见上文。该名称可以带模式限定, + 例如CREATE OPERATOR myschema.+ (...)。如果不带 + 模式限定,该操作符将在当前模式中创建。同一模式中的两个操作符如果 + 针对不同的数据类型进行操作,可以具有相同的名称。这被称为 + 重载。 + + + + + + function_name + + + 用于实现该操作符的函数。 + + + + + + left_type + + + 该操作符左操作数的数据类型(如果有)。对于左一元操作符,应省略此选项。 + + + + + + right_type + + + 该操作符右操作数的数据类型(如果有)。对于右一元操作符,应省略此选项。 + + + + + + com_op + + + 该操作符的交换子。 + + + + + + neg_op + + + 该操作符的求反器。 + + + + + + res_proc + + + 该操作符的限制选择度估计函数。 + + + + + + join_proc + + + 该操作符的连接选择度估算函数。 + + + + + + HASHES + + + 表示该操作符可以支持哈希连接。 + + + + + + MERGES + + + 表示该操作符可以支持归并连接。 + + + + + + + 要在com_op或其他可选参数中给出带模式限定 + 的操作符名称,请使用OPERATOR()语法,例如: + +COMMUTATOR = OPERATOR(myschema.===) , + + + + + 注解 + + + 更多信息请参见。 + + + + 无法在CREATE OPERATOR中指定一个操作符的 + 词法优先级,因为解析器的优先级行为是硬写在代码中的。详见 + 。 + + + + 废弃的选项SORT1SORT2、 + LTCMP以及GTCMP以前被用来指定与 + 可归并连接的操作符相关联的排序操作符名称。现在已不再需要这样做,因 + 为相关操作符的信息会通过查找 B-树操作符族来获得。如果给出这些选项 + 中的任意一个,除了会隐式将MERGES设为真之外,其余 + 都会被忽略。 + + + + 使用从数据库中删除用户定义的操作符。 + 使用修改数据库中的操作符。 + + + + + 示例 + + + 以下命令定义一个新的面积相等操作符,适用的数据类型为 box: + +CREATE OPERATOR === ( + LEFTARG = box, + RIGHTARG = box, + PROCEDURE = area_equal_procedure, + COMMUTATOR = ===, + NEGATOR = !==, + RESTRICT = area_restriction_procedure, + JOIN = area_join_procedure, + HASHES, MERGES +); + + + + + 兼容性 + + + CREATE OPERATOR是 + PostgreSQL扩展。在 SQL + 标准中没有用户定义操作符的规定。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_opfamily.sgml b/zh/9.6/ref/create_opfamily.sgml new file mode 100644 index 00000000..1942faf9 --- /dev/null +++ b/zh/9.6/ref/create_opfamily.sgml @@ -0,0 +1,111 @@ + + + + + CREATE OPERATOR FAMILY + + + + CREATE OPERATOR FAMILY + 7 + SQL - 语言语句 + + + + CREATE OPERATOR FAMILY + 定义一个新的操作符族 + + + + +CREATE OPERATOR FAMILY name USING index_method + + + + + 描述 + + + CREATE OPERATOR FAMILY创建一个新的操作符族。 + 操作符族定义了一组相关的操作符类,以及可能还有一些与这些操作符类兼容、 + 但对任何单个索引的正常工作都不是必需的额外操作符和支持函数。(那些对 + 索引必不可少的操作符和函数应归入相应的操作符类,而不是作为操作符族中的 + 松散成员。通常,单一数据类型的操作符属于操作符类,而跨 + 数据类型的操作符则可以作为操作符族中的松散成员,该操作符族包含这两种 + 数据类型对应的操作符类。) + + + + 新的操作符族最初是空的。应当通过随后发出的 + CREATE OPERATOR CLASS命令向其中添加所包含的 + 操作符类,并可选择通过 + ALTER OPERATOR FAMILY命令添加 + 松散操作符及其对应的支持函数。 + + + + 如果给出了模式名称,该操作符族会被创建在指定的模式中。否则,它会被 + 创建在当前模式中。只有当同一模式中的两个操作符族用于不同的索引方法时, + 它们才能具有相同的名称。 + + + + 定义操作符族的用户将成为其拥有者。当前,创建该对象的用户必须是超级用户。 + (之所以这样限制,是因为错误的操作符族定义可能会使服务器混乱,甚至导致 + 崩溃。) + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 要创建的操作符族名称。该名称可以是模式限定的。 + + + + + + index_method + + + 该操作符族所对应的索引方法名称。 + + + + + + + + 兼容性 + + + CREATE OPERATOR FAMILY是一种 + PostgreSQL扩展。SQL 标准中没有 + CREATE OPERATOR FAMILY语句。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/create_policy.sgml b/zh/9.6/ref/create_policy.sgml new file mode 100644 index 00000000..46ed6b7e --- /dev/null +++ b/zh/9.6/ref/create_policy.sgml @@ -0,0 +1,403 @@ + + + + + CREATE POLICY + + + + CREATE POLICY + 7 + SQL - 语言语句 + + + + CREATE POLICY + 为表定义新的行级安全策略 + + + + +CREATE POLICY name ON table_name + [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ] + [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ] + [ USING ( using_expression ) ] + [ WITH CHECK ( check_expression ) ] + + + + + 描述 + + + CREATE POLICY命令为一个表定义一条新的行级 + 安全性策略。请注意,必须先在该表上启用行级安全性(使用 + ALTER TABLE ... ENABLE ROW LEVEL SECURITY), + 已创建的策略才会被应用。 + + + 策略授予选择、插入、更新或删除符合相应策略表达式的行的权限。使用USING中指定的表达式检查现有表行,而使用WITH CHECK中指定的表达式检查通过INSERTUPDATE创建的新行。当USING表达式对给定行返回 true 时,该行对用户可见;如果返回 false 或 null,则该行不可见。当WITH CHECK表达式对某行返回 true 时,该行会被插入或更新;如果返回 false 或 null,则发生错误。 + + 对于INSERTUPDATE语句,会在触发BEFORE触发器之后、进行任何实际数据修改之前强制执行WITH CHECK表达式。因此,BEFORE ROW触发器可能会修改要插入的数据,从而影响安全策略检查的结果。WITH CHECK表达式会在任何其他约束之前强制执行。 + + + 策略名称按表区分。因此,同一个策略名可以用于许多不同的表,并且在 + 每个表上都可以有适合该表的定义。 + + + + 策略可以针对特定命令或特定角色应用。除非另有指定,新建策略默认 + 适用于所有命令和角色。多个策略可以应用于同一命令;更多细节见下文。 + 总结了不同类型的策略如何应用于 + 特定命令。 + + + + 对于既可以具有USING又可以具有 + WITH CHECK表达式的策略(ALL + 和UPDATE),如果未定义 + WITH CHECK表达式,那么USING + 表达式将同时用于决定哪些行可见(普通USING情形) + 以及允许写入哪些新行(WITH CHECK情形)。 + + + + 如果某个表启用了行级安全性,但不存在适用的策略,则会假定存在一条 + 默认拒绝策略,因此没有任何行可见或可更新。 + + + + + 参数 + + + + name + + + 要创建的策略名称。它必须不同于该表上任何其他策略的名称。 + + + + + + table_name + + + 该策略适用的表的名称(可选模式限定)。 + + + + + + command + + + 该策略适用的命令。有效选项是 + ALLSELECT、 + INSERTUPDATE + 和DELETEALL是默认值。 + 有关这些策略如何应用的细节见下文。 + + + + + + role_name + + + 该策略适用的角色(或多个角色)。默认是PUBLIC, + 即对所有角色应用该策略。 + + + + + + using_expression + + 任何SQL条件表达式(返回boolean)。条件表达式不能包含聚合函数或窗口函数。如果启用行级安全性,该表达式会添加到引用该表的查询中。表达式返回 true 的行将对用户可见。表达式返回 false 或 null 的任何行对用户都不可见(在SELECT中),也不能被修改(在UPDATEDELETE中)。这类行会被静默抑制,不会报告错误。 + + + + + check_expression + + 任何SQL条件表达式(返回boolean)。条件表达式不能包含聚合函数或窗口函数。如果启用行级安全性,该表达式会用于针对该表的INSERTUPDATE查询。只有表达式计算结果为 true 的行才会被允许。如果对于任何插入的记录或更新产生的任何记录,表达式计算结果为 false 或 null,则会抛出错误。请注意,check_expression是针对行预计的新内容计算的,而不是针对原始内容。 + + + + + + + 针对每种命令的策略 + + + + + ALL + + 对策略使用ALL意味着无论命令类型如何,该策略都适用于所有命令。如果存在ALL策略且还存在更具体的策略,则ALL策略和更具体的策略(或多个策略)都会被应用。此外,ALL策略会同时应用于查询的选择端和修改端,并在两端使用USING表达式(如果只定义了USING表达式)。 + 例如,如果发出UPDATE,则ALL策略既适用于UPDATE能够选择哪些行进行更新(应用USING表达式),也适用于更新后的行,以检查它们是否允许被添加回表中(如果定义了WITH CHECK表达式则应用该表达式,否则应用USING表达式)。如果INSERTUPDATE命令尝试向表中添加不通过ALL策略的WITH CHECK表达式的行,则整个命令都会中止。 + + + + + SELECT + + 对策略使用SELECT意味着该策略适用于SELECT查询,以及在策略所定义的关系上需要SELECT权限的任何时候。因此,在SELECT查询期间,只会返回关系中通过SELECT策略的记录;需要SELECT权限的查询(例如UPDATE)也只能看到被SELECT策略允许的记录。SELECT策略不能有WITH CHECK表达式,因为它只适用于从关系中检索记录的情况。 + + + + + INSERT + + 对策略使用INSERT意味着该策略适用于INSERT命令。插入的不通过该策略的行会导致策略违反错误,并且整个INSERT命令都会中止。INSERT策略不能有USING表达式,因为它只适用于向关系添加记录的情况。 + 请注意,带有ON CONFLICT DO UPDATEINSERT只会为通过INSERT路径添加到关系中的行检查INSERT策略的WITH CHECK表达式。 + + + + + UPDATE + + 对策略使用UPDATE意味着该策略适用于UPDATESELECT FOR UPDATESELECT FOR SHARE命令,以及INSERT命令的辅助ON CONFLICT DO UPDATE子句。由于UPDATE涉及取出一条现有记录并用新的修改后记录替换它,UPDATE策略同时接受USING表达式和WITH CHECK表达式。USING表达式决定UPDATE命令将看到哪些记录并对其操作,而WITH CHECK表达式定义允许将哪些修改后的行存回关系。 + + + 任何更新后的值未通过WITH CHECK表达式的行 + 都会导致错误,并且整个命令将被中止。如果只指定了一个 + USING子句,那么该子句将被用于 + USINGWITH CHECK两种情况。 + + + + 通常,UPDATE命令还需要从待更新关系的列中读取 + 数据(例如在WHERE子句、 + RETURNING子句,或SET + 子句右侧的表达式中)。这种情况下,正在被更新的关系上也需要 + SELECT权限,并且除了 + UPDATE策略外,还会应用适当的 + SELECTALL策略。这样, + 用户除了必须通过UPDATE或 + ALL策略获准更新这些行之外,还必须通过 + SELECTALL策略访问 + 正在被更新的行。 + + + INSERT命令带有辅助ON CONFLICT DO UPDATE子句时,如果采用UPDATE路径,则首先根据任何UPDATE策略的USING表达式检查要更新的行,然后根据WITH CHECK表达式检查更新后的新行。但是请注意,与独立的UPDATE命令不同,如果现有行不通过USING表达式,则会抛出错误(UPDATE路径绝不会被静默跳过)。 + + + + + DELETE + + 对策略使用DELETE意味着该策略适用于DELETE命令。DELETE命令只能看到通过该策略的行。如果某些行不通过DELETE策略的USING表达式,则它们可能通过SELECT可见,但不能被删除。 + + + 在多数情况下,DELETE命令也需要从其删除所针对 + 的关系中的列读取数据(例如在WHERE子句或 + RETURNING子句中)。这种情况下,该关系上也 + 需要SELECT权限,并且除了 + DELETE策略外,还会应用适当的 + SELECTALL策略。这样, + 用户除了必须通过DELETE或 + ALL策略获准删除这些行之外,还必须通过 + SELECTALL策略访问 + 正在被删除的行。 + + + + DELETE策略不能具有WITH + CHECK表达式,因为它只适用于正在从关系中删除行的情况, + 所以没有新行需要检查。 + + + + + + + + 按命令类型应用的策略 + + + + + + + 命令 + SELECT/ALL策略 + INSERT/ALL策略 + UPDATE/ALL策略 + DELETE/ALL策略 + + + USING 表达式 + WITH CHECK 表达式 + USING 表达式 + WITH CHECK 表达式 + USING 表达式 + + + + + SELECT + 现有行 + + + + + + + SELECT FOR UPDATE/SHARE + 现有行 + + 现有行 + + + + + INSERT + + 新行 + + + + + + INSERT ... RETURNING + 新行 如果需要读取现有行或新行(例如引用关系中列的WHERERETURNING子句)。 + 新行 + + + + + + UPDATE + 现有 & 新行 + + 现有行 + 新行 + + + + DELETE + 现有行 + + + + 现有行 + + + ON CONFLICT DO UPDATE + 现有 & 新行 + + 现有行 + 新行 + + + + +
+ +
+ + + 多条策略的应用 + + + 当不同命令类型的多条策略应用于同一命令时(例如 + SELECTUPDATE策略应用于 + UPDATE命令),用户必须同时具有这两种权限 + (例如既有从该关系中选取行的权限,也有更新这些行的权限)。因此, + 一种策略类型的表达式会与另一种策略类型的表达式使用 + AND操作符组合。 + + + + 当同一命令类型的多条策略应用于同一命令时,则至少要有一条策略 + 授予对该关系的访问权。因此,该类型的所有策略的表达式都会使用 + OR操作符组合。如果没有适用的策略, + 则访问被拒绝。 + + + + 请注意,就组合多条策略而言,ALL策略会被视为与 + 当前正在应用的其他策略同一类型。 + + + 例如,在UPDATE命令要求同时具备SELECTUPDATE权限时,如果每种类型有多个适用策略,则将按以下方式组合: +( + expression from SELECT/ALL policy 1 + OR + expression from SELECT/ALL policy 2 + OR + ... +) +AND +( + expression from UPDATE/ALL policy 1 + OR + expression from UPDATE/ALL policy 2 + OR + ... +) + + + + +
+ + + 注解 + + + 要为一个表创建或修改策略,你必须是该表的拥有者。 + + + + 虽然策略会应用于针对数据库中表的显式查询,但当系统执行内部引用 + 完整性检查或验证约束时,并不会应用这些策略。这意味着仍然存在间接判断 + 某个给定值是否存在的方法。一个例子是,尝试向某个主键列或带有唯一约束 + 的列插入重复值。如果插入失败,用户就能推断该值已经存在。(这个例子 + 假定策略允许该用户插入自己无权看见的行。)另一个例子是,用户被允许 + 向一个引用了另一张表的表中插入数据,而被引用的那张表本身对其是隐藏 + 的。用户可以通过向引用表插入值来判断其存在性;插入成功就表示该值 + 存在于被引用表中。要解决这些问题,可以仔细设计策略,防止用户插入、 + 删除或更新那些可能暗示其本来无权看见的值是否存在的行,或者改用生成 + 的值(例如代理键)来代替具有外部含义的键。 + + + + 通常,为了防止受保护的数据无意间暴露给可能不可信的用户定义函数, + 系统会在应用用户查询中出现的条件之前,先强制执行安全策略施加的 + 过滤条件。不过,被系统(或系统管理员)标记为 + LEAKPROOF的函数和操作符由于被假定为可信,可以在 + 策略表达式之前求值。 + + + 由于策略表达式会直接添加到用户的查询中,因此它们会以执行整个查询的用户权限运行。因此,使用给定策略的用户必须能够访问表达式中引用的任何表或函数,否则在尝试查询启用了行级安全性的表时,只会收到权限被拒绝错误。不过,这不会改变视图的工作方式。与普通查询和视图一样,视图所引用表的权限检查和策略会使用视图所有者的权限,以及适用于视图所有者的任何策略。 + + 更多讨论和实际示例请参见 + + + + + 兼容性 + + + CREATE POLICY是一种PostgreSQL扩展。 + + + + + 另见 + + + + + + + + +
diff --git a/zh/9.6/ref/create_role.sgml b/zh/9.6/ref/create_role.sgml new file mode 100644 index 00000000..6e63b03a --- /dev/null +++ b/zh/9.6/ref/create_role.sgml @@ -0,0 +1,334 @@ + + + + + CREATE ROLE + + + + CREATE ROLE + 7 + SQL - 语言语句 + + + + CREATE ROLE + 定义一个新的数据库角色 + + + + +CREATE ROLE name [ [ WITH ] option [ ... ] ] + +其中option可以是: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + | IN ROLE role_name [, ...] + | IN GROUP role_name [, ...] + | ROLE role_name [, ...] + | ADMIN role_name [, ...] + | USER role_name [, ...] + | SYSID uid + + + + + 描述 + + + CREATE ROLE向 + PostgreSQL数据库集簇中新增一个角色。角色是一种 + 可以拥有数据库对象并拥有数据库权限的实体;根据其使用方式,它既可以视为 + 用户,也可以视为,或者兼具这两种身份。有关 + 用户管理和认证的信息,请参见。要使用该命令,你必须拥有 + CREATEROLE权限,或者是数据库超级用户。 + + + + 注意,角色是在数据库集簇级别定义的,因此在该集簇中的所有数据库里都有效。 + + + + + 参数 + + + + name + + + 新角色的名称。 + + + + + + SUPERUSER + NOSUPERUSER + + + 这些子句决定新角色是否为超级用户;超级用户可以绕过数据库中的 + 所有访问限制。超级用户身份具有危险性,只有在确有需要时才应使用。要创建 + 新的超级用户,你自己必须先是超级用户。若未指定,默认值是 + NOSUPERUSER。 + + + + + + CREATEDB + NOCREATEDB + + 这些子句定义角色创建数据库的能力。如果指定CREATEDB,则允许正在定义的角色创建新数据库。指定NOCREATEDB会禁止角色创建数据库。如果未指定,默认值为NOCREATEDB + + + + + CREATEROLE + NOCREATEROLE + + 这些子句决定是否允许角色创建新角色(即执行CREATE ROLE)。具有CREATEROLE权限的角色还可以更改和删除其他角色。如果未指定,默认值为NOCREATEROLE + + + + + INHERIT + NOINHERIT + + 这些子句决定角色是否继承其所属角色的权限。具有INHERIT属性的角色可以自动使用直接或间接所属的所有角色获得的任何数据库权限。没有INHERIT时,成为另一个角色的成员只授予对该角色执行SET ROLE的能力;另一个角色的权限只有在执行该操作后才可用。如果未指定,默认值为INHERIT + + + + + LOGIN + NOLOGIN + + 这些子句决定是否允许角色登录;也就是说,在客户端连接期间,是否可以将该角色作为初始会话授权名称。具有LOGIN属性的角色可以视为用户。没有该属性的角色可用于管理数据库权限,但通常意义上不属于用户。如果未指定,默认值为NOLOGIN,但通过其另一种拼写调用CREATE ROLE时除外。 + + + + + REPLICATION + NOREPLICATION + + 这些子句决定角色是否为复制角色。角色必须具有此属性(或是超级用户),才能以复制模式(物理复制或逻辑复制)连接到服务器,以及创建或删除复制槽。具有REPLICATION属性的角色权限非常高,因此该属性只应赋予实际用于复制的角色。如果未指定,默认值为NOREPLICATION。要创建具有REPLICATION属性的新角色,必须是超级用户。 + + + + + BYPASSRLS + NOBYPASSRLS + + 这些子句决定角色是否绕过每个行级安全(RLS)策略。NOBYPASSRLS是默认值。要创建具有BYPASSRLS属性的新角色,必须是超级用户。 + + + 注意,pg_dump 默认会把row_security设置为 + OFF,以确保能够转储表中的全部内容。如果运行 pg_dump + 的用户没有适当权限,将返回错误。不过,超级用户和被转储表的拥有者总是 + 会绕过 RLS。 + + + + + + CONNECTION LIMIT connlimit + + + 如果该角色可以登录,此项指定该角色可建立多少并发连接。-1(默认值) + 表示不限制。注意,只有普通连接会计入此限制;预备事务和后台工作进程 + 连接都不会计入此限制。 + + + + + + PASSWORD password + + + 设置角色的密码。(密码只对具有LOGIN属性的角色有用, + 但即使没有该属性,你仍然可以为角色定义密码。)如果你不打算使用密码认证, + 可以省略此选项。如果未指定密码,密码将被设为 null,并且该用户的密码认证 + 将始终失败。也可以显式写为PASSWORD NULL。 + + + + + + ENCRYPTED + UNENCRYPTED + + + 这些关键字控制密码是否以加密形式存储在系统目录中。 + (如果两者都未指定,默认行为由配置参数 + 决定。)如果给出的密码字符串已经是 + MD5 加密格式,那么无论指定的是ENCRYPTED还是 + UNENCRYPTED,它都会按原样以加密形式存储 + (因为系统无法解密指定的已加密密码字符串)。这样便可在转储/恢复期间 + 重新装载已加密的密码。 + + + + + + VALID UNTIL 'timestamp' + + VALID UNTIL子句设置一个日期和时间,在此之后角色的密码将不再有效。如果省略此子句,密码将始终有效。 + + + + + IN ROLE role_name + + IN ROLE子句列出一个或多个现有角色,新角色会立即作为新成员加入这些角色。(请注意,无法通过此选项将新角色添加为管理员;要实现这一点,请使用单独的GRANT命令。) + + + + + IN GROUP role_name + + IN GROUPIN ROLE的已废弃写法。 + + + + + ROLE role_name + + ROLE子句列出一个或多个现有角色,这些角色会自动作为新角色的成员加入。(实际上,这会使新角色成为一个。) + + + + + ADMIN role_name + + ADMIN子句类似于ROLE,但指定的角色会以WITH ADMIN OPTION加入新角色,从而获得向其他人授予该角色成员资格的权利。 + + + + + USER role_name + + USER子句是ROLE子句的已废弃写法。 + + + + + SYSID uid + + SYSID子句会被忽略,但为向后兼容而接受。 + + + + + + + 注解 + + 使用更改角色属性,并使用删除角色。CREATE ROLE指定的所有属性都可以由后续的ALTER ROLE命令修改。 + + 向作为组使用的角色添加和移除成员,推荐使用 + + VALID UNTIL子句只为密码定义过期时间,而不是为角色本身定义过期时间。特别是,使用非基于密码的认证方式登录时,不会强制执行该过期时间。 + + INHERIT属性控制可授予权限的继承(即数据库对象的访问权限和角色成员资格)。它不适用于CREATE ROLEALTER ROLE设置的特殊角色属性。例如,即使设置了INHERIT,成为拥有CREATEDB权限的角色成员也不会立即获得创建数据库的能力;在创建数据库之前,必须先通过成为该角色。 + + INHERIT属性出于向后兼容的原因而成为默认值:在PostgreSQL的早期版本中,用户总能访问其所属组的所有权限。不过,NOINHERIT更接近 SQL 标准规定的语义。 + + 请谨慎对待CREATEROLE权限。CREATEROLE角色的权限不存在继承这一概念。这意味着,即使某个角色没有特定权限但被允许创建其他角色,它仍可轻易创建一个权限不同于自身的角色(创建具有超级用户权限的角色除外)。例如,如果角色user具有CREATEROLE权限但没有CREATEDB权限,它仍然可以创建一个具有CREATEDB权限的新角色。因此,应将拥有CREATEROLE权限的角色视为几乎等同超级用户的角色。 + + + PostgreSQL包含一个程序,其功能与CREATE ROLE相同 + (实际上它就是调用此命令),但它可以从命令 shell 中运行。 + + + + CONNECTION LIMIT选项只是近似地被强制执行;如果两个新会话 + 在几乎同一时刻启动,而此时该角色只剩下一个连接,则两者都 + 可能失败。此外,该限制从不对超级用户强制执行。 + + + + 用此命令指定未加密密码时必须谨慎。密码会以明文形式传输到服务器,也可能被 + 记录在客户端命令历史或服务器日志中。而命令 + 传输的是加密密码。此外,包含 + \password命令,可用于稍后安全地更改密码。 + + + + + 示例 + + + 创建一个可登录但没有密码的角色: + +CREATE ROLE jonathan LOGIN; + + + + + 创建一个带密码的角色: + +CREATE USER davide WITH PASSWORD 'jw8s0F4'; + + (CREATE USERCREATE ROLE相同, + 只是它隐含指定了LOGIN。) + + + + 创建一个密码有效期截止到 2004 年底的角色。当 2005 年的第一秒过去后,该密码 + 就不再有效。 + + +CREATE ROLE miriam WITH LOGIN PASSWORD 'jw8s0F4' VALID UNTIL '2005-01-01'; + + + + + 创建一个可以创建数据库并管理角色的角色: + +CREATE ROLE admin WITH CREATEDB CREATEROLE; + + + + + 兼容性 + + CREATE ROLE语句在 SQL 标准中有定义,但该标准只要求以下语法 +CREATE ROLE name [ WITH ADMIN role_name ] +多个初始管理员,以及CREATE ROLE的所有其他选项,都是PostgreSQL扩展。 + + SQL 标准定义了用户和角色的概念,但将它们视为不同的概念,并将定义用户的所有命令留给各数据库实现自行规定。在PostgreSQL中,我们选择将用户和角色统一为一种实体。因此,与标准相比,角色具有更多可选属性。 + + + 将用户赋予 NOINHERIT 属性,而将角色赋予 + INHERIT 属性,最接近 SQL 标准规定的行为。 + + + + + 参见 + + + + + + + + + + + diff --git a/zh/9.6/ref/create_rule.sgml b/zh/9.6/ref/create_rule.sgml new file mode 100644 index 00000000..82f3276e --- /dev/null +++ b/zh/9.6/ref/create_rule.sgml @@ -0,0 +1,266 @@ + + + + + CREATE RULE + + + + CREATE RULE + 7 + SQL - 语言语句 + + + + CREATE RULE + 定义一条新的重写规则 + + + + +CREATE [ OR REPLACE ] RULE name AS ON event + TO table_name [ WHERE condition ] + DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) } + +其中 event 可以是以下之一: + + SELECT | INSERT | UPDATE | DELETE + + + + + 描述 + + + CREATE RULE定义一条应用于指定表或视图的 + 新规则。CREATE OR REPLACE RULE将创建一条 + 新规则,或者替换同一个表上同名的现有规则。 + + + + PostgreSQL规则系统允许定义在数据库表上执行 + 插入、更新或删除时的替代动作。粗略地说,当在某个给定表上执行某个命令 + 时,一条规则会导致额外的命令被执行。另一种情况是,INSTEAD + 规则可以用另一个命令替换给定命令,或者让某个命令完全不执行。规则也 + 用于实现 SQL 视图。必须认识到,规则实际上是一种命令转换机制,或者说 + 是命令宏。这种转换发生在命令开始执行之前。如果你真正需要的是对每个物理 + 行独立触发的操作,那么你很可能应该使用触发器,而不是规则。有关规则系统 + 的更多信息见。 + + + + 目前,ON SELECT规则必须是无条件的INSTEAD规则,其动作必须由单个SELECT命令组成。因此,ON SELECT规则实际上会把表变为视图,其可见内容是该规则的SELECT命令返回的行,而不是表中原来存储的内容(如果有)。直接编写CREATE VIEW命令,被认为比创建真实表并为其定义ON SELECT规则的风格更好。 + + + + 可以通过定义ON INSERTON UPDATE + 和ON DELETE规则(或其中足以满足你需求的任意子集), + 来营造出可更新视图的效果,从而将视图上的更新动作替换为对其他表的适当 + 更新。如果想支持INSERT RETURNING等功能,那么务必在这些 + 规则中的每一条里都放入合适的RETURNING子句。 + + + + 如果你尝试对复杂视图更新使用有条件规则,这里有一个陷阱:对于你希望 + 允许在该视图上执行的每一种动作,必须都有一条无条件的 + INSTEAD规则。如果该规则是有条件的,或者不是 + INSTEAD规则,那么系统仍会拒绝执行该更新动作,因为它认为 + 在某些情况下最终可能还是会尝试在该视图的虚拟表上执行该动作。如果你想 + 用有条件规则处理所有有用的情况,可以加上一条无条件的 + DO INSTEAD NOTHING规则,以确保系统明白它永远不会 + 被要求去更新这个虚拟表。然后,把这些有条件规则改成非 + INSTEAD规则;在它们适用的情况下,它们会附加在默认的 + INSTEAD NOTHING动作之上。(不过,这种方法当前仍无法支持 + RETURNING查询。) + + + + + 足够简单、可自动更新的视图(见)不需要依靠用户创建的规则来实现更新。 + 虽然你仍然可以显式创建规则,但自动更新转换通常比显式规则性能更好。 + + + + 另一种值得考虑的办法是使用INSTEAD OF触发器(见 + )代替规则。 + + + + + + 参数 + + + + name + + + 要创建的规则的名称。它必须与同一个表上任何其他规则的名称相区分。 + 同一个表上同一种事件类型的多条规则会按名称的字母顺序应用。 + + + + + + event + + + 事件是SELECT、 + INSERTUPDATE或者 + DELETE之一。注意,包含ON + CONFLICT子句的INSERT + 不能用于带有INSERT或 + UPDATE规则的表。请考虑改用 + 可更新的视图。 + + + + + + table_name + + + 规则适用的表或者视图的名称(可以是模式限定的)。 + + + + + + condition + + + 任意的SQL条件表达式(返回 + boolean)。条件表达式不能引用NEW和 + OLD之外的任何表,并且不能包含聚合函数。 + + + + + + + + INSTEAD表示这些命令应当 + 替代原始命令执行。 + + + + + + + + ALSO表示这些命令应当在原始命令 + 之外执行。 + + + + 如果既未指定ALSO,也未指定 + INSTEAD,则默认值为ALSO。 + + + + + + command + + + 组成规则动作的命令。可用的命令有SELECT、 + INSERTUPDATE、 + DELETE或者NOTIFY。 + + + + + + + 在condition和 + command中,可以使用特殊表名 + NEWOLD来引用目标表中的值。 + 在ON INSERTON UPDATE规则中, + NEW可用于引用待插入或待更新的新行。在 + ON UPDATEON DELETE规则中, + OLD可用于引用将被更新或删除的现有行。 + + + + + 注解 + + + 要在表上创建或者修改规则,必须是表的拥有者。 + + + + 在视图上的INSERTUPDATE或 + DELETE规则中,可以添加一个输出视图各列的 + RETURNING子句。如果该规则分别由 + INSERT RETURNINGUPDATE RETURNING + 或DELETE RETURNING命令触发,那么这个子句将用于计算输出。 + 当规则由不带RETURNING的命令触发时,该规则的 + RETURNING子句会被忽略。当前实现只允许无条件的 + INSTEAD规则包含RETURNING;此外,对于同一事件的 + 所有规则,总共最多只能有一个RETURNING子句。(这可以确保只有一个 + 候选RETURNING子句可用于计算结果。)如果任何可用规则中都没有 + RETURNING子句,那么视图上的RETURNING查询将被拒绝。 + + + + 避免循环规则非常重要。例如,尽管下面的两条规则定义都被 + PostgreSQL所接受, + SELECT命令将导致 + PostgreSQL报告错误,因为规则会发生递归展开: + + +CREATE RULE "_RETURN" AS + ON SELECT TO t1 + DO INSTEAD + SELECT * FROM t2; + +CREATE RULE "_RETURN" AS + ON SELECT TO t2 + DO INSTEAD + SELECT * FROM t1; + +SELECT * FROM t1; + + + + + 当前,如果一个规则动作包含一个NOTIFY命令, + 该NOTIFY命令将被无条件执行,也就是说,即使 + 没有任何应当应用该规则的行,也会发出NOTIFY。 + 例如: + +CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable; + +UPDATE mytable SET name = 'foo' WHERE id = 42; + + 在UPDATE期间会发送一个NOTIFY事件, + 无论是否存在匹配条件id = 42的行。这是一个实现限制, + 未来版本中可能会修复。 + + + + + 兼容性 + + + CREATE RULE是一种 + PostgreSQL语言扩展, + 整个查询重写系统也是如此。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_schema.sgml b/zh/9.6/ref/create_schema.sgml new file mode 100644 index 00000000..afac35cd --- /dev/null +++ b/zh/9.6/ref/create_schema.sgml @@ -0,0 +1,206 @@ + + + + + CREATE SCHEMA + + + + CREATE SCHEMA + 7 + SQL - 语言语句 + + + + CREATE SCHEMA + 定义一个新模式 + + + + +CREATE SCHEMA schema_name [ AUTHORIZATION role_specification ] [ schema_element [ ... ] ] +CREATE SCHEMA AUTHORIZATION role_specification [ schema_element [ ... ] ] +CREATE SCHEMA IF NOT EXISTS schema_name [ AUTHORIZATION role_specification ] +CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_specification + +其中role_specification可以是: + + user_name + | CURRENT_USER + | SESSION_USER + + + + + 描述 + + + CREATE SCHEMA在当前数据库中创建一个新模式。 + 该模式名必须与当前数据库中任何现有模式的名称不同。 + + + + 模式本质上是一个命名空间:它包含具名对象(表、数据类型、函数和操作符), + 这些对象的名称可以与其他模式中的对象重名。访问具名对象时,可以在其名称前加上 + 模式名作为前缀来限定名称,或者设置一个包含所需模式的搜索路径。 + 指定非限定对象名的CREATE命令会在当前模式中创建该对象 + (即搜索路径最前面的模式,可通过函数current_schema确定)。 + + + + CREATE SCHEMA还可以选择包含子命令,以便在新模式中创建 + 对象。这些子命令基本上会被当作在创建模式之后单独发出的命令来处理。不过,如果 + 使用了AUTHORIZATION子句,则所有创建的对象都将归该用户所有。 + + + + + 参数 + + + + schema_name + + + 要创建的模式名称。如果省略, + user_name将被用作模式名。 + 该名称不能以pg_开头,因为这类名称保留给系统模式。 + + + + + + user_name + + + 将拥有新模式的用户的角色名。如果省略,则默认为执行该命令的用户。要创建由另一个角色拥有的模式,你必须是该角色的直接或间接成员,或者是超级用户。 + + + + + + schema_element + + + 定义要在该模式中创建的对象的 SQL 语句。当前,只有CREATE + TABLECREATE VIEWCREATE + INDEXCREATE SEQUENCECREATE + TRIGGERGRANT可作为 + CREATE SCHEMA中的子句使用。其他类型的对象可以在模式 + 创建之后通过单独的命令创建。 + + + + + + IF NOT EXISTS + + + 如果同名模式已经存在,则不执行任何操作(但会发出一条提示)。 + 使用该选项时不能包含 + schema_element子命令。 + + + + + + + + 注解 + + + 要创建一个模式,执行该命令的用户必须拥有当前数据库的CREATE + 权限(当然,超级用户可以绕过这项检查)。 + + + + + 示例 + + + 创建一个模式: + +CREATE SCHEMA myschema; + + + + + 为用户joe创建一个模式,该模式也将被命名为 + joe: + +CREATE SCHEMA AUTHORIZATION joe; + + + + + 创建一个被用户joe拥有的名为test的模式, + 除非已经有一个名为test的模式(不管joe + 是否拥有那个预先存在的模式)。 + +CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION joe; + + + + + 创建一个模式并且在其中创建一个表和视图: + +CREATE SCHEMA hollywood + CREATE TABLE films (title text, release date, awards text[]) + CREATE VIEW winners AS + SELECT title, release FROM films WHERE awards IS NOT NULL; + + 请注意,各个子命令末尾都不带分号。 + + + + 下面是实现相同结果的一种等效写法: + +CREATE SCHEMA hollywood; +CREATE TABLE hollywood.films (title text, release date, awards text[]); +CREATE VIEW hollywood.winners AS + SELECT title, release FROM hollywood.films WHERE awards IS NOT NULL; + + + + + + 兼容性 + + + SQL 标准允许在CREATE SCHEMA中使用 + DEFAULT CHARACTER SET子句,并允许使用比 + PostgreSQL当前所接受的更多子命令类型。 + + + + SQL 标准规定,CREATE SCHEMA中的子命令可以按任意顺序 + 出现。当前的PostgreSQL实现并不能处理子命令中 + 所有前向引用的情况;有时可能需要重新排列子命令的顺序,以避免前向引用。 + + + + 根据 SQL 标准,模式的拥有者总是拥有其中的所有对象。 + PostgreSQL允许模式包含由模式拥有者之外的用户 + 所拥有的对象。只有当模式拥有者把其模式上的CREATE权限 + 授予其他人,或者超级用户选择在该模式中创建对象时,才会发生这种情况。 + + + + IF NOT EXISTS选项是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_sequence.sgml b/zh/9.6/ref/create_sequence.sgml new file mode 100644 index 00000000..4f140611 --- /dev/null +++ b/zh/9.6/ref/create_sequence.sgml @@ -0,0 +1,340 @@ + + + + + CREATE SEQUENCE + + + + CREATE SEQUENCE + 7 + SQL - 语言语句 + + + + CREATE SEQUENCE + 定义一个新的序列发生器 + + + + +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] name [ INCREMENT [ BY ] increment ] + [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ] + [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ] + [ OWNED BY { table_name.column_name | NONE } ] + + + + + + 描述 + + + CREATE SEQUENCE创建一个新的序列号发生器。这会创建并 + 初始化一个名为name的特殊单行表。该发生器归发出该命令 + 的用户所有。 + + + + 如果给定了模式名,则序列会在指定模式中创建。否则,它会在当前模式中 + 创建。临时序列存在于一个特殊模式中,因此创建临时序列时不能指定模式 + 名。序列名必须不同于同一模式中任何其他关系(表、序列、索引、视图、 + 物化视图或外部表)的名称。 + + + + 创建序列后,可以使用函数 + nextval、 + currval以及 + setval来操作该序列。这些函数的文档见 + 。 + + + + 虽然不能直接更新序列,但可以使用如下查询: + + +SELECT * FROM name; + + + 来查看序列的参数和当前状态。特别是,序列的 + last_value字段会显示任一会话最近分配的值。(当然, + 如果其他会话正在调用nextval,那么到该值被打印出来 + 时,它可能已经过时。) + + + + + 参数 + + + + TEMPORARYTEMP + + + 如果指定该项,则该序列对象只为当前会话创建,并会在会话退出时自动 + 删除。临时序列存在期间,已有的同名永久序列在本会话中不可见,除非 + 使用模式限定名来引用它们。 + + + + + + IF NOT EXISTS + + + 如果同名关系已经存在,则不会抛出错误,而是发出一条提示。请注意,不能保证现有关系与本来要创建的序列有任何相似之处——它甚至可能根本不是序列。 + + + + + + name + + + 如果同名关系已经存在,则不抛出错误。在这种情况下会发出一个提示。 + 注意,这并不保证现有关系与本应创建出的序列有任何相似之处 — + 它甚至可能根本不是序列。 + + + + + + increment + + + 可选子句INCREMENT BY increment指定加到当前序列值上以生成新值的增量。正值生成升序序列,负值生成降序序列。默认值为 1。 + + + + + + minvalue + NO MINVALUE + + + + 可选子句MINVALUE minvalue确定序列可生成的 + 最小值。如果未提供此子句,或者指定了 + ,则使用默认值。升序序列和降序序列的 + 默认值分别为 1 和 -263-1。 + + + + + + maxvalue + NO MAXVALUE + + + + 可选子句MAXVALUE maxvalue确定序列的最大值。 + 如果未提供此子句,或者指定了 + ,则使用默认值。升序序列和降序序列的 + 默认值分别为 263-1 和 -1。 + + + + + + start + + + 可选子句START WITH start 允许序列从任意值开始。升序序列的默认起始值为minvalue,降序序列则为maxvalue。 + + + + + + cache + + + 可选子句CACHE cache指定要预分配并存储在内存中以加快访问的序列号数量。最小值为 1(一次只能生成一个值,即不缓存),这也是默认值。 + + + + + + CYCLE + NO CYCLE + + + + CYCLE选项允许序列在升序序列达到maxvalue、或降序序列达到minvalue时分别回绕。如果达到该限制, + 下一个生成的数字将分别是minvaluemaxvalue。 + + + + 如果指定NO CYCLE,则当序列达到其限值后,任何对 + nextval的调用都会返回错误。如果既未指定 + CYCLE也未指定NO CYCLE,则 + 默认使用NO CYCLE。 + + + + + + OWNED BY table_name.column_name + OWNED BY NONE + + + OWNED BY选项使序列与特定表列关联,从而在删除该列(或整个表)时,也自动删除该序列。指定表必须与序列具有相同的所有者,并且位于同一模式中。默认值OWNED BY NONE表示不存在这种关联。 + + + + + + + + + 注解 + + + 使用DROP SEQUENCE删除序列。 + + + + 序列基于bigint算术,因此其范围不能超过八字节整数的范围 + (-9223372036854775808 到 9223372036854775807)。 + + + + 由于nextvalsetval调用从不回滚, + 如果需要对序列号进行无间隙分配,就不能使用序列对象。 + 可以通过对包含计数器的表加排他锁来构造无间隙分配;但这种方案比序列 + 对象昂贵得多,尤其是在许多事务需要并发获取序列号时。 + + + + 如果对一个将由多个会话并发使用的序列对象设置了大于 1 的cache,则可能得到意想不到的结果。 + 每个会话会在一次访问该序列对象时分配并缓存连续的序列值,并相应增加 + 该序列对象的last_value。随后,该会话中接下来的 + cache-1 次nextval + 调用会直接返回预分配的值,而不接触序列对象。因此,任何在会话中已分配 + 但未使用的数字都会在会话结束时丢失,从而在序列中造成空洞。 + + + + 此外,虽然多个会话保证会分配到互不相同的序列值,但从所有会话整体来看, + 这些值的生成顺序可能会乱序。例如,当cache设置为 10 时,会话 A 可能保留值 + 1..10 并返回nextval=1;然后会话 B 可能保留值 + 11..20 并返回nextval=11,而此时会话 A 尚未生成 + nextval=2。因此,当cache设置为 1 时,可以安全地假定 + nextval值按顺序生成;当cache设置大于 1 时,只应假定 + nextval值彼此不同,而不应假定它们严格按顺序生成。 + 此外,last_value会反映任一会话最近保留的值,不管该值 + 是否已经由nextval返回。 + + + + 另一个需要注意的问题是,在这种序列上执行的setval + 不会立刻被其他会话察觉,直到它们用完自己缓存的所有预分配值为止。 + + + + + + 示例 + + + 创建一个名为serial的升序序列,从 101 开始: + +CREATE SEQUENCE serial START 101; + + + + + 从该序列中取出下一个数字: + +SELECT nextval('serial'); + + nextval +--------- + 101 + + + + + 从该序列中取出下一个数字: + +SELECT nextval('serial'); + + nextval +--------- + 102 + + + + + 在INSERT命令中使用该序列: + +INSERT INTO distributors VALUES (nextval('serial'), 'nothing'); + + + + + 在一次COPY FROM之后更新序列值: + +BEGIN; +COPY distributors FROM 'input_file'; +SELECT setval('serial', max(id)) FROM distributors; +END; + + + + + + 兼容性 + + + CREATE SEQUENCE符合SQL + 标准,但下列情况除外: + + + + 不支持标准的AS data_type表达式。 + + + + + 获取下一个值是使用nextval()函数,而不是标准的 + NEXT VALUE FOR表达式完成的。 + + + + + OWNED BY子句是PostgreSQL扩展。 + + + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/create_server.sgml b/zh/9.6/ref/create_server.sgml new file mode 100644 index 00000000..78427f0e --- /dev/null +++ b/zh/9.6/ref/create_server.sgml @@ -0,0 +1,180 @@ + + + + + CREATE SERVER + + + + CREATE SERVER + 7 + SQL - 语言语句 + + + + CREATE SERVER + 定义一个新的外部服务器 + + + + +CREATE SERVER [IF NOT EXISTS] server_name [ TYPE 'server_type' ] [ VERSION 'server_version' ] + FOREIGN DATA WRAPPER fdw_name + [ OPTIONS ( option 'value' [, ... ] ) ] + + + + + + 描述 + + + + + CREATE SERVER定义一个新的外部服务器。 + 定义该服务器的用户将成为其拥有者。 + + + + + + 外部服务器通常封装外部数据包装器用来访问外部数据资源的连接信息。 + 还可以通过用户映射指定额外的、特定于用户的连接信息。 + + + + + + 服务器名称在数据库内必须唯一。 + + + + + + 要创建服务器,必须在所使用的外部数据包装器上具有USAGE权限。 + + + + + + 参数 + + + + + + server_name + + + + 要创建的外部服务器的名称。 + + + + + + + server_type + + + + 可选的服务器类型,可能对外部数据包装器有用。 + + + + + + + server_version + + + + 可选的服务器版本,可能对外部数据包装器有用。 + + + + + + + fdw_name + + + + 管理该服务器的外部数据包装器的名称。 + + + + + + + OPTIONS ( option 'value' [, ... ] ) + + + + 这个子句指定服务器的选项。这些选项通常定义该服务器的连接细节, + 但实际的名称和值取决于该服务器的外部数据包装器。 + + + + + + + + + 注解 + + + 在使用模块时,可以将外部服务器的名称用作 + 函数的一个参数,以指示连 + 接参数。要以这种方式使用它,必须在该外部服务器上具有 + USAGE权限。 + + + + + + 示例 + + + + + 创建一个使用外部数据包装器postgres_fdw + 的服务器myserver: + +CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432'); + + 详见。 + + + + + + + 兼容性 + + + + + CREATE SERVER符合 ISO/IEC 9075-9 (SQL/MED)。 + + + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_table.sgml b/zh/9.6/ref/create_table.sgml new file mode 100644 index 00000000..7890e103 --- /dev/null +++ b/zh/9.6/ref/create_table.sgml @@ -0,0 +1,1166 @@ + + + + + CREATE TABLE + + + + CREATE TABLE + 7 + SQL - 语言语句 + + + + CREATE TABLE + 定义一个新表 + + + + +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [ + { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ] + | table_constraint + | LIKE source_table [ like_option ... ] } + [, ... ] +] ) +[ INHERITS ( parent_table [, ... ] ) ] +[ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] +[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] +[ TABLESPACE tablespace_name ] + +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name + OF type_name [ ( + { column_name WITH OPTIONS [ column_constraint [ ... ] ] + | table_constraint } + [, ... ] +) ] +[ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] +[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] +[ TABLESPACE tablespace_name ] + +其中column_constraint为: + +[ CONSTRAINT constraint_name ] +{ NOT NULL | + NULL | + CHECK ( expression ) [ NO INHERIT ] | + DEFAULT default_expr | + UNIQUE index_parameters | + PRIMARY KEY index_parameters | + REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] + [ ON DELETE action ] [ ON UPDATE action ] } +[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + +table_constraint为: + +[ CONSTRAINT constraint_name ] +{ CHECK ( expression ) [ NO INHERIT ] | + UNIQUE ( column_name [, ... ] ) index_parameters | + PRIMARY KEY ( column_name [, ... ] ) index_parameters | + EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] | + FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] + [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON UPDATE action ] } +[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + +like_option为: + +{ INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } + +UNIQUEPRIMARY KEYEXCLUDE约束中的index_parameters为: + +[ WITH ( storage_parameter [= value] [, ... ] ) ] +[ USING INDEX TABLESPACE tablespace_name ] + +EXCLUDE约束中的exclude_element为: + +{ column_name | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] + + + + + + 描述 + + + CREATE TABLE 将在当前数据库中创建一个新的、初始为空的表。该表归发出该命令的用户所有。 + + + + 如果给出了模式名(例如 CREATE TABLE + myschema.mytable ...),则表将在指定模式中创建。 + 否则,它将在当前模式中创建。临时表存在于一个特殊模式中,因此创建临时表时不能给出模式名。 + 表名必须与同一模式中任何其他表、序列、索引、视图或外部表的名称不同。 + + + + CREATE TABLE 还会自动创建一种数据类型,用以表示与该表一行对应的复合类型。因此,表名不能与同一模式中任何已有数据类型同名。 + + + + 可选的约束子句指定插入或更新要成功时,新行或更新后的行必须满足的约束(测试)。约束是一种 SQL 对象,可用多种方式帮助定义表中允许的值集合。 + + + + 定义约束有两种方式:表约束和列约束。列约束作为列定义的一部分定义。表约束则不绑定到特定列,并且可以涵盖多个列。每个列约束也都可以写成表约束;当约束只影响一列时,列约束只是一种书写上的方便。 + + + + 要创建表,必须分别对所有列类型或 OF 子句中的类型拥有 USAGE 权限。 + + + + + 参数 + + + + + TEMPORARYTEMP + + + 如果指定该选项,表将创建为临时表。 + 临时表会在会话结束时自动删除,或者也可在当前事务结束时删除(见下文 ON COMMIT)。 + 在临时表存在期间,同名的现有永久表对当前会话不可见,除非使用带模式限定的名称引用它们。 + 在临时表上创建的任何索引也都会自动成为临时索引。 + + + + 自动清理守护进程不能访问并且因此也不能清理或分析临时表。由于这个原因,应该通过会话的 SQL 命令执行合适的清理和分析操作。例如,如果一个临时表将要被用于复杂的查询,最好在把它填充完毕后在其上运行ANALYZE。 + + + + 可以在 TEMPORARYTEMP 前写 GLOBALLOCAL。这在当前的 PostgreSQL 中没有区别,而且已弃用;见下文 。 + + + + + + UNLOGGED + + + 如果指定该选项,表将创建为不记录 WAL 的表。写入不记录 WAL 的表的数据不会写入预写式日志(见 ),因此它们比普通表快得多。不过,它们不具备崩溃安全性:在崩溃或非正常关闭后,不记录 WAL 的表会被自动截断。不记录 WAL 的表的内容也不会复制到备库。在不记录 WAL 的表上创建的任何索引也都会自动成为不记录 WAL 的。 + + + + + + IF NOT EXISTS + + + 如果已存在同名关系,则不抛出错误,而是发出一条提示。注意,这并不保证现有关系与本应创建出的关系有任何相似之处。 + + + + + + table_name + + + 要创建的表名(可选地带模式限定)。 + + + + + + OF type_name + + 创建一个类型化表,其结构取自指定的复合类型(名称可以带模式限定)。类型化表与其类型绑定;例如,如果删除该类型(使用DROP TYPE ... CASCADE),该表也会被删除。 + + 创建类型化表时,列的数据类型由底层复合类型决定,不由CREATE TABLE命令指定。不过,CREATE TABLE命令可以为表添加默认值和约束,并指定存储参数。 + + + + + column_name + + + 要在新表中创建的列名。 + + + + + + data_type + + + 列的数据类型。这可以包括数组说明符。有关 + PostgreSQL 支持的数据类型的更多信息,请参见 。 + + + + + + COLLATE collation + + COLLATE子句为列指定排序规则(该列必须属于支持排序规则的数据类型)。如果未指定,则使用列数据类型的默认排序规则。 + + + + + INHERITS ( parent_table [, ... ] ) + + + + 可选的 INHERITS 子句指定一组表,新表将自动从中继承所有列。 + 父表可以是普通表或外部表。 + + + + 使用 INHERITS 会在新子表与其父表之间建立持久关系。 + 对父表的模式修改通常也会传播到子表,且默认情况下,对父表的扫描会包含子表的数据。 + + + + 如果同一列名出现在多个父表中,除非这些父表中该列的数据类型全部匹配,否则会报错。 + 如果没有冲突,这些重复列会合并为新表中的单个列。 + 如果新表的列名列表中包含一个同样来自继承的列名,其数据类型也必须与继承列匹配,并且列定义会合并为一个。 + 如果新表显式为该列指定了默认值,该默认值会覆盖继承声明中的任何默认值。 + 否则,任何为该列指定默认值的父表都必须指定相同的默认值,否则会报错。 + + + + CHECK 约束基本上也按与列相同的方式合并: + 如果多个父表和/或新表定义中包含同名的 CHECK 约束,则这些约束必须拥有相同的检查表达式,否则会报错。 + 同名且表达式相同的约束将合并为一份。 + 父表中标记为 NO INHERIT 的约束不会被考虑。 + 注意,新表中未命名的 CHECK 约束永远不会被合并,因为系统总会为它选择一个唯一名称。 + + + + 列的 STORAGE 设置也会从父表复制过来。 + + + + + + LIKE source_table [ like_option ... ] + + + LIKE 子句指定一个表,新表会自动从中复制所有列名、数据类型及其非空约束。 + + + 与 INHERITS 不同,新表和原表在创建完成后就完全脱钩了。对原表的修改不会应用到新表,也不可能在扫描原表时包含新表的数据。 + + 只有指定INCLUDING DEFAULTS时,才会复制所复制列定义的默认表达式。默认行为是不包含默认表达式,因此新表中的所复制列将具有空默认值。请注意,复制调用数据库修改函数(例如nextval)的默认值,可能会在原表和新表之间创建功能性关联。 + 非空约束始终会复制到新表。只有指定INCLUDING CONSTRAINTS时,才会复制CHECK约束。列约束和表约束之间不作区分。 + 只有指定INCLUDING INDEXES时,才会在新表上创建原表的索引、PRIMARY KEYUNIQUEEXCLUDE约束。新索引和约束的名称按照默认规则选择,与原名称无关。(此行为可以避免新索引可能发生名称重复错误。) + 只有指定INCLUDING STORAGE时,才会复制所复制列定义的STORAGE设置。默认行为是不包含STORAGE设置,因此新表中复制的列使用其类型特定的默认设置。有关STORAGE设置的更多信息,请参见 + 只有指定INCLUDING COMMENTS时,才会复制所复制列、约束和索引的注释。默认行为是不包含注释,因此新表中复制的列和约束没有注释。 + INCLUDING ALLINCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS的简写形式。 + 请注意,与INHERITS不同,LIKE复制的列和约束不会与同名的列和约束合并。如果显式指定了相同的名称,或在另一个LIKE子句中指定了相同的名称,则会报错。 + + LIKE 子句也可用于从视图、外部表或复合类型复制列定义。不适用的选项(例如从视图复制 INCLUDING INDEXES)会被忽略。 + + + + + + CONSTRAINT constraint_name + + + + 列约束或表约束的可选名称。如果约束被违反,错误消息中会包含该约束名,因此诸如 col must be positive 这样的约束名可以向客户端应用传达有用的约束信息。(若约束名中包含空格,则需要用双引号指定。)如果未指定约束名,系统会生成一个。 + + + + + + NOT NULL + + + 该列不允许包含空值。 + + + + + + NULL + + + + 该列允许包含空值。这是默认情况。 + + + + 该子句仅为兼容非标准 SQL 数据库而提供,不建议在新应用中使用。 + + + + + + CHECK ( expression ) [ NO INHERIT ] + + + + CHECK 子句指定一个产生布尔结果的表达式。要使插入或更新成功,新行或更新后的行必须满足该表达式。计算结果为 TRUE 或 UNKNOWN 的表达式视为成功。如果插入或更新操作中的任何一行得到 FALSE 结果,就会抛出错误异常,并且插入或更新不会修改数据库。作为列约束指定的检查约束只应引用该列的值,而出现在表约束中的表达式可以引用多个列。 + + + + 当前,CHECK 表达式不能包含子查询,也不能引用当前行的列之外的变量(参见 )。可以引用系统列 tableoid,但不能引用其他系统列。 + + + + 标记为 NO INHERIT 的约束不会传播到子表。 + + + + 当一个表有多个 CHECK 约束时,在检查完 NOT NULL 约束之后,会按名称的字母顺序对每一行进行检查。(9.5 之前的 PostgreSQL 版本并不保证 CHECK 约束的特定触发顺序。) + + + + + + DEFAULT + default_expr + + DEFAULT子句为其所在列定义的列指定默认数据值。该值可以是任何不含变量的表达式(不允许子查询,也不允许交叉引用当前表中的其他列)。默认表达式的数据类型必须与该列的数据类型匹配。 + + + 默认值表达式会用于任何未为该列指定值的插入操作。如果一列没有默认值,则默认值为 null。 + + + + + + + UNIQUE(列约束) + UNIQUE ( column_name [, ... ] )(表约束) + + + + UNIQUE 约束指定表中一列或多列组成的一组只能包含唯一值。 + 表级唯一约束的行为与列级唯一约束相同,只是它还能跨越多列。因此,该约束 + 要求任意两行在这些列中至少有一列不同。 + + + 对于唯一约束,空值不被视为相等。 + + + 每个唯一约束都应引用一组列,这组列应不同于该表上任何其他唯一约束或 + 主键约束所引用的列集合。(否则,冗余的唯一约束将被丢弃。) + + + + + + PRIMARY KEY(列约束) + PRIMARY KEY ( column_name [, ... ] )(表约束) + + + PRIMARY KEY 约束指定表的一列或多列只能包含唯一 + (不重复)且非空的值。无论作为列约束还是表约束,一个表都只能指定一个 + 主键。 + + + + 主键约束所引用的列集合应不同于同一表上定义的任何唯一约束所引用的列集 + 合。(否则,该唯一约束是冗余的,会被丢弃。) + + + + PRIMARY KEY 强制的数据约束与 + UNIQUENOT NULL 的组合相同。不 + 过,将一组列标识为主键还会为模式设计提供元数据,因为主键意味着其他表可以 + 将这组列作为行的唯一标识符来依赖。 + + + + 添加 PRIMARY KEY 约束会自动在约束所用的列或列组上创建 + 唯一 B-树索引。 + + + + + EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] + + + EXCLUDE 子句定义一个排他约束。它保证如果任意两行在 + 指定列或表达式上使用指定操作符进行比较,这些比较不会全部返回 + TRUE。如果所有指定操作符都测试相等,这就等价于 + UNIQUE 约束,尽管普通唯一约束会更快。不过,排他约束可 + 以指定比简单相等更一般的约束。例如,你可以通过使用 + && 操作符来指定一个约束,使表中不存在两个包含重 + 叠圆的行(见 )。 + + + 排他约束通过索引实现,因此每个指定的操作符都必须与索引访问方法index_method的适当操作符类关联(见)。这些操作符必须满足交换律。每个exclude_element都可以选择指定操作符类和/或排序选项;详见 + + + 访问方法必须支持 amgettuple(见 + );目前这意味着不能使用 GIN。 + 虽然允许,但在排他约束上使用 B-树或 hash 索引意义不大,因为它们做不到 + 比普通唯一约束更好的事情。因此,实践中访问方法总是 + GiSTSP-GiST。 + + + + predicate 允许你只在表的一个 + 子集上指定排他约束;在内部,这会创建一个部分索引。注意, + predicate 周围的圆括号是必需的。 + + + + + + REFERENCES reftable [ ( refcolumn ) ] [ MATCH matchtype ] [ ON DELETE action ] [ ON UPDATE action ](列约束) + + FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] [ MATCH matchtype ] [ ON DELETE action ] [ ON UPDATE action ](表约束) + + + 这些子句指定外键约束,要求新表的一个或多个列组成的列组只能包含与被引用表某一行的被引用列值匹配的值。如果省略refcolumn列表,则使用reftable的主键。被引用列必须是被引用表中不可延迟的唯一约束或主键约束的列。请注意,不能在临时表和永久表之间定义外键约束。 + + + 插入到引用列中的值会按照给定的匹配类型,与被引用表及其被引用列中的值进 + 行匹配。共有三种匹配类型:MATCH FULL、 + MATCH PARTIALMATCH SIMPLE + (默认值)。MATCH FULL 不允许多列外键中的某一列为 + 空,除非所有外键列都为空;如果它们都为空,则不要求该行在被引用表中有匹 + 配行。MATCH SIMPLE 允许任意外键列为空;如果其中任何一 + 列为空,则不要求该行在被引用表中有匹配行。 + MATCH PARTIAL 目前尚未实现。(当然,可以对引用列应用 + NOT NULL 约束,以防止出现这些情况。) + + + 此外,当被引用列中的数据发生变化时,会对本表列中的数据执行某些操作。ON DELETE子句指定删除被引用表中的被引用行时要执行的操作。同样,ON UPDATE子句指定将被引用表中的被引用列更新为新值时要执行的操作。如果行被更新,但被引用列实际上没有变化,则不执行任何操作。除NO ACTION检查以外的引用操作都不能延迟,即使该约束声明为可延迟也是如此。每个子句可以指定以下操作: + + NO ACTION + + 产生错误,指出删除或更新会违反外键约束。如果该约束被延迟,则会在约束检查时仍存在引用行的情况下产生这个错误。这是默认操作。 + + + + + RESTRICT + + 产生错误,指出删除或更新会违反外键约束。这与NO ACTION相同,但检查不能延迟。 + + + + + CASCADE + + + 分别删除任何引用已删除行的行,或将引用列的值更新为被引用列的新值。 + + + + + + SET NULL + + 将引用列设置为空值。 + + + + + SET DEFAULT + + 将引用列设置为其默认值。(如果默认值不为空,则被引用表中必须存在与这些默认值匹配的行,否则操作会失败。) + + + + + + 如果被引用列经常变化,可以考虑在引用列上添加索引,使与外键约束关联的引用操作能够更高效地执行。 + + + + + DEFERRABLE + NOT DEFERRABLE + + 这控制约束是否可以延迟。不可延迟的约束会在每条命令之后立即检查。可延迟约束的检查可以推迟到事务结束(使用命令)。NOT DEFERRABLE是默认值。目前,只有UNIQUEPRIMARY KEYEXCLUDEREFERENCES(外键)约束接受此子句。NOT NULLCHECK约束不可延迟。请注意,不能将可延迟约束用作包含ON CONFLICT DO UPDATE子句的INSERT语句中的冲突仲裁器。 + + + + + INITIALLY IMMEDIATE + INITIALLY DEFERRED + + 如果约束可延迟,则此子句指定检查约束的默认时间。如果约束为INITIALLY IMMEDIATE,则在每条语句之后检查。这是默认值。如果约束为INITIALLY DEFERRED,则仅在事务结束时检查。可以使用命令更改约束检查时间。 + + + + + WITH ( storage_parameter [= value] [, ... ] ) + + 该子句为表或索引指定可选的存储参数;详情见。表的WITH子句还可以包含OIDS=TRUE(或仅写OIDS),以指定为新表的行分配 OID(对象标识符);也可以包含OIDS=FALSE,以指定行不应具有 OID。如果未指定OIDS,默认设置取决于配置参数。(如果新表继承自任何具有 OID 的表,则会强制使用OIDS=TRUE,即使命令指定了OIDS=FALSE也是如此。) + + 如果显式或隐式指定了OIDS=FALSE,新表将不存储 OID,也不会为插入其中的行分配 OID。通常认为这样做是值得的,因为它会减少 OID 的消耗,从而推迟 32 位 OID 计数器回卷。一旦计数器回卷,就不能再假定 OID 是唯一的,这会大大降低它们的用途。此外,不在表中包含 OID 可以减少在磁盘上存储该表所需的空间,在大多数机器上每行可减少 4 字节,从而略微提高性能。 + + 要在表创建后移除其 OID,请使用 + + + + + WITH OIDS + WITHOUT OIDS + + 这些是过时的语法,分别等价于WITH (OIDS)WITH (OIDS=FALSE)。如果要同时指定OIDS设置和存储参数,必须使用WITH ( ... )语法;见上文。 + + + + + ON COMMIT + + + 可以使用 ON COMMIT 控制临时表在事务块结束时的行为。三种 + 选项如下: + + + + PRESERVE ROWS + + + 在事务结束时不执行任何特殊操作。这是默认行为。 + + + + + + DELETE ROWS + + 临时表中的所有行都会在每个事务块结束时删除。实际上,每次提交时都会自动执行一次 + + + + + DROP + + + 在当前事务块结束时删除临时表。 + + + + + + + + + TABLESPACE tablespace_name + + tablespace_name是要创建新表的表空间名称。如果未指定,则查询;如果表是临时表,则查询 + + + + + USING INDEX TABLESPACE tablespace_name + + + + 该子句允许选择与 UNIQUEPRIMARY KEY + 或 EXCLUDE 约束相关联的索引要创建在哪个表空间中。若未 + 指定,则参考 ;如果该表是临时表, + 则参考 。 + + + + + + + + 存储参数 + + + 存储参数 + + + WITH子句可以为表以及与UNIQUEPRIMARY KEYEXCLUDE约束关联的索引指定存储参数。索引的存储参数记载于。当前可用于表的存储参数列在下面。对于其中许多参数,如下所示,还存在一个同名但带有toast.前缀的附加参数,用于控制表的二级TOAST表(如果有)的行为(有关 TOAST 的更多信息请参见)。如果设置了表参数值而未设置等效的toast.参数,则 TOAST 表会使用表参数的值。 + + + + + fillfactor (integer) + + 表的填充因子是 10 到 100 之间的百分比。100(完全填充)是默认值。指定较小的填充因子时,INSERT操作只将表页填充到指定百分比;每页的剩余空间保留用于更新该页上的行。这样,UPDATE就有机会将行的更新副本放在与原行相同的页面上,这比放在不同页面上更高效。对于从不更新其条目的表,完全填充是最佳选择;但对于频繁更新的表,适合使用较小的填充因子。不能为 TOAST 表设置此参数。 + + + + + parallel_workers (integer) + + 这设置用于协助并行扫描该表的工作进程数量。如果未设置,系统将根据关系大小确定一个值。规划器选择的实际工作进程数量可能更少,例如由于的设置。 + + + + + autovacuum_enabled, toast.autovacuum_enabled (boolean) + + + + 为特定表启用或禁用自动清理守护进程。如果为真,自动清理守护进程将按照 + 中讨论的规则,在该表上执行自动 + VACUUM 和/或 ANALYZE 操作。如果为 + 假,则该表不会被自动清理,但为了防止事务 ID 回卷,仍可能对其执行自动清 + 理。有关回卷防护的更多信息,见 。 + 注意,如果 参数为假,则自动清理守护进程 + 根本不会运行(防止事务 ID 回卷的情况除外);为单独表设置存储参数也不会 + 覆盖这一点。因此,显式将此存储参数设为 true 往往意义不 + 大,设为 false 才更有用。 + + + + + + autovacuum_vacuum_threshold, toast.autovacuum_vacuum_threshold (integer) + + + + 参数的每表取值。 + + + + + + autovacuum_vacuum_scale_factor, toast.autovacuum_vacuum_scale_factor (floating point) + + + + 参数的每表取值。 + + + + + + autovacuum_analyze_threshold (integer) + + + + 参数的每表取值。 + + + + + + autovacuum_analyze_scale_factor (floating point) + + + + 参数的每表取值。 + + + + + + autovacuum_vacuum_cost_delay, toast.autovacuum_vacuum_cost_delay (integer) + + + + 参数的每表取值。 + + + + + + autovacuum_vacuum_cost_limit, toast.autovacuum_vacuum_cost_limit (integer) + + + + 参数的每表取值。 + + + + + + autovacuum_freeze_min_age, toast.autovacuum_freeze_min_age (integer) + + + + 参数的每表取值。注意,自动清理 + 会忽略大于系统范围 设置一 + 半的每表 autovacuum_freeze_min_age 参数。 + + + + + + autovacuum_freeze_max_age, toast.autovacuum_freeze_max_age (integer) + + + + 参数的每表取值。注意,自 + 动清理会忽略大于系统范围设置的每表 + autovacuum_freeze_max_age 参数(它只能设置得更小)。 + + + + + + autovacuum_freeze_table_age, toast.autovacuum_freeze_table_age (integer) + + + + 参数的每表取值。 + + + + + + autovacuum_multixact_freeze_min_age, toast.autovacuum_multixact_freeze_min_age (integer) + + + + 参数的每表取值。注意, + 自动清理会忽略大于系统范围 + 设置一半的每表 + autovacuum_multixact_freeze_min_age 参数。 + + + + + + autovacuum_multixact_freeze_max_age, toast.autovacuum_multixact_freeze_max_age (integer) + + + + 参数的每表取值。注 + 意,自动清理会忽略大于系统范围设置的每表 + autovacuum_multixact_freeze_max_age 参数(它只能设置得更 + 小)。 + + + + + + autovacuum_multixact_freeze_table_age, toast.autovacuum_multixact_freeze_table_age (integer) + + + + 参数的每表取值。 + + + + + + log_autovacuum_min_duration, toast.log_autovacuum_min_duration (integer) + + + + 参数的每表取值。 + + + + + + user_catalog_table (boolean) + + + + 将该表声明为逻辑复制用途的附加目录表。详见 + 。不能为 TOAST 表设置此参数。 + + + + + + + + + + + 注解 + + 不建议在新应用中使用 OID:在可能的情况下,优先使用SERIAL或其他序列生成器作为表的主键。不过,如果应用确实使用 OID 来标识表中的特定行,建议在该表的oid列上创建唯一约束,以确保即使计数器回卷,表中的 OID 也确实能唯一标识行。不要假定 OID 在不同表之间唯一;如果需要数据库范围的唯一标识符,请组合使用tableoid和行 OID。 + + + 对于没有主键的表,不建议使用OIDS=FALSE,因为既没有 OID,也没有唯一数据键时,很难标识特定行。 + + + + PostgreSQL为每一个唯一约束和主键约束自动创建一个索引来强制唯一性。因此,没有必要显式地为主键列创建一个索引(详见)。 + + + + 在当前的实现中,唯一约束和主键不会被继承。这使得继承与唯一约束的组合相当不实用。 + + + + 一个表不能有超过 1600 列(实际上,由于元组长度限制,有效的限制通常更低)。 + + + + + + + 示例 + + + 创建表films和表distributors: + + +CREATE TABLE films ( + code char(5) CONSTRAINT firstkey PRIMARY KEY, + title varchar(40) NOT NULL, + did integer NOT NULL, + date_prod date, + kind varchar(10), + len interval hour to minute +); + +CREATE TABLE distributors ( + did integer PRIMARY KEY DEFAULT nextval('serial'), + name varchar(40) NOT NULL CHECK (name <> '') +); + + + + + 创建一个带二维数组列的表: + + +CREATE TABLE array_int ( + vector int[][] +); + + + + + 为表films定义一个唯一表约束。唯一表约束可以定义在表的一列或多列上: + + +CREATE TABLE films ( + code char(5), + title varchar(40), + did integer, + date_prod date, + kind varchar(10), + len interval hour to minute, + CONSTRAINT production UNIQUE(date_prod) +); + + + + + 定义一个列检查约束: + + +CREATE TABLE distributors ( + did integer CHECK (did > 100), + name varchar(40) +); + + + + + 定义一个表检查约束: + + +CREATE TABLE distributors ( + did integer, + name varchar(40), + CONSTRAINT con1 CHECK (did > 100 AND name <> '') +); + + + + + 为表films定义一个主键表约束: + + +CREATE TABLE films ( + code char(5), + title varchar(40), + did integer, + date_prod date, + kind varchar(10), + len interval hour to minute, + CONSTRAINT code_title PRIMARY KEY(code,title) +); + + + + + 为表distributors定义一个主键约束。下面的两个示例是等价的,第一个使用表约束语法,第二个使用列约束语法: + + +CREATE TABLE distributors ( + did integer, + name varchar(40), + PRIMARY KEY(did) +); + +CREATE TABLE distributors ( + did integer PRIMARY KEY, + name varchar(40) +); + + + + + 为列name指定一个字面常量默认值,将列did的默认值设为从某个序列对象中取下一个值,并让modtime的默认值为插入该行的时间: + + +CREATE TABLE distributors ( + name varchar(40) DEFAULT 'Luso Films', + did integer DEFAULT nextval('distributors_serial'), + modtime timestamp DEFAULT current_timestamp +); + + + + + 在表distributors上定义两个NOT NULL列约束,其中一个显式指定了名称: + + +CREATE TABLE distributors ( + did integer CONSTRAINT no_null NOT NULL, + name varchar(40) NOT NULL +); + + + + + 为name列定义一个唯一约束: + + +CREATE TABLE distributors ( + did integer, + name varchar(40) UNIQUE +); + + + 同样的唯一约束用表约束指定: + + +CREATE TABLE distributors ( + did integer, + name varchar(40), + UNIQUE(name) +); + + + + + 创建同样的表,并为该表及其唯一索引都指定 70% 的填充因子: + + +CREATE TABLE distributors ( + did integer, + name varchar(40), + UNIQUE(name) WITH (fillfactor=70) +) +WITH (fillfactor=70); + + + + + 创建表circles,并添加一个排他约束以防任意两个圆重叠: + + +CREATE TABLE circles ( + c circle, + EXCLUDE USING gist (c WITH &&) +); + + + + + 在表空间diskvol1中创建表cinemas: + + +CREATE TABLE cinemas ( + id serial, + name text, + location text +) TABLESPACE diskvol1; + + + + + 创建一个复合类型和一个类型化表: + +CREATE TYPE employee_type AS (name text, salary numeric); + +CREATE TABLE employees OF employee_type ( + PRIMARY KEY (name), + salary WITH OPTIONS DEFAULT 1000 +); + + + + + + 兼容性 + + + CREATE TABLE 命令符合 SQL 标准,但有下 + 列例外。 + + + + + 临时表 + + + 尽管 CREATE TEMPORARY TABLE 的语法看起来类似于 SQL 标 + 准,但其效果并不相同。按标准,临时表只需定义一次,并会自动存在于每个需 + 要它的会话中(内容初始为空)。而 PostgreSQL 要求 + 每个会话都为每个要使用的临时表发出自己的 + CREATE TEMPORARY TABLE 命令。这使不同会话可以出于不同目 + 的使用相同的临时表名;而标准做法则要求给定临时表名的所有实例都必须具有 + 相同的表结构。 + + + + 标准对临时表行为的定义在实践中被广泛忽略。PostgreSQL + 在这一点上的行为与多种其他 SQL 数据库相似。 + + + + SQL 标准还区分全局和局部临时表,其中局部临时表在每个会话内的每个 SQL 模 + 块中都有独立的内容集合,但其定义仍在多个会话之间共享。由于 + PostgreSQL 不支持 SQL 模块,这一区别在 + PostgreSQL 中没有意义。 + + + + 出于兼容性考虑,PostgreSQL 接受在临时表声明中使 + 用 GLOBALLOCAL 关键字,但它们目前 + 没有效果。不鼓励使用这些关键字,因为未来版本的 + PostgreSQL 可能会采用更符合标准的解释。 + + + + 临时表的 ON COMMIT 子句也与 SQL 标准相似,但存在一些差 + 异。如果省略 ON COMMIT 子句,SQL 规定默认行为是 + ON COMMIT DELETE ROWS。然而, + PostgreSQL 中的默认行为是 + ON COMMIT PRESERVE ROWS。SQL 中不存在 + ON COMMIT DROP 选项。 + + + + + 非延迟唯一性约束 + + + 当 UNIQUEPRIMARY KEY 约束不可延 + 迟时,只要有行被插入或修改,PostgreSQL 就会立刻 + 检查唯一性。SQL 标准规定应只在语句结束时强制唯一性;例如,当单个命令会更 + 新多个键值时,这两者就会产生差异。若要获得符合标准的行为,应将约束声明为 + DEFERRABLE 但不延迟(即 + INITIALLY IMMEDIATE)。注意,这可能明显慢于立即检查唯一 + 性。 + + + + + + 列检查约束 + + + SQL 标准规定,CHECK 列约束只能引用其所作用的列;只有 + CHECK 表约束才能引用多列。 + PostgreSQL 并不强制这一限制;它对列检查约束和表 + 检查约束一视同仁。 + + + + + + <literal>EXCLUDE</literal> 约束 + + + EXCLUDE 约束类型是 PostgreSQL 的扩展。 + + + + + + <literal>NULL</literal> <quote>约束</quote> + + + NULL 约束(实际上并不是约束)是 + PostgreSQL 对 SQL 标准的扩展;提供它是为了 + 与其他一些数据库系统兼容(以及与 NOT NULL 约束保持 + 对称)。由于它本来就是任意列的默认情况,所以它的存在只是噪声。 + + + + + + 继承 + + + 通过 INHERITS 子句实现的多重继承是 PostgreSQL 的语言扩展。SQL:1999 及后续标准使用不同的语法和语义定义了单继承。PostgreSQL 尚不支持 SQL:1999 风格的继承。 + + + + + 零列的表 + + + PostgreSQL 允许创建没有列的表(例如 + CREATE TABLE foo();)。这是对 SQL 标准的扩展,标准不允许 + 零列的表。零列的表本身并不十分有用,但若禁止它们,就会让 + ALTER TABLE DROP COLUMN 出现奇怪的特殊情况,因此忽略这 + 一规范限制看起来更整洁。 + + + + + + + <literal>LIKE</literal> 子句 + + + 虽然 SQL 标准中存在 LIKE 子句,但 + PostgreSQL 接受的许多 LIKE + 选项并不在标准中,而标准中的某些选项又没有被 + PostgreSQL 实现。 + + + + + <literal>WITH</literal> 子句 + + + WITH 子句是 PostgreSQL 的扩 + 展;存储参数和 OID 都不属于标准内容。 + + + + + + 表空间 + + + PostgreSQL 的表空间概念不是标准的一部分。因此, + TABLESPACEUSING INDEX TABLESPACE + 子句都是扩展。 + + + + + 类型化表 + + + 类型化表实现了 SQL 标准的一个子集。按照标准,类型化表除了具有与底层复合 + 类型相对应的列之外,还应有一个额外的自引用列。 + PostgreSQL 不显式支持自引用列,但使用 OID 功能可以达到相同的效果。 + + + + + + + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/create_table_as.sgml b/zh/9.6/ref/create_table_as.sgml new file mode 100644 index 00000000..2fe8b076 --- /dev/null +++ b/zh/9.6/ref/create_table_as.sgml @@ -0,0 +1,295 @@ + + + + + CREATE TABLE AS + + + + CREATE TABLE AS + 7 + SQL - 语言语句 + + + + CREATE TABLE AS + 根据查询结果定义一个新表 + + + + +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name + [ (column_name [, ...] ) ] + [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] + [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] + [ TABLESPACE tablespace_name ] + AS query + [ WITH [ NO ] DATA ] + + + + + 描述 + + + CREATE TABLE AS创建一个表,并用 + SELECT命令计算得到的数据填充该表。 + 该表的列具有与SELECT输出列对应的名称和数据类型 + (但可以通过显式给出新的列名列表来覆盖列名)。 + + + + CREATE TABLE AS与创建视图有些 + 相似,但实际上差异很大:它会创建一个新表,并且只执行一次该查询 + 来完成新表的初始填充。这个新表不会跟踪该查询所引用源表的后续变 + 化。 + 相比之下,视图每次被查询时都会重新执行其定义中的 + SELECT语句。 + + + + + 参数 + + + + GLOBALLOCAL + + + + 出于兼容性考虑,这些关键字会被忽略。它们的用法已废弃;详见 + 。 + + + + + + + + TEMPORARYTEMP + + + + 如果指定,将把该表创建为临时表。详见 + 。 + + + + + + UNLOGGED + + + + 如果指定,该表将创建为不记录 WAL 的表。详见 + 。 + + + + + + IF NOT EXISTS + + + + 如果已存在同名关系,则不抛出错误。这种情况下会发出一条提示。 + 详情请参见。 + + + + + + table_name + + + + 要创建的表名(可选地带模式限定)。 + + + + + + column_name + + + + 新表中某一列的名称。如果未提供列名,则采用查询输出列的列名。 + + + + + + WITH ( storage_parameter [= value] [, ... ] ) + + 此子句为新表指定可选存储参数;有关更多信息,请参见WITH子句还可以包含OIDS=TRUE(或仅包含OIDS),以指定应为新表的行分配 OID(对象标识符),或者包含OIDS=FALSE,以指定这些行不应有 OID。更多信息请参见 + + + + + WITH OIDS + WITHOUT OIDS + + 这些是分别等同于WITH (OIDS)WITH (OIDS=FALSE)的过时语法。如果希望同时指定OIDS设置和存储参数,则必须使用WITH ( ... )语法;见上文。 + + + + + ON COMMIT + + 临时表在事务块结束时的行为可以使用以下选项控制:ON COMMIT。三个选项是: + + PRESERVE ROWS + + + 在事务结束时不采取特殊动作。这是默认行为。 + + + + + + DELETE ROWS + + 临时表中的所有行将在每个事务块结束时删除。本质上,每次提交时都会执行一次自动的 + + + + + DROP + + + 临时表会在当前事务块结束时被删除。 + + + + + + + + + TABLESPACE tablespace_name + + tablespace_name是要创建新表的表空间名称。如果未指定,则查询;如果表是临时表,则查询 + + + + + query + + 可以是TABLE或命令,也可以是运行已预备的SELECTTABLEVALUES查询的命令。 + + + + + WITH [ NO ] DATA + + + + 这个子句指定是否将查询产生的数据复制到新表中。如果不复制,则只 + 复制表结构。默认会复制数据。 + + + + + + + + + 注解 + + + 这个命令在功能上类似于,但更推荐 + 使用它,因为它更不容易与SELECT INTO语法的其 + 他用法相混淆。此外,CREATE TABLE AS提供的功能 + 是SELECT INTO所提供功能的超集。 + + + CREATE TABLE AS命令允许用户显式指定是否包含 OID。如果未显式指定是否存在 OID,则使用配置变量。 + + + + 示例 + + + 创建一个新表films_recent,其中只包含表 + films中的近期条目: + + +CREATE TABLE films_recent AS + SELECT * FROM films WHERE date_prod >= '2002-01-01'; + + + + + 要完整复制一个表,也可以使用TABLE命令的简写 + 形式: + + +CREATE TABLE films2 AS + TABLE films; + + + + 创建一个新的临时表films_recent,其中只包含来自以下表的近期条目:films,使用预备语句。新表具有 OID,并将在提交时删除: +PREPARE recentfilms(date) AS + SELECT * FROM films WHERE date_prod > $1; +CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS + EXECUTE recentfilms('2002-01-01'); + + + + + 兼容性 + + + CREATE TABLE AS符合SQL标准。以下是非标准扩展: + + + 标准要求在子查询子句外围加括号;在 + PostgreSQL中,这些括号是可选的。 + + + + + + 在标准中,WITH [ NO ] DATA子句是必需的; + 在 PostgreSQL 中它是可选的。 + + + + + PostgreSQL处理临时表的方式与标 + 准有相当大的不同;详见。 + + + + + WITH子句是PostgreSQL扩展;标准中既没有存储参数,也没有 OID。 + + + + + PostgreSQL的表空间概念不是标准的一 + 部分。因此,TABLESPACE子句是一种扩展。 + + + + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_tablespace.sgml b/zh/9.6/ref/create_tablespace.sgml new file mode 100644 index 00000000..89a08e7b --- /dev/null +++ b/zh/9.6/ref/create_tablespace.sgml @@ -0,0 +1,167 @@ + + + + + CREATE TABLESPACE + + + + CREATE TABLESPACE + 7 + SQL - 语言语句 + + + + CREATE TABLESPACE + 定义一个新表空间 + + + + +CREATE TABLESPACE tablespace_name + [ OWNER { new_owner | CURRENT_USER | SESSION_USER } ] + LOCATION 'directory' + [ WITH ( tablespace_option = value [, ... ] ) ] + + + + + + 描述 + + + + + CREATE TABLESPACE注册一个新的集簇级别的表空间。 + 该表空间的名称必须不同于数据库集簇中任何现有表空间的名称。 + + + + + + 表空间允许超级用户在文件系统中定义一个替代位置,使数据库对象 + (如表和索引)的数据文件可以存放在那里。 + + + + + + 具有适当权限的用户可以将 + tablespace_name传递给 + CREATE DATABASECREATE TABLE、 + CREATE INDEXADD CONSTRAINT, + 从而将这些对象的数据文件存储在指定的表空间中。 + + + + + + + 表空间不能脱离定义它的集簇单独使用;见 + 。 + + + + + + + + 参数 + + + + tablespace_name + + + + 要创建的表空间名称。名称不能以pg_开头,因为 + 这类名称保留给系统表空间使用。 + + + + + + + user_name + + + + 将成为该表空间所有者的用户名。若省略,则默认为执行该命令的用 + 户。只有超级用户可以创建表空间,但他们可以将表空间的所有权赋予 + 非超级用户。 + + + + + + + directory + + 将用于表空间的目录。该目录应为空,并且必须归PostgreSQL系统用户所有。必须使用绝对路径名指定该目录。 + + + + + tablespace_option + + 要设置或重置的表空间参数。目前唯一可用的参数是seq_page_costrandom_page_costeffective_io_concurrency。为特定表空间设置其中任一值,会覆盖规划器根据同名配置参数(请参见)建立的、读取该表空间中表页面的通常代价估计。如果一个表空间位于比 I/O 子系统其余部分更快或更慢的磁盘上,这可能很有用。 + + + + + + + 注解 + + 表空间仅在支持符号链接的系统上受支持。 + + + CREATE TABLESPACE不能在事务块内执行。 + + + + + 示例 + + 创建一个表空间dbspace,位置为/data/dbs: + +CREATE TABLESPACE dbspace LOCATION '/data/dbs'; + + + + 创建一个表空间indexspace,位置为/data/indexes,所有者为用户genevieve: + +CREATE TABLESPACE indexspace OWNER genevieve LOCATION '/data/indexes'; + + + + + + 兼容性 + + + + + CREATE TABLESPACE是一种PostgreSQL扩展。 + + + + + + + 另见 + + + + + + + + + + + + diff --git a/zh/9.6/ref/create_transform.sgml b/zh/9.6/ref/create_transform.sgml new file mode 100644 index 00000000..1f43a984 --- /dev/null +++ b/zh/9.6/ref/create_transform.sgml @@ -0,0 +1,218 @@ + + + + + CREATE TRANSFORM + + + + CREATE TRANSFORM + 7 + SQL - 语言语句 + + + + CREATE TRANSFORM + 定义一个新的转换 + + + + + +CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAGE lang_name ( + FROM SQL WITH FUNCTION from_sql_function_name (argument_type [, ...]), + TO SQL WITH FUNCTION to_sql_function_name (argument_type [, ...]) +); + + + + + + + 描述 + + + + + CREATE TRANSFORM定义一种新的转换。 + CREATE OR REPLACE TRANSFORM会创建一种新的转换, + 或替换现有定义。 + + + + + + 转换指定如何将一种数据类型适配到某种过程语言。例如,在用 + PL/Python 编写一个使用hstore类型的函数时,PL/Python + 事先并不知道该如何在 Python 环境中表示hstore值。语言 + 实现通常默认使用文本表示,但这并不方便,例如在更适合使用关联数组或 + 列表时就是如此。 + + + + + + 转换指定两个函数: + + + + 一个from SQL函数,用于将该类型从 SQL 环境转换到该 + 语言。该函数会作用于用这种语言编写的函数的参数。 + + + + + + 一个to SQL函数,用于将该类型从该语言转换到 SQL 环 + 境。该函数会作用于用这种语言编写的函数的返回值。 + + + + 不必同时提供这两个函数。如果其中一个未指定,则会在需要时使用该语言相 + 关的默认行为。(若要彻底阻止某一方向上的转换,也可以编写一个总是报错 + 的转换函数。) + + + + + + 要创建一种转换,你必须拥有该类型并具有其上的 + USAGE权限,具有该语言上的 + USAGE权限,并且在指定了 from-SQL 和 to-SQL 函数时, + 还必须拥有这些函数并具有其上的EXECUTE权限。 + + + + + + 参数 + + + + type_name + + + + + 该转换的数据类型名称。 + + + + + + + lang_name + + + + + 该转换所用语言的名称。 + + + + + + + from_sql_function_name(argument_type [, ...]) + + + + + 用于将该类型从 SQL 环境转换到该语言的函数名称。它必须接受一个 + internal类型参数,并返回internal类型。 + 实际传入的参数将是该转换对应的类型,因此编写该函数时应按这一点 + 处理。(但不允许声明一个返回internal、却没有至少一个 + internal类型参数的 SQL 层函数。)实际返回值将是语言 + 实现特有的某个对象。 + + + + + + + to_sql_function_name(argument_type [, ...]) + + + + + 用于将该类型从该语言转换到 SQL 环境的函数名称。它必须接受一个 + internal类型参数,并返回该转换对应的类型。实际参数值 + 将是语言实现特有的某个对象。 + + + + + + + + + 注解 + + + 使用删除转换。 + + + + + 示例 + + 为类型hstore和语言plpythonu创建转换时,首先设置该类型和语言: +CREATE TYPE hstore ...; + +CREATE LANGUAGE plpythonu ...; +然后创建所需的函数: +CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS ...; + +CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS ...; +最后创建转换,将它们连接起来: +CREATE TRANSFORM FOR hstore LANGUAGE plpythonu ( + FROM SQL WITH FUNCTION hstore_to_plpython(internal), + TO SQL WITH FUNCTION plpython_to_hstore(internal) +); +实际上,这些命令会被封装在扩展中。 + + + contrib部分包含若干提供转换的扩展,可以作为实 + 际示例。 + + + + + + 兼容性 + + + + + 这种形式的CREATE TRANSFORM是 + PostgreSQL的一种扩展。 + SQL标准中也有CREATE + TRANSFORM命令,但它用于将数据类型适配到客户端语言。 + PostgreSQL不支持那种用法。 + + + + + + + 另见 + + + + + , + , + , + + + + + + diff --git a/zh/9.6/ref/create_trigger.sgml b/zh/9.6/ref/create_trigger.sgml new file mode 100644 index 00000000..dfc850b2 --- /dev/null +++ b/zh/9.6/ref/create_trigger.sgml @@ -0,0 +1,493 @@ + + + + + CREATE TRIGGER + + + + CREATE TRIGGER + 7 + SQL - 语言语句 + + + + CREATE TRIGGER + 定义一个新触发器 + + + + +CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] } + ON table_name + [ FROM referenced_table_name ] + [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] + [ FOR [ EACH ] { ROW | STATEMENT } ] + [ WHEN ( condition ) ] + EXECUTE PROCEDURE function_name ( arguments ) + +其中event为以下之一: + + INSERT + UPDATE [ OF column_name [, ... ] ] + DELETE + TRUNCATE + + + + + 描述 + + CREATE TRIGGER创建一个新触发器。触发器会关联到指定的表、视图或外部表,并在发生某些事件时执行指定的函数function_name + + + 可以指定触发器在尝试对某一行执行该操作之前引发 + (即在检查约束以及尝试执行INSERT、 + UPDATEDELETE之前); + 也可以在该操作完成之后引发(即在检查约束以及完成 + INSERTUPDATE或 + DELETE之后);或者取代该操作执行 + (用于视图上的插入、更新或删除)。如果触发器在事件之前引发,或者改为取代该事件执行, + 则它可以跳过对当前行的操作,或者修改待插入的行 + (仅适用于INSERTUPDATE操作)。 + 如果触发器在事件之后引发,则所有更改(包括其他触发器的影响) + 对该触发器都是可见的。 + + + + 被标记为FOR EACH ROW的触发器会针对该操作修改的每一行调用一次。 + 例如,一个影响 10 行的DELETE会导致目标关系上的 + ON DELETE触发器分别被调用 10 次,即每个被删除的行调用一次。 + 相比之下,被标记为FOR EACH STATEMENT的触发器对于任意给定操作只执行一次, + 不管该操作修改了多少行(特别是,修改零行的操作仍会导致所有适用的 + FOR EACH STATEMENT触发器执行)。注意,对于带 + ON CONFLICT DO UPDATE子句的INSERT, + INSERTUPDATE语句级触发器都会被引发。 + + + + 被指定为取代该触发事件执行的 INSTEAD OF 触发器必须标记为 + FOR EACH ROW,并且只能定义在视图上。视图上的 + BEFOREAFTER 触发器必须标记为 + FOR EACH STATEMENT。 + + + + 此外,也可以把触发器定义为在 TRUNCATE 时引发,但只能是 + FOR EACH STATEMENT。 + + + + 下表汇总了哪些类型的触发器可用于表、视图和外部表: + + + + + + + + 时机 + 事件 + 行级 + 语句级 + + + + + + BEFORE + INSERT/UPDATE/DELETE + 表和外部表 + 表、视图和外部表 + + + + TRUNCATE + + + + + + AFTER + INSERT/UPDATE/DELETE + 表和外部表 + 表、视图和外部表 + + + + TRUNCATE + + + + + + INSTEAD OF + INSERT/UPDATE/DELETE + 视图 + + + + + TRUNCATE + + + + + + + + + 此外,触发器定义还可以指定一个布尔型 WHEN 条件, + 用于测试是否应当引发该触发器。在行级触发器中, + WHEN 条件可以检查该行各列的新值和/或旧值。语句级触发器也可以带有 + WHEN 条件,不过这一特性对它们的用处不大,因为该条件无法引用表中的任何值。 + + + + 如果针对同一事件定义了多个同类触发器,它们将按名称的字母顺序引发。 + + + 指定CONSTRAINT选项时,此命令会创建约束触发器。这与普通触发器相同,只是可以使用调整触发器触发的时间。约束触发器必须是表上的AFTER ROW触发器。它们可以在导致触发事件的语句结束时触发,也可以在包含该语句的事务结束时触发;后一种情况下称为延迟。也可以使用SET CONSTRAINTS强制尚待执行的延迟触发器立即触发。约束触发器应在其实现的约束被违反时引发异常。 + + + SELECT 不会修改任何行,因此无法创建 + SELECT 触发器。对于这类情形,规则和视图更为合适。 + + + + 参见以了解有关触发器的更多信息。 + + + + + 参数 + + + + name + + + + 新触发器的名称。它必须不同于同一表上的任何其他触发器名称。 + 该名称不能是模式限定名 — 触发器会继承其所属表的模式。 + 对于约束触发器,这也是使用SET CONSTRAINTS + 修改触发器行为时所使用的名称。 + + + + + + BEFORE + AFTER + INSTEAD OF + + + + 决定该函数是在事件之前、之后调用,还是取代该事件执行。 + 约束触发器只能指定为AFTER。 + + + + + + event + + + INSERTUPDATEDELETE或 + TRUNCATE 之一;它指定了哪个事件将引发该触发器。 + 可以使用OR指定多个事件。 + + + + 对于 UPDATE 事件,可以使用以下语法指定列列表: + +UPDATE OF column_name1 [, column_name2 ... ] + + 只有当列出的列中至少有一列被列为 UPDATE 命令的目标时,触发器才会触发。 + + + INSTEAD OF UPDATE 事件不支持列列表。 + + + + + + table_name + + + + 该触发器所作用的表、视图或外部表的名称(可以是模式限定的)。 + + + + + + referenced_table_name + + + + 约束所引用的另一张表的名称(可以是模式限定的)。 + 此选项用于外键约束,不建议用于一般用途。它只能为约束触发器指定。 + + + + + + DEFERRABLE + NOT DEFERRABLE + INITIALLY IMMEDIATE + INITIALLY DEFERRED + + + + 触发器的默认时机。有关这些约束选项的细节,请参见 + 。它们只能为约束触发器指定。 + + + + + + FOR EACH ROW + FOR EACH STATEMENT + + + 这指定触发器过程应针对触发事件影响的每一行触发一次,还是仅针对每条 SQL 语句触发一次。如果两者均未指定,则默认值为FOR EACH STATEMENT。约束触发器只能指定FOR EACH ROW + + + + + condition + + + + 一个布尔表达式,用于决定触发器函数是否会实际执行。 + 如果指定了WHEN,则仅当 + condition 返回 + true 时才会调用该函数。在 + FOR EACH ROW 触发器中,WHEN + 条件可以分别写成 + OLD.column_name + 或 + NEW.column_name + 来引用旧行值和/或新行值中的列。当然, + INSERT 触发器不能引用 OLD, + 而 DELETE 触发器不能引用 NEW。 + + + INSTEAD OF触发器不支持WHEN条件。 + + + + 当前,WHEN 表达式不能包含子查询。 + + + + 请注意,对于约束触发器,WHEN 条件的求值不会被延迟, + 而是在行操作执行后立即发生。如果该条件求值结果不为真, + 则该触发器不会被加入延迟执行队列。 + + + + + + function_name + + + 一个由用户提供的函数,声明为不接受参数并返回 + trigger 类型;当触发器被触发时,就会执行该函数。 + + + + + + arguments + + + + 一个可选的、以逗号分隔的参数列表,在执行触发器时会传给该函数。 + 这些参数是字符串字面常量。这里也可以写简单名称和数字常量, + 但它们都会被转换成字符串。请查阅该触发器函数所用实现语言的说明, + 以了解如何在函数内部访问这些参数;这可能与普通函数参数不同。 + + + + + + + + 注解 + + 要在表上创建触发器,用户必须拥有该表的TRIGGER权限。用户还必须拥有触发器函数的EXECUTE权限。 + + 使用删除触发器。 + + + 列限定触发器(即使用 UPDATE OF + column_name 语法定义的触发器)会在其任一列被列为 + UPDATE 命令的 SET 列表目标时引发。 + 即便触发器没有引发,列值仍有可能发生变化,因为 + BEFORE UPDATE 触发器对行内容所作的更改不会被考虑在内。 + 反过来,诸如 UPDATE ... SET x = x ... 这样的命令会引发列 + x 上的触发器,即使该列的值实际上并未变化。 + + + + 有少量内置触发器函数可用于解决常见问题,而无需自己编写触发器代码; + 参见。 + + + BEFORE触发器中,WHEN条件会在函数执行前(或者本应执行前)立即求值,因此使用WHEN与在触发器函数开头测试相同条件并无实质区别。特别要注意的是,该条件看到的NEW行是当前值,它可能已经被更早触发的触发器修改过。此外,BEFORE触发器的WHEN条件不允许检查NEW行的系统列(例如oid),因为这些列此时尚未被设置。 + + + 在 AFTER 触发器中,WHEN 条件会在行变更发生后立即求值, + 并决定是否将一个事件排入队列,以便在语句结束时引发该触发器。因此,当 + AFTER 触发器的 WHEN 条件不返回真时, + 就没有必要将事件排队,也不需要在语句结束时重新获取该行。 + 如果触发器只需针对少数几行引发,这能显著提升那些会修改很多行的语句的速度。 + + + + 只有当视图上的动作由行级 INSTEAD OF 触发器处理时, + 视图上的语句级触发器才会引发。如果该动作由 INSTEAD 规则处理, + 那么规则发出的那些语句会代替原先引用该视图的语句执行,因此最终引发的是替代语句所引用表上的触发器。 + 同样,如果该视图是自动可更新的,那么该动作会通过把语句自动重写为作用于视图基表的动作来处理, + 因而最终引发的是基表上的语句级触发器。 + + + + PostgreSQL 7.3 之前的版本中,必须将触发器函数声明为返回占位类型opaque,而不是trigger。为了支持加载旧转储文件,CREATE TRIGGER将接受声明为返回opaque的函数,但会发出通知,并将该函数声明的返回类型更改为trigger + + + + 示例 + + 执行该函数check_account_update,每当表accounts中的一行即将被更新时调用它: +CREATE TRIGGER check_update + BEFORE UPDATE ON accounts + FOR EACH ROW + EXECUTE PROCEDURE check_account_update(); +相同,但仅当列balance被指定为UPDATE命令的目标时才执行该函数: +CREATE TRIGGER check_update + BEFORE UPDATE OF balance ON accounts + FOR EACH ROW + EXECUTE PROCEDURE check_account_update(); +这种形式仅在列balance的值确实发生变化时才执行该函数: +CREATE TRIGGER check_update + BEFORE UPDATE ON accounts + FOR EACH ROW + WHEN (OLD.balance IS DISTINCT FROM NEW.balance) + EXECUTE PROCEDURE check_account_update(); + + + 调用一个函数记录 accounts 的更新,但仅在确有内容发生变化时才调用: + + +CREATE TRIGGER log_update + AFTER UPDATE ON accounts + FOR EACH ROW + WHEN (OLD.* IS DISTINCT FROM NEW.*) + EXECUTE PROCEDURE log_account_update(); +执行该函数view_insert_row,针对每一行将行插入视图底层的表中: + + +CREATE TRIGGER view_insert + INSTEAD OF INSERT ON my_view + FOR EACH ROW + EXECUTE PROCEDURE view_insert_row(); + + + + + 包含一个用 C 编写的触发器函数的完整示例。 + + + + + 兼容性 + + + + + PostgreSQL 中的 CREATE TRIGGER + 语句实现了 SQL 标准的一个子集。当前仍缺少以下功能: + + + + + SQL 允许为在触发动作定义中使用的行或行 + 定义别名(例如 CREATE TRIGGER ... ON tablename REFERENCING + OLD ROW AS somename NEW ROW AS othername ...)。由于 + PostgreSQL 允许用任意多种用户定义的语言编写 + 触发器过程,对数据的访问是以语言相关的方式处理的。 + + + + + + PostgreSQL 不允许在语句级触发器中引用旧表和新表, + 即包含所有旧行和/或新行的表,它们在 SQL 标准中由 + OLD TABLENEW TABLE 子句引用。 + + + + + + PostgreSQL 只允许通过执行用户定义函数来完成触发动作。 + 标准则允许把许多其他 SQL 命令(例如 CREATE TABLE)作为触发动作执行。 + 通过创建一个执行所需命令的用户定义函数,这一限制并不难绕过。 + + + + + + + + SQL 规定多个触发器应按创建时间顺序引发。 + PostgreSQL 则按名称顺序引发,这被认为更方便。 + + + + SQL 规定,级联删除上的 BEFORE DELETE 触发器应在级联 + DELETE 完成之后引发。 + PostgreSQL 的行为则是 + BEFORE DELETE 总会在删除动作之前引发,即使是级联删除也一样。 + 这被认为更一致。如果 BEFORE 触发器在由引用动作引起的更新过程中修改行或阻止更新, + 也会出现非标准行为。这可能导致约束违反,或者使存储的数据不符合引用约束。 + + + + 使用 OR 为单个触发器指定多个动作的能力,是 + PostgreSQL 对 SQL 标准的扩展。 + + + + 能够在 TRUNCATE 上引发触发器,是 + PostgreSQL 对 SQL 标准的扩展; + 能够在视图上定义语句级触发器也是如此。 + + + CREATE CONSTRAINT TRIGGERPostgreSQLSQL标准的扩展。 + + + + + + 参见 + + + + + + + + + diff --git a/zh/9.6/ref/create_tsconfig.sgml b/zh/9.6/ref/create_tsconfig.sgml new file mode 100644 index 00000000..caf8d05b --- /dev/null +++ b/zh/9.6/ref/create_tsconfig.sgml @@ -0,0 +1,121 @@ + + + + + CREATE TEXT SEARCH CONFIGURATION + + + + CREATE TEXT SEARCH CONFIGURATION + 7 + SQL - 语言语句 + + + + CREATE TEXT SEARCH CONFIGURATION + 定义一个新的文本搜索配置 + + + + +CREATE TEXT SEARCH CONFIGURATION name ( + PARSER = parser_name | + COPY = source_config +) + + + + + 描述 + + + CREATE TEXT SEARCH CONFIGURATION + 创建一个新的文本搜索配置。文本搜索配置指定一个能把字符串拆分为记号 + 的文本搜索解析器,以及一些可用于判断哪些记号对搜索有意义的词典。 + + + + 如果只指定了解析器,那么新文本搜索配置最初没有从记号类型到词典的映射, + 因而会忽略所有词。要让该配置真正可用,必须使用后续的ALTER TEXT SEARCH + CONFIGURATION命令创建映射。 + 另一种方式是复制一个现有的文本搜索配置。 + + + + 如果给出了一个模式名称,则文本搜索配置会被创建在指定的模式中。否则它将会 + 被创建在当前模式中。 + + + + 定义该文本搜索配置的用户会成为其拥有者。 + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 要创建的文本搜索配置的名称。该名称可以是模式限定的。 + + + + + + parser_name + + + 此配置要使用的文本搜索解析器的名称。 + + + + + + source_config + + + 要复制的现有文本搜索配置的名称。 + + + + + + + + 注解 + + + PARSERCOPY选项是互斥的,因为当 + 一个已有的配置被复制时,它的解析器选择也会一并被复制。 + + + + + + 兼容性 + + + 在 SQL 标准中没有 + CREATE TEXT SEARCH CONFIGURATION语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_tsdictionary.sgml b/zh/9.6/ref/create_tsdictionary.sgml new file mode 100644 index 00000000..ad77cd39 --- /dev/null +++ b/zh/9.6/ref/create_tsdictionary.sgml @@ -0,0 +1,137 @@ + + + + + CREATE TEXT SEARCH DICTIONARY + + + + CREATE TEXT SEARCH DICTIONARY + 7 + SQL - 语言语句 + + + + CREATE TEXT SEARCH DICTIONARY + 定义一个新的文本搜索字典 + + + + +CREATE TEXT SEARCH DICTIONARY name ( + TEMPLATE = template + [, option = value [, ... ]] +) + + + + + 描述 + + + CREATE TEXT SEARCH DICTIONARY创建一个 + 新的文本搜索字典。文本搜索字典指定一种在搜索时识别哪些词值得关注、 + 哪些词不值得关注的方式。字典依赖于文本搜索模板,后者规定了实际执行 + 这项工作的函数。通常,字典会提供一些选项,用来控制模板函数的具体 + 行为。 + + + + 如果给出了一个模式名称,那么该文本搜索字典会被创建在指定的模式中。 + 否则它会被创建在当前模式中。 + + + + 定义文本搜索字典的用户将成为其拥有者。 + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 要创建的文本搜索字典的名称。该名称可以被模式限定。 + + + + + + template + + + 用于定义该字典基本行为的文本搜索模板名称。 + + + + + + option + + + 要为此字典设置的模板相关选项的名称。 + + + + + + value + + + 模板相关选项要使用的值。如果该值不是简单的标识符或数字,则必须 + 用引号括起(当然,如果你愿意,也始终可以加引号)。 + + + + + + + 这些选项可以按任意顺序出现。 + + + + + 示例 + + + 下面的示例命令创建了一个基于 Snowball 且使用非标准停用词列表的 + 字典。 + + + +CREATE TEXT SEARCH DICTIONARY my_russian ( + template = snowball, + language = russian, + stopwords = myrussian +); + + + + + 兼容性 + + + 在 SQL 标准中没有 + CREATE TEXT SEARCH DICTIONARY语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_tsparser.sgml b/zh/9.6/ref/create_tsparser.sgml new file mode 100644 index 00000000..d8ebb16a --- /dev/null +++ b/zh/9.6/ref/create_tsparser.sgml @@ -0,0 +1,147 @@ + + + + + CREATE TEXT SEARCH PARSER + + + + CREATE TEXT SEARCH PARSER + 7 + SQL - 语言语句 + + + + CREATE TEXT SEARCH PARSER + 定义一个新的全文检索解析器 + + + + +CREATE TEXT SEARCH PARSER name ( + START = start_function , + GETTOKEN = gettoken_function , + END = end_function , + LEXTYPES = lextypes_function + [, HEADLINE = headline_function ] +) + + + + + 描述 + + + CREATE TEXT SEARCH PARSER创建一个 + 新的全文检索解析器。全文检索解析器定义了一种方法,用于将文本字符串 + 拆分成记号并为这些记号指定类型(类别)。解析器本身并没有太大用处, + 必须与一些全文检索字典一起绑定到一个全文检索配置中,才能用于搜索。 + + + + 如果给出了一个模式名称,那么全文检索解析器将被创建在指定的模式中。 + 否则它会被创建在当前模式中。 + + + + 要使用CREATE TEXT SEARCH PARSER,你必须是超级用户。 + 之所以有此限制,是因为错误的全文检索解析器定义可能会让服务器陷入 + 混乱,甚至崩溃。 + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 要创建的全文检索解析器的名称。该名称可以是模式限定的。 + + + + + + start_function + + + 该解析器的启动函数的名称。 + + + + + + gettoken_function + + + 该解析器的获取下一个记号的函数名称。 + + + + + + end_function + + + 该解析器的结束函数的名称。 + + + + + + lextypes_function + + + 该解析器的 lextypes 函数的名称(该函数返回它所产生的记号类型集合的 + 信息)。 + + + + + + headline_function + + + 该解析器的 headline 函数的名称(该函数对一组记号生成摘要)。 + + + + + + + 如有必要,函数名称可以是模式限定的。这里未给出参数类型,因为每类函数 + 的参数列表都是预先确定的。除 headline 函数外,其余函数都是必需的。 + + + + 这些参数可以按任意顺序出现,不必与上面展示的顺序一致。 + + + + + 兼容性 + + + 在 SQL 标准中没有 + CREATE TEXT SEARCH PARSER语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_tstemplate.sgml b/zh/9.6/ref/create_tstemplate.sgml new file mode 100644 index 00000000..f6d2128f --- /dev/null +++ b/zh/9.6/ref/create_tstemplate.sgml @@ -0,0 +1,119 @@ + + + + + CREATE TEXT SEARCH TEMPLATE + + + + CREATE TEXT SEARCH TEMPLATE + 7 + SQL - 语言语句 + + + + CREATE TEXT SEARCH TEMPLATE + 定义一个新的全文检索模板 + + + + +CREATE TEXT SEARCH TEMPLATE name ( + [ INIT = init_function , ] + LEXIZE = lexize_function +) + + + + + 描述 + + + CREATE TEXT SEARCH TEMPLATE创建一个 + 新的全文检索模板。全文检索模板定义实现全文检索字典的函数。模板本身 + 并无直接用途,必须先实例化为字典后才能使用。字典通常会指定要传递给 + 模板函数的参数。 + + + + 如果给出了模式名称,则全文检索模板会被创建在指定模式中。否则它会被 + 创建在当前模式中。 + + + + 要使用CREATE TEXT SEARCH TEMPLATE,你 + 必须是超级用户。之所以有此限制,是因为错误的全文检索模板定义可能使 + 服务器陷入混乱,甚至崩溃。将模板与字典分离的原因在于,模板封装了定 + 义字典时那些不安全的方面。而在定义字典时可设置的参数, + 对非特权用户来说是安全的,因此创建字典不必是特权操作。 + + + + 更多信息请参见。 + + + + + 参数 + + + + name + + + 要创建的全文检索模板名称。该名称可以是模式限定的。 + + + + + + init_function + + + 该模板的 init 函数的名称。 + + + + + + lexize_function + + + 该模板的 lexize 函数名称。 + + + + + + + 如有必要,函数名称可以是模式限定的。这里没有给出参数类型,因为每一 + 类函数的参数列表都是预先确定的。lexize 函数是必需的,但 init 函数 + 是可选的。 + + + + 这些参数可以按任意顺序出现,不必局限于上面显示的顺序。 + + + + + 兼容性 + + + 在 SQL 标准中没有 + CREATE TEXT SEARCH TEMPLATE语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/create_type.sgml b/zh/9.6/ref/create_type.sgml new file mode 100644 index 00000000..70b6d0d2 --- /dev/null +++ b/zh/9.6/ref/create_type.sgml @@ -0,0 +1,870 @@ + + + + + CREATE TYPE + + + + CREATE TYPE + 7 + SQL - 语言语句 + + + + CREATE TYPE + 定义一种新的数据类型 + + + + +CREATE TYPE name AS + ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] ) + +CREATE TYPE name AS ENUM + ( [ 'label' [, ... ] ] ) + +CREATE TYPE name AS RANGE ( + SUBTYPE = subtype + [ , SUBTYPE_OPCLASS = subtype_operator_class ] + [ , COLLATION = collation ] + [ , CANONICAL = canonical_function ] + [ , SUBTYPE_DIFF = subtype_diff_function ] +) + +CREATE TYPE name ( + INPUT = input_function, + OUTPUT = output_function + [ , RECEIVE = receive_function ] + [ , SEND = send_function ] + [ , TYPMOD_IN = type_modifier_input_function ] + [ , TYPMOD_OUT = type_modifier_output_function ] + [ , ANALYZE = analyze_function ] + [ , INTERNALLENGTH = { internallength | VARIABLE } ] + [ , PASSEDBYVALUE ] + [ , ALIGNMENT = alignment ] + [ , STORAGE = storage ] + [ , LIKE = like_type ] + [ , CATEGORY = category ] + [ , PREFERRED = preferred ] + [ , DEFAULT = default ] + [ , ELEMENT = element ] + [ , DELIMITER = delimiter ] + [ , COLLATABLE = collatable ] +) + +CREATE TYPE name + + + + + 描述 + + + CREATE TYPE在当前数据库中注册一种新的数据 + 类型。定义该类型的用户将成为其拥有者。 + + + + 如果给出了模式名,则该类型会在指定模式中创建;否则会在当前模式中 + 创建。类型名必须不同于同一模式中任何现有类型或域的名称。(由于表 + 也具有关联的数据类型,类型名还必须不同于同一模式中任何现有表的名 + 称。) + + + + 如上面的语法概要所示,CREATE TYPE有五种形 + 式。它们分别用于创建复合类型枚 + 举类型范围类型基础类 + 型shell 类型。下文将依次讨论 + 前四种。shell 类型只是稍后定义某种类型时使用的占位符;它通过执行 + 除类型名外不带任何参数的CREATE TYPE来创 + 建。正如相应小节所述,创建范围类型和基础类型时,需要用 shell 类 + 型作为前向引用。 + + + + + 复合类型 + + + 第一种形式的CREATE TYPE创建复合类型。复合 + 类型由属性名和数据类型列表指定。如果某个属性的数据类型支持排序规 + 则,还可以指定该属性的排序规则。复合类型本质上与表的行类型相同, + 但如果目的只是定义一种类型,使用CREATE TYPE + 就不必实际创建表。例如,独立的复合类型可用作函数的参数类型或返回 + 类型。 + + + + 要创建复合类型,必须对所有属性类型都拥有USAGE + 权限。 + + + + + 枚举类型 + + + 如中所述,第二种形式的 + CREATE TYPE创建枚举(enum)类型。枚举类 + 型接受一个 + 带引号标签的列表,其中每个标签的长度都必须小于 + NAMEDATALEN字节(在标准 + PostgreSQL构建中为 64 字节)。(也 + 可以创建零标签的枚举类型,但在使用 + 至少添加一个标签之前,这种类型不能用来保存值。) + + + + + 范围类型 + + + 如中所述,第三种形式的 + CREATE TYPE创建范围类型。 + + + + 范围类型的subtype可 + 以是任何带有关联 B-树操作符类的类型(该操作符类用于确定范围类型值 + 的顺序)。通常使用子类型默认的 B-树操作符类来确定顺序;若要使用 + 非默认操作符类,可用subtype_opclass指定其名称。如果 + 子类型支持排序规则,而你希望在范围排序中使用非默认排序规则,可用 + collation选项指定所 + 需排序规则。 + + + + 可选的canonical函数 + 必须接受一个正在定义的范围类型值作为参数,并返回同一类型的值。在 + 适用时,它用于将范围值转换为规范形式。更多信息见。创建 + canonical函数有些棘 + 手,因为它必须在声明范围类型之前定义。为此,必须先创建一种 shell + 类型,它除了名称和拥有者外没有任何属性,只是一个占位符类型。这可 + 通过执行不带任何附加参数的命令CREATE TYPE + name完成。然后就可以把该 + shell 类型用作参数类型和结果类型来声明该函数,最后再用同一名称声 + 明范围类型。这样会自动用有效的范围类型替换 shell 类型条目。 + + + + 可选的subtype_diff + 函数必须接受两个subtype类型的值作为参数,并返回 + 一个表示这两个给定值之差的double precision值。虽 + 然这是可选的,但提供该函数可显著提高该范围类型列上 GiST 索引的效 + 率。详见。 + + + + + 基础类型 + + + 第四种形式的CREATE TYPE创建一种新的基础类 + 型(标量类型)。要创建新的基础类型,你必须是超级用户。(这样限制 + 是因为错误的类型定义可能使服务器陷入混乱,甚至导致其崩溃。) + + + + 这些参数可以按任意顺序出现,不必局限于上面展示的顺序,而且大多数 + 都是可选的。在定义该类型之前,必须先注册两个或更多函数(使用 + CREATE FUNCTION)。支持函数 + input_function和 + output_function是必 + 需的;receive_function、 + send_function、 + type_modifier_input_function、 + type_modifier_output_function、 + analyze_function则 + 是可选的。通常这些函数必须用 C 或其他低级语言编写。 + + + + input_function将 + 类型的外部文本表示转换为该类型的操作符和函数所使用的内部表 + 示。output_function + 执行相反的转换。输入函数可以声明为接受一个cstring + 参数,或者接受三个参数,类型分别为cstring、 + oidinteger。第一个参数是以 C 字符 + 串表示的输入文本,第二个参数是该类型自身的 OID(数组类型例外,此 + 时传入的是其元素类型的 OID),第三个参数是在已知情况下目标列的 + typmod(未知则传入 -1)。输入函数必须返回该 + 数据类型本身的值。通常输入函数应声明为 STRICT;否则,在读取 NULL + 输入值时会以 NULL 作为第一个参数调用它。除非函数抛出错误,否则在 + 这种情况下仍必须返回 NULL。(这种情况主要是为了支持可能需要拒绝 + NULL 输入的域输入函数。)输出函数必须声明为接受一个新数据类型参 + 数,并且必须返回cstring类型。对于 NULL 值不会调用 + 输出函数。 + + + + 可选的receive_function + 把类型的外部二进制表示转换为内部表示。如果未提供此函数,该类型就 + 不能参与二进制输入。外部二进制表示应选择为既能低成本转换为内部形 + 式,又具有合理可移植性。(例如,标准整数数据类型把网络字节序用作 + 外部二进制表示,而内部表示则使用机器的本地字节序。) + 接收函数 + 应进行充分检查以确保值有效。它可以声明为接受一个 + internal参数,或者接受三个参数,类型分别为 + internaloidinteger。 + 第一个参数是指向保存已接收字节串的StringInfo缓冲区 + 的指针;其余可选参数与文本输入函数相同。接收函数必须返回该数据类 + 型本身的值。通常,接收函数应声明为 STRICT;否则,在读取 NULL 输 + 入值时会以 NULL 作为第一个参数调用它。除非函数抛出错误,否则在这 + 种情况下仍必须返回 NULL。(这种情况主要是为了支持可能需要拒绝 + NULL 输入的域接收函数。)类似地,可选的 + send_function把内 + 部表示转换为外部二进制表示。如果未提供此函数,该类型就不能参与二 + 进制输出。发送函数必须声明为接受一个新数据类型参数,并且必须返回 + bytea类型。对于 NULL 值不会调用发送函数。 + + + + 读到这里,你可能会问:既然新类型本身还没创建,输入和输出函数怎么 + 能声明为返回或接受这个新类型呢?答案是,应先把该类型定义为一种 + shell 类型,它除了名称和拥有者外没有任何 + 属性,只是一个占位符类型。这可通过执行不带任何附加参数的命令 + CREATE TYPE name + 完成。然后就可以定义引用该 shell 类型的 C I/O 函数。最后,再用带 + 完整定义的CREATE TYPE替换该 shell 条目,生 + 成一个完整且有效的类型定义,此后新类型就能正常使用。 + + + + 如果该类型支持修饰符,也就是附加在类型声明上的可选约束,例如 + char(5)numeric(30,2), + 就需要可选的type_modifier_input_function和 + type_modifier_output_function。 + PostgreSQL允许用户定义类型接受一个 + 或多个简单常量或标识符作为修饰符。不过,这些信息必须能够打包成单 + 个非负整数值,以便存储在系统目录中。声明的修饰符会以 + cstring数组的形式传递给type_modifier_input_function。 + 它必须检查这些值是否有效(若无效则抛出错误),若有效则返回一个非 + 负integer值,该值将作为列的typmod + 存储。如果该类型没有type_modifier_input_function, + 就会拒绝类型修饰符。type_modifier_output_function + 则把内部整数 typmod 值转换回适合用户显示的正确形式。它必须返回一 + 个cstring值,即精确追加到类型名后的字符 + 串;例如,numeric的该函数可能返回 + (30,2)。允许省略type_modifier_output_function; + 在这种情况下,默认显示格式只是把存储的 typmod 整数值放在圆括号 + 中。 + + + + 可选的analyze_function + 为该数据类型的列执行类型专用的统计信息收集。默认情况下,如果该类 + 型有默认的 B-树操作符类,ANALYZE将尝试使用 + 该类型的equalsless-than操作符收 + 集统计信息。对于非标量类型,这种行为很可能不合适,因此可以通过指 + 定自定义分析函数来覆盖。分析函数必须声明为接受一个 + internal参数并返回boolean结果。分 + 析函数的详细 API 见src/include/commands/vacuum.h。 + + + + 虽然新类型内部表示的细节只有 I/O 函数以及你为该类型编写的其他函 + 数才知道,但仍有若干内部表示属性必须向 + PostgreSQL声明。其中最重要的是 + internallength。 + 基础类型可以是定长的,此时internallength为正整数;也可 + 以是变长的,此时将internallength设为 + VARIABLE。(在内部,这通过把 + typlen设为 -1 表示。)所有变长类型的内部表 + 示都必须以一个 4 字节整数开头,用来给出该类型该值的总长度。(注 + 意,如中所述,长度字段通常是经 + 过编码的;直接访问它并不明智。) + + + + 可选标志PASSEDBYVALUE表示该数据类型的值按值 + 传递,而不是按引用传递。按值传递的类型必须是定长的,且其内部表示 + 不能大于Datum类型的大小(某些机器上为 4 字节,另 + 一些为 8 字节)。 + + + + alignment参数指定该 + 数据类型所需的存储对齐方式。允许的值分别对应按 1、2、4 或 8 字节 + 边界对齐。注意,变长类型的对齐至少必须为 4,因为它们的第一个组成 + 部分必然是一个int4。 + + + + storage参数允许为 + 变长数据类型选择存储策略。(定长类型只允许 + plain。)plain表示该类型 + 数据始终内联存储且不压缩。extended表示系统 + 会先尝试压缩较长的数据值,如果仍然过长,就把该值移出主表行。 + external允许把值移出主表,但系统不会尝试压缩 + 它。main允许压缩,但不鼓励把值移出主表。(采 + 用这种存储策略的数据项在没有其他办法让一行适配时仍可能被移出主 + 表,但与extendedexternal + 数据项相比,它们会被优先保留在主表中。) + + + + 如所述,除plain之外所 + 有storage值都意味 + 着该数据类型的函数能够处理经过TOAST的 + 值。具体指定哪一种其他值,只是决定可 TOAST 数据类型列的默认 + TOAST 存储策略;用户仍可使用ALTER TABLE SET STORAGE + 为单个列选择其他策略。 + + + + like_type参数提供 + 了指定数据类型基本表示属性的另一种方法:从某个现有类型复制这些属 + 性。internallength、 + passedbyvalue、 + alignment和 + storage的值都从指 + 定类型复制而来。(虽然可以通过同时给出LIKE子 + 句和这些选项来覆盖其中某些值,但通常不建议这样做。)当新类型的底 + 层实现以某种方式借用现有类型时,以这种方式指定表 + 示属性尤其有用。 + + + + category和 + preferred参数可用 + 于在存在歧义时帮助控制应用哪一种隐式类型转换。每种数据类型都属于 + 一个由单个 ASCII 字符命名的类别,并且在其类别内要么是 + 首选的,要么不是。当这一规则有助于解析重载函数或 + 操作符时,解析器会优先转换为首选类型(但只会从同一类别中的其他类 + 型转换)。更多细节见。对于与任何其他 + 类型之间都没有隐式类型转换的类型,保持这些设置的默认值就足够了。 + 不过,对于一组彼此存在隐式类型转换的相关类型,把它们都标记为属于 + 同一类别,并选择一两个最通用的类型作为该类别的 + 首选类型,通常会有帮助。category参数在将用户定义类型加 + 入现有内置类别(例如数值类型或字符串类型)时尤其有用。不过,也可 + 以创建全新的纯用户定义类型类别。为这种类别命名时,可选择任一非大 + 写字母的 ASCII 字符。 + + + + 如果用户希望该数据类型列的默认值不是空值,可以指定默认值。用 + DEFAULT关键字指定默认值。(该默认值可以被附 + 加到具体列上的显式DEFAULT子句覆盖。) + + + + 若要表明一种类型是数组类型,可用ELEMENT + 关键字指定数组元素的类型。例如,要定义由 4 字节整数 + (int4)构成的数组,可指定 + ELEMENT = int4。更多关于数组类型的细节见下文。 + + + + 若要指定该类型数组在外部表示中用于分隔各值的分隔符,可把 + delimiter设为特定 + 字符。默认分隔符是逗号(,)。注意,这个分隔 + 符关联的是数组元素类型,而不是数组类型本身。 + + + + 如果可选的布尔参数 + collatable为真,则 + 该类型的列定义和表达式可以通过COLLATE子句携 + 带排序规则信息。是否实际使用这些排序规则信息取决于操作该类型的函 + 数实现;仅仅把类型标记为支持排序规则并不会自动实现这一点。 + + + + + 数组类型 + + + 每当创建用户定义类型时,PostgreSQL + 都会自动创建一个关联的数组类型,其名称由元素类型名前加一个下划线 + 组成;必要时还会截断,以保持其长度小于 + NAMEDATALEN字节。(如果这样生成的名称与现有 + 类型名冲突,就会重复这一过程,直到找到不冲突的名称。)这种隐式 + 创建的数组类型是变长的,并使用内置输入/输出函数 + array_inarray_out。 +该数组类型会跟踪其元素类型的拥有者 + 或模式的任何变化,并在元素类型被删除时一并删除。 + + + + 如果系统会自动创建正确的数组类型,你可能会合理地问,为什么还需 + 要选项。唯一有用的情况是:你正在创建一种定长类型,而它在内部恰好是若干相同元素 + 组成的数组,并且除了为整个类型提供的操作之外,你还希望允许通过下 + 标直接访问这些元素。例如,类型point在内部就表示为 + 两个浮点数,可以用point[0]和 + point[1]访问。注意,这种机制只适用于内部形 + 式恰好是一串相同定长字段的定长类型。可用下标访问的变长类型必须采用array_inarray_out使用的通用内部表示。由于历史原因(也就是说这显然 + 不对,但现在改已经太晚了),定长数组类型的下标从零开始,而变长数 + 组则从一开始。 + + + + + + 参数 + + + + name + + + + 要创建的类型名称(可选地带有模式限定)。 + + + + + + attribute_name + + + + 复合类型的一个属性(列)的名称。 + + + + + + data_type + + + + 将成为复合类型一列的现有数据类型名称。 + + + + + + collation + + + + 要与复合类型的某一列或范围类型关联的现有排序规则名称。 + + + + + + label + + + + 表示枚举类型某个值所关联文本标签的字符串字面量。 + + + + + + subtype + + + + 范围类型所表示范围的元素类型名称。 + + + + + + subtype_operator_class + + + + 子类型的 B-树操作符类名称。 + + + + + + canonical_function + + + + 范围类型规范化函数的名称。 + + + + + + subtype_diff_function + + + + 子类型差分函数的名称。 + + + + + + input_function + + + + 将数据从类型的外部文本形式转换为内部形式的函数名。 + + + + + + output_function + + + + 将数据从类型的内部形式转换为外部文本形式的函数名。 + + + + + + receive_function + + + + 将数据从类型的外部二进制形式转换成内部形式的函数名。 + + + + + + send_function + + + + 将数据从类型的内部形式转换为外部二进制形式的函数名。 + + + + + + type_modifier_input_function + + + + 将类型的修饰符数组转换为内部形式的函数名。 + + + + + + type_modifier_output_function + + + + 将类型修饰符的内部形式转换为外部文本形式的函数名。 + + + + + + analyze_function + + + + 为该数据类型执行统计分析的函数名。 + + + + + + internallength + + + + 一个数字常量,用于指定新类型内部表示的字节长度。默认假定它是变 + 长的。 + + + + + + alignment + + + + 该数据类型的存储对齐需求。如果被指定,它必须是 + charint2、 + int4或者double。默认是 + int4。 + + + + + + storage + + + + 该数据类型的存储策略。如果被指定,必须是 + plainexternal、 + extended或者main。 + 默认是plain。 + + + + + + like_type + + + + 与新类型具有相同表示形式的现有数据类型名称。除非在本 + CREATE TYPE命令的其他位置显式覆盖,否则 + internallength、 + passedbyvalue、 + alignment和 + storage的值都会从该 + 类型复制。 + + + + + + category + + + + 该类型的类别码(单个 ASCII 字符)。默认值是表示 + 用户定义类型'U'。其他标准 + 类别码见。你也可以 + 选择其他 ASCII 字符来创建自定义类别。 + + + + + + preferred + + + + 若该类型是其类型类别中的首选类型,则为真,否则为假。默认值为 + 假。在现有类型类别中创建新的首选类型时要格外小心,因为这可能导 + 致出人意料的行为变化。 + + + + + + default + + + + 该数据类型的默认值。若省略,默认值为 null。 + + + + + + element + + + + 正在创建的类型是数组;该参数指定数组元素类型。 + + + + + + delimiter + + + + 在由该类型构成的数组中各值之间使用的分隔符字符。 + + + + + + collatable + + + + 如果该类型的操作可以使用排序规则信息,则为真。默认为假。 + + + + + + + + 注解 + + + 由于数据类型一旦创建,其使用方式就不再受限制,因此创建基础类型或 + 范围类型,相当于对类型定义中提到的那些函数授予公共执行权限。这对 + 适合用于类型定义的那类函数来说通常不是问题。但如果设计一种类型 + 时,需要在把它转换为外部形式或从外部形式转换回来时使用 + 秘密信息,就应当三思。 + + + + 在PostgreSQL 8.3 之前,自动生成的数 + 组类型名称总是恰好等于元素类型名称前加一个下划线字符 + (_)。(因此,类型名称的长度限制比其他名称少 + 一个字符。)虽然现在通常仍是这样,但在名称达到最大长度或与以下划 + 线开头的用户类型名冲突时,数组类型名称可能与此不同。因此,依赖这 + 一约定编写代码的做法已经弃用。请改用 + pg_type.typarray + 来定位与给定类型关联的数组类型。 + + + + 建议避免使用以下划线开头的类型名和表名。虽然服务器会改变生成的数组 + 类型名称以避免与用户给定的名称冲突,但仍然存在混淆风险,特别是对 + 旧客户端软件而言,它们可能会假定以下划线开头的类型名总是表示数 + 组。 + + + + 在PostgreSQL 8.2 之前,不存在 shell + 类型创建语法CREATE TYPE + name。创建新基础类型的做法 + 是先创建它的输入函数。在这种做法下, + PostgreSQL会首先把新数据类型名视为 + 输入函数的返回类型。此时 shell 类型会被隐式创建,然后就可以在其 + 余 I/O 函数的定义中引用它。这种做法仍然有效,但已弃用,并且可能 + 在未来某个版本中被禁止。另外,为避免由于函数定义中的简单拼写错误 + 而意外使系统目录充满 shell 类型,只有在输入函数用 C 编写时,才会 + 以这种方式创建 shell 类型。 + + + PostgreSQL 7.3 以前的版本中,通常完全不创建 shell 类型,而是将函数中对类型名的前向引用替换为占位伪类型 opaque。在 7.3 以前,cstring参数和结果也必须声明为opaque。为了支持载入旧转储文件,CREATE TYPE会接受使用opaque声明的 I/O 函数,但会发出通知,并修改函数声明以使用正确的类型。 + + + + + + 示例 + + + 这个示例创建一种复合类型,并在函数定义中使用它: + +CREATE TYPE compfoo AS (f1 int, f2 text); + +CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$ + SELECT fooid, fooname FROM foo +$$ LANGUAGE SQL; + + + + + 这个示例创建一种枚举类型,并在表定义中使用它: + +CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed'); + +CREATE TABLE bug ( + id serial, + description text, + status bug_status +); + + + + + 这个示例创建一种范围类型: + +CREATE TYPE float8_range AS RANGE (subtype = float8, subtype_diff = float8mi); + + + + + 这个示例创建基础类型box,然后在表定义中使用它: + +CREATE TYPE box; + +CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ; +CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ; + +CREATE TYPE box ( + INTERNALLENGTH = 16, + INPUT = my_box_in_function, + OUTPUT = my_box_out_function +); + +CREATE TABLE myboxes ( + id integer, + description box +); + + + + + 如果box的内部结构是由四个 + float4元素构成的数组,则也可以改为这样写: + +CREATE TYPE box ( + INTERNALLENGTH = 16, + INPUT = my_box_in_function, + OUTPUT = my_box_out_function, + ELEMENT = float4 +); + + 这样就能通过下标访问 box 值的各个分量。除此之外,该类型的行为与 + 前例相同。 + + + + 这个示例创建一种大对象类型,并在表定义中使用它: + +CREATE TYPE bigobj ( + INPUT = lo_filein, OUTPUT = lo_fileout, + INTERNALLENGTH = VARIABLE +); +CREATE TABLE big_objs ( + id integer, + obj bigobj +); + + + + + 更多示例(包括配套的输入和输出函数)请见。 + + + + + 兼容性 + + + 第一种CREATE TYPE形式,即创建复合类型的形式, + 符合SQL标准。其他形式都是 + PostgreSQL扩展。 + SQL标准中的CREATE TYPE + 语句还定义了PostgreSQL尚未实现的其 + 他形式。 + + + + 支持创建零属性的复合类型,是 + PostgreSQL对标准的一种特有背离(类 + 似于CREATE TABLE中的同类情况)。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/create_user.sgml b/zh/9.6/ref/create_user.sgml new file mode 100644 index 00000000..2c3e91f4 --- /dev/null +++ b/zh/9.6/ref/create_user.sgml @@ -0,0 +1,76 @@ + + + + + CREATE USER + + + + CREATE USER + 7 + SQL - 语言语句 + + + + CREATE USER + 定义一个新的数据库角色 + + + + +CREATE USER name [ [ WITH ] option [ ... ] ] + +其中option可以是: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + | IN ROLE role_name [, ...] + | IN GROUP role_name [, ...] + | ROLE role_name [, ...] + | ADMIN role_name [, ...] + | USER role_name [, ...] + | SYSID uid + + + + + 描述 + + + CREATE USER现为 + 的别名。 + 唯一的区别在于:当命令写为CREATE USER时, + 默认假定为LOGIN;而当命令写为 + CREATE ROLE时,默认假定为NOLOGIN。 + + + + + 兼容性 + + + CREATE USER语句是 + PostgreSQL的一种扩展。 + SQL 标准将用户的定义留给具体实现决定。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/create_user_mapping.sgml b/zh/9.6/ref/create_user_mapping.sgml new file mode 100644 index 00000000..549dc54f --- /dev/null +++ b/zh/9.6/ref/create_user_mapping.sgml @@ -0,0 +1,116 @@ + + + + + CREATE USER MAPPING + + + + CREATE USER MAPPING + 7 + SQL - 语言语句 + + + + CREATE USER MAPPING + 定义用户到外部服务器的新映射 + + + + +CREATE USER MAPPING FOR { user_name | USER | CURRENT_USER | PUBLIC } + SERVER server_name + [ OPTIONS ( option 'value' [ , ... ] ) ] + + + + + 描述 + + + CREATE USER MAPPING定义用户到外部服务器的映射。 + 用户映射通常封装连接信息,外部数据包装器会将其与外部服务器所封装 + 的信息结合起来,以访问外部数据资源。 + + + + 外部服务器的拥有者可以为该服务器上的任何用户创建用户映射。此外,如 + 果某个用户已被授予该服务器上的USAGE权限,那么该 + 用户也可以为自己的用户名创建用户映射。 + + + + + 参数 + + + + + user_name + + + 被映射到外部服务器的现有用户的名称。 + CURRENT_USER + 和USER都匹配当前用户的名称。当指定 + PUBLIC时,会创建一个所谓的公共映射;当没有适 + 用的特定用户映射时,就会使用它。 + + + + + + server_name + + + 将为其创建用户映射的现有服务器的名称。 + + + + + + OPTIONS ( option 'value' [, ... ] ) + + + 该子句指定用户映射的选项。这些选项通常定义该映射实际的用户名和 + 密码。选项名必须唯一。允许的选项名和值取决于该服务器的外部数据包 + 装器。 + + + + + + + + 示例 + + + 为用户bob、服务器foo创建一个用户映射: + +CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret'); + + + + + + 兼容性 + + + CREATE USER MAPPING符合 ISO/IEC 9075-9(SQL/MED)。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/create_view.sgml b/zh/9.6/ref/create_view.sgml new file mode 100644 index 00000000..4efdf60c --- /dev/null +++ b/zh/9.6/ref/create_view.sgml @@ -0,0 +1,364 @@ + + + + + CREATE VIEW + + + + CREATE VIEW + 7 + SQL - 语言语句 + + + + CREATE VIEW + 定义一个新视图 + + + + +CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] VIEW name [ ( column_name [, ...] ) ] + [ WITH ( view_option_name [= view_option_value] [, ... ] ) ] + AS query + [ WITH [ CASCADED | LOCAL ] CHECK OPTION ] + + + + + + 描述 + + + CREATE VIEW定义一个视图,其内容来自一个查询。该 + 视图不会被实际物化。相反,每次在查询中引用该视图时,都会执行定义它 + 的查询。 + + + + CREATE OR REPLACE VIEW与之类似,但如果已经存在 + 同名视图,则会用新定义替换它。新查询必须生成与现有视图查询相同的列 + (即列名、列顺序和数据类型都相同),但可以在列表末尾附加额外的列。 + 生成输出列的计算方式则可以完全不同。 + + + + 如果给出了模式名称(例如,CREATE VIEW + myschema.myview ...),则视图将在指定的模式中创建。 + 否则,它将在当前模式中创建。临时视图存在于一个特殊模式中,因此在创建 + 临时视图时不能给出模式名称。 + 视图的名称必须与同一模式中的任何其他关系(表、序列、索引、视图、 + 物化视图或外部表)的名称不同。 + + + + + 参数 + + + + TEMPORARYTEMP + + + + 如果指定该选项,视图将被创建为临时视图。临时视图会在当前会话结束 + 时自动删除。当临时视图存在时,除非使用模式限定名称引用,否则具有 + 相同名称的现有永久关系对当前会话不可见。 + + + + 如果视图引用的任何表是临时表,则该视图也会被创建为临时视图 + (无论是否指定了TEMPORARY)。 + + + + + + RECURSIVE + + + + 创建一个递归视图。语法 + +CREATE RECURSIVE VIEW [ schema . ] view_name (column_names) AS SELECT ...; + + 等效于 + +CREATE VIEW [ schema . ] view_name AS WITH RECURSIVE view_name (column_names) AS (SELECT ...) SELECT column_names FROM view_name; + + 递归视图必须指定视图列名列表。 + + + + + + name + + + + 要创建的视图名称(可以带模式限定)。 + + + + + + column_name + + + + 视图各列使用的名称列表,可选。若未给出,则从查询中推断列名。 + + + + + + WITH ( view_option_name [= view_option_value] [, ... ] ) + + + 该子句为视图指定可选参数;支持以下参数: + + + + check_option (string) + + 该参数可以是localcascaded,等同于指定WITH [ CASCADED | LOCAL ] CHECK OPTION(见下文)。可以使用更改现有视图上的此选项。 + + + + + security_barrier (boolean) + + 如果视图旨在提供行级安全,则应使用此选项。完整细节请参见 + + + + + + + + + query + + 一个将为该视图提供列和行的命令。 + + + + + WITH [ CASCADED | LOCAL ] CHECK OPTION + + CHECK OPTION + + + WITH CHECK OPTION + + + + + 该选项控制自动可更新视图的行为。指定该选项时,INSERTUPDATE + 命令会在视图上执行检查,以确保新行满足视图定义条件(即检查新行以确保它们可通过视图看到)。如果不满足条件,更新将被拒绝。如果CHECK OPTION 未指定, + INSERTUPDATE 命令可以在视图上创建通过该视图不可见的行。支持以下检查选项: + + + + LOCAL + + + 新行只会根据该视图自身直接定义的条件进行检查。底层基视图上定义 + 的任何条件都不会被检查(除非它们也指定了 + CHECK OPTION)。 + + + + + + CASCADED + + + 新行会根据该视图及所有底层基视图的条件进行检查。如果指定了 + CHECK OPTION,但既未指定 + LOCAL也未指定CASCADED, + 则假定为CASCADED。 + + + + + + + + CHECK OPTION不能与RECURSIVE视图一起使用。 + + + 注意,CHECK OPTION仅支持自动可更新且不带INSTEAD OF触发器或INSTEAD规则的视图。如果一个自动可更新视图定义在带有INSTEAD OF触发器的基视图之上,则可以使用LOCAL CHECK OPTION检查该自动可更新视图的条件,但不会检查带有INSTEAD OF触发器的基视图上的条件(级联检查选项不会继续向下级联到触发器可更新视图,而直接定义在触发器可更新视图上的任何检查选项也会被忽略)。如果该视图或其任何基关系带有会导致INSERTUPDATE命令被重写的INSTEAD规则,则在重写后的查询中所有检查选项都会被忽略,包括来自定义在带有INSTEAD规则的关系之上的自动可更新视图的任何检查选项。 + + + + + + + 注解 + + 使用语句删除视图。 + + + 应留意视图列的名称和类型是否按预期确定。例如: + +CREATE VIEW vista AS SELECT 'Hello World'; + + 这种写法在两方面都不好:列名默认是?column?,而 + 且列数据类型默认为unknown。如果想要在视图结果中使用 + 字符串字面量,可使用类似下面的写法: + +CREATE VIEW vista AS SELECT text 'Hello World' AS hello; + + + + 对视图中引用的表的访问由视图所有者的权限决定。在某些情况下,这可以用于为底层表提供安全但受限的访问。不过,并非所有视图都能防止篡改;详情请参见。视图调用的函数与直接从使用该视图的查询中调用时的处理方式相同。因此,视图的用户必须拥有调用视图所用全部函数的权限。 + + 对现有视图使用CREATE OR REPLACE VIEW时,只会更改视图的定义性 SELECT 规则。其他视图属性(包括所有权、权限和非 SELECT 规则)保持不变。必须拥有该视图才能替换它(包括作为所有者角色的成员)。 + + + 可更新视图 + + + 可更新视图 + + + + 简单视图是自动可更新的:系统允许 + INSERTUPDATEDELETE 语句以与普通表相同的方式用于该视图。满足以下所有条件的视图是自动可更新的: + + + + + 该视图的FROM列表中必须恰好只有一项,而且这一 + 项必须是一个表或另一个可更新视图。 + + + + + + 视图定义的顶层不能包含WITHDISTINCT、 + GROUP BYHAVING、 + LIMIT或者OFFSET子句。 + + + + + + 视图定义的顶层不能包含集合操作(UNION、 + INTERSECT或者EXCEPT)。 + + + + + 视图的 SELECT 列表不能包含任何聚合函数、窗口函数或集合返回函数。 + + + + + 自动可更新视图可以同时包含可更新列和不可更新列。如果某列只是简单引用了底层基关系中的一个可更新列,则该列是可更新的;否则,该列为只读,如果INSERTUPDATE语句试图为其赋值,就会报错。 + + 如果视图是自动可更新的,系统会把视图上的任何INSERTUPDATEDELETE语句转换为底层基关系上的对应语句。带有ON CONFLICT UPDATE子句的INSERT语句也得到完全支持。 + + 如果自动可更新视图包含WHERE条件,那么在该视图上执行UPDATEDELETE语句时,该条件会限制底层基关系中哪些行可被修改。不过,UPDATE仍可能把某一行改成不再满足WHERE条件,从而使其不再能通过该视图看到。类似地,INSERT命令也可能插入不满足WHERE条件的基关系行,因此这些行通过该视图不可见(ON CONFLICT UPDATE也可能类似地影响现有的、通过该视图不可见的行)。可以使用CHECK OPTION阻止INSERTUPDATE命令创建这类通过该视图不可见的行。 + + + 如果自动可更新视图带有security_barrier属性,那么 + 视图上的所有WHERE条件(以及任何使用标记为 + LEAKPROOF的操作符的条件)总会先于视图使用者添加 + 的任何条件求值。详见。请注意,因此 + 那些最终不会返回的行(因为它们未通过用户的WHERE条 + 件)仍可能被锁定。可以使用EXPLAIN查看哪些条件是 + 在关系级别应用的(因而不会锁定行),哪些则不是。 + + + 默认情况下,不满足所有这些条件的更复杂视图是只读的:系统不允许在该视图上执行插入、更新或删除。可以通过在视图上创建INSTEAD OF触发器来实现可更新视图的效果;这些触发器必须把针对该视图的插入等操作转换为在其他表上执行的适当动作。有关更多信息,请参见。另一种可能性是创建规则(参见),但实际上触发器更容易理解和正确使用。 + + 请注意,在视图上执行插入、更新或删除的用户必须拥有该视图上相应的插入、更新或删除权限。此外,视图所有者必须拥有底层基关系上的相关权限,但执行更新的用户不需要拥有底层基关系上的任何权限(参见)。 + + + + + 示例 + + + 创建一个包含所有喜剧电影的视图: + + +CREATE VIEW comedies AS + SELECT * + FROM films + WHERE kind = 'Comedy'; + + 该视图将包含视图创建时位于film表中的列。尽管使用了*创建视图,但以后添加到表中的列不会成为视图的一部分。 + + + 创建一个视图,使用 LOCAL CHECK OPTION: + + +CREATE VIEW universal_comedies AS + SELECT * + FROM comedies + WHERE classification = 'U' + WITH LOCAL CHECK OPTION; +这将创建一个基于 comedies 视图的视图,只显示满足 kind = 'Comedy' 和 classification = 'U' 的电影。任何尝试 INSERTUPDATE 该视图中行的操作,如果新行不满足 classification = 'U',都会被拒绝,但电影的 kind 不会被检查。 + + 创建一个视图,使用 CASCADED CHECK OPTION: + + +CREATE VIEW pg_comedies AS + SELECT * + FROM comedies + WHERE classification = 'PG' + WITH CASCADED CHECK OPTION; +这将创建一个视图,同时检查以下新行属性:kindclassification + + 创建一个同时包含可更新列和不可更新列的视图: +CREATE VIEW comedies AS + SELECT f.*, + country_code_to_name(f.country_code) AS country, + (SELECT avg(r.rating) + FROM user_ratings r + WHERE r.film_id = f.id) AS avg_rating + FROM films f + WHERE f.kind = 'Comedy'; +此视图将支持 INSERTUPDATEDELETE。来自 films 表的所有列都可以更新,而计算列 countryavg_rating 将是只读的。 + + + 创建一个由 1 到 100 的数字组成的递归视图: + +CREATE RECURSIVE VIEW public.nums_1_100 (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums_1_100 WHERE n < 100; + + 请注意,虽然递归视图的名称在此CREATE中使用模式限定名,但它的内部自引用没有使用模式限定名。这是因为隐式创建的 CTE 名称不能使用模式限定名。 + + + + + + 兼容性 + + + CREATE OR REPLACE VIEW是 + PostgreSQL的语言扩展。临时视图的概念也是如 + 此。WITH ( ... )子句同样是扩展,安全屏障视图和安 + 全调用者视图也是如此。 + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/createdb.sgml b/zh/9.6/ref/createdb.sgml new file mode 100644 index 00000000..059fa5fa --- /dev/null +++ b/zh/9.6/ref/createdb.sgml @@ -0,0 +1,334 @@ + + + + + createdb + + + + createdb + 1 + 应用程序 + + + + createdb + 创建新的PostgreSQL数据库 + + + + + createdb + connection-option + option + dbname + description + + + + + + 描述 + + createdb创建一个新的PostgreSQL数据库。 + + + + 通常,执行此命令的数据库用户会成为新数据库的所有者。不过,如果执行用户具有相应的权限,也可以通过选项指定不同的所有者。 + + + createdbSQL命令的包装器。通过此工具创建数据库与通过其他访问服务器的方法创建数据库,在效果上没有区别。 + + + + + + 选项 + + + createdb接受以下命令行参数: + + dbname + + + + 指定要创建的数据库名称。该名称必须在此数据库集簇中的所有PostgreSQL数据库中唯一。 + 默认情况下,会创建一个与当前系统用户同名的数据库。 + + + + + + description + + + + 指定要与新创建的数据库关联的注释。 + + + + + + + + + + + 指定数据库的默认表空间。(该名称将按双引号标识符处理。) + + + + + + + + + + + 回显createdb生成并发送到服务器的命令。 + + + + + + + + + + + 指定该数据库使用的字符集编码。PostgreSQL服务器支持的字符集见。 + + + + + + + + + 指定此数据库要使用的区域设置。这相当于同时指定 + + + + + + + + + 指定该数据库要使用的 LC_COLLATE 设置。 + + + + + + + + + + 指定该数据库要使用的 LC_CTYPE 设置。 + + + + + + + + + + + 指定将成为新数据库拥有者的数据库用户。 + (该名称按双引号标识符处理。) + + + + + + + + + + + 指定用于构建该数据库的模板数据库。(该名称按双引号标识符处理。) + + + + + + + + + + + 打印createdb的版本并退出。 + + + + + + + + + + + 显示有关createdb命令行参数的帮助信息,并退出。 + + + + + + + + 选项对应底层 SQL 命令的选项;有关这些选项的更多信息请参见该命令。 + + + createdb还接受以下用于连接参数的命令行参数: + + + + + + 指定服务器所在机器的主机名。如果该值以斜杠开头,则它将被用作 Unix 域套接字的目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 以该用户名进行连接。 + + + + + + + + + + 从不发出密码提示。如果服务器要求密码认证,而密码又无法通过诸如.pgpass文件等其他方式获得,则连接尝试将失败。该选项适合没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制createdb在连接到数据库之前提示输入密码。 + + + + 这个选项并非必不可少,因为如果服务器要求密码认证,createdb会自动提示输入密码。不过,createdb会浪费一次连接尝试,才知道服务器需要密码。在某些情况下,为了避免这次额外的连接尝试,提前指定是值得的。 + + + + + + + + + 指定在创建新数据库时要连接到的数据库名称。如果未指定,将使用postgres数据库;若该数据库不存在(或者它正是要创建的新数据库),则改用template1。 + 这也可以是一个连接字符串。如果是这种情况,其中的连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + + 环境 + + + + PGDATABASE + + + + 如果设置了该变量,其值就是要创建的数据库名称,除非被命令行覆盖。 + + + + + + PGHOST + PGPORT + PGUSER + + + + + 默认连接参数。如果命令行和PGDATABASE都没有指定要创建的数据库名称,则PGUSER也会决定该名称。 + + + + + + + 与大多数其他PostgreSQL工具一样,该工具也使用libpq支持的环境变量(见)。 + + + + + + + + 诊断 + + + 如果遇到问题,请参阅中关于潜在问题和错误消息的讨论。数据库服务器必须在目标主机上运行。此外,由libpq前端库使用的任何默认连接设置和环境变量也都会生效。 + + + + + + + + 示例 + + + 要使用默认数据库服务器创建demo数据库: + +$ createdb demo + + + + + 要使用主机eden上、端口为 5000 的服务器,并以 + LATIN1编码方案创建demo数据库, + 可以看看下面的底层命令: + +$ createdb -p 5000 -h eden -E LATIN1 -e demo +CREATE DATABASE demo ENCODING 'LATIN1'; + + + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/createlang.sgml b/zh/9.6/ref/createlang.sgml new file mode 100644 index 00000000..542119cf --- /dev/null +++ b/zh/9.6/ref/createlang.sgml @@ -0,0 +1,255 @@ + + + + + createlang + + + + createlang + 1 + 应用程序 + + + + createlang + 安装一个 PostgreSQL 过程语言 + + + + + createlang + connection-option + langname + dbname + + + + createlang + connection-option + + dbname + + + + + + 描述 + + + createlang 是一个向 PostgreSQL 数据库中添加过程语言的工具。 + + + + createlang 只是对 SQL 命令的一个包装。 + + + + + createlang 已被弃用,在未来的 PostgreSQL 版本中可能会被移除。建议直接使用 CREATE EXTENSION 命令。 + + + + + + + 选项 + + + createlang 接受下列命令行参数: + + + + langname + + + 指定要安装的过程语言的名称。(该名称会被转换为小写。) + + + + + + + + + + 指定要向哪个数据库添加该语言。默认使用与当前系统用户同名的数据库。 + + + + + + + + + + 在执行 SQL 命令时把它们显示出来。 + + + + + + + + + + 显示目标数据库中已安装语言的列表。 + + + + + + + + + + 打印 createlang 的版本并退出。 + + + + + + + + + + 显示有关 createlang 命令行参数的帮助并退出。 + + + + + + + + + createlang 还接受下列用于连接参数的命令行参数: + + + + + + + + 指定服务器所在主机的主机名。如果该值以斜杠开头,则将其用作 Unix 域套接字所在目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 要用来连接的用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而密码又无法通过诸如.pgpass文件等其他方式获得,则连接尝试将失败。该选项可用于没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制 createlang 在连接数据库之前提示输入密码。 + + + + 该选项绝非必需,因为如果服务器要求密码认证,createlang会自动提示输入密码。不过,createlang会浪费一次连接尝试来发现服务器需要密码。在某些情况下,输入可以避免这次额外的连接尝试。 + + + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 和大部分其他 PostgreSQL 工具一样,这个工具也使用 libpq 支持的环境变量(参见)。 + + + + + + + 诊断 + + + 大多数错误消息都是不言自明的。如果不然,可以带 选项运行 createlang,然后查看相应的 SQL 命令了解详情。此外,libpq 前端库使用的任何默认连接设置和环境变量也都适用。 + + + + + + 注解 + + + 使用 可移除一个语言。 + + + + + + 示例 + + + 要将语言 pltcl 安装到数据库 template1 中: + +$ createlang pltcl template1 + + 注意,将该语言安装到 template1 中后,之后创建的数据库中也会自动安装它。 + + + + + 参见 + + + + + + + + + diff --git a/zh/9.6/ref/createuser.sgml b/zh/9.6/ref/createuser.sgml new file mode 100644 index 00000000..390e3dd8 --- /dev/null +++ b/zh/9.6/ref/createuser.sgml @@ -0,0 +1,443 @@ + + + + + createuser + + + + createuser + 1 + 应用程序 + + + + createuser + 定义一个新的PostgreSQL用户账户 + + + + + createuser + connection-option + option + username + + + + + + 描述 + + createuser创建一个新的PostgreSQL用户(更准确地说,是一个角色)。只有超级用户以及具有CREATEROLE权限的用户才能创建新用户,因此调用createuser的用户必须能够以超级用户或具有CREATEROLE权限的用户身份连接。 + + + 如果希望创建新的超级用户,必须以超级用户身份连接,而不能仅凭CREATEROLE权限。超级用户意味着能够绕过数据库内所有访问权限检查,因此不应轻易授予超级用户访问权限。 + + createuserSQL命令的包装器。通过此工具创建用户与通过其他访问服务器的方法创建用户,在效果上没有区别。 + + + + + + 选项 + + + createuser接受以下命令行参数: + + username + + 指定要创建的PostgreSQL用户的名称。该名称必须不同于此PostgreSQL安装中所有现有角色的名称。 + + + + + + + + + + 为新用户设置最大连接数。默认不设限制。 + + + + + + + + + + + 允许新用户创建数据库。 + + + + + + + + + + + 不允许新用户创建数据库。这是默认设置。 + + + + + + + + + + + 回显createuser生成并发送给服务器的命令。 + + + + + + + + + + + 加密存储在数据库中的用户密码。如果未指定, + 则使用默认的密码行为。 + + + + + + + + + + 指定此角色应立即作为新成员加入的现有角色。可以使用多个开关,指定此角色要加入的多个角色。 + + + + + + + + + + 新角色将自动继承其所属角色的权限。 + 这是默认设置。 + + + + + + + + + + + 新角色将不会自动继承其所属角色的权限。 + + + + + + + + + + 如果命令行中未指定用户名,则提示输入用户名;此外,还会提示输入命令行中未指定的 + /、 + /、 + /选项。 + (直到 PostgreSQL 9.1 为止,这都是默认行为。) + + + + + + + + + + + 允许新用户登录(也就是说,该用户名可用作初始会话用户标识符)。 + 这是默认设置。 + + + + + + + + + + + 不允许新用户登录。 + (不具有登录权限的角色仍可用于管理数据库权限。) + + + + + + + + + + + 不加密存储在数据库中的用户密码。如果未指定, + 则使用默认的密码行为。 + + + + + + + + + + + 如果给出此选项,createuser会提示输入新用户的密码。 + 如果不打算使用密码认证,则不需要这样做。 + + + + + + + + + 允许新用户创建新角色(即该用户将具有CREATEROLE权限)。 + + + + + + + + + + 不允许新用户创建新角色。这是默认设置。 + + + + + + + + + + + 新用户将成为超级用户。 + + + + + + + + + + + 新用户将不是超级用户。这是默认设置。 + + + + + + + + + + + 打印createuser版本并退出。 + + + + + + + + + + 新用户将具有REPLICATION权限,关于该权限的更完整说明见。 + + + + + + + + + + 新用户将不具有REPLICATION权限,关于该权限的更完整说明见。 + + + + + + + + + + + 显示有关createuser命令行参数的帮助并退出。 + + + + + + + + + createuser还接受以下用于连接参数的命令行参数: + + + + + + 指定服务器所在主机的主机名。如果该值以斜杠开头,则将其用作 Unix 域套接字所在目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 指定连接时使用的用户名(不是要创建的用户名)。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而密码又无法通过诸如.pgpass文件等其他方式获得,则连接尝试将失败。该选项可用于没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制createuser提示输入密码(用于连接到服务器,而不是新用户的密码)。 + + + + 该选项绝非必需,因为如果服务器要求密码认证,createuser会自动提示输入密码。不过,createuser会浪费一次连接尝试来发现服务器需要密码。在某些情况下,输入可以避免这次额外的连接尝试。 + + + + + + + + + + 环境 + + + + PGHOST + PGPORT + PGUSER + + + + + 默认连接参数 + + + + + + + 与大多数其他PostgreSQL工具一样,此工具也使用 + libpq支持的环境变量(见)。 + + + + + + + + 诊断 + + + 若遇到困难,请参见, + 其中讨论了潜在问题和错误消息。数据库服务器必须在目标主机上运行。 + 此外,libpq前端库使用的任何默认连接设置和环境变量也都会生效。 + + + + + + + 示例 + + + 要在默认数据库服务器上创建用户joe: + +$ createuser joe + + + + + 要在默认数据库服务器上创建用户joe,并提示输入一些额外属性: + +$ createuser --interactive joe +Shall the new role be a superuser? (y/n) n +Shall the new role be allowed to create databases? (y/n) n +Shall the new role be allowed to create more new roles? (y/n) n + + + + + 要通过主机eden、端口 5000 上的服务器创建同一个用户joe, + 并显式指定各项属性,同时查看底层命令: + +$ createuser -h eden -p 5000 -S -D -R -e joe +CREATE ROLE joe NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN; + + + + 创建用户joe作为超级用户,并立即分配密码: +$ createuser -P -s -e joe +Enter password for new role: xyzzy +Enter it again: xyzzy +CREATE ROLE joe PASSWORD 'md5b5f5ba1a423792b526f799ae4eb3d59e' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN; +上面的示例中,输入密码时实际上不会回显新密码,但为清晰起见,我们显示了所输入的内容。如你所见,密码在发送给客户端之前会被加密。如果使用了选项, +密码出现在回显的命令中(也可能出现在服务器日志以及其他地方), +因此在这种情况下,如果还有其他人能看到你的屏幕,就不要使用 + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/deallocate.sgml b/zh/9.6/ref/deallocate.sgml new file mode 100644 index 00000000..462e51b4 --- /dev/null +++ b/zh/9.6/ref/deallocate.sgml @@ -0,0 +1,95 @@ + + + + + DEALLOCATE + + + + prepared statements + removing + + + + DEALLOCATE + 7 + SQL - 语言语句 + + + + DEALLOCATE + 释放一个预备语句 + + + + +DEALLOCATE [ PREPARE ] { name | ALL } + + + + + 描述 + + + DEALLOCATE用于释放一个先前准备好的 SQL 语句。 + 如果不显式释放预备语句,则它会在会话结束时被释放。 + + + + 更多关于预备语句的信息,请参见。 + + + + + 参数 + + + + PREPARE + + + 此关键字会被忽略。 + + + + + + name + + + 要释放的预备语句的名称。 + + + + + + ALL + + + 释放所有预备语句。 + + + + + + + + 兼容性 + + + SQL 标准中包含一个DEALLOCATE语句,但它只用于嵌入式 SQL。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/declare.sgml b/zh/9.6/ref/declare.sgml new file mode 100644 index 00000000..7087c7d8 --- /dev/null +++ b/zh/9.6/ref/declare.sgml @@ -0,0 +1,273 @@ + + + + + DECLARE + + + + cursor + DECLARE + + + + DECLARE + 7 + SQL - 语言语句 + + + + DECLARE + 定义一个游标 + + + + +DECLARE name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] + CURSOR [ { WITH | WITHOUT } HOLD ] FOR query + + + + + 描述 + + + DECLARE允许用户创建游标,游标可用于从较大的查询中一次取出少量行。 + 游标创建后,可使用从中取出行。 + + + + + + 本页面描述的是 SQL 命令层面的游标用法。如果想要在 + PL/pgSQL函数中使用游标,规则会有所不同 + — 见。 + + + + + + 参数 + + + + name + + + + 要创建的游标名称。它必须与该会话中任何其他活动游标的名称不同。 + + + + + + BINARY + + + + 使游标以二进制格式而不是文本格式返回数据。 + + + + + + INSENSITIVE + + 表示从游标取出的数据不应受到游标创建后对其底层表所做更新的影响。在PostgreSQL中,这是默认行为;因此该关键字没有效果,仅为兼容 SQL 标准而接受它。 + + + + + SCROLL + NO SCROLL + + SCROLL指定游标可用于以非顺序方式(例如向后)取出行。 + 根据查询执行计划的复杂程度,指定SCROLL可能会给查询执行带来性能开销。 + NO SCROLL指定游标不能以非顺序方式取出行。 + 默认情况下只在某些情形下允许滚动,这与显式指定 + SCROLL并不相同。详见下文 + 。 + + + + + + WITH HOLD + WITHOUT HOLD + + + WITH HOLD指定在创建该游标的事务成功提交后,仍可继续使用该游标。 + WITHOUT HOLD指定该游标不能在创建它的事务之外使用。 + 如果既未指定WITHOUT HOLD也未指定WITH HOLD,默认值是WITHOUT HOLD。 + + + + + + query + + + 一个提供游标要返回的行的或者命令。 + + + + + + + 关键字BINARYINSENSITIVESCROLL可以按任意顺序出现。 + + + + + 注解 + + + 普通游标以文本格式返回数据,就像SELECT产生的结果一样。 + BINARY选项指定游标应以二进制格式返回数据。 + 这减少了服务器和客户端两端的转换工作量,但代价是程序员需要付出更多精力来处理与平台相关的二进制数据格式。 + 举例来说,如果某个查询从一个整型列返回值 1,那么默认游标会返回字符串 1, + 而二进制游标则会返回一个 4 字节字段,其中包含该值的内部表示(采用大端字节序)。 + + + + 应谨慎使用二进制游标。许多应用程序(包括 + psql)都没有准备好处理二进制游标, + 而是期望返回的数据为文本格式。 + + + + + + 当客户端应用使用扩展查询协议发出FETCH命令时, + Bind 协议消息会指定数据应以文本格式还是二进制格式提取。 + 这一选择会覆盖定义游标时所指定的方式。因此,在使用扩展查询协议时, + 二进制游标这一概念实际上已经过时了 + — 任何游标都可以按文本或二进制方式处理。 + + + + + 除非指定了WITH HOLD,否则该命令创建的游标只能在当前事务中使用。 + 因此,DECLARE若不带WITH HOLD,在事务块之外就毫无用处:游标只能存活到该语句执行完成。 + 所以,如果在事务块之外使用这种命令,PostgreSQL会报错。 + 可使用(或)来定义事务块。 + + + + 如果指定了WITH HOLD,且创建游标的事务成功提交, + 那么在同一会话中的后续事务里仍可继续访问该游标。 + (但如果创建事务被中止,游标会被移除。) + 使用WITH HOLD创建的游标会在对其发出显式CLOSE命令时关闭, + 或在会话结束时关闭。在当前实现中,这种游标所表示的行会被复制到临时文件或内存区域中, + 以便它们在后续事务中仍然可用。 + + + + 当查询包括FOR UPDATEFOR SHARE时, + 不能指定WITH HOLD。 + + + + 在定义将用于向后取出行的游标时,应指定SCROLL选项。 + 这是 SQL 标准所要求的。不过,为了兼容早期版本, + 如果游标的查询计划足够简单,以致支持向后取出不需要额外开销, + PostgreSQL也允许在未指定SCROLL的情况下向后取出行。 + 不过,建议应用开发者不要依赖于从未用SCROLL创建的游标中向后取出行。 + 如果指定了NO SCROLL,那么无论如何都不允许向后取出行。 + + + + 当查询包含FOR UPDATEFOR SHARE时, + 同样不允许向后取出行。因此在这种情况下不能指定SCROLL。 + + + + 可滚动游标和WITH HOLD游标如果调用了任何易变函数(参见),可能产生意外结果。重新取出先前已取出的行时,函数可能会再次执行,导致结果与第一次不同。对于这种情况,一个变通方法是将游标声明为WITH HOLD,并在读取其中任何行之前提交事务。这样会强制将游标的整个输出物化到临时存储中,使易变函数对每行恰好执行一次。 + + + 如果游标的查询包含FOR UPDATEFOR SHARE,则返回的行会在首次取出时被锁定,与带有这些选项的普通命令相同。此外,返回的行将是最新版本;因此,这些选项提供了相当于 SQL 标准所称的敏感游标的行为。(将INSENSITIVEFOR UPDATEFOR SHARE一起指定会报错。) + + + + 通常都建议使用FOR UPDATE,如果游标打算与 + UPDATE ... WHERE CURRENT OF或 + DELETE ... WHERE CURRENT OF一起使用。 + 使用FOR UPDATE可以防止其他会话在这些行被取出之后、被更新之前更改它们。 + 如果不使用FOR UPDATE,而某一行在游标创建后已经被更改, + 那么后续的WHERE CURRENT OF命令将不会有任何效果。 + + + + 使用FOR UPDATE的另一个原因是:如果没有它,而后续的WHERE CURRENT OF + 所针对的游标查询不符合 SQL 标准关于简单可更新的规则,则该命令可能失败。 + (特别是,游标必须只引用一个表,且不能使用分组或ORDER BY)。 + 对于并非简单可更新的游标,是否可用取决于计划选择的细节,可能能工作,也可能不能。 + 因此在最坏情况下,应用可能在测试中可用,却会在生产中失败。 + 如果指定了FOR UPDATE,则可保证该游标可更新。 + + + + 不将FOR UPDATEWHERE CURRENT OF一起使用的主要原因, + 是你需要游标可滚动,或者需要它对后续更新不敏感(也就是说,继续显示旧数据)。 + 如果这是需求,请务必仔细注意上面的警告。 + + + + + SQL 标准只为嵌入式SQL中的游标作出规定。 + PostgreSQL服务器没有为游标实现OPEN语句; + 游标在声明时即被视为打开。 + 不过,ECPGPostgreSQL的嵌入式 SQL 预处理器, + 它支持标准 SQL 的游标约定, + 包括涉及DECLAREOPEN语句的那些约定。 + + + + 你可以通过查询pg_cursors + 系统视图查看所有可用游标。 + + + + + + 示例 + + + 声明一个游标: + +DECLARE liahona CURSOR FOR SELECT * FROM films; + + 更多游标用法示例见。 + + + + + 兼容性 + + SQL 标准规定,游标默认是否对底层数据的并发更新敏感,由实现决定。在PostgreSQL中,游标默认不敏感,可以通过指定FOR UPDATE使其敏感。其他产品的行为可能不同。 + + + SQL 标准只允许在嵌入式SQL和模块中使用游标。 + PostgreSQL允许以交互方式使用游标。 + + + + 二进制游标是PostgreSQL的一种扩展。 + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/delete.sgml b/zh/9.6/ref/delete.sgml new file mode 100644 index 00000000..a3147334 --- /dev/null +++ b/zh/9.6/ref/delete.sgml @@ -0,0 +1,241 @@ + + + + + DELETE + + + + DELETE + 7 + SQL - 语言语句 + + + + DELETE + 删除表中的行 + + + + +[ WITH [ RECURSIVE ] with_query [, ...] ] +DELETE FROM [ ONLY ] table_name [ * ] [ [ AS ] alias ] + [ USING from_item [, ...] ] + [ WHERE condition | WHERE CURRENT OF cursor_name ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] + + + + + 描述 + + + DELETE从指定表中删除满足 + WHERE子句的行。如果省略WHERE + 子句,效果就是删除表中的所有行。结果是一个合法但为空的表。 + + + + + 是一种PostgreSQL扩展,它提供了一种更快的机制来移除表中的所有行。 + + + + + 有两种方法可以利用数据库中其他表所包含的信息来删除一个表中的行: + 使用子选择,或者在USING子句中指定附加表。 + 哪种技术更合适取决于具体情况。 + + + + 可选的RETURNING子句使DELETE + 基于每个实际被删除的行计算并返回一个或多个值。可以计算使用该表 + 列和/或USING中提到的其他表列的任意表达式。 + RETURNING列表的语法与SELECT + 的输出列表相同。 + + + + 要从一个表中删除行,必须具有该表上的DELETE权限, + 以及USING子句中出现的任何表或者其值会在 + condition中读取的任 + 何表上的SELECT权限。 + + + + + 参数 + + + + with_query + + + WITH子句允许你指定一个或多个子查询,这些子查 + 询可在DELETE查询中按名称引用。详见 + 。 + + + + + + table_name + + + 要从中删除行的表名(可以是模式限定的)。如果在表名前指定了 + ONLY,只会从所提及表中删除匹配行。如果未指定 + ONLY,还会删除继承自该表的任何表中的匹配行。 + 可选地,可以在表名后指定*,以显式指示包含后代 + 表。 + + + + + + alias + + + 目标表的替代名称。提供别名时,它会完全隐藏该表的实际名称。 + 例如,给定DELETE FROM foo AS f, + DELETE语句的其余部分必须将该表称为 + f,而不是foo。 + + + + + + from_item + + 一个表表达式,允许其他表的列出现在WHERE条件中。这里使用的语法与相同,即SELECT语句中的相应子句;例如,可以为表名指定别名。不要把目标表重复写成from_item,除非打算进行自连接(这种情况下,它必须在from_item中带有别名出现)。 + + + + + condition + + + 一个返回boolean类型值的表达式。只有使这个表达式返 + 回true的行才会被删除。 + + + + + + cursor_name + + WHERE CURRENT OF条件中使用的游标名称。要删除的行是最近从该游标取出的那一行。该游标必须是针对DELETE目标表的非分组查询。注意,WHERE CURRENT OF不能与布尔条件一起指定。有关游标使用的更多信息,请参阅中关于WHERE CURRENT OF的说明。 + + + + + output_expression + + 每行删除之后,由DELETE命令计算并返回的表达式。该表达式可以使用table_name所指定的表或USING中列出的表中的任何列名。写成*可返回所有列。 + + + + + output_name + + 返回列所使用的名称。 + + + + + + + 输出 + + + 在成功完成时,一个DELETE命令会返回以下形式 + 的命令标签: + +DELETE count + + count是被删除的行数。 + 注意,当删除被BEFORE DELETE触发器抑制时,这个 + 数量可能小于匹配condition + 的行数。如果count为 0, + 则该查询没有删除任何行(这不被视为错误)。 + + + + 如果DELETE命令包含RETURNING + 子句,其结果将类似于一个SELECT语句,其中包含 + RETURNING列表中定义的列和值,并在该命令删除的 + 行上进行计算。 + + + + + 注解 + + + PostgreSQL允许通过在 + USING子句中指定其他表,在 + WHERE条件中引用这些表的列。例如,要删除由给定 + 制片人制作的所有电影,可以这样做: + +DELETE FROM films USING producers + WHERE producer_id = producers.id AND producers.name = 'foo'; + + 本质上,这里发生的是在films和 + producers之间进行连接,并将所有成功连接 + 到的films行标记为删除。这种语法不是标准 + 的。更标准的写法是: + +DELETE FROM films + WHERE producer_id IN (SELECT id FROM producers WHERE name = 'foo'); + + 在某些情况下,连接形式比子选择形式更容易书写或者执行更快。 + + + + + 示例 + + + 删除所有电影,但音乐剧除外: + +DELETE FROM films WHERE kind <> 'Musical'; + + + + + 清空表films: + +DELETE FROM films; + + + + + 删除已完成的任务,并返回被删除行的完整详情: + +DELETE FROM tasks WHERE status = 'DONE' RETURNING *; + + + + + 删除游标c_tasks当前所定位的 + tasks行: + +DELETE FROM tasks WHERE CURRENT OF c_tasks; + + + + + + 兼容性 + + + 这个命令符合SQL标准,不过 + USINGRETURNING子句是 + PostgreSQL扩展,在 + DELETE中使用WITH也是扩展。 + + + + diff --git a/zh/9.6/ref/discard.sgml b/zh/9.6/ref/discard.sgml new file mode 100644 index 00000000..63c210bc --- /dev/null +++ b/zh/9.6/ref/discard.sgml @@ -0,0 +1,110 @@ + + + + + DISCARD + + + + DISCARD + 7 + SQL - 语言语句 + + + + DISCARD + 丢弃会话状态 + + + + +DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP } + + + + + 描述 + + + DISCARD释放与数据库会话关联的内部资源。 + 该命令可用于部分或完全重置会话状态。 + 它提供了若干子命令,用于释放不同类型的资源; + DISCARD ALL变体涵盖了其他所有子命令, + 并且还会重置额外的状态。 + + + + + 参数 + + + + + PLANS + + + 释放所有缓存的查询计划,从而在下一次使用相关预备语句时强制重新规划。 + + + + + + SEQUENCES + + + 丢弃所有缓存的序列相关状态,包括 + currval()/lastval()信息, + 以及所有尚未由nextval()返回的预分配序列值。 + (关于预分配序列值的说明,请参见。) + + + + + + TEMPORARYTEMP + + + 删除当前会话中创建的所有临时表。 + + + + + + ALL + + 释放与当前会话关联的所有临时资源,并将会话重置为初始状态。目前,这与执行以下语句序列具有相同效果: +SET SESSION AUTHORIZATION DEFAULT; +RESET ALL; +DEALLOCATE ALL; +CLOSE ALL; +UNLISTEN *; +SELECT pg_advisory_unlock_all(); +DISCARD PLANS; +DISCARD SEQUENCES; +DISCARD TEMP; + + + + + + + + + 注解 + + + DISCARD ALL不能在事务块内部执行。 + + + + + 兼容性 + + + DISCARDPostgreSQL扩展。 + + + diff --git a/zh/9.6/ref/do.sgml b/zh/9.6/ref/do.sgml new file mode 100644 index 00000000..28b0592e --- /dev/null +++ b/zh/9.6/ref/do.sgml @@ -0,0 +1,123 @@ + + + + + DO + + + + 匿名代码块 + + + + DO + 7 + SQL - 语言语句 + + + + DO + 执行匿名代码块 + + + + +DO [ LANGUAGE lang_name ] code + + + + + 描述 + + + DO执行匿名代码块,换言之, + 就是执行一个以过程语言编写的瞬时匿名函数。 + + + + 该代码块会被当作一个不带参数、返回void的函数体。 + 它只会被解析并执行一次。 + + + + 可选的LANGUAGE子句可以写在代码块之前,也可以写在之后。 + + + + + 参数 + + + + code + + + 要执行的过程语言代码。 + 与CREATE FUNCTION一样,它必须以字符串字面值形式指定。 + 建议使用美元引用的字符串字面值。 + + + + + + lang_name + + + 编写该代码所用的过程语言名称。 + 如果省略,默认值为plpgsql。 + + + + + + + + 注解 + + + 要使用的过程语言必须已经通过CREATE LANGUAGE安装到当前数据库中。 + plpgsql默认已安装,而其他语言则不会。 + + + + 用户必须拥有该过程语言的USAGE权限; + 如果该语言是不受信任的,则必须是超级用户。 + 这与用该语言创建函数时的权限要求相同。 + + + + + 示例 + + 将模式public中所有视图上的全部权限授予角色webuser: + +DO $$DECLARE r record; +BEGIN + FOR r IN SELECT table_schema, table_name FROM information_schema.tables + WHERE table_type = 'VIEW' AND table_schema = 'public' + LOOP + EXECUTE 'GRANT ALL ON ' || quote_ident(r.table_schema) || '.' || quote_ident(r.table_name) || ' TO webuser'; + END LOOP; +END$$; + + + + + 兼容性 + + + SQL 标准中没有DO语句。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/drop_access_method.sgml b/zh/9.6/ref/drop_access_method.sgml new file mode 100644 index 00000000..3cf87b0d --- /dev/null +++ b/zh/9.6/ref/drop_access_method.sgml @@ -0,0 +1,107 @@ + + + + + DROP ACCESS METHOD + + + + DROP ACCESS METHOD + 7 + SQL - 语言语句 + + + + DROP ACCESS METHOD + 移除一个访问方法 + + + + +DROP ACCESS METHOD [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP ACCESS METHOD删除一个现有访问方法。 + 只有超级用户才能删除访问方法。 + + + + + 参数 + + + + IF EXISTS + + + 如果该访问方法不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有访问方法的名称。 + + + + + + CASCADE + + + 自动删除依赖于该访问方法的对象 + (例如操作符类、操作符族和索引),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该访问方法,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 删除访问方法heptree: + +DROP ACCESS METHOD heptree; + + + + + 兼容性 + + + DROP ACCESS METHOD是一种PostgreSQL扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_aggregate.sgml b/zh/9.6/ref/drop_aggregate.sgml new file mode 100644 index 00000000..c70b941d --- /dev/null +++ b/zh/9.6/ref/drop_aggregate.sgml @@ -0,0 +1,165 @@ + + + + + DROP AGGREGATE + + + + DROP AGGREGATE + 7 + SQL - 语言语句 + + + + DROP AGGREGATE + 移除一个聚合函数 + + + + +DROP AGGREGATE [ IF EXISTS ] name ( aggregate_signature ) [ CASCADE | RESTRICT ] + +其中 aggregate_signature 是: + +* | +[ argmode ] [ argname ] argtype [ , ... ] | +[ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + + + + + 描述 + + + DROP AGGREGATE移除一个现有聚合函数。 + 要执行此命令,当前用户必须是该聚合函数的拥有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该聚合函数不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有聚合函数的名称(可以被模式限定)。 + + + + + + argmode + + + + 参数的模式:INVARIADIC。 + 如果省略,默认值为IN。 + + + + + + argname + + + + 一个参数的名称。注意,DROP AGGREGATE + 实际上不会关注参数名,因为确定聚合函数标识所需的只有参数数据类型。 + + + + + + argtype + + + 聚合函数作用于其上的输入数据类型。要引用一个零参数聚合函数, + 请在参数说明列表的位置写*。要引用一个有序集聚合函数, + 请在直接参数说明和聚合参数说明之间写上ORDER BY。 + + + + + + CASCADE + + + 自动删除依赖于该聚合函数的对象(例如使用它的视图),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该聚合函数,则拒绝删除它。这是默认值。 + + + + + + + + 注解 + + + 关于引用有序集聚合的其他语法,参见。 + + + + + 示例 + + + 要移除用于类型integer的聚合函数myavg: + +DROP AGGREGATE myavg(integer); + + + + + 要移除假想集聚合函数myrank,它接受任意个排序列组成的列表 + 以及与之匹配的直接参数列表: + +DROP AGGREGATE myrank(VARIADIC "any" ORDER BY VARIADIC "any"); + + + + + + + 兼容性 + + + SQL 标准中没有DROP AGGREGATE语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_cast.sgml b/zh/9.6/ref/drop_cast.sgml new file mode 100644 index 00000000..877ede67 --- /dev/null +++ b/zh/9.6/ref/drop_cast.sgml @@ -0,0 +1,114 @@ + + + + + DROP CAST + + + + DROP CAST + 7 + SQL - 语言语句 + + + + DROP CAST + 移除一个类型转换 + + + + +DROP CAST [ IF EXISTS ] (source_type AS target_type) [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP CAST移除一个先前定义的类型转换。 + + + + 要删除一种类型转换,你必须拥有其源数据类型或目标数据类型。 + 这与创建该类型转换所需的权限相同。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该类型转换不存在,则不要抛出错误。这种情况下会发出一条提示。 + + + + + + source_type + + + + 该类型转换的源数据类型的名称。 + + + + + + target_type + + + + 该类型转换的目标数据类型的名称。 + + + + + + CASCADE + RESTRICT + + + + 这些关键字没有任何作用,因为没有对象依赖于类型转换。 + + + + + + + + 示例 + + + 要删除从类型text到类型int的类型转换: + +DROP CAST (text AS int); + + + + + 兼容性 + + + DROP CAST命令符合 SQL 标准。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_collation.sgml b/zh/9.6/ref/drop_collation.sgml new file mode 100644 index 00000000..83f1144c --- /dev/null +++ b/zh/9.6/ref/drop_collation.sgml @@ -0,0 +1,109 @@ + + + + + DROP COLLATION + + + + DROP COLLATION + 7 + SQL - 语言语句 + + + + DROP COLLATION + 删除一个排序规则 + + + + +DROP COLLATION [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP COLLATION删除一个先前定义的排序规则。 + 要删除一个排序规则,你必须拥有该排序规则。 + + + + + 参数 + + + + IF EXISTS + + + 如果该排序规则不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + + 排序规则的名称。该名称可以使用模式限定。 + + + + + + CASCADE + + + 自动删除依赖于该排序规则的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该排序规则,则拒绝删除该排序规则。这是默认值。 + + + + + + + + 示例 + + + 删除名为german的排序规则: + +DROP COLLATION german; + + + + + 兼容性 + + + DROP COLLATION命令符合SQL标准, + 但IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_conversion.sgml b/zh/9.6/ref/drop_conversion.sgml new file mode 100644 index 00000000..1a1d2d2c --- /dev/null +++ b/zh/9.6/ref/drop_conversion.sgml @@ -0,0 +1,102 @@ + + + + + DROP CONVERSION + + + + DROP CONVERSION + 7 + SQL - 语言语句 + + + + DROP CONVERSION + 移除一个转换 + + + + +DROP CONVERSION [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP CONVERSION移除一个先前定义的转换。 + 要能够删除一个转换,你必须拥有该转换。 + + + + + 参数 + + + + IF EXISTS + + + 如果该转换不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + + 转换的名称。转换名可以是模式限定的。 + + + + + + CASCADE + RESTRICT + + + + 这些关键字没有任何作用,因为没有对象依赖于转换。 + + + + + + + + 示例 + + + 移除名为myname的转换: + +DROP CONVERSION myname; + + + + + 兼容性 + + + SQL 标准中没有DROP CONVERSION语句,但有一个 + DROP TRANSLATION语句,它与CREATE TRANSLATION + 语句配套,而后者又类似于 PostgreSQL 中的CREATE CONVERSION语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_database.sgml b/zh/9.6/ref/drop_database.sgml new file mode 100644 index 00000000..acb5254c --- /dev/null +++ b/zh/9.6/ref/drop_database.sgml @@ -0,0 +1,90 @@ + + + + + DROP DATABASE + + + + DROP DATABASE + 7 + SQL - 语言语句 + + + + DROP DATABASE + 移除一个数据库 + + + + +DROP DATABASE [ IF EXISTS ] name + + + + + 描述 + + DROP DATABASE删除一个数据库。它会移除数据库的系统目录条目,并删除包含数据的目录。只有数据库拥有者才能执行此命令。此外,当你或任何其他人连接到目标数据库时,都不能执行它。(请连接到postgres或任何其他数据库来发出此命令。) + + + DROP DATABASE无法撤销。请谨慎使用! + + + + + 参数 + + + + IF EXISTS + + + 如果该数据库不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的数据库名称。 + + + + + + + + 注解 + + + DROP DATABASE不能在事务块内执行。 + + + + 由于该命令不能在连接到目标数据库时执行,因此改用程序 + 可能更方便,它是此命令的一个包装器。 + + + + + 兼容性 + + + SQL 标准中没有DROP DATABASE语句。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/drop_domain.sgml b/zh/9.6/ref/drop_domain.sgml new file mode 100644 index 00000000..84bd038f --- /dev/null +++ b/zh/9.6/ref/drop_domain.sgml @@ -0,0 +1,108 @@ + + + + + DROP DOMAIN + + + + DROP DOMAIN + 7 + SQL - 语言语句 + + + + DROP DOMAIN + 移除一个域 + + + + +DROP DOMAIN [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP DOMAIN移除一个域。只有域的拥有者才能移除它。 + + + + + 参数 + + + + IF EXISTS + + + 如果该域不存在,则不会抛出错误,而是发出一条提示。 + + + + + + name + + + 要移除的现有域的名称(可以是模式限定的)。 + + + + + + CASCADE + + + 自动删除依赖于该域的对象(例如表列),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该域,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要移除域box: + + +DROP DOMAIN box; + + + + + 兼容性 + + + 此命令符合 SQL 标准,但IF EXISTS选项是 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_event_trigger.sgml b/zh/9.6/ref/drop_event_trigger.sgml new file mode 100644 index 00000000..4cd25424 --- /dev/null +++ b/zh/9.6/ref/drop_event_trigger.sgml @@ -0,0 +1,110 @@ + + + + + DROP EVENT TRIGGER + + + + DROP EVENT TRIGGER + 7 + SQL - 语言语句 + + + + DROP EVENT TRIGGER + 移除一个事件触发器 + + + + +DROP EVENT TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP EVENT TRIGGER移除一个现有的事件触发器。 + 要执行此命令,当前用户必须是该事件触发器的所有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该事件触发器不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的事件触发器名称。 + + + + + + CASCADE + + + 自动删除依赖于该触发器的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该触发器,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 销毁事件触发器snitch: + + +DROP EVENT TRIGGER snitch; + + + + + 兼容性 + + + SQL 标准中没有DROP EVENT TRIGGER语句。 + + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/drop_extension.sgml b/zh/9.6/ref/drop_extension.sgml new file mode 100644 index 00000000..1991dc1c --- /dev/null +++ b/zh/9.6/ref/drop_extension.sgml @@ -0,0 +1,111 @@ + + + + + DROP EXTENSION + + + + DROP EXTENSION + 7 + SQL - 语言语句 + + + + DROP EXTENSION + 移除一个扩展 + + + + +DROP EXTENSION [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + DROP EXTENSION从数据库中移除扩展。删除扩展也会删除它所包含的对象。 + + + 要使用DROP EXTENSION,必须拥有该扩展。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该扩展不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 一个已安装扩展的名称。 + + + + + + CASCADE + + + 自动删除依赖于该扩展的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + 如果有任何对象依赖于扩展,则拒绝删除它(其自身成员对象以及同一DROP命令中列出的其他扩展除外)。这是默认行为。 + + + + + + + 示例 + + + 要从当前数据库中移除扩展hstore: + +DROP EXTENSION hstore; + + 如果数据库中使用了hstore的任何对象, + 例如某些表具有hstore类型的列,则该命令会失败。 + 加上CASCADE选项可以强制一并移除这些依赖对象。 + + + + + 兼容性 + + + DROP EXTENSIONPostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_foreign_data_wrapper.sgml b/zh/9.6/ref/drop_foreign_data_wrapper.sgml new file mode 100644 index 00000000..fc2f4a6f --- /dev/null +++ b/zh/9.6/ref/drop_foreign_data_wrapper.sgml @@ -0,0 +1,109 @@ + + + + + DROP FOREIGN DATA WRAPPER + + + + DROP FOREIGN DATA WRAPPER + 7 + SQL - 语言语句 + + + + DROP FOREIGN DATA WRAPPER + 移除一个外部数据包装器 + + + + +DROP FOREIGN DATA WRAPPER [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP FOREIGN DATA WRAPPER移除一个现有的外部数据包装器。 + 要执行此命令,当前用户必须是该外部数据包装器的拥有者。 + + + + + 参数 + + + + IF EXISTS + + + 如果该外部数据包装器不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有外部数据包装器的名称。 + + + + + + CASCADE + + + 自动删除依赖于该外部数据包装器的对象 + (例如外部表和服务器),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该外部数据包装器,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 删除外部数据包装器dbi: + +DROP FOREIGN DATA WRAPPER dbi; + + + + + 兼容性 + + + DROP FOREIGN DATA WRAPPER符合 ISO/IEC 9075-9(SQL/MED)。 + IF EXISTS子句是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_foreign_table.sgml b/zh/9.6/ref/drop_foreign_table.sgml new file mode 100644 index 00000000..cc70e64d --- /dev/null +++ b/zh/9.6/ref/drop_foreign_table.sgml @@ -0,0 +1,109 @@ + + + + + DROP FOREIGN TABLE + + + + DROP FOREIGN TABLE + 7 + SQL - 语言语句 + + + + DROP FOREIGN TABLE + 移除一个外部表 + + + + +DROP FOREIGN TABLE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP FOREIGN TABLE移除一个外部表。 + 只有外部表的拥有者才能移除它。 + + + + + 参数 + + + + IF EXISTS + + + 如果该外部表不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要删除的外部表名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该外部表的对象(例如视图),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该外部表,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要销毁两个外部表filmsdistributors: + + +DROP FOREIGN TABLE films, distributors; + + + + + 兼容性 + + + 此命令符合 ISO/IEC 9075-9(SQL/MED),但标准每条命令只允许删除一个外部表, + 而IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_function.sgml b/zh/9.6/ref/drop_function.sgml new file mode 100644 index 00000000..48f331e8 --- /dev/null +++ b/zh/9.6/ref/drop_function.sgml @@ -0,0 +1,148 @@ + + + + + DROP FUNCTION + + + + DROP FUNCTION + 7 + SQL - 语言语句 + + + + DROP FUNCTION + 移除一个函数 + + + + +DROP FUNCTION [ IF EXISTS ] name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) + [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP FUNCTION移除一个现有函数的定义。 + 要执行此命令,用户必须是该函数的拥有者。必须指定该函数的参数类型, + 因为可能存在多个名称相同但参数列表不同的函数。 + + + + + 参数 + + + + IF EXISTS + + + 如果该函数不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有函数的名称(可以被模式限定)。 + + + + + + argmode + + + + 参数的模式:INOUT、 + INOUTVARIADIC。 + 如果省略,默认值为IN。注意, + DROP FUNCTION实际上并不关注OUT参数, + 因为确定函数标识只需要输入参数。因此,只列出 + ININOUT和 + VARIADIC参数就足够了。 + + + + + + argname + + + + 一个参数的名称。注意,DROP FUNCTION + 实际上并不关注参数名,因为确定函数标识只需要参数数据类型。 + + + + + + argtype + + + + 函数参数的数据类型(如果有,可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该函数的对象(例如操作符或触发器),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该函数,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 此命令删除平方根函数: + + +DROP FUNCTION sqrt(integer); + + + + + 兼容性 + + + DROP FUNCTION语句在 SQL 标准中有定义, + 但与该命令不兼容。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_group.sgml b/zh/9.6/ref/drop_group.sgml new file mode 100644 index 00000000..8dc907d6 --- /dev/null +++ b/zh/9.6/ref/drop_group.sgml @@ -0,0 +1,53 @@ + + + + + DROP GROUP + + + + DROP GROUP + 7 + SQL - 语言语句 + + + + DROP GROUP + 移除一个数据库角色 + + + + +DROP GROUP [ IF EXISTS ] name [, ...] + + + + + 描述 + + + DROP GROUP 现在是 + 的别名。 + + + + + 兼容性 + + + SQL 标准中没有 DROP GROUP 语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_index.sgml b/zh/9.6/ref/drop_index.sgml new file mode 100644 index 00000000..17cdce94 --- /dev/null +++ b/zh/9.6/ref/drop_index.sgml @@ -0,0 +1,130 @@ + + + + + DROP INDEX + + + + DROP INDEX + 7 + SQL - 语言语句 + + + + DROP INDEX + 移除一个索引 + + + + +DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP INDEX从数据库系统中删除一个现有索引。 + 要执行此命令,你必须是该索引的拥有者。 + + + + + 参数 + + + + CONCURRENTLY + + + 删除索引时,不阻塞该索引所在表上的并发查询、插入、更新和删除。 + 普通的DROP INDEX + 会对该表获取ACCESS EXCLUSIVE锁,阻塞其他访问, + 直到索引删除完成。使用该选项时,命令会改为等待相冲突的事务完成。 + + + 使用此选项时有若干限制需要注意。只能指定一个索引名,并且不支持 + CASCADE选项。(因此,支撑UNIQUE + 或PRIMARY KEY约束的索引不能以这种方式删除。) + 另外,普通的DROP INDEX可以在事务块内执行, + 但DROP INDEX CONCURRENTLY不可以。 + + 对于临时表,DROP INDEX始终是非并发的, + 因为没有其他会话可以访问它们,而且非并发删除索引的代价更低。 + + + + + + IF EXISTS + + + 如果该索引不存在,则不要抛出错误。这种情况下会发出一条提示。 + + + + + + name + + + 要移除的索引名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该索引的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该索引,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 此命令会删除索引title_idx: + + +DROP INDEX title_idx; + + + + + 兼容性 + + + DROP INDEXPostgreSQL + 的一种语言扩展。SQL 标准中没有关于索引的规定。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_language.sgml b/zh/9.6/ref/drop_language.sgml new file mode 100644 index 00000000..423baf7d --- /dev/null +++ b/zh/9.6/ref/drop_language.sgml @@ -0,0 +1,118 @@ + + + + + DROP LANGUAGE + + + + DROP LANGUAGE + 7 + SQL - 语言语句 + + + + DROP LANGUAGE + 移除一种过程语言 + + + + +DROP [ PROCEDURAL ] LANGUAGE [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP LANGUAGE移除一种先前注册的过程语言的定义。 + 要使用DROP LANGUAGE,必须是超级用户或该语言的所有者。 + + + + + 从PostgreSQL 9.1 起,大多数过程语言都已被实现为扩展, + 因此应当使用 + 而不是DROP LANGUAGE来移除它们。 + + + + + + 参数 + + + + + IF EXISTS + + + 如果该语言不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + 现有过程语言的名称。为向后兼容,名称可以用单引号括起。 + + + + + CASCADE + + + 自动删除依赖于该语言的对象(例如以该语言编写的函数), + 以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该语言,则拒绝删除它。 + 这是默认值。 + + + + + + + + 示例 + + + 此命令移除过程语言plsample: + + +DROP LANGUAGE plsample; + + + + + 兼容性 + + + SQL 标准中没有DROP LANGUAGE语句。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/drop_materialized_view.sgml b/zh/9.6/ref/drop_materialized_view.sgml new file mode 100644 index 00000000..ce4dfe2a --- /dev/null +++ b/zh/9.6/ref/drop_materialized_view.sgml @@ -0,0 +1,109 @@ + + + + + DROP MATERIALIZED VIEW + + + + DROP MATERIALIZED VIEW + 7 + SQL - 语言语句 + + + + DROP MATERIALIZED VIEW + 移除一个物化视图 + + + + +DROP MATERIALIZED VIEW [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP MATERIALIZED VIEW删除一个现有的物化视图。 + 要执行此命令,你必须是该物化视图的拥有者。 + + + + + 参数 + + + + IF EXISTS + + + 如果该物化视图不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的物化视图名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该物化视图的对象(例如其他物化视图或常规视图), + 以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该物化视图,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 这个命令将移除名为order_summary的物化视图: + +DROP MATERIALIZED VIEW order_summary; + + + + + 兼容性 + + + DROP MATERIALIZED VIEW是一种PostgreSQL扩展。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/drop_opclass.sgml b/zh/9.6/ref/drop_opclass.sgml new file mode 100644 index 00000000..53d1b5a7 --- /dev/null +++ b/zh/9.6/ref/drop_opclass.sgml @@ -0,0 +1,142 @@ + + + + + DROP OPERATOR CLASS + + + + DROP OPERATOR CLASS + 7 + SQL - 语言语句 + + + + DROP OPERATOR CLASS + 移除一个操作符类 + + + + +DROP OPERATOR CLASS [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP OPERATOR CLASS删除一个现有操作符类。 + 要执行此命令,你必须是该操作符类的拥有者。 + + + + DROP OPERATOR CLASS不会删除该类所引用的任何操作符或函数。 + 如果有索引依赖于该操作符类,你将需要指定CASCADE + 才能完成删除。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该操作符类不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 一个现有操作符类的名称(可以被模式限定)。 + + + + + + index_method + + + 该操作符类所属索引访问方法的名称。 + + + + + + CASCADE + + + 自动删除依赖于该操作符类的对象(例如索引),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该操作符类,则拒绝删除它。这是默认值。 + + + + + + + + 注解 + + + DROP OPERATOR CLASS不会删除包含该类的操作符族, + 即使该族中已经没有任何其他成员 + (尤其是在该族由CREATE OPERATOR CLASS隐式创建的情况下)。 + 空的操作符族并无害处,但为了整洁起见,你也许会希望用 + DROP OPERATOR FAMILY删除该族;或者更好的做法是, + 一开始就直接使用DROP OPERATOR FAMILY。 + + + + + 示例 + + + 移除 B-tree 操作符类widget_ops: + + +DROP OPERATOR CLASS widget_ops USING btree; + + + 如果仍有索引使用该操作符类,此命令将不会成功。 + 加上CASCADE可以在删除操作符类的同时删除这类索引。 + + + + + 兼容性 + + + SQL 标准中没有DROP OPERATOR CLASS语句。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/drop_operator.sgml b/zh/9.6/ref/drop_operator.sgml new file mode 100644 index 00000000..7539343b --- /dev/null +++ b/zh/9.6/ref/drop_operator.sgml @@ -0,0 +1,137 @@ + + + + + DROP OPERATOR + + + + DROP OPERATOR + 7 + SQL - 语言语句 + + + + DROP OPERATOR + 移除一个操作符 + + + + +DROP OPERATOR [ IF EXISTS ] name ( { left_type | NONE } , { right_type | NONE } ) [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP OPERATOR从数据库系统中删除一个现有操作符。 + 要执行此命令,你必须是该操作符的拥有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该操作符不存在,则不要抛出错误。这种情况下会发出一条提示。 + + + + + + name + + + 一个现有操作符的名称(可以是模式限定的)。 + + + + + + left_type + + + 操作符左操作数的数据类型;如果该操作符没有左操作数,请写 + NONE。 + + + + + + right_type + + 操作符右操作数的数据类型;如果操作符没有右操作数,请写NONE + + + + + CASCADE + + + 自动删除依赖于该操作符的对象(例如使用它的视图),以及依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该操作符,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + 删除幂操作符a^b,其类型为 integer: + +DROP OPERATOR ^ (integer, integer); + + + + 删除前缀一元按位取反操作符 ~b,其类型为 bit: + +DROP OPERATOR ~ (none, bit); + + + + 删除后缀一元阶乘操作符 x!,其类型为 bigint: + +DROP OPERATOR ! (bigint, none); + + + + + + 兼容性 + + + SQL 标准中没有DROP OPERATOR语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_opfamily.sgml b/zh/9.6/ref/drop_opfamily.sgml new file mode 100644 index 00000000..743c2568 --- /dev/null +++ b/zh/9.6/ref/drop_opfamily.sgml @@ -0,0 +1,131 @@ + + + + + DROP OPERATOR FAMILY + + + + DROP OPERATOR FAMILY + 7 + SQL - 语言语句 + + + + DROP OPERATOR FAMILY + 移除一个操作符族 + + + + +DROP OPERATOR FAMILY [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP OPERATOR FAMILY删除一个现有的操作符族。 + 要执行此命令,你必须是该操作符族的拥有者。 + + + + DROP OPERATOR FAMILY还会删除该族中包含的所有操作符类, + 但不会删除该族引用的任何操作符或函数。如果有任何索引依赖于该族内的操作符类, + 你需要指定CASCADE才能完成删除。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该操作符族不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 一个现有操作符族的名称(可以是模式限定的)。 + + + + + + index_method + + + 该操作符族所对应的索引访问方法名称。 + + + + + + CASCADE + + + 自动删除依赖于该操作符族的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该操作符族,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 移除 B-tree 操作符族float_ops: + + +DROP OPERATOR FAMILY float_ops USING btree; + + + 如果仍有索引使用该族内的操作符类,此命令将不会成功。 + 加上CASCADE可以在删除操作符族的同时删除这类索引。 + + + + + 兼容性 + + + SQL 标准中没有DROP OPERATOR FAMILY语句。 + + + + + 另见 + + + + + + + + + + + diff --git a/zh/9.6/ref/drop_owned.sgml b/zh/9.6/ref/drop_owned.sgml new file mode 100644 index 00000000..ea20668a --- /dev/null +++ b/zh/9.6/ref/drop_owned.sgml @@ -0,0 +1,113 @@ + + + + + DROP OWNED + + + + DROP OWNED + 7 + SQL - 语言语句 + + + + DROP OWNED + 移除一个数据库角色所拥有的数据库对象 + + + + +DROP OWNED BY { name | CURRENT_USER | SESSION_USER } [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP OWNED删除当前数据库中由指定角色之一拥有的所有对象。 + 同时,授予给这些角色的、针对当前数据库中的对象或共享对象 + (数据库、表空间)的任何权限也会被撤销。 + + + + + 参数 + + + + name + + + 一个角色的名称;该角色拥有的对象将被删除,并且授予给该角色的权限将被撤销。 + + + + + + CASCADE + + + 自动删除依赖于受影响对象的对象,并进一步删除所有依赖于这些对象的对象 + (参见)。 + + + + + + RESTRICT + + + 如果有其他数据库对象依赖于某个受影响对象,则拒绝删除该角色拥有的对象。 + 这是默认值。 + + + + + + + + 注解 + + DROP OWNED经常被用来为移除一个或多个角色做准备。 + 因为DROP OWNED只影响当前数据库中的对象, + 所以通常需要在包含待移除角色所拥有对象的每个数据库中执行此命令。 + + + + 使用CASCADE选项可能会使该命令递归到由其他用户拥有的对象。 + + + 命令提供了另一种选择,它会重新分配由一个或多个角色拥有的所有数据库对象的拥有关系。不过,REASSIGN OWNED不处理针对其他对象的权限。 + + + 由这些角色拥有的数据库和表空间不会被移除。 + + + + 更多讨论请见。 + + + + + 兼容性 + + + DROP OWNED命令是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_policy.sgml b/zh/9.6/ref/drop_policy.sgml new file mode 100644 index 00000000..9c8e7f44 --- /dev/null +++ b/zh/9.6/ref/drop_policy.sgml @@ -0,0 +1,115 @@ + + + + + DROP POLICY + + + + DROP POLICY + 7 + SQL - 语言语句 + + + + DROP POLICY + 从一个表中移除一条行级安全性策略 + + + + +DROP POLICY [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP POLICY从表中移除指定的策略。注意,如果移除了某表的最后一条策略, + 并且该表仍通过ALTER TABLE启用了行级安全性,那么将采用默认拒绝策略。 + 无论该表是否存在策略,都可以使用 + ALTER TABLE ... DISABLE ROW LEVEL SECURITY + 来禁用该表的行级安全性。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该策略不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要删除的策略名称。 + + + + + + table_name + + + 该策略所在表的名称(可以被模式限定)。 + + + + + + CASCADE + RESTRICT + + + + 由于策略上不存在依赖关系,这些关键字不起任何作用。 + + + + + + + + + 示例 + + + 要删除表my_table上名为p1的策略: + + +DROP POLICY p1 ON my_table; + + + + + + 兼容性 + + + DROP POLICYPostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_role.sgml b/zh/9.6/ref/drop_role.sgml new file mode 100644 index 00000000..b6058ac6 --- /dev/null +++ b/zh/9.6/ref/drop_role.sgml @@ -0,0 +1,107 @@ + + + + + DROP ROLE + + + + DROP ROLE + 7 + SQL - 语言语句 + + + + DROP ROLE + 移除一个数据库角色 + + + + +DROP ROLE [ IF EXISTS ] name [, ...] + + + + + 描述 + + DROP ROLE移除指定的角色。要删除超级用户角色,你自己也必须是超级用户;要删除非超级用户角色,必须拥有CREATEROLE权限。 + + 如果某角色仍在该集簇的任何数据库中被引用,就不能移除它,否则会报错。在删除角色之前,必须删除它拥有的所有对象(或重新分配其拥有关系),并撤销该角色在其他对象上被授予的任何权限。命令可用于此目的;更多讨论参见 + + + 不过,不必手工移除与该角色相关的角色成员资格; + DROP ROLE会自动撤销目标角色在其他角色中的任何成员资格, + 以及其他角色在目标角色中的任何成员资格。 + 其他角色不会被删除,也不会受到影响。 + + + + + 参数 + + + + IF EXISTS + + + 如果该角色不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的角色名称。 + + + + + + + + 注解 + + + PostgreSQL包含一个程序, + 它与此命令具有相同功能(实际上它就是调用此命令), + 但可以从命令 shell 中运行。 + + + + + 示例 + + + 删除一个角色: + +DROP ROLE jonathan; + + + + + 兼容性 + + + SQL 标准定义了DROP ROLE,但它一次只允许删除一个角色, + 并且规定的权限要求也不同于PostgreSQL。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/drop_rule.sgml b/zh/9.6/ref/drop_rule.sgml new file mode 100644 index 00000000..00932e10 --- /dev/null +++ b/zh/9.6/ref/drop_rule.sgml @@ -0,0 +1,118 @@ + + + + + DROP RULE + + + + DROP RULE + 7 + SQL - 语言语句 + + + + DROP RULE + 移除一条重写规则 + + + + +DROP RULE [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP RULE移除一条重写规则。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该规则不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的规则名称。 + + + + + + table_name + + + 该规则适用的表或视图名称(可以是模式限定的)。 + + + + + + CASCADE + + + 自动删除依赖于该规则的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该规则,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要移除重写规则newrule: + + +DROP RULE newrule ON mytable; + + + + + 兼容性 + + + DROP RULEPostgreSQL + 的一种语言扩展,整个查询重写系统也是如此。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_schema.sgml b/zh/9.6/ref/drop_schema.sgml new file mode 100644 index 00000000..fb7576bd --- /dev/null +++ b/zh/9.6/ref/drop_schema.sgml @@ -0,0 +1,121 @@ + + + + + DROP SCHEMA + + + + DROP SCHEMA + 7 + SQL - 语言语句 + + + + DROP SCHEMA + 移除一个模式 + + + + +DROP SCHEMA [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP SCHEMA从数据库中移除一个或多个模式。 + + + + 一个模式只能由其拥有者或超级用户删除。注意,即使该模式的拥有者并不拥有模式内的某些对象, + 也仍然可以删除该模式(从而删除其中的全部对象)。 + + + + + 参数 + + + + IF EXISTS + + + 如果该模式不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 模式名称。 + + + + + + CASCADE + + + 自动删除模式中包含的对象(表、函数等),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果该模式中含有任何对象,则拒绝删除它。这是默认值。 + + + + + + + + 注解 + + + 使用CASCADE选项时,该命令除所命名的模式外,还可能删除其他模式中的对象。 + + + + + 示例 + + + 要从数据库中移除模式mystuff及其包含的全部内容: + + +DROP SCHEMA mystuff CASCADE; + + + + + 兼容性 + + + DROP SCHEMA完全符合 SQL 标准,但标准每条命令只允许删除一个模式, + 另外IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_sequence.sgml b/zh/9.6/ref/drop_sequence.sgml new file mode 100644 index 00000000..6a34c691 --- /dev/null +++ b/zh/9.6/ref/drop_sequence.sgml @@ -0,0 +1,109 @@ + + + + + DROP SEQUENCE + + + + DROP SEQUENCE + 7 + SQL - 语言语句 + + + + DROP SEQUENCE + 移除一个序列 + + + + +DROP SEQUENCE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP SEQUENCE移除序列。 + 只有该序列的拥有者或超级用户才能删除它。 + + + + + 参数 + + + + IF EXISTS + + + 如果该序列不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要删除的序列名称(可以是模式限定的)。 + + + + + + CASCADE + + + 自动删除依赖于该序列的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该序列,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要移除序列serial: + + +DROP SEQUENCE serial; + + + + + 兼容性 + + + DROP SEQUENCE符合SQL标准,但标准每条命令只允许删除一个序列, + 而IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_server.sgml b/zh/9.6/ref/drop_server.sgml new file mode 100644 index 00000000..ff32c7ba --- /dev/null +++ b/zh/9.6/ref/drop_server.sgml @@ -0,0 +1,108 @@ + + + + + DROP SERVER + + + + DROP SERVER + 7 + SQL - 语言语句 + + + + DROP SERVER + 移除一个外部服务器描述符 + + + + +DROP SERVER [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP SERVER移除一个现有外部服务器描述符。 + 要执行此命令,当前用户必须是该服务器的拥有者。 + + + + + 参数 + + + + IF EXISTS + + + 如果该服务器不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有服务器的名称。 + + + + + + CASCADE + + + 自动删除依赖于该服务器的对象(例如用户映射),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该服务器,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 如果服务器foo存在,则删除它: + +DROP SERVER IF EXISTS foo; + + + + + 兼容性 + + + DROP SERVER符合 ISO/IEC 9075-9(SQL/MED)。 + IF EXISTS子句是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_table.sgml b/zh/9.6/ref/drop_table.sgml new file mode 100644 index 00000000..fc5b0a5e --- /dev/null +++ b/zh/9.6/ref/drop_table.sgml @@ -0,0 +1,119 @@ + + + + + DROP TABLE + + + + DROP TABLE + 7 + SQL - 语言语句 + + + + DROP TABLE + 移除一个表 + + + + +DROP TABLE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TABLE从数据库中移除表。 + 只有表拥有者、模式拥有者以及超级用户才能删除表。 + 若要清空表中的行而不销毁该表,请使用 + 或 + 。 + + + + DROP TABLE总会移除目标表上存在的所有索引、规则、触发器和约束。 + 但是,要删除一个被视图或另一张表的外键约束引用的表,必须指定CASCADE。 + (CASCADE会将依赖于该表的视图整个删除,但在外键场景下,它只会删除外键约束, + 不会把另一张表整体删除。) + + + + + 参数 + + + + IF EXISTS + + + 如果该表不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要删除的表名称(可选地使用模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该表的对象(例如视图),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该表,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要删除两个表filmsdistributors: + + +DROP TABLE films, distributors; + + + + + 兼容性 + + + 此命令符合 SQL 标准,只是标准每条命令只允许删除一个表, + 且IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_tablespace.sgml b/zh/9.6/ref/drop_tablespace.sgml new file mode 100644 index 00000000..e342cafa --- /dev/null +++ b/zh/9.6/ref/drop_tablespace.sgml @@ -0,0 +1,105 @@ + + + + + DROP TABLESPACE + + + + DROP TABLESPACE + 7 + SQL - 语言语句 + + + + DROP TABLESPACE + 移除一个表空间 + + + + +DROP TABLESPACE [ IF EXISTS ] name + + + + + 描述 + + + DROP TABLESPACE从系统中移除一个表空间。 + + + + 一个表空间只能由其拥有者或超级用户删除。 + 删除前,该表空间中必须不含任何数据库对象。即使当前数据库中没有对象正在使用该表空间, + 其他数据库中的对象也可能仍然驻留在该表空间中。 + 此外,如果任何活动会话的设置中列出了该表空间, + 那么由于该表空间中可能仍有临时文件,DROP也可能失败。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该表空间不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 表空间的名称。 + + + + + + + + 注解 + + + DROP TABLESPACE不能在事务块内执行。 + + + + + + 示例 + + + 要从系统中移除表空间mystuff: + +DROP TABLESPACE mystuff; + + + + + 兼容性 + + + DROP TABLESPACE是一种PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_transform.sgml b/zh/9.6/ref/drop_transform.sgml new file mode 100644 index 00000000..bbd1cd77 --- /dev/null +++ b/zh/9.6/ref/drop_transform.sgml @@ -0,0 +1,122 @@ + + + + + DROP TRANSFORM + + + + DROP TRANSFORM + 7 + SQL - 语言语句 + + + + DROP TRANSFORM + 移除一个转换 + + + + +DROP TRANSFORM [ IF EXISTS ] FOR type_name LANGUAGE lang_name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TRANSFORM移除一个先前定义的转换。 + + + + 要能够删除一种转换,你必须拥有该类型和该语言。 + 这与创建一种转换所需的权限相同。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该转换不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + type_name + + + + 该转换的数据类型名称。 + + + + + + lang_name + + + + 该转换所用语言的名称。 + + + + + + CASCADE + + + 自动删除依赖于该转换的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该转换,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + 要删除转换,其类型为 hstore,语言为 plpythonu: + +DROP TRANSFORM FOR hstore LANGUAGE plpythonu; + + + + + 兼容性 + + + 这种形式的DROP TRANSFORM是 + PostgreSQL的一种扩展。详见。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_trigger.sgml b/zh/9.6/ref/drop_trigger.sgml new file mode 100644 index 00000000..73a84426 --- /dev/null +++ b/zh/9.6/ref/drop_trigger.sgml @@ -0,0 +1,119 @@ + + + + + DROP TRIGGER + + + + DROP TRIGGER + 7 + SQL - 语言语句 + + + + DROP TRIGGER + 移除一个触发器 + + + + +DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TRIGGER移除一个现有触发器的定义。 + 要执行此命令,当前用户必须是定义该触发器的表的所有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该触发器不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的触发器的名称。 + + + + + + table_name + + + 定义该触发器的表的名称(可以是模式限定的)。 + + + + + + CASCADE + + + 自动删除依赖于该触发器的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该触发器,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 删除表films上的触发器if_dist_exists: + + +DROP TRIGGER if_dist_exists ON films; + + + + + 兼容性 + + + PostgreSQL中的DROP TRIGGER语句 + 与 SQL 标准不兼容。在 SQL 标准中,触发器名称不是表局部的,因此该命令仅为 + DROP TRIGGER name。 + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/drop_tsconfig.sgml b/zh/9.6/ref/drop_tsconfig.sgml new file mode 100644 index 00000000..0881769f --- /dev/null +++ b/zh/9.6/ref/drop_tsconfig.sgml @@ -0,0 +1,113 @@ + + + + + DROP TEXT SEARCH CONFIGURATION + + + + DROP TEXT SEARCH CONFIGURATION + 7 + SQL - 语言语句 + + + + DROP TEXT SEARCH CONFIGURATION + 移除一个文本搜索配置 + + + + +DROP TEXT SEARCH CONFIGURATION [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TEXT SEARCH CONFIGURATION删除一个现有文本搜索配置。 + 要执行此命令,你必须是该配置的拥有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该文本搜索配置不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有文本搜索配置的名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该文本搜索配置的对象,以及所有进一步依赖于这些对象的对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该文本搜索配置,则拒绝删除该配置。这是默认值。 + + + + + + + + 示例 + + + 移除文本搜索配置my_english: + + +DROP TEXT SEARCH CONFIGURATION my_english; + + + 如果已有索引在to_tsvector调用中引用了该配置, + 此命令将不会成功。加上CASCADE可以在删除该文本搜索配置的同时删除这类索引。 + + + + + 兼容性 + + + SQL 标准中没有DROP TEXT SEARCH CONFIGURATION语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_tsdictionary.sgml b/zh/9.6/ref/drop_tsdictionary.sgml new file mode 100644 index 00000000..ba5c3de3 --- /dev/null +++ b/zh/9.6/ref/drop_tsdictionary.sgml @@ -0,0 +1,113 @@ + + + + + DROP TEXT SEARCH DICTIONARY + + + + DROP TEXT SEARCH DICTIONARY + 7 + SQL - 语言语句 + + + + DROP TEXT SEARCH DICTIONARY + 移除一个文本搜索字典 + + + + +DROP TEXT SEARCH DICTIONARY [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TEXT SEARCH DICTIONARY删除一个现有文本搜索字典。 + 要执行此命令,你必须是该字典的拥有者。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该文本搜索字典不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 一个现有文本搜索字典的名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该文本搜索字典的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该文本搜索字典,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 移除文本搜索字典english: + + +DROP TEXT SEARCH DICTIONARY english; + + + 如果已有文本搜索配置使用该字典,此命令将不会成功。 + 加上CASCADE可以在删除字典的同时删除这类配置。 + + + + + 兼容性 + + + SQL 标准中没有DROP TEXT SEARCH DICTIONARY语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_tsparser.sgml b/zh/9.6/ref/drop_tsparser.sgml new file mode 100644 index 00000000..a3ea49d7 --- /dev/null +++ b/zh/9.6/ref/drop_tsparser.sgml @@ -0,0 +1,113 @@ + + + + + DROP TEXT SEARCH PARSER + + + + DROP TEXT SEARCH PARSER + 7 + SQL - 语言语句 + + + + DROP TEXT SEARCH PARSER + 移除一个文本搜索解析器 + + + + +DROP TEXT SEARCH PARSER [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TEXT SEARCH PARSER删除一个现有文本搜索解析器。 + 要使用此命令,你必须是超级用户。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该文本搜索解析器不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 一个现有文本搜索解析器的名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该文本搜索解析器的对象,以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该文本搜索解析器,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 移除文本搜索解析器my_parser: + + +DROP TEXT SEARCH PARSER my_parser; + + + 如果已有文本搜索配置使用该解析器,此命令将不会成功。 + 加上CASCADE可以在删除解析器的同时删除这类配置。 + + + + + 兼容性 + + + SQL 标准中没有DROP TEXT SEARCH PARSER语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_tstemplate.sgml b/zh/9.6/ref/drop_tstemplate.sgml new file mode 100644 index 00000000..2a0fda88 --- /dev/null +++ b/zh/9.6/ref/drop_tstemplate.sgml @@ -0,0 +1,113 @@ + + + + + DROP TEXT SEARCH TEMPLATE + + + + DROP TEXT SEARCH TEMPLATE + 7 + SQL - 语言语句 + + + + DROP TEXT SEARCH TEMPLATE + 移除一个文本搜索模板 + + + + +DROP TEXT SEARCH TEMPLATE [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TEXT SEARCH TEMPLATE删除一个现有文本搜索模板。 + 要使用此命令,你必须是超级用户。 + + + + + 参数 + + + + + IF EXISTS + + + 如果该文本搜索模板不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 现有文本搜索模板的名称(可以被模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该文本搜索模板的对象,以及所有进一步依赖于这些对象的对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该文本搜索模板,则拒绝删除该模板。这是默认值。 + + + + + + + + 示例 + + + 移除文本搜索模板thesaurus: + + +DROP TEXT SEARCH TEMPLATE thesaurus; + + + 如果已有文本搜索字典使用该模板,此命令将不会成功。加上CASCADE + 可以在删除该模板的同时删除这类字典。 + + + + + 兼容性 + + + SQL 标准中没有DROP TEXT SEARCH TEMPLATE语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_type.sgml b/zh/9.6/ref/drop_type.sgml new file mode 100644 index 00000000..ae45c9ac --- /dev/null +++ b/zh/9.6/ref/drop_type.sgml @@ -0,0 +1,110 @@ + + + + + DROP TYPE + + + + DROP TYPE + 7 + SQL - 语言语句 + + + + DROP TYPE + 移除一个数据类型 + + + + +DROP TYPE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP TYPE移除一个用户定义的数据类型。 + 只有该类型的拥有者才能移除它。 + + + + + 参数 + + + + IF EXISTS + + + 如果该类型不存在,则不会抛出错误,而是发出一条提示。 + + + + + + name + + + 要移除的数据类型的名称(可以是模式限定的)。 + + + + + + CASCADE + + + 自动删除依赖于该类型的对象(例如表列、函数和操作符),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该类型,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 要移除数据类型box: + +DROP TYPE box; + + + + + 兼容性 + + + 此命令与 SQL 标准中的对应命令相似,但IF EXISTS选项是 + PostgreSQL扩展。不过请注意, + CREATE TYPE命令的大部分内容,以及 + PostgreSQL中的数据类型扩展机制,都不同于 SQL 标准。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_user.sgml b/zh/9.6/ref/drop_user.sgml new file mode 100644 index 00000000..613f563d --- /dev/null +++ b/zh/9.6/ref/drop_user.sgml @@ -0,0 +1,55 @@ + + + + + DROP USER + + + + DROP USER + 7 + SQL - 语言语句 + + + + DROP USER + 移除一个数据库角色 + + + + +DROP USER [ IF EXISTS ] name [, ...] + + + + + 描述 + + + DROP USER只是 + 的另一种拼写。 + + + + + 兼容性 + + + DROP USER语句是 + PostgreSQL的一种扩展。 + SQL 标准将用户的定义留给具体实现决定。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/drop_user_mapping.sgml b/zh/9.6/ref/drop_user_mapping.sgml new file mode 100644 index 00000000..7b8907e4 --- /dev/null +++ b/zh/9.6/ref/drop_user_mapping.sgml @@ -0,0 +1,104 @@ + + + + + DROP USER MAPPING + + + + DROP USER MAPPING + 7 + SQL - 语言语句 + + + + DROP USER MAPPING + 删除外部服务器的用户映射 + + + + +DROP USER MAPPING [ IF EXISTS ] FOR { user_name | USER | CURRENT_USER | PUBLIC } SERVER server_name + + + + + 描述 + + + DROP USER MAPPING删除外部服务器上的一个现有用户映射。 + + + + 外部服务器的拥有者可以删除该服务器上任何用户的用户映射。 + 此外,如果某个用户已被授予该服务器上的USAGE权限, + 那么该用户也可以删除其自己用户名对应的用户映射。 + + + + + 参数 + + + + IF EXISTS + + + 如果该用户映射不存在,则不要报错。这种情况下会发出一个提示。 + + + + + + user_name + + + 该映射的用户名。CURRENT_USERUSER都匹配当前用户的名称。 + PUBLIC用于匹配系统中当前及未来的所有用户名。 + + + + + + server_name + + + 该用户映射所属服务器的名称。 + + + + + + + + 示例 + + + 如果用户bob在服务器foo上的用户映射存在,则将其删除: + +DROP USER MAPPING IF EXISTS FOR bob SERVER foo; + + + + + 兼容性 + + + DROP USER MAPPING符合 ISO/IEC 9075-9(SQL/MED)。 + IF EXISTS子句是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/drop_view.sgml b/zh/9.6/ref/drop_view.sgml new file mode 100644 index 00000000..692a0970 --- /dev/null +++ b/zh/9.6/ref/drop_view.sgml @@ -0,0 +1,108 @@ + + + + + DROP VIEW + + + + DROP VIEW + 7 + SQL - 语言语句 + + + + DROP VIEW + 移除一个视图 + + + + +DROP VIEW [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + 描述 + + + DROP VIEW删除一个现有视图。 + 要执行此命令,你必须是该视图的拥有者。 + + + + + 参数 + + + + IF EXISTS + + + 如果该视图不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + name + + + 要移除的视图名称(可选地使用模式限定)。 + + + + + + CASCADE + + + 自动删除依赖于该视图的对象(例如其他视图),以及进一步依赖于这些对象的所有对象 + (参见)。 + + + + + + RESTRICT + + + 如果有任何对象依赖于该视图,则拒绝删除它。这是默认值。 + + + + + + + + 示例 + + + 这个命令将移除名为kinds的视图: + +DROP VIEW kinds; + + + + + 兼容性 + + + 此命令符合 SQL 标准,只是标准每条命令只允许删除一个视图, + 且IF EXISTS选项是PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/dropdb.sgml b/zh/9.6/ref/dropdb.sgml new file mode 100644 index 00000000..d702fec0 --- /dev/null +++ b/zh/9.6/ref/dropdb.sgml @@ -0,0 +1,263 @@ + + + + + dropdb + + + + dropdb + 1 + 应用程序 + + + + dropdb + 移除一个PostgreSQL数据库 + + + + + dropdb + connection-option + option + dbname + + + + + + 描述 + + + dropdb删除一个现有的 + PostgreSQL数据库。 + 执行此命令的用户必须是数据库超级用户或该数据库的拥有者。 + + + dropdbSQL命令的包装器。通过此工具删除数据库与通过其他访问服务器的方法删除数据库,在效果上没有区别。 + + + + + + 选项 + + + dropdb接受以下命令行参数: + + dbname + + + 指定要移除的数据库名称。 + + + + + + + + + + 回显dropdb生成并发送给服务器的命令。 + + + + + + + + + + 在执行任何破坏性操作之前发出确认提示。 + + + + + + + + + + 打印dropdb版本并退出。 + + + + + + + + + 如果数据库不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + + + + + 显示有关dropdb命令行参数的帮助并退出。 + + + + + + + + + + dropdb也接受下列用于连接参数的命令行参数: + + + + + + + + 指定服务器所在机器的主机名。如果该值以斜杠开头, + 则它将被用作 Unix 域套接字的目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 指定用于连接的用户名。 + + + + + + + + + + 从不发出密码提示。如果服务器要求密码认证,而密码又无法通过诸如 + .pgpass文件等其他方式获得,则连接尝试将失败。 + 该选项适合没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制dropdb在连接数据库之前提示输入密码。 + + + + 这个选项并非必不可少,因为如果服务器要求密码认证, + dropdb会自动提示输入密码。 + 不过,dropdb会浪费一次连接尝试,才知道服务器需要密码。 + 在某些情况下,为了避免这次额外的连接尝试,提前指定是值得的。 + + + + + + + + + 指定在删除目标数据库时要连接到的数据库名称。如果未指定,将使用 + postgres数据库;若该数据库不存在(或者它正是要被删除的数据库), + 则改用template1。这也可以是一个 + 连接字符串。如果是这种情况, + 其中的连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 环境 + + + + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他PostgreSQL工具一样,此工具也使用 + libpq支持的环境变量 + (见)。 + + + + + + + 诊断 + + + 若遇到困难,请参见, + 其中讨论了潜在问题和错误消息。数据库服务器必须运行在目标主机上。 + 此外,libpq前端库使用的任何默认连接设置和环境变量也都会生效。 + + + + + + + 示例 + + + 要在默认数据库服务器上删除数据库demo: + +$ dropdb demo + + + + + 要使用主机eden、端口 5000 上的服务器删除数据库demo, + 并带确认提示以及查看底层命令: + +$ dropdb -p 5000 -h eden -i -e demo +Database "demo" will be permanently deleted. +Are you sure? (y/n) y +DROP DATABASE demo; + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/droplang.sgml b/zh/9.6/ref/droplang.sgml new file mode 100644 index 00000000..9fa13fa8 --- /dev/null +++ b/zh/9.6/ref/droplang.sgml @@ -0,0 +1,254 @@ + + + + + droplang + + + + droplang + 1 + 应用程序 + + + + droplang + 移除一个 PostgreSQL 过程语言 + + + + + droplang + connection-option + langname + dbname + + + + droplang + connection-option + + dbname + + + + + + 描述 + + + + droplang 是一个从 PostgreSQL 数据库中移除现有过程语言的工具。 + + + + droplang 只是对 SQL 命令的一个包装。 + + + + + droplang 已被弃用,在未来的 PostgreSQL 版本中可能会被移除。建议直接使用 DROP EXTENSION 命令。 + + + + + + + 选项 + + + droplang 接受下列命令行参数: + + + + langname + + + 指定要移除的过程语言的名称。(该名称会被转换为小写。) + + + + + + + + + + 指定要从哪个数据库中移除该语言。默认使用与当前系统用户同名的数据库。 + + + + + + + + + + 在执行 SQL 命令时把它们显示出来。 + + + + + + + + + + 显示目标数据库中已安装语言的列表。 + + + + + + + + + + 打印 droplang 的版本并退出。 + + + + + + + + + + 显示有关 droplang 命令行参数的帮助并退出。 + + + + + + + + + droplang 还接受下列用于连接参数的命令行参数: + + + + + + + + 指定服务器所在主机的主机名。如果该值以斜杠开头,则将其用作 Unix 域套接字所在目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 要用来连接的用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而密码又无法通过诸如.pgpass文件等其他方式获得,则连接尝试将失败。该选项可用于没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制 droplang 在连接数据库之前提示输入密码。 + + + + 该选项绝非必需,因为如果服务器要求密码认证,droplang会自动提示输入密码。不过,droplang会浪费一次连接尝试来发现服务器需要密码。在某些情况下,输入可以避免这次额外的连接尝试。 + + + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 和大部分其他 PostgreSQL 工具一样,这个工具也使用 libpq 支持的环境变量(参见)。 + + + + + + + 诊断 + + + 大多数错误消息都是不言自明的。如果不然,可以带 选项运行 droplang,然后查看相应的 SQL 命令了解详情。此外,libpq 前端库使用的任何默认连接设置和环境变量也都适用。 + + + + + + 注解 + + + 使用 可添加一个语言。 + + + + + + 示例 + + + 要移除语言 pltcl: + +$ droplang pltcl dbname + + + + + 参见 + + + + + + + + + diff --git a/zh/9.6/ref/dropuser.sgml b/zh/9.6/ref/dropuser.sgml new file mode 100644 index 00000000..ae544634 --- /dev/null +++ b/zh/9.6/ref/dropuser.sgml @@ -0,0 +1,249 @@ + + + + + dropuser + + + + dropuser + 1 + 应用程序 + + + + dropuser + 移除一个PostgreSQL用户账户 + + + + + dropuser + connection-option + option + username + + + + + + 描述 + + dropuser移除现有的PostgreSQL用户。只有超级用户和拥有CREATEROLE权限的用户才能移除PostgreSQL用户。(要移除超级用户,你自己也必须是超级用户。) + + dropuserSQL命令的包装器。通过此工具删除用户与通过其他访问服务器的方法删除用户,在效果上没有区别。 + + + + + + 选项 + + + dropuser接受下列命令行参数: + + + + username + + + 指定要移除的PostgreSQL用户名。 + 如果命令行中未指定名称,且使用了/选项, + 则会提示输入名称。 + + + + + + + + + + 回显dropuser生成并发送给服务器的命令。 + + + + + + + + + + 在实际移除该用户之前提示确认;如果命令行中未指定用户名,也会提示输入用户名。 + + + + + + + + + + 打印dropuser版本并退出。 + + + + + + + + + 如果该用户不存在,则不要抛出错误。这种情况下会发出一个提示。 + + + + + + + + + + 显示有关dropuser命令行参数的帮助并退出。 + + + + + + + + + dropuser也接受下列用于连接参数的命令行参数: + + + + + + + + 指定服务器运行所在主机的主机名。如果该值以斜杠开头, + 则它将被用作 Unix 域套接字所在目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 用于连接的用户名(不是要删除的用户名)。 + + + + + + + + + + 从不发出密码提示。如果服务器要求密码认证,而密码又无法通过诸如 + .pgpass文件之类的其他方式获得,则连接尝试将失败。 + 该选项适合没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制dropuser在连接到数据库之前提示输入密码。 + + + + 这个选项绝非必需,因为如果服务器要求密码认证, + dropuser会自动提示输入密码。 + 不过,dropuser会浪费一次连接尝试来发现服务器需要密码。 + 在某些情况下,输入值得一试,以避免这次额外的连接尝试。 + + + + + + + + + + 环境 + + + + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他PostgreSQL工具一样,此工具也使用 + libpq支持的环境变量 + (见)。 + + + + + + + 诊断 + + + 若遇到困难,请参见, + 其中讨论了潜在问题和错误消息。数据库服务器必须在目标主机上运行。 + 此外,libpq前端库使用的任何默认连接设置和环境变量也都适用。 + + + + + + + 示例 + + + 要从默认数据库服务器上移除用户joe: + +$ dropuser joe + + + + + 要使用主机eden、端口 5000 上的服务器移除用户joe, + 并带确认提示以及查看底层命令: + +$ dropuser -p 5000 -h eden -i -e joe +Role "joe" will be permanently removed. +Are you sure? (y/n) y +DROP ROLE joe; + + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/ecpg-ref.sgml b/zh/9.6/ref/ecpg-ref.sgml new file mode 100644 index 00000000..08f0a7dc --- /dev/null +++ b/zh/9.6/ref/ecpg-ref.sgml @@ -0,0 +1,238 @@ + + + + + ecpg + + + + ecpg + 1 + 应用程序 + + + + ecpg + 嵌入式 SQL C 预处理器 + + + + + ecpg + option + file + + + + + + 描述 + + + ecpg是供 C 程序使用的嵌入式 SQL 预处理器。它会把嵌入了 SQL 语句的 C 程序转换成普通 C 代码,方法是将 SQL 调用替换为特殊的函数调用。生成的输出文件随后可以由任何 C 编译器工具链处理。 + + + + ecpg会将命令行中给出的每个输入文件转换成对应的 C 输出文件。 + 若输入文件名没有任何扩展名,则假定其扩展名为.pgc。随后会把该文件的扩展名替换为 + .c,以构造输出文件名。 + 不过,也可以用选项覆盖输出文件名。 + + + + 如果输入文件名只是-ecpg就会从标准输入 + 读取程序(并写到标准输出,除非使用覆盖该行为)。 + + + + 本参考页不描述嵌入式 SQL 语言。有关该主题的更多信息,见。 + + + + + + 选项 + + + ecpg 接受以下命令行参数: + + + + + + + 自动从 SQL 代码生成某些 C 代码。目前,这适用于EXEC SQL TYPE。 + + + + + + + + + 设置兼容模式。mode可以是INFORMIXINFORMIX_SE。 + + + + + + + + + 定义一个 C 预处理器符号。 + + + + + + + + + 处理头文件。指定该选项时,输出文件扩展名变为.h而不是.c, + 默认输入文件扩展名也变为.pgh而不是.pgc。此外,还会强制启用选项。 + + + + + + + + + 也解析系统 include 文件。 + + + + + + + + + 指定一个附加的 include 路径,用于查找通过EXEC SQL INCLUDE包含的文件。默认路径依次为 + .(当前目录)、 + /usr/local/include、 + 在编译时定义的PostgreSQL include 目录(默认: + /usr/local/pgsql/include),以及 + /usr/include。 + + + + + + + + + 指定ecpg应将全部输出写入给定的filename。 + 写成-o -可将全部输出发送到标准输出。 + + + + + + + + + 选择运行时行为。option 可以是以下值之一: + + + + + + 不使用指示器,而改用特殊值表示空值。历史上曾有数据库采用这种方式。 + + + + + + + + 在使用前预备所有语句。libecpg会维护一个已预备语句的缓存;如果某条语句再次执行,就会复用它。若缓存已满,libecpg会释放使用次数最少的语句。 + + + + + + + + 出于兼容性原因,允许使用问号作为占位符。这在很久以前曾是默认行为。 + + + + + + + + + + + + 打开事务自动提交。在此模式下,除非 SQL 命令位于显式事务块中,否则每条 SQL 命令都会自动提交。在默认模式下,只有发出EXEC SQL COMMIT时才会提交命令。 + + + + + + + + + 打印附加信息,包括版本和 include 路径。 + + + + + + + + + 打印ecpg版本并退出。 + + + + + + + + + + 显示关于ecpg命令行参数的帮助信息,并退出。 + + + + + + + + + + + 注解 + + + 在编译预处理后的 C 代码文件时,编译器必须能够在PostgreSQL的 include 目录中找到ECPG头文件。因此,调用编译器时你可能需要使用选项(例如-I/usr/local/pgsql/include)。 + + + + 使用嵌入式 SQL 的 C 程序必须与libecpg库链接,例如可使用链接器选项-L/usr/local/pgsql/lib -lecpg。 + + + + 适合该安装的这两个目录的值可以通过查出。 + + + + + + 示例 + + + 如果你有一个名为prog1.pgc的嵌入式 SQL C 源文件,就可以使用下列命令序列创建一个可执行程序: + +ecpg prog1.pgc +cc -I/usr/local/pgsql/include -c prog1.c +cc -o prog1 prog1.o -L/usr/local/pgsql/lib -lecpg + + + + diff --git a/zh/9.6/ref/end.sgml b/zh/9.6/ref/end.sgml new file mode 100644 index 00000000..bb375657 --- /dev/null +++ b/zh/9.6/ref/end.sgml @@ -0,0 +1,94 @@ + + + + + END + + + + END + 7 + SQL - 语言语句 + + + + END + 提交当前事务 + + + + +END [ WORK | TRANSACTION ] + + + + + 描述 + + + END提交当前事务。 + 该事务所做的所有更改都会对其他会话可见, + 并且如果发生崩溃,也能保证其持久性。 + 该命令是PostgreSQL扩展, + 等效于。 + + + + + 参数 + + + + WORK + TRANSACTION + + + 可选关键字,没有任何作用。 + + + + + + + + 注解 + + 使用 中止事务。 + + + 在事务块之外发出END不会造成任何影响,但会产生一条警告消息。 + + + + + 示例 + + + 提交当前事务,并使所有更改永久生效: + +END; + + + + + 兼容性 + + + 命令ENDPostgreSQL扩展, + 提供了与 SQL 标准规定的等效的功能。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/execute.sgml b/zh/9.6/ref/execute.sgml new file mode 100644 index 00000000..66d25bee --- /dev/null +++ b/zh/9.6/ref/execute.sgml @@ -0,0 +1,105 @@ + + + + + EXECUTE + + + + prepared statements + executing + + + + EXECUTE + 7 + SQL - 语言语句 + + + + EXECUTE + 执行一个预备语句 + + + + +EXECUTE name [ ( parameter [, ...] ) ] + + + + + 描述 + + + EXECUTE用于执行一个先前创建的预备语句。由于预备语句只在一个会话期间内存在,因此该预备语句必须由当前会话中较早执行的PREPARE语句创建。 + + + + 如果创建该语句的PREPARE语句指定了某些参数,那么传递给EXECUTE语句的参数集合就必须与之兼容,否则将引发错误。注意,预备语句(不同于函数)不会根据其参数的类型或个数进行重载;预备语句的名称在一个数据库会话内必须唯一。 + + + + 有关预备语句的创建和用法的更多信息,请参见。 + + + + + 参数 + + + + name + + + 要执行的预备语句名称。 + + + + + + parameter + + + 预备语句中某个参数的实际值。它必须是一个表达式,并且其求值结果必须与该参数的数据类型兼容,该数据类型在创建预备语句时已确定。 + + + + + + + + 输出 + + EXECUTE返回的命令标签是该预备语句的命令标签,而不是EXECUTE。 + + + + + 示例 + + 示例位于一节,详见文档。 + + + + + 兼容性 + + + SQL 标准包含EXECUTE语句,但它只用于嵌入式 SQL。这里的EXECUTE语句还使用了稍有不同的语法。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/explain.sgml b/zh/9.6/ref/explain.sgml new file mode 100644 index 00000000..8255a9f3 --- /dev/null +++ b/zh/9.6/ref/explain.sgml @@ -0,0 +1,325 @@ + + + + + EXPLAIN + + + + 预备语句 + 显示查询计划 + + + + 游标 + 显示查询计划 + + + + EXPLAIN + 7 + SQL - 语言语句 + + + + EXPLAIN + 显示一个语句的执行计划 + + + + +EXPLAIN [ ( option [, ...] ) ] statement +EXPLAIN [ ANALYZE ] [ VERBOSE ] statement + +其中option为以下之一: + + ANALYZE [ boolean ] + VERBOSE [ boolean ] + COSTS [ boolean ] + BUFFERS [ boolean ] + TIMING [ boolean ] + FORMAT { TEXT | XML | JSON | YAML } + + + + + 描述 + + + 这个命令显示PostgreSQL规划器为给定语句生成的执行计划。执行计划会显示将如何扫描该语句引用的表 — 例如普通顺序扫描、索引扫描等 —,如果引用了多个表,还会显示将使用哪些连接算法来汇集每个输入表中的所需行。 + + + + 显示结果中最关键的部分是语句执行代价的估计值,它是规划器对运行该语句需要多长时间的猜测(以任意代价单位衡量,但按惯例表示磁盘页面抓取次数)。实际上会显示两个数字:返回第一行之前的启动代价,以及返回全部行的总代价。对大多数查询来说,总代价才是关键;但在某些场景中,例如EXISTS中的子查询,规划器会选择启动代价最小而不是总代价最小的计划(因为无论如何执行器都会在得到一行后停止)。此外,如果你用LIMIT子句限制返回的行数,规划器会在端点代价之间作适当插值,以估计哪个计划实际上最便宜。 + + + + ANALYZE选项会让该语句被实际执行,而不仅仅是生成计划。随后会把实际运行统计信息加入显示结果,包括每个计划节点中耗费的总时间(以毫秒计)以及它实际返回的总行数。这有助于判断规划器的估计是否接近实际情况。 + + + + + 请记住,使用 ANALYZE 选项时,语句会被实际执行。虽然 EXPLAIN 会丢弃 SELECT 返回的任何输出,但语句的其他副作用仍会照常发生。如果希望使用 EXPLAIN ANALYZE 分析 INSERTUPDATE, + DELETECREATE TABLE ASEXECUTE 语句而不让命令影响数据,可以采用以下方法: + +BEGIN; +EXPLAIN ANALYZE ...; +ROLLBACK; + + + + + + 不使用括号包围选项列表时,只能指定ANALYZEVERBOSE选项,而且只能按这个顺序指定。在PostgreSQL 9.0 之前,只支持这种不带括号的语法。预计所有新选项都将仅在带括号的语法中得到支持。 + + + + + 参数 + + + + ANALYZE + + + 执行该命令并显示实际运行时间及其他统计信息。此参数默认为FALSE。 + + + + + + VERBOSE + + + 显示有关计划的附加信息。具体包括:计划树中每个节点的输出列列表、模式限定的表名和函数名、总是用其范围表别名标注表达式中的变量,以及总是打印显示统计信息的每个触发器名称。此参数默认为FALSE。 + + + + + + COSTS + + + 包含每个计划节点的估计启动代价和总代价,以及估计行数和每行的估计宽度。此参数默认为TRUE。 + + + + + + BUFFERS + + + 包含缓冲区使用信息。具体来说,会包括共享块命中、读取、写脏和写出的数量,本地块命中、读取、写脏和写出的数量,以及临时块读取和写出的数量。hit表示该块在需要时已经在缓存中找到,因此避免了一次读取。共享块包含普通表和索引的数据;本地块包含临时表和索引的数据;临时块则包含排序、哈希、Materialize 计划节点等场景使用的短期工作数据。dirtied块数表示此查询修改的、先前未修改过的块数;written块数表示在查询处理期间该后端从缓存中逐出的、先前已被写脏的块数。某个上层节点显示的块数包含其所有子节点使用的块数。在文本格式中,只打印非零值。此参数只能在同时启用ANALYZE时使用。默认为FALSE。 + + + + + + TIMING + + + 在输出中包含实际启动时间以及每个节点中耗费的时间。反复读取系统时钟的开销在某些系统上可能会显著拖慢查询,因此当只需要实际行数而不需要精确时间时,把此参数设置为FALSE可能会有用。即使通过这个选项关闭了节点级计时,整个语句的运行时间也总会被测量。此参数只能在同时启用ANALYZE时使用。此参数默认为TRUE。 + + + + + + + FORMAT + + + 指定输出格式,可以是 TEXT、XML、JSON 或 YAML。非文本输出包含与文本输出相同的信息,但更容易被程序解析。此参数默认为TEXT。 + + + + + + boolean + + + 指定所选选项应开启还是关闭。可以写TRUEON或1来启用选项,写FALSEOFF或0来禁用它。boolean值也可以省略,在这种情况下假定其值为TRUE。 + + + + + + statement + + + 任何你希望查看其执行计划的SELECT、INSERT、UPDATE、DELETE、VALUES、EXECUTE、DECLARE、CREATE TABLE AS或CREATE MATERIALIZED VIEW AS语句。 + + + + + + + + 输出 + + + 该命令的结果是为statement选择的计划的文本描述,并可选择附带执行统计信息。描述了所提供的信息。 + + + + + 注解 + + + 为了让PostgreSQL查询规划器在优化查询时能够做出相当有根据的决策,查询中用到的所有表的pg_statistic数据都应保持最新。通常autovacuum 守护进程会自动处理这一点。但如果某个表最近内容发生了大量变化,你可能需要手工执行一次,而不是等待 autovacuum 跟上这些变化。 + + + + 为了度量执行计划中每个节点的运行时代价,当前的EXPLAIN ANALYZE实现会给查询执行增加性能分析开销。因此,对一个查询运行EXPLAIN ANALYZE有时会比正常执行该查询慢得多。开销大小取决于查询的性质以及所用平台。最坏情况出现在那些自身每次执行耗时极少的计划节点上,以及获取当前时间的操作系统调用相对较慢的机器上。 + + + + + 示例 + + + 要显示一个只有单个integer列且包含 10000 行的表上的简单查询计划: + + +EXPLAIN SELECT * FROM foo; + + QUERY PLAN +--------------------------------------------------------- + Seq Scan on foo (cost=0.00..155.00 rows=10000 width=4) +(1 row) + + + + + 下面是同一查询,但使用 JSON 输出格式: + +EXPLAIN (FORMAT JSON) SELECT * FROM foo; + QUERY PLAN +-------------------------------- + [ + + { + + "Plan": { + + "Node Type": "Seq Scan",+ + "Relation Name": "foo", + + "Alias": "foo", + + "Startup Cost": 0.00, + + "Total Cost": 155.00, + + "Plan Rows": 10000, + + "Plan Width": 4 + + } + + } + + ] +(1 row) + + + + + 如果存在索引,并且我们使用了带有可索引WHERE条件的查询,EXPLAIN可能会显示不同的计划: + + +EXPLAIN SELECT * FROM foo WHERE i = 4; + + QUERY PLAN +-------------------------------------------------------------- + Index Scan using fi on foo (cost=0.00..5.98 rows=1 width=4) + Index Cond: (i = 4) +(2 rows) + + + + + 下面是同一查询,但采用 YAML 格式: + +EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4'; + QUERY PLAN +------------------------------- + - Plan: + + Node Type: "Index Scan" + + Scan Direction: "Forward"+ + Index Name: "fi" + + Relation Name: "foo" + + Alias: "foo" + + Startup Cost: 0.00 + + Total Cost: 5.98 + + Plan Rows: 1 + + Plan Width: 4 + + Index Cond: "(i = 4)" +(1 row) + + + XML 格式留给读者自行练习。 + + + 下面是同一计划,但隐藏了代价估计: + + +EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4; + + QUERY PLAN +---------------------------- + Index Scan using fi on foo + Index Cond: (i = 4) +(2 rows) + + + + + 下面是使用聚合函数的查询的查询计划示例: + + +EXPLAIN SELECT sum(i) FROM foo WHERE i < 10; + + QUERY PLAN +--------------------------------------------------------------------- + Aggregate (cost=23.93..23.93 rows=1 width=4) + -> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4) + Index Cond: (i < 10) +(3 rows) + + + + + 下面是使用 EXPLAIN EXECUTE 显示预备查询执行计划的示例: + + +PREPARE query(int, int) AS SELECT sum(bar) FROM test + WHERE id > $1 AND id < $2 + GROUP BY foo; + +EXPLAIN ANALYZE EXECUTE query(100, 200); + + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + HashAggregate (cost=9.54..9.54 rows=1 width=8) (actual time=0.156..0.161 rows=11 loops=1) + Group Key: foo + -> Index Scan using test_pkey on test (cost=0.29..9.29 rows=50 width=8) (actual time=0.039..0.091 rows=99 loops=1) + Index Cond: ((id > $1) AND (id < $2)) + Planning time: 0.197 ms + Execution time: 0.225 ms +(6 rows) + + + + + 当然,这里显示的具体数字取决于所涉及表的实际内容。还要注意,由于规划器的改进,这些数字甚至所选查询策略在PostgreSQL的不同版本之间都可能发生变化。此外,ANALYZE命令使用随机采样来估计数据统计信息;因此,即使表中数据的实际分布没有变化,在重新运行一次ANALYZE之后,代价估计也可能发生变化。 + + + + + 兼容性 + + + SQL 标准中没有定义EXPLAIN语句。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/fetch.sgml b/zh/9.6/ref/fetch.sgml new file mode 100644 index 00000000..6b6dea65 --- /dev/null +++ b/zh/9.6/ref/fetch.sgml @@ -0,0 +1,387 @@ + + + + + + FETCH + + + + cursor + FETCH + + + FETCH + 7 + SQL - 语言语句 + + + + FETCH + 使用游标从查询中检索行 + + + + + +FETCH [ direction [ FROM | IN ] ] cursor_name + +其中direction可以为空,或为以下之一: + + NEXT + PRIOR + FIRST + LAST + ABSOLUTE count + RELATIVE count + count + ALL + FORWARD + FORWARD count + FORWARD ALL + BACKWARD + BACKWARD count + BACKWARD ALL + + + + + 描述 + + + FETCH使用先前创建的游标检索行。 + + + + 游标带有一个关联位置,FETCH会使用该位置。 + 游标位置可以位于查询结果的第一行之前、结果中的任意一行上,或者 + 位于结果的最后一行之后。游标创建时位于第一行之前。提取若干行后, + 游标会定位在最近提取的那一行上。如果FETCH + 越过了可用行的末尾,那么游标会留在最后一行之后;如果是向后提取, + 则会留在第一行之前。FETCH ALL或者 + FETCH BACKWARD ALL总是会让游标位于最后一行之后 + 或第一行之前。 + + + + NEXTPRIORFIRST、 + LASTABSOLUTERELATIVE + 这些形式会在适当地移动游标后提取一行。如果不存在这样的行,则返回空 + 结果,并根据情况将游标定位在第一行之前或最后一行之后。 + + + + 使用FORWARD和BACKWARD的形式会在 + 向前或向后的方向上提取指定数量的行,并将游标定位在最后返回的那一行 + 上(如果count超过可用 + 行数,则定位在所有行之后或之前)。 + + + + RELATIVE 0FORWARD 0以及 + BACKWARD 0都会请求提取当前行而不移动游标,也就是 + 重新提取最近一次提取的行。除非游标位于第一行之前或最后一行之后, + 否则该操作都会成功;在这两种情况下,不会返回任何行。 + + + + + 本页面描述的是 SQL 命令层面上的游标用法。如果想要在 + PL/pgSQL函数中使用游标,规则会有所不同 + — 请见。 + + + + + + 参数 + + + + direction + + direction 定义抓取方向和要抓取的行数。它可以是以下值之一: + + + + NEXT + + + 提取下一行。如果省略direction,这将是默认值。 + + + + + + PRIOR + + + 提取前一行。 + + + + + + FIRST + + + 提取查询的第一行(与ABSOLUTE 1相同)。 + + + + + + LAST + + + 提取查询的最后一行(与ABSOLUTE -1相同)。 + + + + + + ABSOLUTE count + + + 提取查询的第count行; + 如果count为负,则提取 + 从末尾算起的第abs(count)行。如果 + count超出范围, + 则定位在第一行之前或最后一行之后。特别地, + ABSOLUTE 0会定位在第一行之前。 + + + + + + RELATIVE count + + + 抓取后面的第count行,或前面的第abs(count)行(当count为负时)。RELATIVE 0会重新抓取当前行(如果存在)。 + + + + + + count + + + 提取接下来的count行 + (与FORWARD count相同)。 + + + + + + ALL + + + 提取所有剩余行(与FORWARD ALL相同)。 + + + + + + FORWARD + + + 提取下一行(与NEXT相同)。 + + + + + + FORWARD count + + + 提取接下来的count行。 + FORWARD 0会重新提取当前行。 + + + + + + FORWARD ALL + + + 提取所有剩余行。 + + + + + + BACKWARD + + + 提取前一行(与PRIOR相同)。 + + + + + + BACKWARD count + + + 提取前面的count行 + (反向扫描)。BACKWARD 0会重新提取当前行。 + + + + + + BACKWARD ALL + + + 提取之前的所有行(反向扫描)。 + + + + + + + + + count + + count是一个可以带符号的整数常量,决定抓取位置或抓取行数。对于FORWARD和BACKWARD,指定负的count等同于交换FORWARD和BACKWARD的方向。 + + + + + + cursor_name + + + 一个已打开游标的名称。 + + + + + + + + 输出 + + + 成功完成时,FETCH命令会返回如下形式的命令标签: + +FETCH count + + 其中count是提取到的行数 + (可能为零)。注意,在psql中,命令标签实际上 + 不会显示,因为psql会改为显示提取到的行。 + + + + + 注解 + + + 如果打算使用除FETCH NEXT或带正数计数的 + FETCH FORWARD之外的任何FETCH + 变体,则应当在声明游标时使用SCROLL选项。对于简单查询, + PostgreSQL允许从未使用SCROLL + 声明的游标向后提取,但最好不要依赖这种行为。如果游标声明为 + NO SCROLL,则不允许向后提取。 + + + + ABSOLUTE提取并不比通过相对移动到达目标行更快: + 无论如何,底层实现都必须遍历所有中间行。负值的绝对提取甚至更糟: + 必须先把查询读到末尾以找到最后一行,然后再从那里反向遍历。不过, + 回卷到查询起始处(如FETCH ABSOLUTE 0)是很快的。 + + + + 用于定义游标。 + 使用可以在不检索 + 数据的情况下改变游标位置。 + + + + + 示例 + + + 下面的示例展示了如何使用游标遍历一个表: + + +BEGIN WORK; + +-- 建立一个游标: +DECLARE liahona SCROLL CURSOR FOR SELECT * FROM films; + +-- 从游标 liahona 中提取前 5 行: +FETCH FORWARD 5 FROM liahona; + + code | title | did | date_prod | kind | len +-------+-------------------------+-----+------------+----------+------- + BL101 | The Third Man | 101 | 1949-12-23 | Drama | 01:44 + BL102 | The African Queen | 101 | 1951-08-11 | Romantic | 01:43 + JL201 | Une Femme est une Femme | 102 | 1961-03-12 | Romantic | 01:25 + P_301 | Vertigo | 103 | 1958-11-14 | Action | 02:08 + P_302 | Becket | 103 | 1964-02-03 | Drama | 02:28 + +-- 提取前一行: +FETCH PRIOR FROM liahona; + + code | title | did | date_prod | kind | len +-------+---------+-----+------------+--------+------- + P_301 | Vertigo | 103 | 1958-11-14 | Action | 02:08 + +-- 关闭游标并结束事务: +CLOSE liahona; +COMMIT WORK; + + + + + 兼容性 + + + SQL 标准只为嵌入式 SQL 定义了FETCH。 + 这里描述的FETCH变体会像 + SELECT结果那样返回数据,而不是把数据放入主变量中。 + 除此之外,FETCH与 SQL 标准完全向上兼容。 + + + + 涉及FORWARDBACKWARD的 + FETCH形式,以及形式FETCH countFETCH + ALL(其中隐含了FORWARD)都是 + PostgreSQL扩展。 + + + + SQL 标准只允许在游标名之前使用FROM;允许使用 + IN,或者将两者都完全省略,都是扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/grant.sgml b/zh/9.6/ref/grant.sgml new file mode 100644 index 00000000..d3ff7e73 --- /dev/null +++ b/zh/9.6/ref/grant.sgml @@ -0,0 +1,476 @@ + + + + + GRANT + + + + GRANT + 7 + SQL - 语言语句 + + + + GRANT + 定义访问权限 + + + + +GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } + [, ...] | ALL [ PRIVILEGES ] } + ON { [ TABLE ] table_name [, ...] + | ALL TABLES IN SCHEMA schema_name [, ...] } + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column_name [, ...] ) + [, ...] | ALL [ PRIVILEGES ] ( column_name [, ...] ) } + ON [ TABLE ] table_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { { USAGE | SELECT | UPDATE } + [, ...] | ALL [ PRIVILEGES ] } + ON { SEQUENCE sequence_name [, ...] + | ALL SEQUENCES IN SCHEMA schema_name [, ...] } + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] } + ON DATABASE database_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON DOMAIN domain_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN DATA WRAPPER fdw_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN SERVER server_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { EXECUTE | ALL [ PRIVILEGES ] } + ON { FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) [, ...] + | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON LANGUAGE lang_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] } + ON LARGE OBJECT loid [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] } + ON SCHEMA schema_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { CREATE | ALL [ PRIVILEGES ] } + ON TABLESPACE tablespace_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT { USAGE | ALL [ PRIVILEGES ] } + ON TYPE type_name [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + +GRANT role_name [, ...] TO role_specification [, ...] + [ WITH ADMIN OPTION ] + [ GRANTED BY role_specification ] + +其中role_specification可以是: + + [ GROUP ] role_name + | PUBLIC + | CURRENT_USER + | SESSION_USER + + + + + 描述 + + + GRANT命令有两种基本变体:一种用于授予数据库对象(表、列、视图、外部表、序列、数据库、外部数据包装器、外部服务器、函数、过程语言、模式或表空间)上的权限,另一种用于授予角色的成员资格。这两种变体在许多方面很相似,但差别也足够大,因此分别介绍。 + + + + 在数据库对象上 GRANT + + + 这种 GRANT 命令变体将数据库对象上的特定权限授予一个或多个角色。如果此前已经授予过某些权限,新授予的权限会加到现有权限之上。 + + + + 还可以选择在一个或多个模式中,对同一类型的所有对象授予权限。目前仅对表、序列和函数支持这一功能(但请注意,ALL + TABLES被认为包括视图和外部表)。 + + + + 关键字 PUBLIC 表示要把权限授予所有角色,包括以后可能创建的角色。PUBLIC 可以视为一个隐式定义的组,并且始终包含所有角色。任何特定角色实际拥有的权限,是直接授予给它的权限、授予给它当前所属任一角色的权限,以及授予给 PUBLIC 的权限之和。 + + + + 如果指定了 WITH GRANT OPTION,权限接收者随后可以再把该权限授予其他人。没有授予选项时,接收者不能这样做。授予选项不能授予给 PUBLIC。 + + + + 没有必要向对象拥有者(通常是创建它的用户)授予权限,因为拥有者默认拥有全部权限。(不过,出于安全考虑,拥有者也可以选择撤销自己的某些权限。) + + + + 删除对象或以任何方式更改其定义的权利,不被视为一种可授予的权限;它是拥有者固有的,不能被授予或撤销。(不过,可以通过授予或撤销拥有该对象的角色的成员资格,获得类似效果;见下文。)拥有者还隐式拥有该对象上的全部授予选项。 + + + + PostgreSQL 会将某些类型对象上的默认权限授予PUBLIC。默认情况下,不会在表、表列、序列、外部数据包装器、外部服务器、大对象、模式或表空间上向PUBLIC授予任何权限。对于其他类型的对象,授予PUBLIC的默认权限如下:数据库上的CONNECTTEMPORARY(创建临时表)权限;函数上的EXECUTE权限;以及语言和数据类型(包括域)上的USAGE权限。当然,对象所有者可以使用REVOKE撤销默认权限和显式授予的权限。(为了尽可能安全,请在创建对象的同一事务中执行REVOKE,这样其他用户就没有可以使用该对象的时间窗口。)此外,还可以使用命令更改这些初始默认权限设置。 + + + + 可用权限如下: + + + + SELECT + + + 允许使用读取指定表、视图或序列的任何列,或列出的特定列。还允许使用 TO。在中引用现有列值时也需要此权限。对于序列,此权限还允许使用currval函数。对于大对象,此权限允许读取该对象。 + + + + + + INSERT + + + 允许使用向指定表插入新行。如果列出了特定列,则INSERT命令只能给这些列赋值(其他列因此会获得默认值)。还允许使用 FROM。 + + + + + + UPDATE + + + 允许使用更新指定表的任何列,或列出的特定列。(实际上,任何非简单的UPDATE命令还需要SELECT权限,因为它必须引用表列,以确定要更新的行和/或计算列的新值。)SELECT ... FOR UPDATESELECT ... FOR SHARE除了需要SELECT权限外,也要求至少在一列上具有此权限。对于序列,此权限允许使用nextvalsetval函数。对于大对象,此权限允许写入或截断该对象。 + + + + + + DELETE + + + 允许使用从指定表删除一行。(实际上,任何非简单的DELETE命令还需要SELECT权限,因为它必须引用表列,以确定要删除的行。) + + + + + + TRUNCATE + + + 允许在指定表上使用。 + + + + + + REFERENCES + + + 要创建外键约束,必须在引用列和被引用列上都拥有此权限。此权限可以授予 + 表的所有列,也可以只授予特定列。 + + + + + + TRIGGER + + + 允许在指定表上创建触发器。(参见语句。) + + + + + + CREATE + + + 对于数据库,允许在数据库中创建新的模式。 + + + 对于模式,允许在模式中创建新对象。要重命名现有对象,必须拥有该对象,并且在包含它的模式上具有此权限。 + + + 对于表空间,允许在其中创建表、索引和临时文件,也允许创建以该表空间为默认表空间的数据库。(注意,撤销此权限不会改变现有对象的存放位置。) + + + + + + CONNECT + + + 允许用户连接到指定数据库。此权限在连接启动时检查(此外还会检查pg_hba.conf施加的任何限制)。 + + + + + + TEMPORARY + TEMP + + + 允许在使用指定数据库时创建临时表。 + + + + + + EXECUTE + + + 允许使用指定的函数,以及基于该函数实现的任何操作符。这是唯一适用于函数的权限类型。(此语法也适用于聚合函数。) + + + + + + USAGE + + + 对于过程语言,允许使用指定语言创建以该语言编写的函数。这是唯一适用于过程语言的权限类型。 + + + 对于模式,允许访问指定模式中包含的对象(假定也满足对象自身的权限要求)。本质上,这允许被授权者查找模式内的对象。没有此权限,仍然可能看到对象名称,例如通过查询系统表。此外,撤销此权限后,现有后端中可能仍有先前已执行过这种查找的语句,因此这并不是阻止对象访问的完全安全的方法。 + + + 对于序列,此权限允许使用currvalnextval函数。 + + + 对于类型和域,此权限允许在创建表、函数和其他模式对象时使用该类型或域。(注意,它不控制该类型的一般使用,例如在查询中出现该类型的值。它仅阻止创建依赖该类型的对象。此权限的主要目的是控制哪些用户可以创建对某个类型的依赖,因为这些依赖可能使所有者以后无法更改该类型。) + + + 对于外部数据包装器,此权限使被授权者能够使用该外部数据包装器创建新的服务器。 + + + 对于服务器,此权限使被授权者能够使用该服务器创建外部表,还可以创建、修改或删除该服务器关联的自己的用户映射。 + + + + + + ALL PRIVILEGES + + + 一次授予所有可用权限。PRIVILEGES关键字在PostgreSQL中是可选的,但在严格 SQL 中是必需的。 + + + + + + 其他命令所需的权限列在各自命令的参考页面上。 + + + + + 角色上的 GRANT + + + 这种GRANT命令变体把一个角色的成员资格授予一个或多个其他角色。角色成员资格之所以重要,是因为它会把授予该角色的权限传递给其每个成员。 + + + + 如果指定了WITH ADMIN OPTION,成员就可以将该角色的成员资格继续授予其他人,也可以撤销该角色的成员资格。没有管理选项时,普通用户不能这样做。一个角色不被认为在其自身上持有WITH ADMIN + OPTION,但是在会话用户与该角色相符的数据库会话中,它可以把自身角色的成员资格授予其他角色,或撤销这种资格。数据库超级用户可以向任何人授予或撤销任何角色的成员资格。具有CREATEROLE权限的角色可以授予或撤销任何非超级用户角色的成员资格。 + + + + 如果指定了GRANTED BY,该授权会记录为由指定角色执行。只有数据库超级用户可以使用此选项,除非指定的是执行命令的同一角色。 + + + + 与权限不同,角色成员资格不能授予PUBLIC。还要注意,这种形式的命令不允许把无实际作用的GROUP一词用于role_specification中。 + + + + + + + 注解 + + + 命令用于撤销访问权限。 + + + + 从 PostgreSQL 8.1 起,用户和组的概念已统一为一种称为角色的单一实体。因此,不再需要使用关键字 GROUP 来标识被授权者是用户还是组。GROUP 仍可出现在命令中,但它只是一个噪声词。 + + + + 如果用户对某一列本身,或者对其所在整张表拥有该权限,就可以在该列上执行 SELECTINSERT 等操作。在表级授予某项权限后,再在单列上撤销该权限,并不会产生人们可能期望的效果:表级授权不会受到列级操作的影响。 + + + + 当对象的非拥有者试图在该对象上执行 GRANT 时,如果该用户在该对象上完全没有任何权限,命令会立即失败。只要有某项权限可用,命令就会继续执行,但只会授予那些该用户持有授予选项的权限。如果未持有任何授予选项,GRANT ALL PRIVILEGES 形式会发出警告;而其他形式如果命令中特别列出的任一权限未持有其授予选项,也会发出警告。(原则上,这些说明也适用于对象拥有者;但由于拥有者总是被视为持有全部授予选项,这种情况实际上不会发生。) + + + + 需要注意,数据库超级用户可以访问所有对象,而不受对象权限设置的影响。这可类比于 Unix 系统中的 root 权限。和 root 一样,除非绝对必要,否则不宜以超级用户身份操作。 + + + + 如果超级用户选择执行 GRANTREVOKE 命令,该命令会像由受影响对象的拥有者发出一样执行。特别是,通过这种命令授予的权限看起来会像是由对象拥有者授予的。(对于角色成员资格,则看起来像是由被授予成员资格的角色本身授予的。) + + + + GRANT 和 REVOKE 也可以由并非受影响对象拥有者的角色执行,只要该角色是拥有该对象之角色的成员,或者是持有该对象上 WITH GRANT OPTION 权限之角色的成员。在这种情况下,权限会记录为由实际拥有该对象的角色,或者由持有 WITH GRANT OPTION 权限的角色授予。例如,如果表 t1 由角色 g1 拥有,而角色 u1 是它的成员,那么 u1 可以把 t1 上的权限授予给 u2,但这些权限看起来会像是直接由 g1 授予的。角色 g1 的任何其他成员之后都可以撤销这些权限。 + + + + 如果执行 GRANT 的角色通过多条角色成员资格路径间接持有所需权限,则系统不会指明会被记录为执行该授权的是哪一个上层角色。在这种情况下,最佳做法是使用 SET ROLE 切换成你希望作为其身份执行 GRANT 的那个具体角色。 + + + + 在表上授予权限,并不会自动把权限扩展到该表使用的任何序列,包括绑定到 SERIAL 列的序列。序列上的权限必须单独设置。 + + + + 使用 \dp 命令可以获取表和列的现有权限信息。例如: + +=> \dp mytable + Access privileges + Schema | Name | Type | Access privileges | Column access privileges +--------+---------+-------+-----------------------+-------------------------- + public | mytable | table | miriam=arwdDxt/miriam | col1: + : =r/miriam : miriam_rw=rw/miriam + : admin=arw/miriam +(1 row) + + 由 \dp 显示的条目解释如下: + +rolename=xxxx -- privileges granted to a role + =xxxx -- privileges granted to PUBLIC + + r -- SELECT ("read") + w -- UPDATE ("write") + a -- INSERT ("append") + d -- DELETE + D -- TRUNCATE + x -- REFERENCES + t -- TRIGGER + X -- EXECUTE + U -- USAGE + C -- CREATE + c -- CONNECT + T -- TEMPORARY + arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects) + * -- grant option for preceding privilege + + /yyyy -- role that granted this privilege + + + 上例中的显示结果会出现在用户 miriam 创建表 mytable 并执行以下命令之后: + + +GRANT SELECT ON mytable TO PUBLIC; +GRANT SELECT, UPDATE, INSERT ON mytable TO admin; +GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw; + + + + + 对于非表对象,还有其他\d命令可以显示其权限。 + + + + 如果某个对象的Access privileges列为空,表示该对象具有默认权限(即其权限列为 null)。默认权限始终包括所有者的全部权限,并且可能根据对象类型包含授予PUBLIC的某些权限,如上所述。在对象上首次执行GRANT或REVOKE时,会先实例化默认权限(例如生成{miriam=arwdDxt/miriam}),然后根据指定请求修改它们。同样,Column access + privileges中也只会显示具有非默认权限的列的条目。(注意:此处的默认权限始终指该对象类型的内置默认权限。权限受ALTER DEFAULT PRIVILEGES命令影响的对象,始终会显示显式权限条目,其中包含ALTER的效果。) + + + + 注意,访问权限显示中不会标记所有者隐含的授权选项。只有在显式向某人授予授权选项时,才会出现*。 + + + + + + 示例 + + + 将表 films 上的插入权限授予所有用户: + + +GRANT INSERT ON films TO PUBLIC; + + + + + 将视图 kinds 上的所有可用权限授予用户 manuel: + + +GRANT ALL PRIVILEGES ON kinds TO manuel; + + + 请注意,如果上述命令由超级用户或 kinds 的拥有者执行,确实会授予所有权限;但如果由其他人执行,则只会授予该执行者持有授予选项的那些权限。 + + + + 将角色 admins 的成员资格授予用户 joe: + + +GRANT admins TO joe; + + + + + 兼容性 + + + 根据 SQL 标准,ALL PRIVILEGES 中的 PRIVILEGES 关键字是必需的。SQL 标准也不支持每条命令对多个对象设置权限。 + + + + PostgreSQL 允许对象拥有者撤销自己的普通权限:例如,表拥有者可以通过撤销自己的 INSERTUPDATEDELETETRUNCATE 权限,使该表对自己变成只读。这在 SQL 标准中是不可能的。原因是 PostgreSQL 把拥有者的权限视为拥有者授予给自己的;因此他们也可以撤销这些权限。在 SQL 标准中,拥有者的权限由一个假定实体 _SYSTEM 授予。由于拥有者并不是 _SYSTEM,因此不能撤销这些权利。 + + + + 根据 SQL 标准,授予选项可以授予给 PUBLIC;PostgreSQL 只支持将授予选项授予给角色。 + + + + SQL 标准允许将GRANTED BY选项用于所有形式的GRANT。PostgreSQL 仅在授予角色成员资格时支持它,即使如此,也只有超级用户可以用它指定其他授权者。 + + + + SQL 标准还为其他种类的对象提供 USAGE 权限:字符集、排序规则、翻译。 + + + + 在 SQL 标准中,序列只有 USAGE 这一项权限,它控制 NEXT VALUE FOR 表达式的使用;该表达式等价于 PostgreSQL 中的 nextval 函数。序列上的 SELECTUPDATE 权限都是 PostgreSQL 扩展。把序列的 USAGE 权限应用到 currval 函数上也是 PostgreSQL 扩展(该函数本身也是扩展)。 + + + + 数据库、表空间、模式和语言上的权限都是 PostgreSQL 扩展。 + + + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/import_foreign_schema.sgml b/zh/9.6/ref/import_foreign_schema.sgml new file mode 100644 index 00000000..8e19ae4d --- /dev/null +++ b/zh/9.6/ref/import_foreign_schema.sgml @@ -0,0 +1,148 @@ + + + + + IMPORT FOREIGN SCHEMA + + + + IMPORT FOREIGN SCHEMA + 7 + SQL - 语言语句 + + + + IMPORT FOREIGN SCHEMA + 从一个外部服务器导入表定义 + + + + +IMPORT FOREIGN SCHEMA remote_schema + [ { LIMIT TO | EXCEPT } ( table_name [, ...] ) ] + FROM SERVER server_name + INTO local_schema + [ OPTIONS ( option 'value' [, ... ] ) ] + + + + + 描述 + + + IMPORT FOREIGN SCHEMA创建外部表,用来表示存在于外部服务器上的表。新的外部表将归发出该命令的用户所有,并使用与远程表相匹配的正确列定义和选项创建。 + + + + 默认情况下,外部服务器上某个特定模式中的所有表和视图都会被导入。也可以把待导入表的列表限制为指定的子集,或者排除某些特定表。新的外部表都会创建在目标模式中,该模式必须已经存在。 + + + + 要使用IMPORT FOREIGN SCHEMA,用户必须在外部服务器上拥有USAGE权限,并在目标模式上拥有CREATE权限。 + + + + + 参数 + + + + remote_schema + + + 要从中导入的远程模式。远程模式的具体含义取决于所使用的外部数据包装器。 + + + + + + LIMIT TO ( table_name [, ...] ) + + + 只导入名称与给定表名之一匹配的外部表。远程模式中的其他表将被忽略。 + + + + + + EXCEPT ( table_name [, ...] ) + + + 将指定的外部表排除在导入之外。除这里列出的表外,远程模式中的其他表都会被导入。 + + + + + + server_name + + + 要从中导入的外部服务器。 + + + + + + local_schema + + + 将在其中创建所导入外部表的模式。 + + + + + + OPTIONS ( option 'value' [, ...] ) + + + 导入期间要使用的选项。允许的选项名和值取决于各个外部数据包装器。 + + + + + + + + 示例 + + + 从服务器film_server上的远程模式foreign_films导入表定义,并在本地模式films中创建这些外部表: + + +IMPORT FOREIGN SCHEMA foreign_films + FROM SERVER film_server INTO films; + + + + + 与上例相同,但只导入actorsdirectors这两个表(如果它们存在): + + +IMPORT FOREIGN SCHEMA foreign_films LIMIT TO (actors, directors) + FROM SERVER film_server INTO films; + + + + + + + 兼容性 + + + IMPORT FOREIGN SCHEMA命令符合SQL标准,但OPTIONS子句是PostgreSQL扩展。 + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/initdb.sgml b/zh/9.6/ref/initdb.sgml new file mode 100644 index 00000000..e2452d90 --- /dev/null +++ b/zh/9.6/ref/initdb.sgml @@ -0,0 +1,372 @@ + + + + + initdb + + + + initdb + 1 + 应用程序 + + + + initdb + 创建一个新的PostgreSQL数据库集簇 + + + + + initdb + option + + + + + + directory + + + + + + 描述 + + initdb创建一个新的PostgreSQL数据库集簇。数据库集簇是由单个服务器实例管理的一组数据库。 + + + + 创建数据库集簇包括创建用于存放数据库数据的目录,生成共享系统目录表(属于整个集簇而不是某个特定数据库的表),以及创建template1postgres数据库。以后创建新数据库时,会复制template1数据库中的所有内容。(因此,安装在template1中的任何东西都会自动复制到以后创建的每个数据库中。)postgres数据库是一个默认数据库,供用户、工具程序和第三方应用程序使用。 + + + + 虽然initdb会尝试创建指定的数据目录,但如果所需数据目录的父目录归 + root 所有,它可能没有足够的权限。要在这种环境中初始化,可先由 root 创建一个空的数据目录, + 然后用chown将该目录的所有权赋予数据库用户账户,再用 + su切换为该数据库用户来运行initdb。 + + + + initdb必须以将拥有服务器进程的用户身份运行,因为服务器需要访问 + initdb创建的文件和目录。由于服务器不能以 root 身份运行,因此 + 也绝不能以 root 身份运行initdb。(实际上它会拒绝这样做。) + + + + initdb会初始化数据库集簇的默认区域设置和字符集编码。字符集编码、排序顺序(LC_COLLATE)和字符集分类(LC_CTYPE,例如大写、小写、数字)可以在创建数据库时单独设置。initdbtemplate1数据库确定这些设置,它们将作为所有其他数据库的默认值。 + + + + 要更改默认排序顺序或字符集分类,请使用选项。使用C或POSIX之外的排序顺序也会带来性能损失。因此,在运行initdb时选择正确的区域设置很重要。 + + + + 其余区域设置类别可以在以后启动服务器时更改。还可以使用设置所有区域设置类别的默认值,包括排序顺序和字符集分类。所有服务器区域设置值(lc_*)都可以通过SHOW ALL显示。更多细节见。 + + + + 要修改默认编码,请使用。更多细节见 + 。 + + + + + + 选项 + + + + + + + + + 此选项指定pg_hba.conf中本地用户使用的认证方法(hostlocal行)。除非信任系统上的所有本地用户,否则不要使用trust。为便于安装,默认值为trust。 + + + + + + + + + 该选项指定pg_hba.conf中本地用户通过 TCP/IP 连接时 + (host 行)使用的认证方法。 + + + + + + + + + 该选项指定pg_hba.conf中本地用户通过 Unix 域套接字连接时 + (local 行)使用的认证方法。 + + + + + + + + + + 该选项指定数据库集簇应存放的目录。这是initdb所需的唯一信息, + 但也可以通过设置PGDATA环境变量来省去显式写出它;这通常更方便,因为 + 数据库服务器(postgres)之后也可以通过同一变量找到数据目录。 + + + + + + + + + + 选择模板数据库的编码。这也会成为以后创建的任何数据库的默认编码,除非在创建时覆盖它。默认值由区域设置推导而来,如果无法推导,则使用SQL_ASCIIPostgreSQL服务器支持的字符集在中介绍。 + + + + + + + + + + 在数据页上使用校验和,帮助检测 I/O 系统造成的、否则可能悄无声息的数据损坏。启用校验和可能会带来明显的性能损失。此选项只能在初始化时设置,以后不能更改。如果启用,就会为所有数据库中的所有对象计算校验和。 + + + + + + + + + 设置数据库集簇的默认区域设置。如果未指定该选项,区域设置将继承自 + initdb运行时所在的环境。区域设置支持见 + 。 + + + + + + + + + + + + + + + 类似于,但只在指定的类别中设置区域设置。 + + + + + + + + + 等价于。 + + + + + + + + + + 默认情况下,initdb会等待所有文件安全写入磁盘。此选项使initdb不等待就返回,速度更快,但意味着此后操作系统崩溃可能会造成数据目录损坏。通常,此选项适用于测试,但不应在创建生产安装时使用。 + + + + + + + + + 使initdb从文件中读取数据库超级用户的密码。文件的第一行会被当作密码。 + + + + + + + + + + 将所有数据库文件安全写入磁盘,然后退出。这不会执行任何常规initdb操作。 + + + + + + + + + + 设置默认文本检索配置。更多信息见 + 。 + + + + + + + + + + 选择数据库超级用户的用户名。默认值是运行initdb的有效用户的名称。 + 超级用户的名称本身并不重要,不过即使操作系统用户名称不同,也可以选择沿用惯常的名称 + postgres。 + + + + + + + + + + 使initdb提示输入要赋给数据库超级用户的密码。如果不打算使用密码认证, + 这一点并不重要。否则,在设置密码之前将无法使用密码认证。 + + + + + + + + + + 该选项指定事务日志应存放的目录。 + + + + + + + + + 还可以使用以下较少用到的选项: + + + + + + + + 打印引导后端的调试输出,以及少量普通用户通常不感兴趣的其他消息。引导后端是 + initdb用来创建系统目录表的程序。该选项会产生大量极其乏味的输出。 + + + + + + + + + 指定initdb初始化数据库集簇时应到哪里查找其输入文件。通常不需要这样做。 + 若需要显式指定其位置,系统会提示。 + + + + + + + + + + 默认情况下,如果initdb发现某个错误使其无法完整创建数据库集簇, + 就会删除它在发现无法完成任务之前可能已创建的所有文件。该选项会禁止这种清理,因此对调试有用。 + + + + + + + + 其他选项: + + + + + + + + 打印initdb版本并退出。 + + + + + + + + + + 显示有关initdb命令行参数的帮助并退出。 + + + + + + + + + + + 环境 + + + + PGDATA + + + + 指定数据库集簇应存放的目录;可使用选项覆盖。 + + + + + + TZ + + + + 指定所创建数据库集簇的默认时区。该值应为完整的时区名称 + (见)。 + + + + + + + 与大多数其他PostgreSQL工具程序一样,此工具也使用libpq支持的环境变量(参见)。 + + + + + + 注解 + + + 也可以通过pg_ctl initdb调用initdb。 + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/insert.sgml b/zh/9.6/ref/insert.sgml new file mode 100644 index 00000000..3e507d63 --- /dev/null +++ b/zh/9.6/ref/insert.sgml @@ -0,0 +1,643 @@ + + + + + INSERT + + + + INSERT + 7 + SQL - 语言语句 + + + + INSERT + 在表中插入新行 + + + + +[ WITH [ RECURSIVE ] with_query [, ...] ] +INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ] + { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query } + [ ON CONFLICT [ conflict_target ] conflict_action ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] + +其中conflict_target为以下之一: + + ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ] + ON CONSTRAINT constraint_name + +conflict_action为以下之一: + + DO NOTHING + DO UPDATE SET { column_name = { expression | DEFAULT } | + ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) | + ( column_name [, ...] ) = ( sub-SELECT ) + } [, ...] + [ WHERE condition ] + + + + + 描述 + + + INSERT将新行插入表中。可以插入由值表达式指定的 + 一行或多行,也可以插入由查询产生的零行或多行。 + + + + 目标列名可以按任意顺序列出。如果根本未给出列名列表,则默认使用按 + 声明顺序排列的全部列;或者如果VALUES子句或 + query只提供了N列, + 则默认使用按声明顺序排列的前N个列名。 + VALUES子句或query + 提供的值,会按从左到右的顺序与显式或隐式列列表对应起来。 + + + + 任何未出现在显式或隐式列列表中的列都会填入默认值;如果没有声明默认 + 值,则填入空值。 + + + + 如果任何列的表达式数据类型不正确,将尝试自动进行类型转换。 + + + + ON CONFLICT可用于指定一种替代动作,而不是报出 + 违反唯一约束或排他约束的错误。(见下文。) + + + + 可选的RETURNING子句使INSERT + 基于每个实际插入的行(若使用了ON CONFLICT DO UPDATE + 子句,则也可能是更新后的行)计算并返回一个或多个值。这主要用于获取 + 由默认值提供的值,例如 serial 序列号。不过,也允许使用任何引用该表列 + 的表达式。RETURNING列表的语法与 + SELECT的输出列表相同。只有成功插入或更新的行才 + 会被返回。例如,如果某一行被锁定,但由于不满足 + ON CONFLICT DO UPDATE ... WHERE子句中的 + condition而未被更新, + 则该行不会被返回。 + + + + 要向表中插入行,必须具有该表上的INSERT权限。 + 如果存在ON CONFLICT DO UPDATE子句,还要求具有该 + 表上的UPDATE权限。 + + + + 如果指定了列列表,你只需要对所列列具有INSERT权限。类似地,在指定ON CONFLICT DO UPDATE时,你只需要对列出要更新的列具有UPDATE权限。不过,ON CONFLICT DO UPDATE还要求具有SELECT权限,涵盖ON CONFLICT DO UPDATE表达式或condition中读取其值的任何列。 + + + + 使用RETURNING子句要求具有SELECT权限,涵盖RETURNING中提到的所有列。如果使用query子句从查询中插入行,当然需要在查询使用的任何表或列上具有SELECT权限。 + + + + + 参数 + + + 插入 + + + 本节介绍仅在插入新行时可用的参数。专门用于 + ON CONFLICT子句的参数将单独说明。 + + + + + with_query + + + + WITH子句允许指定一个或多个子查询,这些子查 + 询可以在INSERT查询中按名称引用。详见 + 。 + + + + query + (SELECT语句)本身也可以包含 + WITH子句。在这种情况下, + query中可以引用两组 + with_query,但由于第二组嵌套得更近, + 它具有更高的优先级。 + + + + + + table_name + + + + 现有表的名称(可选地使用模式限定)。 + + + + + + alias + + + + table_name的替代名 + 称。提供别名后,它会完全隐藏表的实际名称。当 + ON CONFLICT DO UPDATE的目标是一个名为 + excluded 的表时,这一点特别有用,因为该名称同时也是表示拟插入 + 行的那个特殊表的名称。 + + + + + + + column_name + + + + 名为table_name的表 + 中某一列的名称。如有需要,列名可以附带子字段名或数组下标。 + (只向组合列的部分字段插入值时,其余字段将为空值。)在 + ON CONFLICT DO UPDATE中引用列时,不要在目 + 标列的指定中包含表名。例如,INSERT INTO table_name ... + ON CONFLICT DO UPDATE SET table_name.col = 1是无效的 + (这与UPDATE的一般行为一致)。 + + + + + + DEFAULT VALUES + + + 所有列都会填入各自的默认值。 + + + + + + expression + + + + 赋给相应列的表达式或值。 + + + + + + DEFAULT + + + 相应列将填入其默认值。 + + + + + + query + + + + 提供要插入行的查询(SELECT语句)。其语法 + 说明请参见。 + + + + + + output_expression + + + INSERT命令在插入或更新每行后要计算并返回的表达式。表达式可以使用table_name所指定表中的任何列名。写*可返回插入或更新行的所有列。 + + + + + + output_name + + + 用于返回列的名称。 + + + + + + + + <literal>ON CONFLICT</literal> 子句 + + UPSERT + + + ON CONFLICT + + + 可选的ON CONFLICT子句指定一种替代动作,用来 + 替代抛出唯一约束或排他约束违背错误。对于每一条拟插入的行,要么插入 + 继续进行;要么如果违反了由conflict_target + 指定的某个作为仲裁的约束或索引,就执行替代的 + conflict_actionON CONFLICT DO + NOTHING的替代动作只是跳过该行的插入。 + ON CONFLICT DO UPDATE的替代动作则是更新与拟插 + 入行冲突的现有行。 + + + + conflict_target可以进行唯一索 + 引推断。执行推断时,它由一个或多个 + index_column_name列 + 和/或index_expression + 表达式,以及可选的index_predicate + 组成。所有在不考虑顺序的情况下恰好包含 + conflict_target指定列/表达式的 + table_name唯一索引, + 都会被推断(选中)为仲裁索引。如果指定了 + index_predicate,则作 + 为推断的进一步要求,候选仲裁索引还必须满足该谓词。注意,这意味着如 + 果存在某个满足其他所有条件的非部分唯一索引(即没有谓词的唯一索 + 引), + 那么该索引也会被推断出来(从而被ON CONFLICT + 使用)。如果推断尝试失败,则会报错。 + + + + ON CONFLICT DO UPDATE保证得到原子的 + INSERTUPDATE结果;只要 + 没有其他独立错误,即使在高并发下,也能保证结果是这两者之一。这也称 + 为UPSERTUPDATE or + INSERT。 + + + + + conflict_target + + + + 通过选择仲裁索引来指定 + ON CONFLICT对哪些冲突采取替代动作。它要么 + 执行唯一索引推断,要么显式命名一个约 + 束。对于ON CONFLICT DO NOTHING,是否指定 + conflict_target是可选的;省略时,将处 + 理与所有可用约束(以及唯一索引)的冲突。对于 + ON CONFLICT DO UPDATE,则必须 + 提供conflict_target。 + + + + + + conflict_action + + + + conflict_action指定一个替代的 + ON CONFLICT动作。它可以是 + DO NOTHING,也可以是 + DO UPDATE子句,用以精确指定发生冲突时要执 + 行的UPDATE动作细节。在 + ON CONFLICT DO UPDATE中, + SETWHERE子句既可以通 + 过表名(或别名)访问现有行,也可以通过特殊表 + excluded访问拟插入的行。如果会读取目标表中 + 与excluded对应的列,则要求对这些列具有 + SELECT权限。 + + + + 注意,所有行级BEFORE INSERT触发器的效果都 + 会反映在excluded值中,因为这些效果可能促 + 成该行被排除在插入之外。 + + + + + + index_column_name + + + + table_name中某一列 + 的名称。用于推断仲裁索引。遵循CREATE INDEX + 的格式。要求对index_column_name + 具有SELECT权限。 + + + + + + index_expression + + + + 与index_column_name + 类似,但用于推断出现在索引定义中的、基于table_name列的表达式(而非简 + 单列)。遵循CREATE INDEX的格式。要求对 + 出现在index_expression + 中的任何列具有SELECT权限。 + + + + + + collation + + + + 指定时,要求相应的index_column_name或 + index_expression + 必须使用特定的排序规则,才能在推断时匹配。通常会省略,因为 + 排序规则通常不会影响是否发生约束违背。遵循 + CREATE INDEX格式。 + + + + + + opclass + + + + 指定时,要求相应的index_column_name或 + index_expression + 必须使用特定的操作符类,才能在推断时匹配。通常会省略,因为一 + 种类型的各个操作符类在相等性语义上往往 + 是等价的,或者只需相信已定义的唯一索引具有所需的相等性定义即 + 可。遵循CREATE INDEX格式。 + + + + + + index_predicate + + + + 用于允许推断部分唯一索引。任何满足该谓词的索引(实际上不一定 + 是部分索引)都可以被推断。遵循CREATE INDEX + 格式。要求对出现在index_predicate + 中的任何列具有SELECT权限。 + + + + + + constraint_name + + + + 显式按名称指定一个作为仲裁的约束,而不 + 是通过推断约束或索引。 + + + + + + condition + + + + 返回boolean值的表达式。只有使该表达式返回 + true的行才会被更新,不过一旦采取 + ON CONFLICT DO UPDATE动作,所有行都会被 + 锁定。请注意,只有在某个冲突已被识别为更新候选后,才会最后计 + 算condition。 + + + + + + 请注意,排他约束不支持在ON CONFLICT DO UPDATE + 中充当仲裁对象。在所有情况下,只有NOT DEFERRABLE + 约束和唯一索引可作为仲裁对象。 + + + + 带有ON CONFLICT DO UPDATE子句的 + INSERT是一种确定性语句。这意 + 味着不允许该命令对任何单个现有行产生多于一次的影响;一旦出现这种 + 情况,就会报出基数违背错误。拟插入的行在受仲裁索引或约束限制的属 + 性上不应彼此重复。 + + + + + 通常更推荐使用唯一索引推断,而不是通过 + ON CONFLICT ON CONSTRAINT + constraint_name直接命 + 名约束。当底层索引以重叠方式被另一个大致等价的索引替换时,推断仍 + 能继续正确工作,例如在删除待替换索引之前先使用 + CREATE UNIQUE INDEX ... CONCURRENTLY。 + + + + + + + + 输出 + + + 成功完成时,INSERT 命令返回以下形式的命令标签: + +INSERT oid count + + 其中,count 是插入或更新的行数。如果 count 恰好为一,且目标表有 OID,则 oid 表示分配给插入行的 OID。该单行必须是插入的,而不是更新的。否则 oid 为零。 + + + + 如果INSERT命令包含RETURNING + 子句,则结果将类似于一个SELECT语句,其中包含 + RETURNING列表定义的列和值,它们是基于该命令插 + 入或更新的行计算出来的。 + + + + + 示例 + + + 向表films插入一行: + + +INSERT INTO films VALUES + ('UA502', 'Bananas', 105, '1971-07-13', 'Comedy', '82 minutes'); + + + + + 在这个示例中,省略了len列,因此它将取默认值: + + +INSERT INTO films (code, title, did, date_prod, kind) + VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama'); + + + + + 这个示例对日期列使用DEFAULT子句,而不是显式 + 指定值: + + +INSERT INTO films VALUES + ('UA502', 'Bananas', 105, DEFAULT, 'Comedy', '82 minutes'); +INSERT INTO films (code, title, did, date_prod, kind) + VALUES ('T_601', 'Yojimbo', 106, DEFAULT, 'Drama'); + + + + + 要插入一行,其所有列都由默认值构成: + + +INSERT INTO films DEFAULT VALUES; + + + + + 使用多行VALUES语法插入多行: + + +INSERT INTO films (code, title, did, date_prod, kind) VALUES + ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'), + ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy'); + + + + + 这个示例从与films列布局相同的 + tmp_films表中选出一些行并插入到 + films表: + + +INSERT INTO films SELECT * FROM tmp_films WHERE date_prod < '2004-05-07'; + + + + + 这个示例向数组列插入值: + + +-- Create an empty 3x3 gameboard for noughts-and-crosses +INSERT INTO tictactoe (game, board[1:3][1:3]) + VALUES (1, '{{" "," "," "},{" "," "," "},{" "," "," "}}'); +-- The subscripts in the above example aren't really needed +INSERT INTO tictactoe (game, board) + VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}'); + + + + + 向表distributors插入一行,并返回由 + DEFAULT子句生成的序号: + + +INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') + RETURNING did; + + + + + 为负责 Acme Corporation 账户的销售人员增加销量计数,并把整个更 + 新后的行连同当前时间记录到日志表中: + +WITH upd AS ( + UPDATE employees SET sales_count = sales_count + 1 WHERE id = + (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation') + RETURNING * +) +INSERT INTO employees_log SELECT *, current_timestamp FROM upd; + + + + 视情况插入或更新新的分销商。假设已经定义了一个唯一索引,用于约束 + 出现在did列中的值。请注意,特殊的 + excluded表用于引用最初拟插入的值: + +INSERT INTO distributors (did, dname) + VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc') + ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname; + + + + 插入一个经销商;如果存在一行导致排除(即在行级插入前触发器触发后,受约束的列与之匹配的行),则对拟插入的行不做任何操作。此示例假定已经定义了一个唯一索引,对以下列中的值施加约束:did。 + +INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH') + ON CONFLICT (did) DO NOTHING; + + + + 根据情况插入或更新新的经销商。此示例假定已经定义了一个唯一索引,对以下列中的值施加约束:didWHERE 子句用于限制实际更新的行(不过,任何未更新的现有行仍会被锁定): + +-- Don't update existing distributors based in a certain ZIP code +INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution') + ON CONFLICT (did) DO UPDATE + SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')' + WHERE d.zipcode <> '21201'; + +-- Name a constraint directly in the statement (uses associated +-- index to arbitrate taking the DO NOTHING action) +INSERT INTO distributors (did, dname) VALUES (9, 'Antwerp Design') + ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING; + + + + 如果可能,插入新的经销商;否则执行 DO NOTHING。此示例假定已经定义了一个唯一索引,用来约束 did 列中的值,所约束的行子集满足以下条件:布尔列 is_active 的求值结果为 + true: + +-- This statement could infer a partial unique index on "did" +-- with a predicate of "WHERE is_active", but it could also +-- just use a regular unique constraint on "did" +INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International') + ON CONFLICT (did) WHERE is_active DO NOTHING; + + + + + + 兼容性 + + + INSERT符合 SQL 标准,但 + RETURNING子句是 + PostgreSQL扩展,在 + INSERT中使用WITH的能力以及 + 使用ON CONFLICT指定替代动作的能力也都是扩展。 + 此外,标准不允许省略列名列表却又不是所有列都由VALUES + 子句或query填充的情况。 + + + + query子句可能存在的限 + 制见。 + + + diff --git a/zh/9.6/ref/listen.sgml b/zh/9.6/ref/listen.sgml new file mode 100644 index 00000000..15477e30 --- /dev/null +++ b/zh/9.6/ref/listen.sgml @@ -0,0 +1,123 @@ + + + + + LISTEN + + + + LISTEN + 7 + SQL - 语言语句 + + + + LISTEN + 监听通知 + + + + +LISTEN channel + + + + + 描述 + + + LISTEN将当前会话注册为名为channel的通知通道的监听者。 + 如果当前会话已经在该通知通道上注册为监听者,则不会执行任何操作。 + + + + 每当命令NOTIFY channel被执行时,无论是由当前会话 + 还是由另一个连接到同一数据库的会话执行,所有当前正在监听该通知通道的 + 会话都会收到通知,而每个会话随后都会通知其所连接的客户端应用。 + + + + 会话可以使用UNLISTEN命令取消在给定通知通道上的监听注册。 + 会话结束时,其监听注册会被自动清除。 + + + + 客户端应用必须采用何种方法来检测通知事件,取决于它所使用的 + PostgreSQL应用程序编程接口。使用 + libpq库时,应用程序将LISTEN + 作为普通 SQL 命令发出,然后必须定期调用 + PQnotifies函数,以判断是否已经收到任何通知事件。 + 其他接口,例如libpgtcl,则提供了处理通知事件的 + 更高层方法;事实上,在libpgtcl中,应用程序员 + 甚至不应直接发出LISTENUNLISTEN。 + 详见所使用接口的相关文档。 + + + + 中对LISTENNOTIFY的使用有更详细的讨论。 + + + + + 参数 + + + + channel + + + 通知通道的名称(任意标识符)。 + + + + + + + + 注解 + + + LISTEN会在事务提交时生效。 + 如果在随后被回滚的事务中执行了LISTEN或 + UNLISTEN,则被监听的通知通道集合不会发生变化。 + + + 执行过LISTEN的事务不能为两阶段提交做准备。 + + + + + 示例 + + + 在psql中配置并执行一组 listen/notify 操作: + + +LISTEN virtual; +NOTIFY virtual; +Asynchronous notification "virtual" received from server process with PID 8448. + + + + + 兼容性 + + + SQL 标准中没有LISTEN语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/load.sgml b/zh/9.6/ref/load.sgml new file mode 100644 index 00000000..f3077da6 --- /dev/null +++ b/zh/9.6/ref/load.sgml @@ -0,0 +1,69 @@ + + + + + LOAD + + + + LOAD + 7 + SQL - 语言语句 + + + + LOAD + 载入共享库文件 + + + + +LOAD 'filename' + + + + + 描述 + + + 该命令将一个共享库文件载入PostgreSQL服务器的地址空间。 + 如果该文件已经载入过,则此命令不执行任何操作。 + 包含 C 函数的共享库文件会在调用其中某个函数时自动载入。 + 因此,显式执行LOAD通常只在要载入的是通过钩子修改服务器行为、而不是提供一组函数的库时才有需要。 + + + + 文件名的指定方式与中共享库名的指定方式相同; + 特别地,可以依赖搜索路径以及系统标准共享库文件名扩展名的自动添加。 + 关于此主题的更多信息,见。 + + + + $libdir/plugins + + + + 非超级用户只能将LOAD用于位于$libdir/plugins/的库文件 — 指定的filename必须以该字符串原样开头。(数据库管理员有责任确保只在该目录中安装安全的库。) + + + + + 兼容性 + + + LOADPostgreSQL的一种扩展。 + + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/lock.sgml b/zh/9.6/ref/lock.sgml new file mode 100644 index 00000000..8faf4500 --- /dev/null +++ b/zh/9.6/ref/lock.sgml @@ -0,0 +1,191 @@ + + + + + LOCK + + + + LOCK + 7 + SQL - 语言语句 + + + + LOCK + 锁定表 + + + + +LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ] + +其中lockmode为以下之一: + + ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE + | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE + + + + + 描述 + + + LOCK TABLE获取一个表级锁;如有必要,会等待任何冲突锁被释放。 + 如果指定了NOWAITLOCK TABLE就不会等待获取所需的锁: + 如果无法立即获得,命令将被中止并报错。锁一旦获得,就会一直持有到当前事务结束。 + (没有UNLOCK TABLE命令;锁总是在事务结束时释放。) + + + + 为引用表的命令自动获取锁时,PostgreSQL始终使用限制最少的可用锁模式。LOCK TABLE用于可能需要更严格锁定的情况。例如,假设一个应用在READ COMMITTED隔离级别运行事务,并且需要确保表中的数据在事务期间保持稳定。为此,可以在查询之前对表获取SHARE锁。这会阻止并发数据更改,确保后续对表的读取能看到已提交数据的稳定视图,因为SHARE锁模式与写入者获取的ROW EXCLUSIVE锁冲突,而你的LOCK TABLE name IN SHARE MODE语句会等待,直到所有并发持有ROW + EXCLUSIVE模式锁的事务提交或回滚。因此,一旦获得该锁,就不存在尚未提交的写入;而且在释放该锁之前,也不会开始任何写入。 + + + + 若要在REPEATABLE READSERIALIZABLE + 隔离级别的事务中达到类似效果,你必须在执行任何SELECT + 或数据修改语句之前执行LOCK TABLE语句。 + REPEATABLE READSERIALIZABLE事务的数据视图, + 会在其第一条SELECT或数据修改语句开始时冻结。 + 在事务稍后再执行LOCK TABLE仍然可以阻止并发写入 + — 但它不能保证该事务读取到的是最新已提交的值。 + + + + 如果这类事务还要修改表中的数据,那么它应使用SHARE ROW EXCLUSIVE锁模式, + 而不是SHARE模式。这样可以确保同一时间只有一个这类事务在运行。 + 否则就可能发生死锁:两个事务都可能先获得SHARE模式, + 然后都无法再获得实际执行更新所需的ROW EXCLUSIVE模式。 + (注意,事务自己的锁永远不会互相冲突,因此事务在持有SHARE模式时仍可获得 + ROW EXCLUSIVE模式,但前提是没有其他人持有SHARE模式。) + 为避免死锁,要确保所有事务都按相同顺序对相同对象获取锁;如果同一对象需要多种锁模式, + 则事务应始终先获取限制最严格的模式。 + + + + 关于锁模式和锁策略的更多信息,请参见。 + + + + + 参数 + + + + name + + + 要锁定的现有表的名称(可选模式限定)。如果在表名前指定了 + ONLY,则只有该表会被锁定。如果未指定ONLY, + 则该表及其所有后代表(如果有)都会被锁定。也可以在表名后指定*, + 以显式表明包含后代表。 + + + + 命令LOCK TABLE a, b;等效于 + LOCK TABLE a; LOCK TABLE b;。这些表会按 + LOCK TABLE命令中指定的顺序逐个锁定。 + + + + + + lockmode + + + 锁模式指定该锁会与哪些锁冲突。锁模式见。 + + + + 如果未指定锁模式,则使用限制最严格的ACCESS EXCLUSIVE模式。 + + + + + + NOWAIT + + + 指定LOCK TABLE不等待任何冲突锁被释放: + 如果指定的锁无法在不等待的情况下立即获得,事务就会中止。 + + + + + + + + 注解 + + + LOCK TABLE ... IN ACCESS SHARE MODE要求对目标表具有SELECT权限。LOCK TABLE ... IN ROW EXCLUSIVE + MODE要求对目标表具有INSERT、UPDATE、DELETE或TRUNCATE权限。所有其他形式的LOCK都要求具有表级UPDATE、DELETE或TRUNCATE权限。 + + + + LOCK TABLE在事务块外毫无用处:锁只会一直持有到该语句结束。 + 因此,如果在事务块外使用LOCKPostgreSQL会报告错误。 + 请使用和 + + (或)来定义事务块。 + + + + LOCK TABLE只处理表级锁,因此名称中带有ROW的模式其实都不准确。这些模式名称通常应理解为:用户打算在被锁定的表中获取行级锁。此外,ROW EXCLUSIVE模式本身也是一种可共享的表锁。请记住,就LOCK TABLE而言,所有锁模式的语义完全相同,差别只在于哪些模式彼此冲突。关于如何获取真正的行级锁,请参阅以及一节(位于SELECT参考文档中)。 + + + + + 示例 + + + 在准备向外键表执行插入时,在主键表上获取一个SHARE锁: + + +BEGIN WORK; +LOCK TABLE films IN SHARE MODE; +SELECT id FROM films + WHERE name = 'Star Wars: Episode I - The Phantom Menace'; +-- 如果未返回记录则执行 ROLLBACK +INSERT INTO films_user_comments VALUES + (_id_, 'GREAT! I was waiting for it for so long!'); +COMMIT WORK; + + + + + 在准备执行删除操作时,在主键表上获取一个SHARE ROW EXCLUSIVE锁: + + +BEGIN WORK; +LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE; +DELETE FROM films_user_comments WHERE id IN + (SELECT id FROM films WHERE rating < 5); +DELETE FROM films WHERE rating < 5; +COMMIT WORK; + + + + + 兼容性 + + + SQL 标准中没有LOCK TABLE,而是使用SET TRANSACTION + 来指定事务的并发级别。PostgreSQL也支持这一点;详见 + 。 + + + + 除ACCESS SHARE、ACCESS EXCLUSIVE和 + SHARE UPDATE EXCLUSIVE锁模式外, + PostgreSQL的锁模式和LOCK TABLE语法 + 与Oracle中的对应语法兼容。 + + + diff --git a/zh/9.6/ref/move.sgml b/zh/9.6/ref/move.sgml new file mode 100644 index 00000000..3501b0a9 --- /dev/null +++ b/zh/9.6/ref/move.sgml @@ -0,0 +1,120 @@ + + + + + MOVE + + + + cursor + MOVE + + + + MOVE + 7 + SQL - 语言语句 + + + + MOVE + 定位游标 + + + + + +MOVE [ direction [ FROM | IN ] ] cursor_name + +其中direction可以为空,或为以下之一: + + NEXT + PRIOR + FIRST + LAST + ABSOLUTE count + RELATIVE count + count + ALL + FORWARD + FORWARD count + FORWARD ALL + BACKWARD + BACKWARD count + BACKWARD ALL + + + + + 描述 + + + MOVE在不检索任何数据的情况下重新定位游标。 + MOVEFETCH命令的工作方式完全相同, + 只是它只定位游标而不返回行。 + + + + MOVE命令的参数与FETCH命令完全相同; + 有关语法和用法的细节,请参阅。 + + + + + 输出 + + + 成功完成时,MOVE命令会返回如下形式的命令标签: + +MOVE count + + 其中count是用相同参数执行 + FETCH命令时本应返回的行数(可能为零)。 + + + + + 示例 + + +BEGIN WORK; +DECLARE liahona CURSOR FOR SELECT * FROM films; + +-- 跳过前 5 行: +MOVE FORWARD 5 IN liahona; +MOVE 5 + +-- 从游标 liahona 中提取第 6 行: +FETCH 1 FROM liahona; + code | title | did | date_prod | kind | len +-------+--------+-----+------------+--------+------- + P_303 | 48 Hrs | 103 | 1982-10-22 | Action | 01:37 +(1 row) + +-- 关闭游标 liahona 并结束事务: +CLOSE liahona; +COMMIT WORK; + + + + + 兼容性 + + + SQL 标准中没有MOVE语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/notify.sgml b/zh/9.6/ref/notify.sgml new file mode 100644 index 00000000..dcda0c70 --- /dev/null +++ b/zh/9.6/ref/notify.sgml @@ -0,0 +1,148 @@ + + + + + NOTIFY + + + + NOTIFY + 7 + SQL - 语言语句 + + + + NOTIFY + 发出一个通知 + + + + +NOTIFY channel [ , payload ] + + + + + 描述 + + + NOTIFY命令向每个客户端应用发送一个通知事件,并可附带一个可选的载荷字符串;这些客户端应用此前都已在当前数据库中针对指定的通道名执行过LISTEN channel。通知对所有用户可见。 + + + + NOTIFY为访问同一个PostgreSQL数据库的一组进程提供了一种简单的进程间通信机制。通知中可以附带载荷字符串;如果需要传递结构化数据,也可以借助数据库中的表,将附加数据从通知发送者传递给一个或多个监听者,从而构建用于传递结构化数据的更高层机制。 + + + + 传递给客户端的通知事件信息包括:通知通道名、发出通知的会话对应的服务器进程PID,以及载荷字符串。如果未指定载荷字符串,则该字符串为空串。 + + + + 在某个数据库中使用哪些通道名以及各自含义,由数据库设计者自行决定。常见的做法是让通道名与数据库中的某个表同名,而通知事件基本上意味着:我改动了这张表,去看看有什么新变化。不过,NOTIFYLISTEN命令并不会强制这种关联。例如,数据库设计者可以使用多个不同的通道名,来标识同一张表上的不同类型变更;或者也可以利用载荷字符串区分不同情况。 + + + + 当NOTIFY用于表明某张特定表发生变化时,一种很有用的编程技巧是把NOTIFY放进由表更新触发的语句级触发器中。这样一来,每当表发生变化时就会自动发出通知,应用程序员也不容易忘记这么做。 + + + + NOTIFY会以几种重要方式与 SQL 事务交互。首先,如果在事务内部执行NOTIFY,那么只有在事务提交之后,通知事件才会被递送;如果事务被中止,则其中所有命令都不会生效,NOTIFY也不例外。这种行为是合理的,但如果你期望通知立即送达,可能会感到意外。其次,如果某个正在监听的会话在事务内部收到了通知信号,那么在该事务结束(提交或中止)之前,通知事件都不会递送给它所连接的客户端。原因同样在于:如果通知在事务内部就已递送,而该事务后来又被中止,我们会希望通知也能被撤销,但服务器一旦把通知发送给客户端,就无法再把它收回。因此,通知事件只会在事务之间递送。由此得出的结论是,使用NOTIFY做实时信号的应用,应尽量让事务保持短小。 + + + + 如果在同一个事务中,针对同一通道名多次发送完全相同载荷字符串的通知,那么数据库服务器可以决定只递送一个通知。另一方面,载荷字符串不同的通知始终会作为不同通知递送。类似地,来自不同事务的通知也绝不会被折叠为一个通知。除了会丢弃重复通知中较后的那些实例之外,NOTIFY还保证来自同一事务的通知会按发送顺序递送;来自不同事务的消息,则会按事务提交顺序递送。 + + + + 执行NOTIFY的客户端自己同时也在监听同一通知通道,这是很常见的情形。在这种情况下,它会像其他监听会话一样收到一个通知事件。根据应用逻辑,这可能导致无用功,例如再次读取自己刚刚写入更新的数据库表。要避免这种额外工作,可以检查通知事件消息中提供的发出通知的服务器进程PID,是否与当前会话自身的PID(可通过libpq获得)相同。如果二者相同,就说明这是当前会话自己发出的通知回送给自己,可以直接忽略。 + + + + + 参数 + + + + channel + + + 要发信号的通知通道名称(任意标识符)。 + + + + + payload + + + 随通知一起传递的载荷字符串。它必须指定为简单的字符串字面值。在默认配置下,它必须短于 8000 字节。(如果需要传递二进制数据或大量信息,最好把它们放入数据库表中,并发送该记录的键。) + + + + + + + + 注解 + + + 系统中有一个队列,用来保存那些已经发送但尚未被所有监听会话处理的通知。如果这个队列被塞满,那么调用NOTIFY的事务会在提交时失败。该队列非常大(标准安装中为 8GB),几乎足以满足所有用例。不过,如果某个会话执行了LISTEN后又长时间停留在一个事务里,就无法进行清理。一旦队列占用达到一半,你就会在日志文件中看到警告,指出究竟是哪个会话阻碍了清理。在这种情况下,应确保该会话结束其当前事务,以便清理能够继续进行。 + + + 函数pg_notification_queue_usage返回当前被待处理通知占用的队列比例。详见。 + + + 执行过NOTIFY的事务不能为两阶段提交做准备。 + + + + pg_notify + + + pg_notify + + + + 要发送通知,也可以使用函数pg_notify(text, text)。该函数的第一个参数是通道名,第二个参数是载荷。如果需要处理非常量的通道名和载荷,它会比NOTIFY命令更容易使用。 + + + + + + 示例 + + + 在psql中配置并执行一组 listen/notify 操作: + + +LISTEN virtual; +NOTIFY virtual; +Asynchronous notification "virtual" received from server process with PID 8448. +NOTIFY virtual, 'This is the payload'; +Asynchronous notification "virtual" with payload "This is the payload" received from server process with PID 8448. + +LISTEN foo; +SELECT pg_notify('fo' || 'o', 'pay' || 'load'); +Asynchronous notification "foo" with payload "payload" received from server process with PID 14728. + + + + + 兼容性 + + + SQL 标准中没有NOTIFY语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/pg_basebackup.sgml b/zh/9.6/ref/pg_basebackup.sgml new file mode 100644 index 00000000..3cc5c863 --- /dev/null +++ b/zh/9.6/ref/pg_basebackup.sgml @@ -0,0 +1,472 @@ + + + + + pg_basebackup + + + + pg_basebackup + 1 + 应用程序 + + + + pg_basebackup + 获取PostgreSQL集簇的基础备份 + + + + + pg_basebackup + option + + + + + 描述 + + pg_basebackup用于获取正在运行的PostgreSQL数据库集簇的基础备份。备份过程不会影响数据库的其他客户端,并且该备份既可用于时间点恢复(见),也可用作日志传送或流复制备库的起点(见)。 + + + pg_basebackup创建数据库集簇文件的二进制副本,同时确保系统自动进入和退出备份模式。备份始终针对整个数据库集簇;无法只备份单个数据库或数据库对象。要备份单个数据库,必须使用之类的工具。 + + 备份通过使用复制协议的普通PostgreSQL连接进行。建立连接时必须使用超级用户或具有REPLICATION权限(参见)的用户,并且pg_hba.conf必须明确允许复制连接。服务器还必须将设置得足够高,以便至少为备份保留一个可用会话。 + + + 可以同时运行多个pg_basebackup,但从性能角度来看,最好只执行一次备份,然后复制其结果。 + + + + pg_basebackup不仅可以从主库获取基础备份,也可以从备库获取。要从备库获取备份,需要将备库配置为能够接受复制连接(即设置max_wal_senders,并配置基于主机的认证)。还需要在主库上启用。 + + + 注意,从备库获取在线备份时有一些限制: + + + 备份历史文件不会在被备份的数据库集簇中创建。 + + + + + 无法保证备份所需的所有 WAL 文件都会在备份结束时被归档。如果你打算将该备份 + 用于归档恢复,并希望确保届时所有必需文件都可用,就需要使用-x + 选项将它们包含在备份中。 + + + + + 如果备库在在线备份过程中被提升为主库,则备份会失败。 + + + + 备份所需的所有 WAL 记录都必须包含足够的整页写入,因此必须在主库上启用full_page_writes,并且不能把pg_compresslog之类的工具用作archive_command,从 WAL 文件中移除整页写入。 + + + + + + + 选项 + + 以下命令行选项控制输出的位置和格式。 + + + + + + 设置写入输出的目标目录。如果该目录不存在, + pg_basebackup会创建它(以及所有缺失的父目录)。 + 如果该目录已经存在,则必须为空。 + + + 当备份采用 tar 格式时,目标目录可以指定为-(短横线),从而将 tar 文件写到stdout。 + + + 此选项是必需的。 + + + + + + + + + 选择输出格式。format可以是以下值之一: + + p + plain + + + 将输出写为普通文件,其布局与源服务器的数据目录和表空间相同。当集簇没有额外表空间时,整个数据库都会放在目标目录中。如果集簇包含额外表空间,则主数据目录会放在目标目录中,而其他所有表空间都会放在与源服务器上相同的绝对路径中。(如需改变这一点,见。) + + + 这是默认格式。 + + + + + + t + tar + + + 将输出写为目标目录中的 tar 文件。主数据目录的内容会写入名为base.tar的文件中,而每个其他表空间都会写入一个以该表空间 OID 命名的独立 tar 文件中。 + + + 如果目标目录指定为-(短横线),tar 内容将写入标准输出,适合通过管道传给例如gzip。只有当集簇没有额外表空间时,才允许这样做。 + + + + + + + + + + + + 从服务器传输数据的最大速率。单位为每秒千字节。使用后缀M表示每秒兆字节。也接受后缀k,但它没有效果。有效值介于每秒 32 千字节和每秒 1024 兆字节之间。 + 目的是限制pg_basebackup对运行中的服务器的影响。 + + 此选项始终影响数据目录的传输。只有当收集方法为fetch时,WAL 文件的传输才会受到影响。 + + + + + + + + + + 在输出目录中(使用 tar 格式时则在基础归档文件中)写入一个最小的recovery.conf,以便设置备库。recovery.conf文件会记录pg_basebackup所用的连接设置,以及复制槽(如果指定了),以便流复制以后使用相同的设置。 + + + + + + + + + + 此选项只能与-X stream一起使用。它会使 WAL 流式传输使用指定的复制槽。如果此基础备份打算用作使用复制槽的流复制备库,那么该备库应在recovery.conf中使用同一个复制槽名称。这样可以确保服务器在基础备份结束与开始流复制之间的这段时间内,不会移除任何必需的 WAL 数据。 + + + + + + + + + 在备份期间,将目录olddir中的表空间重定位到newdir。要使此选项生效,olddir必须与该表空间当前定义的路径完全一致。(但如果备份中没有位于olddir中的表空间,也不算错误。)olddirnewdir都必须是绝对路径。如果路径中包含=符号,请用反斜线转义。可以多次指定此选项,以处理多个表空间。参见下文示例。 + + + 如果以这种方式重定位表空间,主数据目录中的符号链接将被更新为指向新位置。因此,新数据目录已可直接用于启动一个所有表空间都位于更新后位置的新服务器实例。 + + + + 目前,此选项仅适用于 普通文件 输出格式;如果选择了 tar 格式,则会被忽略。 + + + + + + + + 指定事务日志目录的位置。xlogdir必须是绝对路径。只有在备份采用 普通文件 模式时,才能指定事务日志目录。 + + + + + + + + + 使用此选项等价于使用方法为fetch-X。 + + + + + + + + + + 在备份中包含所需的事务日志(WAL)文件。这将包括备份期间生成的所有事务日志。如果指定了此选项,就可以在解包后的目录中直接启动 postmaster,而无需查阅日志归档,从而使输出成为一个完全独立的备份。 + + 支持以下收集事务日志的方法: + + f + fetch + + + 在备份结束时收集事务日志文件。因此,参数必须设置得足够高,以确保在备份结束前不会移除所需的日志数据。如果在传输这些数据之前它们已经被回收,则备份会失败并且无法使用。 + + + + + + s + stream + + 在创建备份的同时流式传输事务日志。这会打开与服务器的第二个连接,并在执行备份时并行开始流式传输事务日志。因此,它会占用由参数配置的两个连接。只要客户端能够跟上接收到的事务日志,使用此模式就不需要在主库上额外保留事务日志。 + + + + + + + + + + + + + + 启用 tar 文件输出的 gzip 压缩,使用默认压缩级别。压缩仅在使用 tar 格式时可用。 + + + + + + + + + + + 启用 tar 文件输出的 gzip 压缩,并指定压缩级别(0 到 9,其中 0 表示不压缩,9 表示最佳压缩)。压缩仅在使用 tar 格式时可用。 + + + + + + 以下命令行选项控制备份的生成和程序的运行。 + + + + + + 将检查点模式设置为 fast(立即)或 spread(默认) + (见)。 + + + + + + + + + + + 设置备份标签。如果未指定,则使用默认值pg_basebackup base backup。 + + + + + + + + + + 启用进度报告。打开此选项后,会在备份过程中给出一个近似的进度报告。由于数据库在备份过程中可能发生变化,因此这只是近似值,最终未必恰好结束在100%。特别是当备份中包含 WAL 时,总数据量无法预先估计;在这种情况下,一旦进度超过不含 WAL 时的总估计值,估计目标大小就会继续增加。 + + 启用此选项后,备份会先遍历整个数据库以统计其大小,然后再返回发送实际内容。这可能使备份稍微耗时更长,尤其会使首次发送数据之前的等待时间更长。 + + + + + + + + + + 启用详细模式。它会在启动和关闭过程中输出一些额外步骤;如果同时启用了进度报告,还会显示当前正在处理的确切文件名。 + + + + + + + + 以下命令行选项控制数据库连接参数。 + + + + + + 以连接字符串的形式指定用于连接服务器的参数;这些参数会覆盖任何相互冲突的命令行选项。 + + 出于与其他客户端应用保持一致的考虑,此选项名为--dbname;但由于pg_basebackup并不连接到集簇中的某个特定数据库,连接字符串中的数据库名会被忽略。 + + + + + + + + + 指定服务器运行所在机器的主机名。如果该值以斜线开头,则它会被用作 Unix 域套接字的目录。默认值取自PGHOST环境变量(如果已设置);否则会尝试使用 Unix 域套接字连接。 + + + + + + + + + + 指定服务器监听连接所使用的 TCP 端口,或本地 Unix 域套接字文件扩展名。默认使用PGPORT环境变量中的值(如果已设置),否则使用编译时确定的默认值。 + + + + + + + + + + + 指定向服务器回送状态包的时间间隔(秒)。这样更便于从服务器端监视进度。值为零将完全禁用周期性状态更新,不过在服务器请求时仍会发送更新,以避免因超时而断开连接。默认值是 10 秒。 + + + + + + + + + + 指定连接时使用的用户名。 + + + + + + + + + + 禁止发出密码提示。如果服务器要求密码认证,而又无法通过其他方式(例如.pgpass文件)获得密码,则连接尝试将失败。此选项对于批处理作业和脚本很有用,因为那种场景下通常没有用户在场输入密码。 + + + + + + + + + + 强制pg_basebackup在连接数据库之前提示输入密码。 + + + + 此选项绝非必需,因为如果服务器要求密码认证,pg_basebackup会自动提示输入密码。不过,pg_basebackup会浪费一次连接尝试来发现服务器需要密码。在某些情况下,输入以避免额外的连接尝试是值得的。 + + + + + + + + 其他选项也可用: + + + + + + + + 输出pg_basebackup的版本并退出。 + + + + + + + + + + 显示pg_basebackup命令行参数的帮助并退出。 + + + + + + + + + + + 环境 + + + 与大多数其他PostgreSQL工具一样,此工具也使用libpq支持的环境变量(见)。 + + + + + + 注解 + + + 在备份开始时,需要在源服务器上执行一次检查点。这可能需要一些时间(尤其是在未使用--checkpoint=fast选项时);在此期间,pg_basebackup看起来会处于空闲状态。 + + + + 备份将包括数据目录和表空间中的所有文件,包括配置文件以及第三方放在这些目录中的任何额外文件。不过,只有普通文件和目录会被复制。除用于表空间的符号链接外,其他符号链接和特殊设备文件都会被跳过。(具体细节见。) + + + + 在 普通文件 格式中,除非使用了--tablespace-mapping选项,否则表空间会备份到其在源服务器上的相同路径。如果不使用此选项,那么当表空间正在使用时,就无法在与服务器相同的主机上执行 普通文件 格式的基础备份,因为备份将不得不写入与原始表空间相同的目录位置。 + + + 使用 tar 格式时,用户有责任在启动 PostgreSQL 服务器之前解包每个 tar 文件。如果存在额外表空间,则其 tar 文件必须解包到正确的位置。在这种情况下,服务器会根据tablespace_map文件的内容,为这些表空间创建符号链接,该文件包含在base.tar文件中。 + + pg_basebackup可以与相同主版本或更低主版本的服务器配合工作,最低支持到 9.1。不过,WAL 流式传输模式(-X stream)仅适用于 9.3 及以上版本服务器,当前版本的 tar 格式(--format=tar)仅适用于 9.5 及以上版本服务器。 + + + + + 示例 + + + 要为服务器mydbserver创建一个基础备份,并将其存储到本地目录/usr/local/pgsql/data中: + +$ pg_basebackup -h mydbserver -D /usr/local/pgsql/data + + + + + 要为本地服务器创建一个备份,为每个表空间各生成一个压缩的 tar 文件,并将其存储在目录backup中,同时在运行期间显示进度报告: + +$ pg_basebackup -D backup -Ft -z -P + + + + + 要为一个仅包含单个表空间的本地数据库创建备份,并使用bzip2进行压缩: + +$ pg_basebackup -D - -Ft | bzip2 > backup.tar.bz2 + + (如果该数据库中有多个表空间,此命令将失败。) + + + 要为本地数据库创建备份,并把位于 /opt/ts 的表空间重定位到 ./backup/ts: + +$ pg_basebackup -D backup/data -T /opt/ts=$(pwd)/backup/ts + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/pg_config-ref.sgml b/zh/9.6/ref/pg_config-ref.sgml new file mode 100644 index 00000000..7ea2f196 --- /dev/null +++ b/zh/9.6/ref/pg_config-ref.sgml @@ -0,0 +1,291 @@ + + + + + pg_config + + + + pg_config + 1 + 应用程序 + + + + pg_config + 获取已安装的PostgreSQL版本的信息 + + + + + pg_config + option + + + + + + 描述 + + + pg_config工具打印当前已安装的PostgreSQL版本的配置参数。例如,希望与PostgreSQL进行接口的软件包可以使用它,以便找到所需的头文件和库。 + + + + + + 选项 + + 要使用 pg_config,请提供以下一个或多个选项: + + + + + 打印用户可执行文件所在的位置。例如,可用它来查找psql程序。通常这也是pg_config程序所在的位置。 + + + + + + + + + 打印文档文件所在的位置。 + + + + + + + + + 打印 HTML 文档文件所在的位置。 + + + + + + + + + 打印客户端接口的 C 头文件所在的位置。 + + + + + + + + + 打印其他 C 头文件所在的位置。 + + + + + + + + + 打印服务器编程所用 C 头文件所在的位置。 + + + + + + + + + 打印目标代码库所在的位置。 + + + + + + + + + 打印动态可加载模块所在的位置,或服务器将搜索这些模块的位置。(其他与体系结构相关的数据文件也可能安装在该目录中。) + + + + + + + + + 打印区域设置支持文件所在的位置。(如果在构建PostgreSQL时未配置区域设置支持,则这将是一个空字符串。) + + + + + + + + + 打印手册页所在的位置。 + + + + + + + + + 打印与体系结构无关的支持文件所在的位置。 + + + + + + + + + 打印系统范围的配置文件所在的位置。 + + + + + + + + + 打印扩展 makefile 的位置。 + + + + + + + + + 打印为构建PostgreSQL而运行configure脚本时给定的选项。这可用于重现完全相同的配置,或者查明某个二进制软件包是用哪些选项构建的。(但请注意,二进制软件包通常包含供应商特定的自定义补丁。)另请参见下面的示例。 + + + + + + + + + 打印构建PostgreSQL时所用的CC变量值。这显示所使用的 C 编译器。 + + + + + + + + + 打印构建PostgreSQL时所用的CPPFLAGS变量值。这显示预处理阶段所需的 C 编译器开关(通常是-I开关)。 + + + + + + + + + 打印构建PostgreSQL时所用的CFLAGS变量值。这显示 C 编译器开关。 + + + + + + + + + 打印构建PostgreSQL时所用的CFLAGS_SL变量值。这显示构建共享库时使用的额外 C 编译器开关。 + + + + + + + + + 打印构建PostgreSQL时所用的LDFLAGS变量值。这显示链接器开关。 + + + + + + + + + 打印构建PostgreSQL时所用的LDFLAGS_EX变量值。这显示仅用于构建可执行文件的链接器开关。 + + + + + + + + + 打印构建PostgreSQL时所用的LDFLAGS_SL变量值。这显示仅用于构建共享库的链接器开关。 + + + + + + + + + 打印构建PostgreSQL时所用的LIBS变量值。这通常包含外部库的-l开关,这些外部库会被链接进PostgreSQL。 + + + + + + + + + 打印PostgreSQL的版本。 + + + + + + + + + + 显示有关pg_config命令行参数的帮助,然后退出。 + + + + 如果指定多个选项,信息将按选项顺序打印,每行一项。如果不指定选项,则打印所有可用信息,并附带标签。 + + + + + + 注解 + + + 选项、 + 、 + 、 + 、 + 、 + 以及是在PostgreSQL 8.1 中加入的。选项是在PostgreSQL 8.4 中加入的。选项是在PostgreSQL 9.0 中加入的。 + + + + + + + 示例 + + + 要重现当前 PostgreSQL 安装的构建配置,请运行下列命令: + +eval ./configure `pg_config --configure` + + pg_config --configure的输出包含 shell 引号,因此带空格的参数能够被正确表示。因此,要得到正确的结果,需要使用eval。 + + + + diff --git a/zh/9.6/ref/pg_controldata.sgml b/zh/9.6/ref/pg_controldata.sgml new file mode 100644 index 00000000..dd5cbc53 --- /dev/null +++ b/zh/9.6/ref/pg_controldata.sgml @@ -0,0 +1,62 @@ + + + + + pg_controldata + + + + pg_controldata + 1 + 应用程序 + + + + pg_controldata + 显示 PostgreSQL 数据库集簇的控制信息 + + + + + pg_controldata + option + datadir + + + + + 描述 + + pg_controldata会打印在initdb期间初始化的信息,例如系统目录版本。 + 它还会显示有关预写式日志和检查点处理的信息。 + 这些信息是整个集簇范围内的,不针对任何单个数据库。 + + + + 该工具只能由初始化该集簇的用户运行,因为它需要对数据目录具有读取权限。 + 可以在命令行中指定数据目录,也可以使用环境变量PGDATA。 + 该工具支持选项,用于输出 + pg_controldata的版本并退出。 + 它还支持选项,用于输出可用参数。 + + + + + 环境 + + + + PGDATA + + + + 默认数据目录位置 + + + + + + diff --git a/zh/9.6/ref/pg_ctl-ref.sgml b/zh/9.6/ref/pg_ctl-ref.sgml new file mode 100644 index 00000000..26f72b48 --- /dev/null +++ b/zh/9.6/ref/pg_ctl-ref.sgml @@ -0,0 +1,543 @@ + + + + + pg_ctl + + + + pg_ctl + 1 + 应用程序 + + + + pg_ctl + 初始化、启动、停止或控制PostgreSQL服务器 + + + + + pg_ctl + + + datadir + initdb-options + + + + pg_ctl + + + seconds + + datadir + filename + options + path + + + + + pg_ctl + + + seconds + + datadir + + + + + + + + + + + pg_ctl + + + seconds + + datadir + + + + + + + + + options + + + + pg_ctl + + + datadir + + + + pg_ctl + + datadir + + + + pg_ctl + + + datadir + + + + pg_ctl + + signal_name + process_id + + + + pg_ctl + + servicename + username + password + datadir + + + + + + + + seconds + + options + + + + pg_ctl + + servicename + + + + + + 描述 + + pg_ctl是一个实用工具,用于初始化PostgreSQL数据库集簇,启动、停止或重启PostgreSQL数据库服务器(),或者显示正在运行服务器的状态。虽然服务器也可以手工启动,但pg_ctl将重定向日志输出、正确地与终端和进程组脱离等任务封装了起来。它还提供了便于实施受控关闭的选项。 + + + + 模式会创建一个新的PostgreSQL数据库集簇,也就是由单个服务器实例管理的一组数据库。该模式会调用initdb命令。详见。 + + + + 模式会启动一个新服务器。服务器在后台启动,其标准输入连接到/dev/null(在 Windows 上则是nul)。在类 Unix 系统上,默认情况下,服务器的标准输出和标准错误会被发送到pg_ctl的标准输出(而不是标准错误)。因此,pg_ctl的标准输出应被重定向到文件,或者通过管道传给另一个进程,例如rotatelogs这样的日志轮转程序;否则postgres会在后台将其输出写入控制终端,并且不会脱离 shell 的进程组。在 Windows 上,默认情况下,服务器的标准输出和标准错误会被发送到终端。使用将服务器输出追加到日志文件,可以改变这些默认行为。建议使用或输出重定向。 + + + + 模式会关闭在指定数据目录中运行的服务器。可以使用选项选择三种不同的关闭方法。Smart模式不允许新连接,然后等待所有现有客户端断开,以及所有在线备份结束。如果服务器处于热备状态,那么在所有客户端断开后,恢复和流复制都会终止。Fast模式(默认值)不等待客户端断开,并会终止正在进行的在线备份。所有活动事务都会回滚,客户端会被强制断开,然后服务器关闭。Immediate模式会立即中止所有服务器进程,而不执行干净关闭。这样会导致服务器在下次启动时进入一次崩溃恢复周期。 + + + + 模式实际上就是先执行停止再执行启动。这使得可以修改postgres的命令行选项。如果服务器启动时在命令行中指定了相对路径,可能失败。 + + + + 模式只是向postgres服务器进程发送一个SIGHUP信号,使其重新读取配置文件(postgresql.confpg_hba.conf等)。这样就可以修改那些无需完全重启服务器即可生效的配置文件选项。 + + + + 模式会检查指定数据目录中是否有服务器正在运行。若有,则显示服务器的PID以及调用它时使用的命令行选项。若服务器未运行,进程返回退出状态 3。若未指定一个可访问的数据目录,进程返回退出状态 4。 + + + + 在模式下,会指示在指定数据目录中运行的备库退出恢复,并开始进行读写操作。 + + + + 模式向指定进程发送信号。这在没有内置kill命令的Microsoft Windows上尤其有用。使用--help可查看受支持的信号名称列表。 + + + + 模式允许在Microsoft Windows上注册一个系统服务。选项允许选择服务启动类型,可以是auto(系统启动时自动启动服务)或demand(按需启动服务)。 + + + + 模式会在Microsoft Windows上注销一个系统服务。这会撤销命令的效果。 + + + + + 选项 + + + + + + + + + + 在支持的平台上,通过解除对核心转储文件施加的任何软资源限制,尝试允许服务器在崩溃时生成核心转储文件。这样就可以从失败的服务器进程中获得栈跟踪,从而有助于调试或诊断问题。 + + + + + + + + + + + 指定数据库配置文件所在的文件系统位置。若省略此选项,则使用环境变量PGDATA。 + + + + + + + + + 将服务器日志输出追加到filename。如果该文件不存在,就会创建它。umask被设置为 077,因此默认情况下其他用户无法访问该日志文件。 + + + + + + + + + + 指定关闭模式。mode可以是smartfastimmediate,也可以是这三者之一的首字母。若省略此选项,则使用fast。 + + + + + + + + + + 指定要直接传递给postgres命令的选项;多次给出的选项会被追加。 + + + + 通常应将选项放在单引号或双引号中,以确保它们作为一个整体被传递。 + + + + + + + + + + 指定要直接传递给initdb命令的选项。 + + + + 通常应将选项放在单引号或双引号中,以确保它们作为一个整体被传递。 + + + + + + + + + + 指定postgres可执行程序的位置。默认情况下,postgres可执行程序取自与pg_ctl相同的目录;如果那里没有,则取自硬编码的安装目录。除非采用了某些非常规方式,并收到找不到postgres可执行程序的错误,否则通常不需要使用此选项。 + + + + 在init模式中,此选项同样指定initdb可执行程序的位置。 + + + + + + + + + + + 只打印错误,不打印信息性消息。 + + + + + + + + + + + 等待启动或关闭完成时最多等待多少秒。默认值为环境变量PGCTLTIMEOUT的值;如果未设置该环境变量,则默认为 60 秒。 + + + + + + + + + + + 打印pg_ctl的版本并退出。 + + + + + + + + + + 等待启动或关闭完成。对关闭来说,等待是默认选项;但对启动来说不是。等待启动时,pg_ctl会反复尝试连接服务器。等待关闭时,pg_ctl会等待服务器移除其PID文件。此选项允许在启动时输入SSL口令。pg_ctl会根据启动或关闭是否成功返回相应的退出代码。 + + + + + + + + + + 不等待启动或关闭完成。对 start 和 restart 模式来说,这是默认行为。 + + + + + + + + + + + 显示有关pg_ctl命令行参数的帮助并退出。 + + + + + + + 用于 Windows 的选项 + + + + + + + + 指定以 Windows 服务方式运行时,pg_ctl写入事件日志所使用的事件源名称。默认值为PostgreSQL。注意,这只控制pg_ctl本身的日志记录;服务器一旦启动,就会使用指定的事件源。如果服务器在启动早期失败,它也可能使用默认事件源PostgreSQL记录日志。 + + + + + + + + + + 要注册的系统服务名称。该名称会同时用作服务名和显示名。 + + + + + + + + + + 用于启动该服务的用户密码。 + + + + + + + + + + 要注册的系统服务的启动类型。start-type 可以是autodemand,或者两者之一的首字母。若省略此选项,则使用auto。 + + + + + + + + + + 用于启动该服务的用户名。对于域用户,请使用DOMAIN\username格式。 + + + + + + + + + + + 环境 + + + + PGCTLTIMEOUT + + + + + 等待启动或关闭完成时,默认的等待秒数上限。如果未设置,默认值为 60 秒。 + + + + + + PGDATA + + + + + 默认数据目录位置。 + + + + + + + pg_ctl与大多数其他PostgreSQL工具一样,也使用libpq所支持的环境变量(参见)。其他服务器相关变量见 + + + + + 文件 + + + + postmaster.pid + + + + + 数据目录中该文件的存在与否,用于帮助pg_ctl确定服务器当前是否正在运行。 + + + + + + postmaster.opts + + + + 如果该文件存在于数据目录中,pg_ctl(在模式下)会将该文件的内容作为选项传递给postgres,除非被选项覆盖。该文件的内容也会在模式下显示出来。 + + + + + + + + + + 示例 + + + 启动服务器 + + + 要启动服务器: + +$ pg_ctl start + + + + + 要启动服务器并等待其开始接受连接: + +$ pg_ctl -w start + + + + + 要使用端口 5433 启动服务器,并在未启用fsync的情况下运行,可使用: + +$ pg_ctl -o "-F -p 5433" start + + + + + 停止服务器 + + 要停止服务器,可使用: + +$ pg_ctl stop + + 选项允许控制服务器如何关闭: + +$ pg_ctl stop -m fast + + + + + 重启服务器 + + + 重启服务器几乎等同于先停止服务器再重新启动,只不过pg_ctl会保存并重用传给前一个运行实例的命令行选项。以最简单的形式重启服务器,可使用: + +$ pg_ctl restart + + + + + 要重启服务器并等待其关闭和重启完成: + +$ pg_ctl -w restart + + + + + 要使用端口 5433 重启服务器,并在重启时禁用fsync,可使用: + +$ pg_ctl -o "-F -p 5433" restart + + + + + 显示服务器状态 + + + 下面是pg_ctl状态输出的示例: + +$ pg_ctl status + +pg_ctl: server is running (PID: 13718) +/usr/local/pgsql/bin/postgres "-D" "/usr/local/pgsql/data" "-p" "5433" "-B" "128" + + 这是在重启模式下会调用的命令行。 + + + + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/pg_dump.sgml b/zh/9.6/ref/pg_dump.sgml new file mode 100644 index 00000000..5a68edb7 --- /dev/null +++ b/zh/9.6/ref/pg_dump.sgml @@ -0,0 +1,1037 @@ + + + + + pg_dump + + + + pg_dump + 1 + 应用程序 + + + + pg_dump + + + 将 PostgreSQL 数据库导出为 SQL 脚本或其他格式 + + + + + + pg_dump + connection-option + option + dbname + + + + + + 描述 + + pg_dump是一个用于备份PostgreSQL数据库的工具。即使数据库正在被并发使用,它也能生成一致的备份。pg_dump不会阻塞其他用户访问数据库(无论读还是写)。 + + pg_dump只转储单个数据库。要备份集簇中所有数据库共有的全局对象(例如角色和表空间),请使用 + + + 转储可以输出为脚本格式或归档文件格式。脚本转储是纯文本文件,包含把数据库 + 重建到保存时状态所需的 SQL 命令。要从这样的脚本恢复,只需将其交给 + 。脚本文件甚至可以在其他机器和其他体系结构上 + 用于重建数据库;经过一些修改后,甚至也可以用于其他 SQL 数据库产品。 + + + + 另一类归档文件格式必须结合 来重建数据库。 + 它们允许 pg_restore 有选择地恢复某些内容, + 甚至在恢复之前重新排列条目。归档文件格式被设计为可跨体系结构移植。 + + + + 使用归档文件格式并配合 pg_restore 时, + pg_dump 提供了一种灵活的归档和传输机制。 + pg_dump 可用于导出整个数据库,而 + pg_restore 可用于检查归档和/或选择要恢复的数据库部分。 + 最灵活的输出文件格式是 custom 格式() + 和 directory 格式()。它们允许选择和 + 重新排序所有归档条目,支持并行恢复,并且默认会压缩。只有 + directory 格式支持并行转储。 + + + + 运行 pg_dump 时,应检查输出中是否有任何警告 + (打印到标准错误),尤其要结合下面列出的限制来查看。 + + + + + + 选项 + + 以下命令行选项控制输出的内容和格式。 + + dbname + + + 指定要转储的数据库名称。如果未指定,则使用环境变量PGDATABASE。 + 如果未设置该变量,则使用连接指定的用户名。 + + + + + + + + + + 只转储数据,不转储模式(数据定义)或统计信息。会转储表数据、大对象和 + 序列值。 + + + + 此选项类似于指定 ,但出于历史原因, + 两者并不完全相同。 + + + + + + + + + 在转储中包含大对象。除非指定了,否则这是默认行为。因此,开关只在已经请求了特定模式或表的转储中,需要把大对象加回来时才有用。注意,大对象被视为数据,因此在使用 --data-only 时会包含,在使用 --schema-only 时则不会包含。 + + + + + + + + 在输出创建数据库对象的命令之前,先输出清理(删除)这些对象的命令。(除非同时指定,否则如果目标数据库中不存在某些对象,恢复时可能会产生一些无害的错误消息。) + + + 当生成一个归档(非文本)输出文件时,此选项将被忽略。对于归档格式, + 可以在调用 pg_restore 时指定该选项。 + + + + + + + + + + 让输出以创建数据库本身并重新连接到新建数据库的命令开始。 + (使用这种形式的脚本时,在目标安装中先连接到哪个数据库再运行脚本 + 都无关紧要。)如果还指定了 ,则脚本会在重新 + 连接到目标数据库之前先删除并重新创建它。 + + + + 当生成一个归档(非文本)输出文件时,此选项将被忽略。对于归档格式, + 可以在调用 pg_restore 时指定该选项。 + + + + + + + + + + 在指定的字符集编码中创建转储。默认情况下,转储将以数据库编码创建。(获得相同结果的另一种方法是将PGCLIENTENCODING环境变量设置为所需的转储编码。) + + + + + + + + + + 将输出发送到指定文件。对于基于文件的输出格式,可以省略此参数, + 这时使用标准输出。不过,对于目录输出格式,必须给出此参数, + 因为它指定的是目标目录而不是文件。在这种情况下,该目录由 + pg_dump 创建,并且在此之前必须不存在。 + + + + + + + + + 选择输出格式。format可以是以下值之一: + + p + plain + + + 输出纯文本 SQL 脚本文件(默认)。 + + + + + + c + custom + + + 输出适合供 pg_restore 使用的 custom 格式归档。 + 与目录输出格式一起,这是最灵活的输出格式,因为它允许在恢复时手工 + 选择并重新排序归档条目。这种格式默认也会压缩。 + + + + + + d + directory + + + 输出适合供 pg_restore 使用的 directory 格式归档。 + 这会创建一个目录,其中每个被转储的表和大对象各有一个文件,外加一个 + 所谓的目录表(Table of Contents)文件,以机器可读格式描述被转储对象, + pg_restore 可以读取它。directory 格式归档 + 可以用标准 Unix 工具来操作;例如,未压缩归档中的文件可以使用 + gzip 工具进行压缩。该格式默认压缩,并且支持并行转储。 + + + + + + t + tar + + + 输出 tar 格式归档,适合供 + pg_restore。tar 格式与 directory 格式兼容: + 解开一个 tar 格式归档就会得到 + 一个有效的 directory 格式归档。不过,tar 格式不支持压缩。另外,使用 + tar 格式时,在恢复过程中不能改变表数据项的相对顺序。 + + + + + + + + + + + + + 以并行方式运行转储,同时转储 njobs + 个表。此选项减少执行转储所需的时间,但也会增加数据库服务器的负载。 + 只能在目录输出格式下使用此选项,因为只有这种输出格式允许多个进程同时 + 写入数据。 + + pg_dump 将打开 + njobs + 1 个数据库连接,因此请确保 + 设置足够高,能够容纳所有连接。 + + + 在并行转储运行期间请求数据库对象上的排他锁,可能导致转储失败。原因是 + pg_dump 的主进程会对稍后由工作进程转储的对象 + 请求共享锁,以确保 + 在转储运行期间没有人删除这些对象。如果另一个客户端随后请求某个表上的 + 排他锁,该锁不会被授予,而是会排队等待主进程释放共享锁。于是, + 对该表的任何其他访问也都不会被授予,并会排在该排他锁请求之后,其中包括 + 试图转储该表的工作进程。如果没有任何预防措施,这就会形成一个经典的死锁 + 场景。为检测这种冲突,pg_dump 工作进程会使用 + NOWAIT 选项再请求一个共享锁。如果工作进程拿不到这个 + 共享锁,就说明这期间已经有人请求了排他锁,而此时已经无法继续转储,因此 + pg_dump 只能中止转储。 + + + 要获得一致的备份,数据库服务器需要支持同步快照。这一特性是在 + PostgreSQL 9.2 中引入的。有了这个特性, + 数据库客户端即使使用不同连接,也能保证看到相同的数据集。pg_dump -j 会使用多个数据库连接: + 它会先由主进程连接数据库一次,然后每个工作任务再各连一次。若没有 + 同步快照特性,就无法保证各个工作任务在各自连接中看到相同的数据,这会 + 导致备份不一致。 + + + 如果要对 9.2 之前的服务器执行并行转储,必须确保从主进程连接数据库起,到最后一个工作任务连接数据库为止,数据库内容都不发生变化。最简单的方法是在开始备份之前,暂停所有访问数据库并修改数据的进程(DDL 和 DML)。对 9.2 之前的PostgreSQL服务器运行pg_dump -j时,还必须指定参数。 + + + + + + + + + + 只转储匹配 schema 的模式; + 这既会选择模式本身,也会选择其中包含的所有对象。未指定此选项时, + 将转储目标数据库中的所有非系统模式。可以通过写多个 + 开关来选择多个模式。schema + 参数按照 psql\d 命令 + 所使用的同样规则进行解释(见 ), + 因此也可以通过在模式中使用通配符来选择多个模式。使用通配符时,如有需要 + 请小心为模式加引号,以防止 shell 展开通配符;见下文的 + 。 + + + + + 当指定 时,pg_dump + 不会尝试转储所选模式可能依赖的任何其他数据库对象。因此,不能保证 + 特定模式转储的结果能够单独成功恢复到一个干净的数据库中。 + + + + + + 指定 时,不会转储大对象等非模式对象。可以使用 + 开关把大对象加回转储中。 + + + + + + + + + + + 不转储任何名称匹配schema的模式。该匹配条件按照与相同的规则解释。可以多次指定,以排除匹配多个条件的模式。 + + + 当同时给出 时,其行为是只 + 转储至少匹配一个 开关但不匹配任何 + 开关的模式。如果出现 而没有 + ,那么匹配 的模式会从原本的正常 + 转储中排除。 + + + + + + + + + 将对象标识符(OID)作为每个表的数据的一部分进行转储。如果应用程序以某种方式引用OID列(例如在外键约束中),请使用此选项。否则,不应使用此选项。 + + + + + + + + + 不输出用于把对象所有权设置成与原始数据库一致的命令。默认情况下, + pg_dump 会发出 ALTER OWNER + 或 SET SESSION AUTHORIZATION 语句来设置新建数据库对象 + 的所有权。除非脚本由超级用户(或拥有脚本中所有对象的同一用户)启动, + 否则这些语句会在运行脚本时报错。若要创建一个可由任意用户恢复、并让该用户 + 拥有所有对象的脚本,请指定 。 + + + + 当生成一个归档(非文本)输出文件时,此选项将被忽略。对于归档格式, + 可以在调用 pg_restore 时指定该选项。 + + + + + + + + + + 这个选项已经过时,但仍然被接受以保持向后兼容性。 + + + + + + + + + + 只转储对象定义(模式),不转储数据或统计信息。 + + 此选项与的作用相反。它类似于指定,但由于历史原因并不完全相同。 + + (不要把它与 选项混淆,后者中的 + schema 一词含义不同。) + + + 要只排除数据库中某些表的表数据,请参见 + 。 + + + + + + + + + + 指定在禁用触发器时要使用的超级用户名。这只在使用 + 时相关。(通常最好省略它,而是以 + 超级用户身份运行生成的脚本。) + + + + + + + + + + 只转储名称匹配 table 的表。这里,包括视图、物化视图、序列和外部表。 + 可以通过写多个 开关来选择多个表。 + table 参数按照 + psql\d 命令所使用的同样 + 规则进行解释(见 ),因此也可以通过 + 在模式中使用通配符来选择多个表。使用通配符时,如有需要请小心为模式加 + 引号,以防止 shell 展开通配符;见下文的 + 。 + + + + 使用 时, + 开关没有作用,因为由 选中的表无论这些开关如何设置 + 都会被转储,而非表对象则不会被转储。 + + + + + 当指定 时,pg_dump + 不会尝试转储所选表可能依赖的任何其他数据库对象。因此,不能保证 + 特定表转储的结果能够单独成功恢复到一个干净的数据库中。 + + + + + 开关的行为与 8.2 之前的PostgreSQL版本并不完全向上兼容。以前,写成-t tab会转储所有名为tab的表,现在则只转储默认搜索路径中可见的那个表。要获得旧行为,可以写成-t '*.tab'。此外,要选择特定模式中的表,必须写成类似-t sch.tab的形式,而不是旧的-n sch -t tab写法。 + + + + + + + + + + 不转储任何匹配 table 的表。 + 该模式按照与 相同的规则解释。 + 可以给出多次,以排除匹配多个模式的表。 + + + + 当同时给出 时,其行为是只 + 转储至少匹配一个 开关但不匹配任何 + 开关的表。如果出现 而没有 + ,那么匹配 的表会从原本的正常 + 转储中排除。 + + + + + + + + + + 指定详细模式。这会使 pg_dump 将详细的对象注释、 + 开始/停止时间写入转储文件,并把进度消息写到标准错误。 + + + + + + + + + 打印 pg_dump 的版本并退出。 + + + + + + + + + + + 不转储访问权限(grant/revoke 命令)。 + + + + + + + + + 指定所用的压缩级别。零表示不压缩。对于自定义格式和目录格式归档,该选项指定对各个表数据段的压缩,默认以适中的级别压缩。对于纯文本输出,设置非零压缩级别会压缩整个输出文件,就像将其传给gzip处理一样;但默认不压缩。tar 归档格式目前完全不支持压缩。 + + + + + + + + 此选项供就地升级实用程序使用。不建议或支持将其用于其他目的。该选项的行为可能在未来的版本中更改而不另行通知。 + + + + + + + + + + 将数据转储为带有显式列名的INSERT命令 + (INSERT INTO table (column, ...) VALUES ...)。这会使恢复变得非常缓慢;它主要用于生成可装入 + 非 PostgreSQL 数据库的转储文件。不过,由于此选项为每一行生成单独的命令,重新装载某一行时出错只会导致该行丢失,而不会导致整个表的内容丢失。 + + + + + + + + 此选项禁用函数体中的 dollar quoting,并强制改用 SQL 标准字符串语法 + 对它们进行引用。 + + + + + + + + + 此选项只在创建包含数据但不包含模式的转储时才相关。它指示 + pg_dump 在输出中包含一些命令,以便在恢复数据时 + 临时禁用目标表上的触发器。如果这些表上存在不希望在数据恢复期间触发的 + 引用完整性检查或其他触发器,请使用此选项。 + + + + 目前,为 输出的这些命令必须由 + 超级用户执行。因此,还应通过 指定一个超级用户名, + 或者更好的做法是谨慎地以超级用户身份运行生成的脚本。 + + + + 当生成一个归档(非文本)输出文件时,此选项将被忽略。对于归档格式, + 可以在调用 pg_restore 时指定该选项。 + + + + + + + + + 此选项只在转储启用了行安全的表内容时才相关。默认情况下, + pg_dump 会将 + 设置为 off,以确保把表中的所有数据都转储出来。如果用户没有足够的权限 + 绕过行安全,则会抛出错误。该参数会指示 pg_dump + 改为将 设置为 on,从而允许用户只转储 + 其有权访问的那部分表内容。 + + + + 请注意,如果当前使用此选项,通常还会希望让转储采用 + INSERT 格式,因为恢复期间的 + COPY FROM 不支持行安全。 + + + + + + + + 不转储任何匹配table模式的表的数据。该模式按照与相同的规则解释。可以多次指定,以排除匹配多个模式的表。当需要某个表的定义但不需要其中的数据时,此选项很有用。 + 要排除数据库中所有表的数据,请参阅 + + + + + + + 清理数据库对象时使用条件命令(即添加IF EXISTS子句)。只有同时指定,此选项才有效。 + + + + + + + 将数据转储为INSERT命令(而不是COPY)。这会使恢复变得非常缓慢;它主要用于生成可装载到非PostgreSQL数据库的转储。不过,由于此选项为每一行生成单独的命令,重新装载某一行时出错只会导致该行丢失,而不会导致整个表的内容丢失。注意,如果重新排列了列顺序,恢复可能会完全失败。选项不受列顺序变化影响,但会更慢。 + + + + + + + + 在转储开始时,不要无限等待获取共享表锁。如果无法在指定的 + timeout 内锁定某个表,就让 + 转储失败。超时可以用 SET statement_timeout 接受的 + 任意格式指定。(允许的值因被转储源服务器的版本而异,但 7.3 以来的所有 + 版本都接受以毫秒为单位的整数。从 7.3 之前的服务器转储时,此选项会被 + 忽略。) + + + + + + + + + 不要转储安全标签。 + + + + + + + + 此选项允许对 9.2 之前的服务器运行pg_dump -j,更多详情请参阅参数的文档。 + + + + + + + + 不输出用于选择表空间的命令。使用此选项时,所有对象在恢复时都会创建在 + 当时默认的表空间中。 + + + + 当生成一个归档(非文本)输出文件时,此选项将被忽略。对于归档格式, + 可以在调用 pg_restore 时指定该选项。 + + + + + + + + + 不要转储不记录 WAL 的表和序列的内容。此选项不会影响是否转储表和序列的 + 定义(模式);它只会抑制表和序列数据的转储。从备库转储时, + 不记录 WAL 的表和序列中的数据始终会被排除。 + + + + + + + + + 强制为所有标识符加引号。当从某个服务器转储数据库,而该服务器的 + PostgreSQL 主版本与 + pg_dump 不同,或者输出打算装入另一主版本的 + 服务器时,推荐使用此选项。默认情况下, + pg_dump 只会为在其自身主版本中属于保留字的标识符 + 加引号。这有时会在处理其他版本服务器时导致兼容性问题,因为这些版本的 + 保留字集合可能略有不同。使用 + 可以防止此类问题,但代价是转储脚本更难阅读。 + + + + + + + + + 仅转储指定的部分。部分名称可以是 + ,或。 + 可以多次指定此选项以选择多个部分。默认情况下是转储所有部分。 + + + 数据部分包含实际的表数据、大对象内容、序列值,以及表、物化视图和 + 外部表的统计信息。post-data 部分包括索引、触发器、规则、索引统计信息, + 以及除已验证的检查约束和非空约束之外的其他约束定义。pre-data 部分 + 包括所有其他数据定义项。 + + + + + + + + + 使用 serializable 事务来执行转储,以确保所用快照 + 与后续数据库状态一致;但这是通过等待事务流到达一个不会出现异常的时点来 + 实现的,从而避免转储失败或导致其他事务因 + serialization_failure 而回滚。有关事务隔离和并发 + 控制的更多信息,请参见 。 + + + + 这个选项对于仅用于灾难恢复的转储没有好处。但对于用来加载数据库副本, + 以供报表或其他只读负载共享,而原始数据库继续更新的转储,它可能有用。 + 如果不使用它,转储可能反映出一种与最终提交事务的任何串行执行都不一致 + 的状态。例如,如果使用批处理技术,转储中可能显示某个批次已经关闭, + 但批次中的全部条目却并未出现。 + + + + 如果在启动 pg_dump 时没有活动的读写事务, + 此选项不会带来任何差别。如果存在活动的读写事务,转储开始时间可能会被 + 延迟一个不确定的时长。一旦开始运行,使用或不使用该开关的性能都是相同的。 + + + + + + + + + 在制作数据库转储时,使用指定的同步快照(详见 + )。 + + + 当需要将转储与逻辑复制槽(参见 ) + 或与并发会话同步时,此选项很有用。 + + + 在并行转储的情况下,将使用此选项定义的快照名称,而不是重新获取一个 + 新快照。 + + + + + + + + 要求每个模式(/)和表(/)限定条件至少匹配要转储的数据库中的一个模式或表。注意,如果所有模式和表限定条件都找不到匹配项,pg_dump即使没有使用也会报错。 + 此选项不影响//。排除模式未匹配到任何对象不会被视为错误。 + + + + + + + + 输出符合 SQL 标准的 SET SESSION AUTHORIZATION 命令, + 而不是 ALTER OWNER 命令来确定对象所有权。这会让 + 转储更符合标准,但根据转储中对象的历史,可能无法正确恢复。另外,使用 + SET SESSION AUTHORIZATION 的转储肯定需要超级用户 + 权限才能正确恢复,而 ALTER OWNER 只需较低权限。 + + + + + + + + + + 显示关于 pg_dump 命令行参数的帮助信息,并退出。 + + + + + + + + 以下命令行选项控制数据库连接参数。 + + + + + + 指定要连接的数据库名称。这等价于在命令行上把 + dbname 作为第一个非选项参数 + 指定。dbname 可以是 + 连接字符串。如果是这样,连接 + 字符串中的参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 指定服务器所在机器的主机名。如果值以斜杠开头,则被用作 Unix 域套接字 + 的目录。默认值取自 PGHOST 环境变量(如果已设置), + 否则尝试使用 Unix 域套接字连接。 + + + + + + + + + + 指定服务器正在监听连接的 TCP 端口,或本地 Unix 域套接字文件扩展名。 + 默认值取自 PGPORT 环境变量(如果已设置),否则使用 + 编译时默认值。 + + + + + + + + + + 用于连接的用户名。 + + + + + + + + + + 绝不发出密码提示。如果服务器要求密码认证,而又无法通过 + .pgpass 文件等其他方式获得密码,则连接尝试将失败。 + 在没有用户在场输入密码的批处理作业和脚本中,此选项很有用。 + + + + + + + + + + 强制 pg_dump 在连接数据库之前提示输入密码。 + + + + 这个选项绝非必需,因为如果服务器要求密码认证, + pg_dump 会自动提示输入密码。不过, + pg_dump 会先浪费一次连接尝试来发现服务器需要 + 密码。在某些情况下,输入 值得,因为可以避免这次 + 额外的连接尝试。 + + + + + + + + + 指定用于创建转储的角色名称。此选项会让 + pg_dump 在连接数据库后发出 + SET ROLE rolename + 命令。当经认证用户(由 指定)缺少 + pg_dump 所需权限,但可以切换到具有所需权限的 + 角色时,此选项很有用。有些安装环境有禁止直接以超级用户登录的策略,使用 + 此选项就可以在不违反该策略的情况下进行转储。 + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGOPTIONS + PGPORT + + PGUSER + + + 默认连接参数。 + + + + + + + + 和大多数其他 PostgreSQL 工具一样,这个工具也使用 + libpq 支持的环境变量(见 + )。 + + + + + + 诊断 + + + pg_dump 在内部执行 SELECT + 语句。如果运行 pg_dump 时遇到问题,请确认能够 + 例如使用 从数据库中查询信息。此外, + libpq 前端库所使用的任何默认连接设置和环境变量 + 也都会生效。 + + + + pg_dump 的数据库活动通常会被累积统计系统收集。 + 如果不希望如此,可以将参数 track_counts 通过 + PGOPTIONSALTER USER 命令设置为 + false。 + + + + + + + 注意 + + + 如果数据库集簇在 template1 数据库中有任何本地添加 + 的内容,要小心把 pg_dump 的输出恢复到一个真正 + 空的数据库中;否则很可能因为这些新增对象的重复定义而报错。要创建一个不含 + 任何本地添加的空数据库,应从 template0 而不是 + template1 复制,例如: + +CREATE DATABASE foo WITH TEMPLATE template0; + + + + + 当选择不包含模式的转储并使用 选项时, + pg_dump 会在插入数据前发出命令禁用用户表上的 + 触发器,并在数据插入完成后发出命令重新启用它们。如果恢复在中途停止, + 系统目录可能会保持在错误状态。 + + + pg_dump生成的转储文件不包含优化器用于决定查询计划的统计信息。因此,从转储文件恢复后,最好运行ANALYZE以确保最佳性能;更多信息请参阅。转储文件也不包含任何ALTER DATABASE ... SET命令;这些设置由与数据库用户及其他整个安装环境的设置一起转储。 + + + 由于 pg_dump 常被用来把数据迁移到更新版本的 + PostgreSQL,因此通常可以期望 + pg_dump 的输出能够装载到 + PostgreSQL 服务器中,而这些服务器的版本比 + pg_dump 更高。 + pg_dump 也可以从版本比它自身更旧的 + PostgreSQL 服务器上转储数据。(目前支持回溯到 + 7.0 版的服务器。)然而,pg_dump 不能从主版本 + 高于它自身的 PostgreSQL 服务器上转储;它甚至 + 会拒绝尝试,以免冒生成无效转储的风险。另外,也不能保证 + pg_dump 的输出能够装载到更旧主版本的服务器上 + — 即使转储正是从该版本服务器上取得的。把转储文件装载到较旧服务器时, + 可能需要手工编辑转储文件,移除旧服务器无法理解的语法。在跨版本场景中, + 建议使用 选项,因为它可以防止 + 不同 PostgreSQL 版本保留字列表差异带来的问题。 + + + + + 示例 + + 要将名为 mydb 的数据库转储到 SQL 脚本文件中: +$ pg_dump mydb > db.sql + + + + 要将这样的脚本重新装载到一个新创建的数据库中,其名称为 newdb: + + +$ psql -d newdb -f db.sql + + + + + 要把一个数据库转储为 custom 格式归档文件: + + +$ pg_dump -Fc mydb > db.dump + + + + + 要把一个数据库转储为 directory 格式归档: + + +$ pg_dump -Fd mydb -f dumpdir + + + + + 要使用 5 个并行工作任务把一个数据库转储为 directory 格式归档: + + +$ pg_dump -Fd mydb -j 5 -f dumpdir + + + + + 要把一个归档文件重新装入到一个(新创建的)名为 + newdb 的数据库: + + +$ pg_restore -d newdb db.dump + + + + + 要转储一个名为 mytab 的表: + + +$ pg_dump -t mytab mydb > db.sql + + + + + 要转储 detroit 模式中名称以 emp + 开头的所有表,但排除名为 employee_log 的表: + + +$ pg_dump -t 'detroit.emp*' -T detroit.employee_log mydb > db.sql + + + + + 要转储名称以 eastwest 开头、并且以 + gsm 结尾的所有模式,同时排除名称中包含单词 + test 的任何模式: + + +$ pg_dump -n 'east*gsm' -n 'west*gsm' -N '*test*' mydb > db.sql + + + + + 同样,使用正则表达式记法来合并这些开关: + + +$ pg_dump -n '(east|west)*gsm' -N '*test*' mydb > db.sql + + + + + 要转储除名称以 ts_ 开头的表之外的所有数据库对象: + + +$ pg_dump -T 'ts_*' mydb > db.sql + + + + 要在 及相关开关中指定大写或大小写混合的名称,必须用双引号括起该名称;否则它会被折叠为小写(参见 )。但双引号对 shell 有特殊意义,因此还必须用引号保护这些双引号。因此,要转储一个名称大小写混合的表,需要写成类似如下的形式: +$ pg_dump -t "\"MixedCaseName\"" mydb > mytab.sql + + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/pg_dumpall.sgml b/zh/9.6/ref/pg_dumpall.sgml new file mode 100644 index 00000000..a57aa686 --- /dev/null +++ b/zh/9.6/ref/pg_dumpall.sgml @@ -0,0 +1,469 @@ + + + + + pg_dumpall + + + + pg_dumpall + 1 + 应用程序 + + + + pg_dumpall + 将一个PostgreSQL数据库集簇转储为脚本文件 + + + + + pg_dumpall + connection-option + option + + + + + 描述 + + + pg_dumpall是一个实用程序,用于写出(转储)一个集簇中的所有PostgreSQL数据库到一个脚本文件中。该脚本文件包含SQL命令,可作为的输入来恢复这些数据库。它通过对集簇中的每个数据库调用来实现这一点。pg_dumpall还会转储所有数据库共有的全局对象。 + (pg_dump不会保存这些对象。) + 目前包括数据库用户和组、表空间,以及适用于整个数据库的访问权限等属性的信息。 + + + 由于pg_dumpall会读取所有数据库中的表,因此通常必须以数据库超级用户身份连接,才能生成完整的转储。另外,要执行保存的脚本,也需要具备超级用户权限,这样才能添加用户和组并创建数据库。 + + + + SQL 脚本将写入标准输出。使用/选项或 shell 操作符将其重定向到文件。 + + + + pg_dumpall需要多次连接到PostgreSQL服务器(每个数据库一次)。如果使用密码认证,它每次都会提示输入密码。在这种情况下准备一个~/.pgpass文件会比较方便。详见。 + + + + + + 选项 + + 以下命令行选项控制输出的内容和格式。 + + + + + + 只转储数据,不转储模式(数据定义)或统计信息。 + + + + + + + + + 包含用于在重新创建数据库之前清理(删除)数据库的 SQL 命令。也会添加用于角色和表空间的DROP命令。 + + + + + + + + + 将输出发送到指定文件。如果省略此选项,则使用标准输出。 + + + + + + + + + + 只转储全局对象(角色和表空间),不转储数据库。 + + + + + + + + + 将对象标识符(OID)作为每个表的数据的一部分进行转储。如果应用程序以某种方式引用OID列(例如在外键约束中),请使用此选项。否则,不应使用此选项。 + + + + + + + + + 不要输出用于将对象所有权设置为与原始数据库一致的命令。默认情况下,pg_dumpall会发出ALTER OWNERSET SESSION AUTHORIZATION语句,以设置已创建模式元素的所有权。除非该脚本由超级用户(或拥有脚本中全部对象的同一用户)启动,否则这些语句在运行时会失败。若要创建一个可由任意用户恢复、并让该用户获得所有对象所有权的脚本,请指定。 + + + + + + + + + + 只转储角色,不转储数据库或表空间。 + + + + + + + + + + 只转储对象定义(模式),不转储数据。 + + + + + + + + + + 指定在禁用触发器时要使用的超级用户名。只有在使用时才相关。(通常更好的做法是省略此选项,而以超级用户身份运行生成的脚本。) + + + + + + + + + + 只转储表空间,不转储数据库或角色。 + + + + + + + + + + 指定详细模式。这会让pg_dumpall把开始/停止时间写入转储文件,并将进度消息输出到标准错误。它还会启用pg_dump的详细输出。 + + + + + + + + + + 打印pg_dumpall的版本并退出。 + + + + + + + + + + + 阻止转储访问权限(GRANT/REVOKE 命令)。 + + + + + + + + + 此选项供就地升级工具使用。不建议也不支持将其用于其他用途。该选项的行为在将来的发行版中可能会在不另行通知的情况下发生变化。 + + + + + + + + + + 将数据转储为带有显式列名的INSERT命令 + (INSERT INTO + table + (column, ...) VALUES + ...)。这会使恢复变得非常缓慢;它主要用于生成可以装入非 PostgreSQL 数据库的转储。 + + + + + + + + + 此选项禁用函数体中的 dollar quoting,并强制改用 SQL 标准字符串语法对它们进行引用。 + + + + + + + + + 此选项只在创建包含数据但不包含模式的转储时才相关。它指示pg_dumpall在输出中包含一些命令,以便在恢复数据时临时禁用目标表上的触发器。如果这些表上存在不希望在数据恢复期间触发的引用完整性检查或其他触发器,请使用此选项。 + + + + 目前,为输出的这些命令必须由超级用户执行。因此,还应通过指定一个超级用户名,或者更好的做法是确保以超级用户身份运行生成的脚本。 + + + + + + + + 使用条件命令(即添加IF EXISTS子句)来清理数据库和其他对象。只有同时指定,此选项才有效。 + + + + + + + + 将数据转储为INSERT命令(而不是COPY)。这会使恢复非常缓慢;它主要用于生成可以装入非 PostgreSQL 数据库的转储。注意,如果重新安排了列顺序,恢复可能会彻底失败。选项可以避免列顺序变化带来的问题,但速度更慢。 + + + + + + + + 在转储开始时,不要无限等待获取共享表锁。如果无法在指定的timeout内锁定某个表,就让转储失败。超时可以用SET + statement_timeout接受的任意格式指定。允许的值因被转储的服务器版本而异,但从 7.3 起,所有版本都接受以毫秒为单位的整数。对 7.3 之前的服务器进行转储时,此选项会被忽略。 + + + + + + + + 不要转储安全标签。 + + + + + + + + + 不输出创建表空间的命令,也不输出用于为对象选择表空间的命令。使用此选项时,所有对象在恢复时都会创建在当时默认的表空间中。 + + + + + + + + + 不要转储不记录 WAL 的表的内容。此选项不影响是否转储表定义(模式);它只会抑制转储表数据。 + + + + + + + + + 强制为所有标识符加引号。当从其PostgreSQL主版本与pg_dumpall不同的服务器转储数据库时,或者当输出打算装入另一主版本服务器中时,建议使用此选项。默认情况下,pg_dumpall只会给在其自身主版本中属于保留字的标识符加引号。处理其他版本服务器时,这有时会带来兼容性问题,因为它们的保留字集合可能略有不同。使用可以避免这类问题,但代价是转储脚本更难阅读。 + + + + + + + + + 输出符合 SQL 标准的SET SESSION AUTHORIZATION命令,而不是用ALTER OWNER命令来确定对象所有权。这会让转储更符合标准,但根据转储中对象的历史,可能无法正确恢复。 + + + + + + + + + + 显示关于pg_dumpall命令行参数的帮助信息,并退出。 + + + + + + + + 以下命令行选项控制数据库连接参数。 + + + + + + 以连接字符串形式指定用于连接服务器的参数;这些参数将覆盖任何冲突的命令行选项。 + + + 该选项名为--dbname,是为了与其他客户端应用程序保持一致;但由于pg_dumpall需要连接多个数据库,连接字符串中的数据库名将被忽略。请使用-l选项指定初始连接所用的数据库名,该连接将用于转储全局对象并发现还应转储哪些数据库。 + + + + + + + + + + 指定运行数据库服务器的机器的主机名。如果该值以斜杠开头,则将其用作 Unix 域套接字的目录。默认值取自PGHOST环境变量(如果已设置);否则会尝试使用 Unix 域套接字连接。 + + + + + + + + + + 指定用于转储全局对象并发现还应转储哪些数据库的连接数据库名。如果未指定,则使用postgres数据库;如果该数据库不存在,则使用template1。 + + + + + + + + + + 指定服务器监听连接的 TCP 端口,或本地 Unix 域套接字文件扩展名。默认值取自PGPORT环境变量(如果已设置),否则使用编译时默认值。 + + + + + + + + + + 用于连接的用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而又无法通过.pgpass文件等其他方式获得密码,则连接尝试会失败。该选项适用于批处理作业和脚本,因为这些场景下没有用户可以输入密码。 + + + + + + + + + + 强制pg_dumpall在连接数据库之前提示输入密码。 + + + + 此选项从来都不是必需的,因为如果服务器要求密码认证,pg_dumpall会自动提示输入密码。不过,pg_dumpall会浪费一次连接尝试来发现服务器需要密码。在某些情况下,键入以避免这次额外的连接尝试是值得的。 + + + + 注意,对每个要转储的数据库都会再次提示输入密码。通常,最好设置一个~/.pgpass文件,而不是依赖手工输入密码。 + + + + + + + + + 指定一个角色名,用于创建转储。此选项会使pg_dumpall在连接数据库后发出SET ROLE rolename命令。当已认证用户(由指定)缺少pg_dumpall所需权限,但可以切换到具备所需权限的角色时,这很有用。有些安装环境不允许直接以超级用户身份登录,而使用此选项可以在不违反该策略的情况下完成转储。 + + + + + + + + + + 环境 + + + + PGHOST + PGOPTIONS + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他PostgreSQL工具一样,此实用程序也使用libpq支持的环境变量(见)。 + + + + + + + 注解 + + + 由于pg_dumpall在内部调用pg_dump,因此某些诊断消息会提到pg_dump。 + + + 恢复完成后,最好对每个数据库运行ANALYZE,以便优化器获得有用的统计信息。也可以运行vacuumdb -a -z来分析所有数据库。 + + + pg_dumpall要求在恢复之前,所有必需的表空间目录都已经存在;否则,位于非默认位置的数据库在创建时将会失败。 + + + + + + 示例 + + 要转储所有数据库: + + +$ pg_dumpall > db.out + + + + 要从此文件重新装载数据库,可以使用: +$ psql -f db.out postgres +(这里连接哪个数据库并不重要,因为 pg_dumpall 创建的脚本文件会包含适当的命令,用于创建并连接到已保存的数据库。) + + + + 另见 + + + 有关可能出现的错误情况,请参见。 + + + + diff --git a/zh/9.6/ref/pg_isready.sgml b/zh/9.6/ref/pg_isready.sgml new file mode 100644 index 00000000..546d14c8 --- /dev/null +++ b/zh/9.6/ref/pg_isready.sgml @@ -0,0 +1,238 @@ + + + + + pg_isready + + + + pg_isready + 1 + 应用程序 + + + + pg_isready + 检查PostgreSQL服务器的连接状态 + + + + + pg_isready + connection-option + option + + + + + + + 描述 + + + + pg_isready是一个用于检查 + PostgreSQL数据库服务器连接状态的工具。其退 + 出状态指示连接检查的结果。 + + + + + + 选项 + + + + + + + + + + 指定要连接的数据库名称。dbname也可以是连接字符串。如果是这样,连接字符串 + 中的参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + + + 指定服务器所在机器的主机名。如果值以斜杠开头,则被用作 Unix 域套接字 + 的目录。 + + + + + + + + + + + + 指定服务器正在监听连接的 TCP 端口,或本地 Unix 域套接字文件扩展名。 + 默认值取自 PGPORT 环境变量;如果未设置,则取编译时 + 指定的端口,通常为 5432。 + + + + + + + + + + + + 不显示状态消息。这在编写脚本时很有用。 + + + + + + + + + + + + 在尝试连接时,返回服务器无响应之前等待的最大秒数。设为 0 表示禁用。 + 默认值为 3 秒。 + + + + + + + + + + + + 以用户username连接到数据库, + 而不是使用默认用户。 + + + + + + + + + + + + 打印pg_isready版本并退出。 + + + + + + + + + + + + 显示有关pg_isready命令行参数的帮助并退出。 + + + + + + + + + + 退出状态 + + + + + 如果服务器正常接受连接,pg_isready会向 + shell 返回0;如果服务器拒绝连接(例如处于启动阶 + 段),则返回1;如果连接尝试未得到响应,则返回 + 2;如果未发起连接尝试(例如由于参数非法),则返 + 回3。 + + + + + + 环境 + + + 与大多数其他PostgreSQL工具一样, + pg_isready也使用libpq + 支持的环境变量(见)。 + + + + + + 注解 + + + + + 要获得服务器状态,无需提供正确的用户名、密码或数据库名。不过,如果提供 + 了不正确的值,服务器会记录一次失败的连接尝试。 + + + + + + + 示例 + + + + + 标准用法: + +$ pg_isready +/tmp:5432 - accepting connections +$ echo $? +0 + + + + + + + 使用连接参数检查一个处于启动阶段的 + PostgreSQL集簇: + +$ pg_isready -h localhost -p 5433 +localhost:5433 - rejecting connections +$ echo $? +1 + + + + + + + 使用连接参数检查一个无响应的 + PostgreSQL集簇: + +$ pg_isready -h someremotehost +someremotehost:5432 - no response +$ echo $? +2 + + + + + + + diff --git a/zh/9.6/ref/pg_receivexlog.sgml b/zh/9.6/ref/pg_receivexlog.sgml new file mode 100644 index 00000000..4733d0a5 --- /dev/null +++ b/zh/9.6/ref/pg_receivexlog.sgml @@ -0,0 +1,356 @@ + + + + + pg_receivexlog + + + + pg_receivexlog + 1 + 应用程序 + + + + pg_receivexlog + 从一个 PostgreSQL 服务器流式接收事务日志 + + + + + pg_receivexlog + option + + + + + + 描述 + + + pg_receivexlog 用于从一个正在运行的 + PostgreSQL 集簇流式接收事务日志。事务日志使用 + 流复制协议接收,并写入一个本地文件目录。该目录可用作执行时间点恢复 + (见)时的归档位置。 + + + + pg_receivexlog 会实时流式接收服务器上正在生成的 + 事务日志,而不像 那样等待段文件完成。 + 因此,使用 pg_receivexlog 时无需设置 + 。 + + + + 与 PostgreSQL 备库的 WAL 接收器不同,pg_receivexlog + 默认只在 WAL 文件关闭时才刷写 WAL 数据。要实时刷写 WAL 数据,必须指定 + 选项。由于 pg_receivexlog + 不应用 WAL,当 等于 + remote_apply 时,不应让它成为同步备库。否则它看起来会像 + 一个永远追不上的备库,并导致事务提交阻塞。为避免这种情况,你应当为 + 配置合适的值,或者为 + pg_receivexlog 指定一个与之不匹配的 + application_name,或者把 synchronous_commit + 的值改为 remote_apply 以外的其他值。 + + + + 事务日志通过一个常规的 PostgreSQL 连接并使用 + 复制协议进行流式传输。建立连接时必须使用超级用户或具有 + REPLICATION 权限的用户(参见), + 并且 pg_hba.conf 必须允许复制连接。服务器还必须将 + 设置得足够高,以便至少为该流保留一个 + 可用会话。 + + + + 如果连接丢失,或者最初就无法建立连接,且错误不是致命的, + pg_receivexlog 将无限重试连接,并尽快恢复流式传输。 + 要避免这种行为,可使用 -n 参数。 + + + + + 选项 + + + + + + + + 要把输出写入的目录。 + + + 此参数为必需项。 + + + + + + + + + 当指定 而同名复制槽已存在时,不报错。 + + + + + + + + + + 不在连接错误上循环重试,而是立即报错退出。 + + + + + + + + + + 指定向服务器发回状态包的间隔秒数。这使服务器端更容易监控进度。 + 值为零将完全禁用周期性状态更新,不过当服务器请求时仍会发送一次更新, + 以避免超时断开连接。默认值为 10 秒。 + + + + + + + + + + 要求 pg_receivexlog 使用一个现有的复制槽 + (见)。使用此选项时, + pg_receivexlog 会向服务器报告刷写位置, + 指明每个段何时已同步到磁盘,这样服务器就可以在不需要该段时将其删除。 + + + + 当 pg_receivexlog 的复制客户端在服务器上 + 被配置为同步备库时,使用复制槽会向服务器报告刷写位置,但只在 WAL + 文件关闭时报告。因此,这种配置会导致主库上的事务长时间等待,实际上 + 无法令人满意地工作。要使其正确工作,还必须指定 + --synchronous 选项(见下文)。 + + + + + + + + + 收到 WAL 数据后立即将其刷写到磁盘。同时在刷写之后立即向服务器发回 + 一个状态包,而不考虑 --status-interval。 + + + + 如果 pg_receivexlog 的复制客户端在服务器上 + 被配置为同步备库,就应指定此选项,以确保向服务器发送及时的反馈。 + + + + + + + + + + 启用详细输出模式。 + + + + + + + 下列命令行选项控制数据库连接参数。 + + + + + + + + 以连接字符串的形式指定用于连接服务器的参数。更多信息见 + 。 + + + 出于与其他客户端应用一致的考虑,该选项名为 --dbname, + 但由于 pg_receivexlog 并不连接到集簇中的任何 + 特定数据库,连接字符串中的数据库名会被忽略。 + + + + + + + + + + 指定服务器所在主机的主机名。如果该值以斜杠开头,则将其用作 Unix 域 + 套接字所在目录。默认值取自 PGHOST 环境变量(若已设置), + 否则尝试 Unix 域套接字连接。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + 默认为 PGPORT 环境变量(若已设置),否则为编译时的 + 内置默认值。 + + + + + + + + + + 要用来连接的用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而密码又无法通过诸如 + .pgpass 文件等其他方式获得,则连接尝试将失败。 + 该选项可用于没有用户在场输入密码的批处理作业和脚本。 + + + + + + + + + + 强制 pg_receivexlog 在连接数据库之前提示 + 输入密码。 + + + + 该选项绝非必需,因为如果服务器要求密码认证, + pg_receivexlog 会自动提示输入密码。不过, + pg_receivexlog 会浪费一次连接尝试来发现 + 服务器需要密码。在某些情况下,输入 可以避免这次 + 额外的连接尝试。 + + + + + + + + 为了控制物理复制槽,pg_receivexlog 可以执行 + 以下两种动作之一: + + + + + + + 以 指定的名称创建一个新的物理复制槽,然后退出。 + + + + + + + + + 删除以 指定名称的复制槽,然后退出。 + + + + + + + + 还有其他一些可用选项: + + + + + + + + 打印 pg_receivexlog 的版本并退出。 + + + + + + + + + + 显示有关 pg_receivexlog 命令行参数的帮助 + 并退出。 + + + + + + + + + + + 环境 + + + 和大部分其他 PostgreSQL 工具一样,这个工具也使用 + libpq 支持的环境变量(参见)。 + + + + + + 注解 + + + 当使用 pg_receivexlog 而不是 + 作为主要 WAL 备份方法时,强烈建议使用 + 复制槽。否则,服务器可以随意回收或删除尚未备份的事务日志文件,因为它无法从 + 或复制槽获得 WAL 流已被归档到什么位置的 + 任何信息。但请注意,如果接收端没有及时获取 WAL 数据,复制槽会占满服务器的 + 磁盘空间。 + + + + + + 示例 + + + 要从位于 mydbserver 的服务器流式接收事务日志,并把它存储 + 到本地目录 /usr/local/pgsql/archive: + +$ pg_receivexlog -h mydbserver -D /usr/local/pgsql/archive + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/pg_recvlogical.sgml b/zh/9.6/ref/pg_recvlogical.sgml new file mode 100644 index 00000000..a4c8c6c7 --- /dev/null +++ b/zh/9.6/ref/pg_recvlogical.sgml @@ -0,0 +1,325 @@ + + + + + pg_recvlogical + + + + pg_recvlogical + 1 + 应用程序 + + + + pg_recvlogical + 控制 PostgreSQL 逻辑解码流 + + + + + pg_recvlogical + option + + + + + 描述 + + pg_recvlogical用于控制逻辑解码复制槽,并从这类复制槽流式传输数据。 + + + + 它会创建复制模式连接,因此除受到与 + 相同的约束外,还要满足逻辑复制的相关约束(见)。 + + + + + 选项 + + 必须至少指定以下选项之一来选择操作: + + + + + + 为由指定的数据库,使用指定的输出插件, + 创建一个名称由指定的新逻辑复制槽。 + + + + + + + + + 删除由指定名称的复制槽,然后退出。 + + + + + + + + + 开始从由指定的逻辑复制槽流式传输更改,并持续运行直到被信号终止。 + 如果服务端的更改流因服务器关闭或断开连接而结束,则除非指定了,否则会循环重试。 + + + + 流格式由创建该槽时指定的输出插件决定。 + + + + 该连接必须连到创建该槽时所用的同一个数据库。 + + + + + + + + 可以一同指定。 + 不能与其他操作组合使用。 + + + 以下命令行选项控制输出的位置和格式以及其他复制行为: + + + + + 将接收到的已解码事务数据写入此文件。使用-表示stdout + + + + + + + + + + 指定pg_recvlogical应当以多高的频率发起fsync()调用, + 以确保输出文件安全刷盘。 + + + + 服务器会偶尔要求客户端执行刷盘,并将刷盘位置报告给服务器。 + 除此之外,此设置还会更频繁地执行刷盘。 + + + + 将间隔指定为0会完全禁用fsync()调用,但仍会向服务器报告进度。 + 在这种情况下,发生崩溃时可能会丢失数据。 + + + + + + + + + 模式下,从给定的 LSN 开始复制。有关其效果的详细信息,请参阅中的文档。在其他模式下忽略此选项。 + + + + + + + + 当指定且指定名称的槽已存在时,不报错。 + + + + + + + + + + 当与服务器的连接丢失时,不要循环重试,直接退出。 + + + + + + + + + + 将选项name传递给输出插件;如果指定了value, + 则将其用作该选项的值。可用选项及其效果取决于所使用的输出插件。 + + + + + + + + + 创建槽时,使用指定的逻辑解码输出插件。参见。如果槽已存在,此选项不起作用。 + + + + + + + + 此选项与中同名选项的效果相同。请参阅那里的说明。 + + + + + + + + + 在模式下,使用名为slot_name的现有逻辑复制槽。 + 在模式下,以此名称创建该槽。 + 在模式下,删除此名称的槽。 + + + + + + + + + + 启用详细模式。 + + + + + + + 以下命令行选项控制数据库连接参数: + + + + + 要连接的数据库。有关其具体含义,请参阅各操作的说明。dbname可以是一个连接字符串。如果如此,连接字符串参数会覆盖任何与之冲突的命令行选项。默认值为用户名。 + + + + + + + + + 指定服务器运行所在机器的主机名。如果该值以斜杠开头, + 则它会被用作 Unix 域套接字的目录。默认值取自 + PGHOST环境变量(如果已设置), + 否则将尝试 Unix 域套接字连接。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口,或本地 Unix 域套接字文件扩展名。 + 默认值取自PGPORT环境变量(如果已设置), + 否则使用编译时的默认值。 + + + + + + + + + + 用于连接的用户名。默认为当前操作系统用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而密码又无法通过 + .pgpass文件等其他方式获得, + 则连接尝试将失败。此选项适用于批处理作业和脚本, + 因为在这些场景中没有用户在场输入密码。 + + + + + + + + + + 强制pg_recvlogical在连接数据库之前提示输入密码。 + + + + 这个选项并非必不可少,因为如果服务器要求密码认证, + pg_recvlogical会自动提示输入密码。 + 不过,pg_recvlogical需要先浪费一次连接尝试, + 才能发现服务器需要密码。在某些情况下,使用 + 来避免这次额外的连接尝试是值得的。 + + + + + + + 还可以使用以下附加选项: + + + + + + 打印pg_recvlogical的版本并退出。 + + + + + + + + + + 显示pg_recvlogical命令行参数的帮助并退出。 + + + + + + + + + 环境 + + + 与大多数其他PostgreSQL工具一样,该工具使用libpq支持的环境变量(见)。 + + + + + + 示例 + + + 示例请参见。 + + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/pg_resetxlog.sgml b/zh/9.6/ref/pg_resetxlog.sgml new file mode 100644 index 00000000..cee88c04 --- /dev/null +++ b/zh/9.6/ref/pg_resetxlog.sgml @@ -0,0 +1,276 @@ + + + + + pg_resetxlog + + + + pg_resetxlog + 1 + 应用程序 + + + + pg_resetxlog + 重置一个 PostgreSQL 数据库集簇的事务日志及其他控制信息 + + + + + pg_resetxlog + + + option + datadir + + + + + 描述 + + pg_resetxlog 清空事务日志(WAL),并有选择地重置 + pg_control 文件中存储的一些其他控制信息。当这些文件 + 损坏时,有时就需要这项功能。它应当只作为最后的手段使用,即当服务器因这种 + 损坏而无法启动时。 + + + + 运行这条命令之后,应当就能启动服务器了,但请记住,由于部分提交的事务, + 数据库中可能包含不一致的数据。你应当立即转储数据、运行 initdb + 并重新装载。重新装载之后,检查不一致之处并按需修复。 + + + + 这个工具只能由安装服务器的用户运行,因为它需要对数据目录的读写访问权限。 + 出于安全原因,你必须在命令行上指定数据目录。pg_resetxlog + 不使用环境变量 PGDATA。 + + + + 如果 pg_resetxlog 抱怨说无法确定 pg_control + 的有效数据,你可以通过指定 (强制)选项迫使它继续执行。 + 在这种情况下,缺失的数据会用貌似合理的值代替。大多数字段都可以期望是匹配的, + 但下一个 OID、下一个事务 ID 及其纪元、下一个多事务 ID 及偏移量、WAL 起始地址 + 这些字段可能需要人工协助。这些字段可以用下面讨论的选项来设置。如果你无法为 + 所有这些字段确定正确的值,仍可以使用 ,但恢复出的数据库 + 必须受到比平常更多的怀疑:必须立即转储并重新装载。在转储之前不要 + 在数据库中执行任何修改数据的操作,因为任何此类操作都很可能使损坏加剧。 + + + + + 选项 + + + + + + + 即使 pg_resetxlog 无法确定 pg_control + 的有效数据,也强制它继续执行,如上文所述。 + + + + + + + + + (不操作)选项指示 pg_resetxlog + 打印从 pg_control 重建的值以及即将更改的值,然后 + 不做任何修改直接退出。这主要是一种调试工具,但在真正让 + pg_resetxlog 执行之前,也可以用作健全性检查。 + + + + + + + + 显示版本信息然后退出。 + + + + + + 显示帮助然后退出。 + + + + + 只有当 pg_resetxlog 无法通过读取 pg_control + 确定合适的值时,才需要下列选项。安全值可以按下述方法确定。对于接受数值参数的 + 选项,可以使用前缀 0x 指定十六进制值。 + + + + + xid,xid + + + 手动设置可检索提交时间的最旧和最新事务 ID。 + + + + 可检索提交时间的最旧事务 ID 的安全值(第一部分)可以通过在数据目录下的 + pg_commit_ts 目录中查找数值最小的文件名来确定。 + 反之,可检索提交时间的最新事务 ID 的安全值(第二部分)可以通过在同一目录中 + 查找数值最大的文件名来确定。文件名为十六进制。 + + + + + + xid_epoch + + + 手动设置下一个事务 ID 的纪元。 + + + + 事务 ID 纪元实际上并不存储在数据库中的任何地方(由 + pg_resetxlog 设置的字段除外),因此就数据库本身而言, + 任何值都可以。你可能需要调整这个值以确保 Slony-I + 和 Skytools 等复制系统正常工作—— + 如果需要,应该可以从下游复制数据库的状态中获得合适的值。 + + + + + + xlogfile + + + 手动设置 WAL 起始地址。 + + + + WAL 起始地址应当大于数据目录下 pg_xlog 目录中 + 现存的任何 WAL 段文件名。这些名称同样是十六进制的,并且分为三部分。 + 第一部分是时间线 ID,通常应保持不变。例如,如果 + 00000001000000320000004Apg_xlog + 中最大的项,则使用 -l 00000001000000320000004B 或更高的值。 + + + + + pg_resetxlog 本身会查看 pg_xlog + 中的文件,并选择一个超出最后一个现存文件名的默认 + 设置。因此,只有当你知道存在当前不在 pg_xlog 中的 + WAL 段文件(例如离线归档中的条目),或者 pg_xlog + 的内容已完全丢失时,才需要手动调整 。 + + + + + + + mxid,mxid + + + 手动设置下一个和最旧的多事务 ID。 + + + + 下一个多事务 ID 的安全值(第一部分)可以通过在数据目录下的 + pg_multixact/offsets 目录中查找数值最大的文件名、 + 加一、再乘以 65536(0x10000)来确定。反之,最旧多事务 ID 的安全值 + ( 的第二部分)可以通过在同一目录中查找数值最小的 + 文件名再乘以 65536 来确定。文件名为十六进制,因此最简单的做法是用十六进制 + 指定选项值并追加四个零。 + + + + + + oid + + + 手动设置下一个 OID。 + + + + 没有同样简单的方法来确定一个超出数据库中最大 OID 的下一个 OID, + 但幸运的是,下一个 OID 设置得是否正确并不关键。 + + + + + + mxoff + + + 手动设置下一个多事务偏移量。 + + + + 安全值可以通过在数据目录下的 pg_multixact/members + 目录中查找数值最大的文件名、加一、再乘以 52352(0xCC80)来确定。 + 文件名为十六进制。这里没有像其他选项那样追加零的简单方法。 + + + + + + + + + 手动设置最旧的未冻结事务 ID。 + + + + 安全值可以通过在数据目录下的 pg_xact 目录中查找数值 + 最小的文件名再乘以 1048576(0x100000)来确定。注意文件名为十六进制。 + 通常最简单的做法是用十六进制指定选项值。例如,如果 + 0007pg_xact 中最小的项, + 则 -u 0x700000 可行(五个尾随零提供正确的乘数)。 + + + + + + xid + + + 手动设置下一个事务 ID。 + + + + 安全值可以通过在数据目录下的 pg_clog 目录中查找数值 + 最大的文件名、加一、再乘以 1048576(0x100000)来确定。注意文件名为十六进制。 + 通常最简单的做法是用十六进制指定选项值。例如,如果 + 0011pg_clog 中最大的项, + 则 -x 0x1200000 可行(五个尾随零提供正确的乘数)。 + + + + + + + + 注解 + + + 服务器正在运行时不得使用这条命令。如果 pg_resetxlog + 在数据目录中发现服务器锁文件,它将拒绝启动。如果服务器崩溃过,可能会留下 + 锁文件;这种情况下你可以删除锁文件以允许 pg_resetxlog + 运行。但在这样做之前,请务必再三确认没有仍然存活的服务器进程。 + + + + pg_resetxlog 只能用于同一大版本的服务器。 + + + + + 参见 + + + + + + diff --git a/zh/9.6/ref/pg_restore.sgml b/zh/9.6/ref/pg_restore.sgml new file mode 100644 index 00000000..48c17e61 --- /dev/null +++ b/zh/9.6/ref/pg_restore.sgml @@ -0,0 +1,782 @@ + + + + + pg_restore + + + + pg_restore + 1 + 应用程序 + + + + pg_restore + + + 从由 pg_dump 创建的归档文件恢复 PostgreSQL 数据库 + + + + + + pg_restore + connection-option + option + filename + + + + + + 描述 + + + pg_restore 是一个用于从 + 以非纯文本格式创建的归档中恢复 PostgreSQL + 数据库的工具。它会发出必要的命令,将数据库重建为保存时的状态。 + 归档文件还允许 pg_restore 有选择地恢复其中的内容, + 甚至在恢复前重新排列各项的顺序。归档文件被设计为可跨体系结构移植。 + + + + pg_restore 可以以两种模式运行。如果指定了数据库名, + pg_restore 就会连接到该数据库,并将归档内容直接恢复到数据库中。 + 否则,它会创建一个脚本,其中包含重建数据库所需的 SQL 命令,并将其写入文件或标准输出。 + 这种脚本输出等价于 pg_dump 的纯文本输出格式。 + 因此,一些控制输出的选项与 pg_dump 的选项相对应。 + + + + 显然,pg_restore 无法恢复归档文件中不存在的信息。 + 例如,如果归档是使用 将数据转储为 INSERT 命令 选项生成的, + pg_restore 就不能使用 COPY 语句装载数据。 + + + + + 选项 + + + pg_restore接受以下命令行参数: + + filename + + + 指定要恢复的归档文件所在位置(对于目录格式归档则为目录)。 + 如果未指定,则使用标准输入。 + + + + + + + + + + 仅恢复数据,不恢复模式(数据定义)或统计信息。 + 如果归档中包含,则会恢复表数据、大对象和序列值。 + + + + 此选项类似于指定 ,但由于历史原因并不完全相同。 + + + + + + + + + 在重新创建数据库对象之前清理(删除)它们。(除非使用,否则如果目标数据库中不存在某些对象,可能会产生一些无害的错误消息。) + + + + + + + + + 在向其中恢复之前先创建数据库。如果同时指定了 , + 则会在连接到目标数据库之前先删除并重新创建它。 + + + + 使用此选项时,由 指定的数据库仅用于发出初始的 + DROP DATABASECREATE DATABASE 命令。 + 所有数据都会恢复到归档中出现的数据库名中。 + + + + + + + + + + 连接到数据库 dbname, + 并直接恢复到该数据库中。dbname 可以是一个 + 连接字符串。如果是这样, + 连接字符串参数会覆盖任何冲突的命令行选项。 + + + + + + + + + + 如果在向数据库发送 SQL 命令时遇到错误,则退出。默认行为是继续执行, + 并在恢复结束时显示错误计数。 + + + + + + + + + 指定生成脚本的输出文件,或者在与一起使用时指定列表输出文件。使用-表示标准输出,这也是默认值。 + + + + + + + + 指定归档格式。不必指定格式,因为 pg_restore 会自动确定格式。如果指定,可以是以下格式之一: + + c + custom + + + 归档采用 pg_dump 的自定义格式。 + + + + + + d + directory + + + 归档是目录格式归档。 + + + + + + t + tar + + + 归档是 tar 归档。 + + + + + + + + + + + + + 仅恢复指定名称索引的定义。可以通过多次指定 来恢复多个索引。 + + + + + + + + + 使用多个并发作业执行pg_restore中最耗时的步骤 — 装载数据、创建索引或创建约束 —。此选项可以显著缩短将大型数据库恢复到运行在多处理器机器上的服务器所需的时间。 + + + 每个作业都是一个进程或一个线程,具体取决于操作系统,并使用一个到服务器的独立连接。 + + + + 此选项的最佳取值取决于服务器、客户端和网络的硬件配置。影响因素包括 CPU 核心数和磁盘配置。 + 一个较好的起点是服务器上的 CPU 核心数,但在很多情况下,更大的值也可能带来更快的恢复速度。 + 当然,取值过高会因为频繁争用而导致性能下降。 + + + 只有自定义格式和目录格式归档支持此选项。输入必须是常规文件或目录(例如不能是管道)。当生成脚本而不是直接连接到数据库服务器时,此选项会被忽略。另外,多个作业不能与选项一起使用。 + + + + + + + + + 列出归档的内容。此操作的输出可用作 选项的输入。 + 请注意,如果在 中使用 + 等过滤开关,它们会限制被列出的项目。 + + + + + + + + + + 仅恢复在 list-file 中列出的归档元素, + 并按它们在文件中出现的顺序进行恢复。请注意,如果在 + list-file 通常是通过编辑先前 + + + + + + + + + + 仅恢复位于指定模式中的对象。可以通过多次指定 来指定多个模式。 + 这可以与 选项结合使用,只恢复特定表。 + + + + + + + + + + 不输出用于设置对象所有权以匹配原始数据库的命令。默认情况下, + pg_restore 会发出 ALTER OWNER 或 + SET SESSION AUTHORIZATION 语句,为所创建的模式元素设置所有权。 + 除非对数据库的初始连接由超级用户发起(或者由脚本中所有对象的同一所有者发起), + 否则这些语句会失败。使用 时,初始连接可以使用任意用户名, + 并且该用户将拥有所有创建的对象。 + + + + + + + + + + 仅恢复指定函数。请务必按转储文件目录中显示的形式准确拼写函数名和参数。 + 可以通过多次指定 来恢复多个函数。 + + + + + + + + + + 此选项已过时,但为了向后兼容仍然被接受。 + + + + + + + + + + 仅恢复模式(数据定义),不恢复数据,前提是归档中存在模式条目。 + + 此选项与的作用相反。它类似于指定,但由于历史原因并不完全相同。 + + (不要将它与 选项混淆,那里 schema + 一词的含义不同。) + + + + + + + + + + 指定在禁用触发器时要使用的超级用户用户名。仅当使用 + 时才相关。 + + + + + + + + + + 仅恢复指定表的定义和/或数据。这里的 table 包括视图、物化视图、 + 序列和外部表。可以通过多次指定 来选择多个表。 + 此选项可以与 选项结合使用,以指定特定模式中的表。 + + + + + 当指定 时,pg_restore + 不会尝试恢复所选表可能依赖的任何其他数据库对象。因此,不能保证将特定表恢复到一个空数据库中一定会成功。 + + + + + 此标志与标志在pg_dump中的行为并不完全相同。pg_restore目前不支持通配符匹配,也不能在其中包含模式名。 + + + + + 在 PostgreSQL 9.6 之前的版本中,此标志只匹配表, + 不匹配其他任何类型的关系。 + + + + + + + + + + + 仅恢复指定名称的触发器。可以通过多次指定 来恢复多个触发器。 + + + + + + + + + 指定详细模式。 + + + + + + + + + 打印 pg_restore 的版本并退出。 + + + + + + + + + + 阻止恢复访问权限(grant/revoke 命令)。 + + + + + + + + + 将恢复作为单个事务执行(也就是把发出的命令包裹在 + BEGIN/COMMIT 中)。这可确保要么所有命令都成功完成, + 要么不应用任何更改。此选项隐含 。 + + + + + + + + + 此选项仅在执行仅数据恢复时才相关。它会指示 + pg_restore 在恢复数据期间发出命令,临时禁用目标表上的触发器。 + 如果你不希望在恢复数据期间触发表上的引用完整性检查或其他触发器,请使用此选项。 + + + + 目前,为 发出的命令必须以超级用户身份执行。 + 因此,你还应通过 指定超级用户用户名,或者更好的是,直接以 + PostgreSQL 超级用户身份运行 pg_restore。 + + + + + + + + + 此选项仅在恢复启用了行安全性的表内容时才相关。默认情况下, + pg_restore 会将 + 设置为关闭,以确保所有数据都能恢复到表中。如果用户没有足够的权限绕过行安全性,就会抛出错误。 + 此参数会指示 pg_restore 改为将 + 设置为打开,从而允许用户尝试在启用行安全性的情况下恢复表内容。 + 如果用户无权将转储中的行插入该表,这仍然可能失败。 + + + + 请注意,此选项当前还要求转储采用 INSERT 格式, + 因为 COPY FROM 不支持行安全性。 + + + + + + + + 清理数据库对象时使用条件命令(即添加IF EXISTS子句)。只有同时指定,此选项才有效。 + + + + + + + + 默认情况下,即使表的创建命令失败(例如因为表已经存在),表数据仍然会被恢复。 + 使用此选项时,这类表的数据会被跳过。如果目标数据库已经包含所需的表内容, + 这种行为会很有用。例如,PostgreSQL 扩展 + (如 PostGIS)的辅助表可能已经装载到目标数据库中; + 指定此选项可以防止向这些表装载重复或过时的数据。 + + + + 此选项仅在直接恢复到数据库时有效,而在生成 SQL 脚本输出时无效。 + + + + + + + + + 不输出用于恢复安全标签的命令,即使归档中包含它们。 + + + + + + + + + 不输出用于选择表空间的命令。使用此选项后,所有对象都会在恢复期间默认的表空间中创建。 + + + + + + + + + 仅恢复指定部分。部分名称可以是 + 或 。可以多次指定此选项来选择多个部分。默认是恢复所有部分。 + + + 数据部分包含实际的表数据以及大对象定义。post-data 部分由索引、触发器、规则以及除已验证检查约束之外的约束定义组成。 + pre-data 部分则由所有其他数据定义项组成。 + + + + + + + + + 要求每个模式限定符 + (/)和表限定符 + (/)至少匹配待恢复文件中的一个模式/表。 + + + + + + + + + 输出符合 SQL 标准的 SET SESSION AUTHORIZATION 命令, + 而不是使用 ALTER OWNER 命令来确定对象所有权。 + 这样会使转储结果更符合标准,但根据转储中对象的历史,可能无法正确恢复。 + + + + + + + + + + 显示关于 pg_restore 命令行参数的帮助信息,并退出。 + + + + + + + + + pg_restore还接受以下用于连接参数的命令行参数: + + + + + + 指定服务器运行所在机器的主机名。如果该值以斜杠开头,则它被用作 Unix 域套接字目录。 + 默认值取自 PGHOST 环境变量(如果已设置),否则会尝试使用 Unix 域套接字连接。 + + + + + + + + + + 指定服务器监听连接所使用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + 默认值取自 PGPORT 环境变量(如果已设置),否则使用编译时的默认值。 + + + + + + + + + + 连接时使用的用户名。 + + + + + + + + + + 从不发出密码提示。如果服务器要求使用密码认证,而又无法通过其他方式(如 + .pgpass 文件)获得密码,则连接尝试会失败。 + 此选项适用于批处理任务和脚本中没有用户在场输入密码的场景。 + + + + + + + + + + 强制 pg_restore 在连接数据库之前提示输入密码。 + + + + 此选项实际上并非必需,因为如果服务器要求密码认证, + pg_restore 会自动提示输入密码。 + 不过,pg_restore 会浪费一次连接尝试来发现服务器需要密码。 + 在某些情况下,键入 以避免这一次额外的连接尝试是值得的。 + + + + + + + + + 指定执行恢复时要使用的角色名。此选项会使 pg_restore + 在连接数据库后发出 SET ROLE + rolename 命令。 + 当已认证用户(由 指定)缺少 + pg_restore 所需的权限,但可以切换到具有所需权限的角色时, + 此选项会很有用。有些安装环境禁止直接以超级用户登录,而使用此选项可以在不违反该策略的情况下执行恢复。 + + + + + + + + + + + 环境 + + + + PGHOST + PGOPTIONS + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他 PostgreSQL 工具一样,该工具也使用 + libpq 支持的环境变量(见 )。 + 但是,当未提供数据库名时,它不会读取 PGDATABASE。 + + + + + + + 诊断 + + + 当使用 选项指定直接数据库连接时, + pg_restore 会在内部执行 SQL 语句。 + 如果运行 pg_restore 时遇到问题,请确保你能够使用例如 + 从该数据库中查询信息。此外, + libpq 前端库使用的任何默认连接设置和环境变量也都会生效。 + + + + + + 注解 + + + 如果你的安装在 template1 数据库中有任何本地添加内容, + 请务必将 pg_restore 的输出装载到一个真正空的数据库中; + 否则很可能会因为这些附加对象的重复定义而报错。要创建一个不带任何本地添加内容的空数据库, + 应从 template0 而不是 template1 复制,例如: + +CREATE DATABASE foo WITH TEMPLATE template0; + + + + + pg_restore 的局限性详述如下。 + + + + + 当把数据恢复到一个预先存在的表中,并且使用了 + 选项时, + pg_restore 会在插入数据前发出命令,禁用用户表上的触发器, + 然后在数据插入后再发出命令重新启用它们。如果恢复在中途停止, + 系统目录可能会处于错误状态。 + + + + + pg_restore 不能有选择地恢复大对象; + 例如,不能只恢复属于某个特定表的大对象。如果归档中包含大对象, + 那么要么所有大对象都会被恢复,要么在通过 、 + 或其他选项排除它们时一个也不会恢复。 + + + + + + + + 关于 pg_dump 的局限性的细节也可参见 + 文档。 + + + 恢复完成后,最好对每个恢复的表运行ANALYZE,以便优化器获得有用的统计信息;更多信息请参阅 + + + + + + 示例 + + + 假设我们已经把一个名为 mydb 的数据库转储到一个自定义格式转储文件中: + + +$ pg_dump -Fc mydb > db.dump + + + + + 要删除该数据库并从转储中重新创建它: + + +$ dropdb mydb +$ pg_restore -C -d postgres db.dump + + + 选项中指定的数据库可以是集簇中任何一个已存在的数据库; + pg_restore 只用它来为 mydb 发出 + CREATE DATABASE 命令。使用 时, + 数据总是恢复到转储文件中出现的那个数据库名中。 + + + 要将转储重新装载到一个新数据库中,其名称为 newdb: + + +$ createdb -T template0 newdb +$ pg_restore -d newdb db.dump +注意,我们没有使用 ,而是直接连接到要恢复到的数据库。还要注意,我们从 template0 而不是 template1 克隆新数据库,以确保它最初为空。 + + + 要重新排列数据库项的顺序,首先需要转储归档的目录: + +$ pg_restore -l db.dump > db.list + + 列表文件由一个头部和每个项各占一行的内容组成,例如: + +; +; Archive created at Mon Sep 14 13:55:39 2009 +; dbname: DBDEMOS +; TOC Entries: 81 +; Compression: 9 +; Dump Version: 1.10-0 +; Format: CUSTOM +; Integer: 4 bytes +; Offset: 8 bytes +; Dumped from database version: 8.3.5 +; Dumped by pg_dump version: 8.3.8 +; +; +; Selected TOC Entries: +; +3; 2615 2200 SCHEMA - public pasha +1861; 0 0 COMMENT - SCHEMA public pasha +1862; 0 0 ACL - public pasha +317; 1247 17715 TYPE public composite pasha +319; 1247 25899 DOMAIN public domain0 pasha + + 分号表示一条注释的开始,而行首的数字表示分配给每个项的内部归档 ID。 + + + + 文件中的行可以被注释掉、删除并重新排序。例如: + +10; 145433 TABLE map_resolutions postgres +;2; 145344 TABLE species postgres +;4; 145359 TABLE nt_header postgres +6; 145402 TABLE species_records postgres +;8; 145416 TABLE ss_old postgres + + 可以将这样的文件作为 pg_restore 的输入,这样它就只会按该顺序恢复项 10 和 6: + +$ pg_restore -L db.list db.dump + + + + + + 参见 + + + + + + + + diff --git a/zh/9.6/ref/pg_rewind.sgml b/zh/9.6/ref/pg_rewind.sgml new file mode 100644 index 00000000..a7e5c3b9 --- /dev/null +++ b/zh/9.6/ref/pg_rewind.sgml @@ -0,0 +1,194 @@ + + + + + pg_rewind + + + + pg_rewind + 1 + 应用程序 + + + + pg_rewind + 将一个PostgreSQL数据目录与另一个从其分叉而来的数据目录同步 + + + + + pg_rewind + option + + + + + + directory + + + + + + + + + + 描述 + + + pg_rewind是一个工具,用于在同一PostgreSQL集簇的两份副本时间线发生分叉后,将其中一份重新同步到另一份。典型场景是故障切换后,让旧主库重新上线,作为跟随新主库的备库。 + + + 其结果相当于用源数据目录替换目标数据目录。对于关系文件,只复制发生变化的块;其他所有文件(包括配置文件)都整体复制。pg_rewind相较于获取新的基础备份或使用rsync等工具,其优势在于pg_rewind无需通读集簇中未发生变化的块。当数据库很大而两个集簇之间只有少量块不同时,这使它快得多。 + + + pg_rewind会检查源集簇和目标集簇的时间线历史,以确定它们发生分叉的位置,并且要求在目标集簇的pg_xlog目录中能够找到一直追溯到该分叉点的 WAL。分叉点可能位于目标时间线、源时间线,或者二者共同的祖先时间线上。在典型的故障切换场景中,目标集簇会在分叉后不久关闭,因此这通常不是问题;但如果目标集簇在分叉后又运行了很长时间,旧的 WAL 文件可能已经不存在。在这种情况下,你可以手动把它们从 WAL 归档复制到pg_xlog目录。pg_rewind的用途并不限于故障切换,例如,备库可以被提升,运行一些写事务,然后再回卷,重新成为备库。 + + + 运行pg_rewind之后首次启动目标服务器时,它会进入恢复模式,并重放源服务器在分叉点之后生成的全部 WAL。如果运行pg_rewind时,源服务器上的某些 WAL 已经不可用,因此无法由pg_rewind会话复制,那么在启动目标服务器时必须能够获取这些 WAL。这可以通过在目标数据目录中创建recovery.conf文件,并在其中设置合适的restore_command来实现。 + + + pg_rewind要求目标服务器满足以下条件之一:要么在postgresql.conf中启用了选项,要么在用initdb初始化集簇时启用了数据校验和(默认即如此)。此外,也必须设置为on,不过它默认已启用。 + + + + 如果pg_rewind在处理过程中失败,目标数据目录很可能已处于无法恢复的状态。在这种情况下,建议重新获取一份新的备份。 + + + 如果pg_rewind发现某些文件无法直接写入,它就会立即失败。例如,当源服务器和目标服务器对只读 SSL 密钥和证书使用相同的文件映射时,就会发生这种情况。如果目标服务器上存在这类文件,建议在运行pg_rewind之前将其移除。回卷完成后,其中一些文件可能已经从源复制过来,这种情况下可能需要删除复制过来的数据,并恢复回卷前使用的那组链接。 + + + + + + 选项 + + + pg_rewind接受以下命令行参数: + + + + + + + 此选项指定要与源同步的目标数据目录。在运行pg_rewind之前,目标服务器必须已正常关闭。 + + + + + + + + + + 指定源服务器数据目录的文件系统路径,以便将目标与之同步。此选项要求源服务器已正常关闭。 + + + + + + + + 指定一个 libpq 连接字符串,用于连接源PostgreSQL服务器,以便将目标与之同步。该连接必须是具有超级用户权限的普通连接(非复制连接)。此选项要求源服务器正在运行且不处于恢复模式。 + + + + + + + + + + 执行除实际修改目标目录之外的所有操作。 + + + + + + + + + + + 启用进度报告。打开该选项后,在从源集簇复制数据时会给出大致的进度信息。 + + + + + + + + + + 打印详细的调试输出,这些输出主要对调试pg_rewind的开发人员有用。 + + + + + + + + + + 显示版本信息,然后退出。 + + + + + + + + + 显示帮助,然后退出。 + + + + + + + + + 环境 + + + 在使用选项时,pg_rewind也会使用libpq支持的环境变量(见)。 + + + + + 注解 + + 使用最近刚提升的在线集簇作为源来执行pg_rewind时,必须在提升后执行CHECKPOINT,使其控制文件反映最新的时间线信息。pg_rewind会使用这些信息检查能否利用指定的源集簇回卷目标集簇。 + + + 工作原理 + + + 基本思路是将源集簇中所有文件系统级别的更改复制到目标集簇: + + + + + + 从源集簇的时间线历史与目标集簇分叉这一点之前的最后一个检查点开始,扫描目标集簇的 WAL 日志。对于每条 WAL 记录,记录它所触及的每个数据块。这样就能得到一份列表,其中包含源集簇分叉出去之后,目标集簇中所有发生过更改的数据块。 + + + + 将所有这些发生过更改的块从源集簇复制到目标集簇,可以使用直接文件系统访问()或 SQL()。 + + + 将所有其他文件,例如pg_clog和配置文件,从源集簇复制到目标集簇(关系文件除外)。 + + + 从故障切换时创建的检查点开始,应用源集簇的 WAL。(严格来说,pg_rewind并不应用 WAL,它只是创建一个备份标签文件,让PostgreSQL在启动时重放从该检查点起的全部 WAL。) + + + + + + diff --git a/zh/9.6/ref/pg_xlogdump.sgml b/zh/9.6/ref/pg_xlogdump.sgml new file mode 100644 index 00000000..64f9968d --- /dev/null +++ b/zh/9.6/ref/pg_xlogdump.sgml @@ -0,0 +1,215 @@ + + + + + pg_xlogdump + + + + pg_xlogdump + 1 + 应用程序 + + + + pg_xlogdump + 以人类可读的形式显示一个 PostgreSQL 数据库集簇的事务日志 + + + + + pg_xlogdump + + + + + + + + + 描述 + + pg_xlogdump 显示事务日志(WAL),主要适用于调试或教学目的。 + + + + 这个工具只能由安装服务器的用户运行,因为它需要对数据目录的只读访问权限。 + + + + + 选项 + + + 下列命令行选项控制输出的位置和格式: + + + + + startseg + + + 从指定的日志段文件开始读取。这隐式地决定了搜索文件的路径以及要使用的时间线。 + + + + + + endseg + + + 读完指定的日志段文件后停止。 + + + + + + + + + + 输出有关备份块的详细信息。 + + + + + + + + + + 在指定的日志位置停止读取,而不是读到日志流的末尾。 + + + + + + + + + + 到达有效 WAL 的末尾后,继续每秒轮询一次,等待新的 WAL 出现。 + + + + + + + + + + 显示指定数量的记录,然后停止。 + + + + + + + + + + 指定搜索日志段文件的目录,或包含此类文件的 pg_xlog 子目录所在的目录。默认在当前目录、当前目录的 pg_xlog 子目录以及 PGDATApg_xlog 子目录中搜索。 + + + + + + + + + + 只显示由指定资源管理器生成的记录。如果名称传入 list,则打印有效资源管理器名称的列表并退出。 + + + + + + + + + + 开始读取的日志位置。默认从找到的最早文件中的第一个有效日志记录开始读取。 + + + + + + + + + + 读取日志记录所用的时间线。如果指定了 startseg,默认使用其中的值;否则默认为 1。 + + + + + + + + + + 打印 pg_xlogdump 的版本并退出。 + + + + + + + + + + 只显示标有给定事务 ID 的记录。 + + + + + + + + + + 显示汇总统计信息(记录数和大小以及整页镜像数),而不是每条记录。可以选择按记录而不是按资源管理器生成统计信息。 + + + + + + + + + + 显示有关 pg_xlogdump 命令行参数的帮助并退出。 + + + + + + + + + 注解 + + 服务器运行时可能给出错误的结果。 + + + + 只显示指定的时间线(若未指定则为默认时间线)。其他时间线中的记录会被忽略。 + + + + pg_xlogdump 无法读取带 .partial 后缀的 WAL 文件。如果需要读取这些文件,必须从文件名中去掉 .partial 后缀。 + + + + + 参见 + + + + + + + diff --git a/zh/9.6/ref/pgarchivecleanup.sgml b/zh/9.6/ref/pgarchivecleanup.sgml new file mode 100644 index 00000000..a3c2f5e3 --- /dev/null +++ b/zh/9.6/ref/pgarchivecleanup.sgml @@ -0,0 +1,172 @@ + + + + + pg_archivecleanup + + + + pg_archivecleanup + 1 + 应用程序 + + + + pg_archivecleanup + 清理PostgreSQL WAL 归档文件 + + + + + pg_archivecleanup + option + archivelocation + oldestkeptwalfile + + + + + 描述 + + + pg_archivecleanup设计为在服务器作为备库运行时用作 + archive_cleanup_command,以清理 WAL 文件归档(见 + )。 + pg_archivecleanup也可作为独立程序使用,以清理 WAL + 文件归档。 + + + 要配置备库使用 pg_archivecleanup,请将以下内容放入其 recovery.conf 配置文件中: +archive_cleanup_command = 'pg_archivecleanup archivelocation %r' +其中,archivelocation 是应从中移除 WAL 段文件的目录。 + 当在中使用时,逻辑上早于%r参数值的所有 WAL 文件都会从archivelocation中移除。这样既能将需要保留的文件数量降到最低,又能保留崩溃后重启能力。如果archivelocation只是该特定备库的临时暂存区,那么使用此参数是合适的;但以下情况下则适合使用:archivelocation打算作为长期 WAL 归档区域,或者多个备库正在从同一归档位置恢复。 + + 作为独立程序使用时,逻辑上早于 oldestkeptwalfile 的所有 WAL + 文件都会从 archivelocation 中移除。在此模式下,如果指定的是 + .partial.backup 文件名,则仅使用其文件名前缀 + 作为 oldestkeptwalfile。这样处理 .backup + 文件名后,就可以无误地删除某个特定基础备份之前归档的所有 WAL 文件。例如,下例会移除所有 + 早于 WAL 文件名 000000010000003700000010 的文件: + +pg_archivecleanup -d archive 000000010000003700000010.00000020.backup + +pg_archivecleanup: keep WAL file "archive/000000010000003700000010" and later +pg_archivecleanup: removing file "archive/00000001000000370000000F" +pg_archivecleanup: removing file "archive/00000001000000370000000E" + + + + pg_archivecleanup 假定 archivelocation + 是服务器属主用户可读写的目录。 + + + + + 选项 + + + pg_archivecleanup接受以下命令行参数: + + + + + + 在 stderr 上输出大量调试日志。 + + + + + + + + + 在 stdout 上打印原本会被移除的文件名(执行试运行)。 + + + + + + + + + + 输出 pg_archivecleanup 的版本号并退出。 + + + + + + extension + + + 指定一个扩展名;在判断文件是否应删除之前,会先从所有文件名中去掉该扩展名。 + 这通常适用于清理由于存储时经过压缩、因而被压缩程序附加了扩展名的归档。 + 例如:-x .gz。 + + + + + + + + + + + 显示有关 pg_archivecleanup 命令行参数的帮助并退出。 + + + + + + + + + 注解 + + + pg_archivecleanup 设计为在作为独立工具使用时与 + PostgreSQL 8.0 及更高版本配合工作,或在作为归档清理命令 + 使用时与 PostgreSQL 9.0 及更高版本配合工作。 + + + + pg_archivecleanup 采用 C 编写,源代码易于修改,并且专门 + 划出了若干区域供你按自身需要修改。 + + + + + 示例 + + 在 Linux 或 Unix 系统上,你可以这样使用: + +archive_cleanup_command = 'pg_archivecleanup -d /mnt/standby/archive %r 2>>cleanup.log' + + 其中归档目录实际上位于备库上,因此 archive_command 通过 NFS 访问它, + 但这些文件对备库来说是本地的。这将会: + + + + + 在 cleanup.log 中产生调试输出 + + + + + 从归档目录中移除不再需要的文件 + + + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/pgbench.sgml b/zh/9.6/ref/pgbench.sgml new file mode 100644 index 00000000..a8e2a10d --- /dev/null +++ b/zh/9.6/ref/pgbench.sgml @@ -0,0 +1,1126 @@ + + + + + pgbench + + + + pgbench + 1 + 应用程序 + + + + pgbench + 运行 PostgreSQL 基准测试 + + + + + pgbench + + option + dbname + + + pgbench + option + dbname + + + + + 描述 + + pgbench是一个用于对PostgreSQL执行基准测试的简单程序。它会反复执行同一组 SQL 命令,也可在多个并发数据库会话中运行,然后计算平均事务速率(每秒事务数)。默认情况下,pgbench测试的是一个大体上基于 TPC-B 的场景,每个事务包含五条SELECTUPDATEINSERT命令。不过,通过编写自己的事务脚本文件,也很容易测试其他场景。 + + + + 下面是 pgbench 的典型输出: + + +transaction type: <builtin: TPC-B (sort of)> +scaling factor: 10 +query mode: simple +number of clients: 10 +number of threads: 1 +number of transactions per client: 1000 +number of transactions actually processed: 10000/10000 +tps = 85.184871 (including connections establishing) +tps = 85.296346 (excluding connections establishing) + + + 前六行报告了一些最重要的参数设置。下一行报告已完成的事务数和预期的事务数(后者就是客户端数与每个客户端的事务数的乘积);除非运行在完成前失败,否则这两个数应该相等。(在 模式下,只打印实际的事务数。)最后两行报告每秒事务数,分别计入和不计入启动数据库会话的时间。 + + + + 默认的类 TPC-B 事务测试要求预先建立特定的表。应使用(初始化)选项调用pgbench来创建并填充这些表。(测试自定义脚本时不需要这一步,但需要自行完成测试所需的准备工作。)初始化命令如下: + + +pgbench -i other-options dbname + + + 其中dbname是已创建好的、用于执行测试的数据库名称。(可能还需要使用和/或选项来指定如何连接到数据库服务器。) + + + + + + pgbench -i会创建四个表pgbench_accounts、 + pgbench_branchespgbench_historypgbench_tellers,并销毁任何已存在的同名表。如果数据库中已经存在这些名称的表,请务必改用其他数据库! + + + + + 在默认的比例因子 1 下,这些表最初包含如下行数: + +表 行数 +--------------------------------- +pgbench_branches 1 +pgbench_tellers 10 +pgbench_accounts 100000 +pgbench_history 0 + + 可以使用(比例因子)选项来增加行数,而且在大多数场景下通常也应该这样做。此时还可以配合使用(fillfactor)选项。 + + + + 完成必要的准备后,就可以使用不带的命令运行基准测试,也就是: + + +pgbench options dbname + + + 几乎在所有情况下,都需要附加一些选项才能得到有意义的测试。最重要的选项是(客户端数)、 + (事务数)、(时间限制)以及(指定自定义脚本文件)。完整列表见下文。 + + + + + 选项 + + + 以下内容分为三个小节:数据库初始化和运行基准测试时使用不同的选项,而有些选项在这两种情况下都适用。 + + + + 初始化选项 + + + pgbench 接受以下命令行初始化参数: + + + + + + + + + + 进入初始化模式所必需。 + + + + + + fillfactor + fillfactor + + + + 以给定的 fillfactor 创建pgbench_accounts、 + pgbench_tellers和 + pgbench_branches表。 + 默认值为 100。 + + + + + + + + + + 初始化后不执行任何清理。 + + + + + + + + + + 将日志切换为安静模式,每 5 秒只输出一条进度消息。默认日志每 100000 行打印一条消息,因此通常每秒输出很多行(尤其是在性能良好的硬件上)。 + + + + + + scale_factor + scale_factor + + + + 将生成的行数乘以比例因子。 + 例如,-s 100会在pgbench_accounts表中创建 10,000,000 行。 + 默认值为 1。 + 当比例达到 20,000 或更大时,用于保存账户标识符的列(aid列) + 将切换到使用更大的整数(bigint), + 以便容纳账户标识符的取值范围。 + + + + + + + + + 在标准表之间创建外键约束。 + + + + + + + + + + 在指定的表空间中创建索引,而不是默认的表空间。 + + + + + + + + + + 在指定的表空间中创建表,而不是默认的表空间。 + + + + + + + + + + 将所有表创建为不记录 WAL 的表,而不是永久表。 + + + + + + + + + + + 基准测试选项 + + + pgbench 接受以下命令行基准测试参数: + + + + scriptname[@weight] + =scriptname[@weight] + + + + 将指定的内置脚本添加到待执行脚本列表中。 + 可用的内置脚本包括:tpcb-like、 + simple-updateselect-only。 + 也接受内置名称的无歧义前缀。 + 使用特殊名称list时,会显示内置脚本列表 + 并立即退出。 + + + + 可选地,可在@后写一个整数权重,以调整此脚本相对于其他脚本的选中概率。 + 默认权重为 1。 + 详情请参见下文。 + + + + + + + clients + clients + + + + 模拟的客户端数量,也就是并发数据库会话的数量。默认值为 1。 + + + + + + + + + + + 为每个事务建立一个新连接,而不是仅在每个客户端会话中执行一次。 + 这对于测量连接开销很有用。 + + + + + + + + + + 打印调试输出。 + + + + + + varname=value + varname=value + + + + 定义一个变量,供自定义脚本使用(见下文)。 + 允许使用多个选项。 + + + + + + filename[@weight] + filename[@weight] + + + + 将从filename读取的事务脚本添加到要执行的脚本列表中。 + + + + 可选地,可在@后写一个整数权重,以调整此脚本相对于其他脚本的选中概率。 + 默认权重为 1。 + (如果脚本文件名本身包含@字符,可追加一个权重以消除歧义,例如filen@me@1。) + 详情见下文。 + + + + + + threads + threads + + + + pgbench中的工作线程数。 + 在多 CPU 机器上使用多个线程可能会有所帮助。 + 客户端尽可能均匀地分布在可用线程中。 + 默认值为 1。 + + + + + + + + + + + 将每个事务所花的时间写入日志文件。 + 详情见下文。 + + + + + + limit + limit + + + 持续时间超过limit毫秒的事务会被单独计数和报告,称为late。 + + + 使用限流()时,若某个事务落后于计划时间超过limit毫秒, + 从而已经不可能满足延迟限制,则它根本不会被发送到服务器。此类事务会被单独计数并报告为skipped。 + + + + + + querymode + querymode + + + 用于向服务器提交查询的协议: + + + simple:使用简单查询协议。 + + + extended:使用扩展查询协议。 + + + prepared:使用带有预备语句的扩展查询协议。 + + + 默认为简单查询协议。(详见 。) + + + + + + + + + + + 在运行测试前不执行任何清理。 + 如果运行的是不包含标准表pgbench_accounts、 + pgbench_branchespgbench_history和 + pgbench_tellers的自定义测试场景,则此选项是必需的。 + + + + + + + + + + + 运行内置的 simple-update 脚本。 + 是的简写。 + + + + + + sec + sec + + + 每sec秒显示一次进度报告。报告包括自运行开始以来的时间、自上次报告以来的 TPS,以及自上次报告以来事务延迟的平均值和标准差。使用限流( + + + + + + + + + 在基准测试完成后,报告每条命令的平均语句延迟(从客户端视角看到的执行时间)。详情见下文。 + + + + + + rate + rate + + + + 以指定速率执行事务,而不是像默认行为那样尽可能快地运行。速率以每秒事务数表示。 + 如果目标速率高于可达到的最大速率,则速率限制不会影响结果。 + + + + 该速率通过让事务沿着一条符合泊松分布的时间线启动来实现。预期开始时间表是根据客户端首次启动的时间向前推进的,而不是根据前一个事务结束的时间。这意味着,当某些事务超过其原定结束时间时,后续事务仍有可能重新赶上计划。 + + + + 启用限流后,运行结束时报告的事务延迟是从计划开始时间计算的,因此它包含每个事务等待前一个事务完成的时间。 + 这段等待时间称为计划滞后时间,其平均值和最大值也会单独报告。若要得到相对于事务实际开始时间的延迟,也就是事务在数据库中实际执行所花费的时间, + 可以用报告中的延迟减去计划滞后时间。 + + + + 如果同时使用, + 一个事务可能会落后太多,以至于在前一个事务结束时已经超过了 + 延迟限制,因为延迟是从计划开始时间计算的。这样的事务 + 不会发送到服务器,而是完全跳过并单独计数。 + + + + 较高的计划滞后时间表明,在所选客户端数和线程数下,系统无法以指定速率处理事务。 + 当平均事务执行时间长于事务之间的计划间隔时,后续事务会不断进一步落后, + 而计划滞后时间也会随着测试持续时间增加。在这种情况下,需要降低指定的事务速率。 + + + + + + scale_factor + scale_factor + + + + 在pgbench输出中报告指定的比例因子。 + 对于内置测试,这没有必要;系统会通过统计pgbench_branches表中的行数来检测正确的比例因子。 + 但在只测试自定义基准(选项)时, + 除非使用此选项,否则比例因子会被报告为 1。 + + + + + + + + + + + 运行内置的 select-only 脚本。 + 是的简写。 + + + + + + transactions + transactions + + + + 每个客户端运行的事务数量。默认值为10。 + + + + + + seconds + seconds + + + + 让测试运行指定的秒数,而不是让每个客户端执行固定数量的事务。 和 + 是互斥的。 + + + + + + + + + + + 在运行测试之前,对四个标准表全部执行清理。 + 如果既不使用也不使用pgbench会对 + pgbench_tellerspgbench_branches + 表进行清理,并截断pgbench_history。 + + + + + + + + + + 聚合间隔的长度(以秒为单位)。只能与-l一起使用—— + 使用此选项时,日志包含每个间隔的摘要(事务数、最小/最大延迟, + 以及两个对方差估计有用的附加字段)。 + + + + 此选项目前在 Windows 上不受支持。 + + + + + + + + + + 显示进度(选项)时,使用时间戳(Unix 纪元)而不是自运行开始以来的秒数。 + 单位为秒,小数点后精确到毫秒。 + 这有助于比较各种工具生成的日志。 + + + + + + + + + 写入日志时使用的采样率,用于减少生成的日志量。如果给定此选项, + 则只记录指定比例的事务。1.0 表示记录全部事务,0.05 表示只记录 5% 的事务。 + + + 处理日志文件时,记得把采样率考虑进去。例如,计算 TPS 值时,需要按采样率将数字乘以相应倍数(例如采样率为 0.01 时,只能得到实际 TPS 的 1/100)。 + + + + + + + + + + + 公共选项 + + + pgbench 接受以下通用命令行参数: + + + + + hostname + hostname + + + 数据库服务器的主机名。 + + + + + + port + port + + + 数据库服务器的端口号。 + + + + + + login + login + + + 连接时使用的用户名。 + + + + + + + + + + 打印pgbench版本并退出。 + + + + + + + + + + 显示pgbench命令行参数的帮助信息并退出。 + + + + + + + + + + + 注解 + + + 在<application>pgbench</application>中实际执行的<quote>事务</quote>是什么? + + + pgbench会从指定列表中随机选取测试脚本来执行。 + 这些脚本既可以是用指定的内置脚本,也可以是用指定的用户脚本。 + 每个脚本都可以在其后加上一个以@引出的相对权重,以改变其被选中的概率。 + 默认权重为1。权重为0的脚本会被忽略。 + + + + 默认的内置事务脚本(也可通过调用)会针对随机选取的aid、 + tidbiddelta在每个事务中发出七条命令。 + 该场景受 TPC-B 基准启发,但并不是真正的 TPC-B,因此才取了这个名字。 + + + + BEGIN; + UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; + SELECT abalance FROM pgbench_accounts WHERE aid = :aid; + UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; + UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; + INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); + END; + + + + 如果选择simple-update内置脚本(也就是),事务中将不包含第 4 步和第 5 步。这会避免在这些表上发生更新争用,但也会让该测试场景更不像 TPC-B。 + + + + 如果选择select-only内置脚本(也就是),则只执行SELECT。 + + + + + 自定义脚本 + + + pgbench支持自定义基准测试场景:只需用从文件中读取的事务脚本(选项)替换默认事务脚本(见上文)即可。在这种情况下,一个事务就表示脚本文件的一次执行。 + + + + 脚本文件包含一个或多个以分号结束的 SQL 命令。空行以及以--开头的行会被忽略。脚本文件还可以包含元命令,它们由pgbench自身解释,详见下文。 + + + + + 在PostgreSQL 9.6 之前,脚本文件中的 SQL 命令以换行结束,因此不能跨行。现在连续 SQL 命令之间必须用分号分隔(如果 SQL 命令后面跟着一个元命令,则不需要分号)。如果需要创建一个既能在旧版也能在新版pgbench下工作的脚本文件,务必将每个 SQL 命令写在单独一行,并以分号结束。 + + + + + 脚本文件提供了简单的变量替换功能。变量可以通过前面介绍的命令行 + + + 自动变量 + + + + + 变量 + 简介 + + + + + + scale + 当前比例因子 + + + + client_id + 标识客户端会话的唯一编号(从零开始) + + + +
+ + + 脚本文件中的元命令以反斜线(\)开头,延伸到行尾。元命令的参数以空白分隔。支持的元命令如下: + + + + + + \set varname expression + + + + + 将变量varname设置为根据expression计算出的值。表达式可以包含整数常量(例如5432)、双精度常量(例如3.14159)、变量引用:variablename、具有通常优先级和结合性的一元操作符(+、-)和二元操作符(+、-、*、/、%)、函数调用以及括号。 + + + + 示例: + +\set ntellers 10 * :scale +\set aid (1021 * random(1, 100000 * :scale)) % (100000 * :scale) + 1 + + + + + + + \sleep number [ us | ms | s ] + + + + + + 让脚本执行休眠指定时长,单位可以是微秒(us)、毫秒(ms)或秒(s)。如果省略单位,则默认为秒。number可以是整数常量,也可以是引用了整数值变量的:variablename。 + + + + 示例: + +\sleep 10 ms + + + + + + + \setshell varname command [ argument ... ] + + + + + + 将变量varname设置为 shell 命令command在给定argument参数下的结果。该命令必须通过标准输出返回一个整数值。 + + + + command和每个argument都可以是文本常量,也可以是引用某个变量的:variablename。如果要使用以冒号开头的argument,请在argument开头再写一个冒号。 + + + + 示例: + +\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon + + + + + + + \shell command [ argument ... ] + + + + + + 与\setshell相同,但命令结果会被丢弃。 + + + + 示例: + +\shell command literal_argument :variable ::literal_starting_with_colon + + + + +
+ + + 内置函数 + + + 中列出的函数都内置于pgbench,可用于\set中的表达式。 + + + + + pgbench 函数 + + + + 函数 + 返回类型 + 简介 + 示例 + 结果 + + + + + abs(a) + a相同 + 绝对值 + abs(-17) + 17 + + + debug(a) + a相同 + a打印到stderr,并返回a + debug(5432.1) + 5432.1 + + + double(i) + double + 转换为 double + double(5432) + 5432.0 + + + greatest(a [, ... ] ) + 若任一a为 double,则为 double,否则为 integer + 参数中的最大值 + greatest(5, 4, 3, 2) + 5 + + + int(x) + integer + 转换为 int + int(5.4 + 3.8) + 9 + + + least(a [, ... ] ) + 若任一a为 double,则为 double,否则为 integer + 参数中的最小值 + least(5, 4, 3, 2.1) + 2.1 + + + pi() + double + 常量 PI 的值 + pi() + 3.14159265358979323846 + + + random(lb, ub) + integer + [lb, ub]中均匀分布的随机整数 + random(1, 10) + 110之间的整数 + + + random_exponential(lb, ub, parameter) + integer + [lb, ub]中服从指数分布的随机整数,见下文 + random_exponential(1, 10, 3.0) + 110之间的整数 + + + random_gaussian(lb, ub, parameter) + integer + [lb, ub]中服从高斯分布的随机整数,见下文 + random_gaussian(1, 10, 2.5) + 110之间的整数 + + + sqrt(x) + double + 平方根 + sqrt(2.0) + 1.414213562 + + + +
+ + + random函数使用均匀分布生成值,即指定范围内所有值被抽到的概率相等。random_exponential和random_gaussian函数需要一个额外的 double 参数,用来决定分布的具体形状。 + + + + + + 对于指数分布,parameter通过在parameter处截断一个快速衰减的指数分布,再将其投影到边界之间的整数上,从而控制分布。准确地说,令 + +f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1 - exp(-parameter)) + + 则minmax之间(含边界)的值i会以f(i) - f(i + 1)的概率被抽中。 + + + + 直观地说,parameter越大,越靠近min的值越容易被抽到,而越靠近max的值越不容易被抽到。parameter越接近 0,分布就越平坦(也就越均匀)。对这种分布的一个粗略近似是:范围内出现频率最高的 1% 的值,即最靠近min的那些值,大约会占到parameter% 的抽样次数。parameter必须严格大于 0。 + + + + + + 对于高斯分布,该区间会映射到一个标准正态分布(经典钟形高斯曲线),并在左侧-parameter和右侧+parameter处截断。区间中部的值更容易被抽到。准确地说,如果PHI(x)是标准正态分布的累积分布函数,均值mu定义为(max + min) / 2.0,则有 + +f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) / + (2.0 * PHI(parameter) - 1) + + 则minmax(包含边界)之间的值i被抽中的概率为:f(i + 0.5) - f(i - 0.5)。直观地说,parameter越大,越靠近区间中间的值被抽到的频率越高,而越靠近minmax边界的值被抽到的频率越低。大约 67% 的值会落在区间中部1.0 / parameter这一段内,也就是均值两侧各占区间长度0.5 / parameter的范围内;约 95% 的值会落在区间中部2.0 / parameter这一段内,也就是均值两侧各占区间长度1.0 / parameter的范围内。例如,如果parameter为 4.0,则 67% 的值会落在区间中间四分之一(1.0 / 4.0)内,也就是从3.0 / 8.05.0 / 8.0;95% 的值会落在区间中间一半(2.0 / 4.0)内,也就是第二和第三四分位。考虑到 Box-Muller 变换的性能,parameter的最小值为 2.0。 + + + + + + 作为一个示例,内置的类 TPC-B 事务的全部定义是: + + +\set aid random(1, 100000 * :scale) +\set bid random(1, 1 * :scale) +\set tid random(1, 10 * :scale) +\set delta random(-5000, 5000) +BEGIN; +UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; +SELECT abalance FROM pgbench_accounts WHERE aid = :aid; +UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; +UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; +INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); +END; + + + 该脚本允许事务的每次迭代都引用不同的随机选中行。(这个示例也说明了为什么每个客户端会话都必须拥有自己的变量 — 否则它们就无法彼此独立地访问不同的行。) + + +
+ + + 逐事务日志记录 + + + 使用选项但未使用时,pgbench会将每个事务所花的时间写入日志文件。日志文件名为pgbench_log.nnn,其中nnn是pgbench进程的 PID。如果选项为 2 或更高(从而创建多个工作线程),则每个工作线程都有自己的日志文件。第一个工作线程的日志文件名与标准单工作线程情况相同。其他工作线程的附加日志文件名为pgbench_log.nnn.mmm,其中mmm是每个工作线程从 1 开始的顺序编号。 + + + + 日志格式如下: + + +client_id transaction_no time script_no time_epoch time_us schedule_lag + + + 其中,time 是事务经过的总时间,单位为微秒, + script_no 标识使用的脚本文件(在通过 指定多个脚本时很有用),而 time_epoch/time_us 分别是 Unix 纪元格式的时间戳和以微秒计的偏移量(适合用来生成带小数秒的 ISO 8601 时间戳),表示事务完成的时间。 + schedule_lag 字段是事务计划开始时间与实际开始时间之间的差值,单位为微秒。它仅在使用 + + + 这里是生成的日志文件的一个片段: + +0 199 2241 0 1175850568 995598 +0 200 2465 0 1175850568 998079 +0 201 2513 0 1175850569 608 +0 202 2038 0 1175850569 2663 + + + 另一个示例使用的是 --rate=100 和 --latency-limit=5(注意额外的 + schedule_lag列): + +0 81 4621 0 1412881037 912698 3005 +0 82 6173 0 1412881037 914578 4304 +0 83 skipped 0 1412881037 914578 5217 +0 83 skipped 0 1412881037 914578 5099 +0 83 4722 0 1412881037 916203 3108 +0 84 4142 0 1412881037 918023 2333 +0 85 2465 0 1412881037 919759 740 + + 在这个示例中,事务 82 迟到了,因为它的延迟(6.173 ms)超过了 + 5 ms 限制。接下来的两个事务被跳过,因为它们在开始之前就已经迟到了。 + + + + 在能够处理大量事务的硬件上运行长时间测试时,日志文件可能会变得非常大。可以使用选项,仅记录事务的随机样本。 + + + + + 聚合日志记录 + + + 使用 选项时,日志使用的格式略有不同: + + +interval_start num_of_transactions latency_sum latency_2_sum min_latency max_latency lag_sum lag_2_sum min_lag max_lag skipped_transactions + + + 其中,interval_start 是时间区间的开始时间(Unix 纪元格式时间戳), + num_of_transactions 是区间内的事务数, + latency_sum 是延迟的总和(这样你可以方便地计算平均延迟)。 + 接下来的两个字段可用于方差估计——latency_sum 是延迟的总和, + 而latency_2_sum 是延迟的平方和。再接下来的两个字段是 + min_latency(区间内的最小延迟)和 + max_latency(区间内的最大延迟)。事务在提交时计入其所在的时间区间。 + 末尾的字段 lag_sumlag_2_sum、 + min_lagmax_lag 仅在使用 + 选项时出现。最后一个字段 skipped_transactions + 仅在还使用 选项时出现。它们根据各事务等待前一事务 + 完成的时间计算,即各事务计划开始时间与实际开始时间之间的差值。 + + + + 下面是输出示例: + +1345828501 5601 1542744 483552416 61 2573 +1345828503 7884 1979812 565806736 60 1479 +1345828505 7208 1979422 567277552 59 1391 +1345828507 7685 1980268 569784714 60 1398 +1345828509 7073 1979779 573489941 236 1411 + + + + 请注意,普通(未聚合)日志文件包含对自定义脚本文件的引用,而聚合日志不包含。因此,如果需要按脚本区分的数据,就必须自行聚合。 + + + + + + 逐语句延迟 + + + 使用选项时,pgbench会收集每个客户端执行的每条语句所经过的事务时间。基准测试完成后,它会报告这些值的平均值,称为每条语句的延迟。 + + + + 对于默认脚本,输出与下面类似: + +starting vacuum...end. +transaction type: <builtin: TPC-B (sort of)> +scaling factor: 1 +query mode: simple +number of clients: 10 +number of threads: 1 +number of transactions per client: 1000 +number of transactions actually processed: 10000/10000 +latency average = 15.844 ms +latency stddev = 2.715 ms +tps = 618.764555 (including connections establishing) +tps = 622.977698 (excluding connections establishing) +script statistics: + - statement latencies in milliseconds: + 0.002 \set aid random(1, 100000 * :scale) + 0.005 \set bid random(1, 1 * :scale) + 0.002 \set tid random(1, 10 * :scale) + 0.001 \set delta random(-5000, 5000) + 0.326 BEGIN; + 0.603 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; + 0.454 SELECT abalance FROM pgbench_accounts WHERE aid = :aid; + 5.528 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; + 7.335 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; + 0.371 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); + 1.212 END; + + + + + 如果指定了多个脚本文件,则会分别为每个脚本文件报告平均值。 + + + + 注意,为逐语句延迟计算收集额外的计时信息会带来一定开销。这会拖慢平均执行速度,并降低计算出的 TPS。减速幅度在很大程度上取决于平台和硬件。比较启用和未启用延迟报告时的平均 TPS 值,是判断这一计时开销是否显著的好方法。 + + + + + + 良好实践 + + + 很容易用pgbench得出完全没有意义的数字。下面给出一些有助于获得有用结果的准则。 + + + + 首先,绝不要相信任何只运行了几秒钟的测试。使用选项让测试至少持续几分钟,以便平滑掉噪声。在某些情况下,可能需要数小时才能得到可复现的结果。一个好做法是把同一测试运行几次,看看结果是否可复现。 + + + + 对于默认的类 TPC-B 测试场景,初始化比例因子()应至少与计划测试的最大客户端数()一样大;否则,测到的主要将是更新争用。pgbench_branches表中只有行,而每个事务都要更新其中一行,因此超过时,必然会有大量事务阻塞等待其他事务。 + + + + 默认测试场景还会对表初始化后的时间长短非常敏感:表中死元组和无效空间的累积会改变结果。要理解这些结果,必须跟踪更新总数以及何时发生清理。如果启用了自动清理,它可能会给测得的性能带来不可预测的变化。 + + + + pgbench的一个局限是:在尝试测试大量客户端会话时,它自己也可能成为瓶颈。可以通过在与数据库服务器不同的机器上运行pgbench来缓解这一点,不过网络延迟必须足够低。甚至可以在多台客户端机器上同时运行多个pgbench实例,对同一台数据库服务器施压。 + + + + + 安全性 + + + 如果不受信任的用户能够访问尚未采用模式的安全使用方式的数据库,就不要在该数据库中运行pgbenchpgbench使用非限定名称,并且不会更改搜索路径。 + + +
+
diff --git a/zh/9.6/ref/pgtestfsync.sgml b/zh/9.6/ref/pgtestfsync.sgml new file mode 100644 index 00000000..420f0ceb --- /dev/null +++ b/zh/9.6/ref/pgtestfsync.sgml @@ -0,0 +1,108 @@ + + + + + pg_test_fsync + + + + pg_test_fsync + 1 + 应用程序 + + + + pg_test_fsync + PostgreSQL确定最快的wal_sync_method + + + + + pg_test_fsync + option + + + + + 描述 + + + pg_test_fsync旨在让你较为合理地了解,在你的 + 具体系统上哪一种最快,并且在识别出 + I/O 问题时提供诊断信息。不过,pg_test_fsync显示出 + 的差异未必会对实际数据库吞吐量产生显著影响,特别是因为许多数据库服务 + 器的速度瓶颈并不在其预写式日志上。 + pg_test_fsync会为每种 + wal_sync_method报告以微秒计的平均文件同步操作时 + 间,这也可用于为优化的取值提供参考。 + + + + + 选项 + + + pg_test_fsync 接受以下命令行选项: + + + + + + + + + 指定用于写入测试数据的文件名。该文件应位于 + pg_xlog目录所在或将要放置的同一文件系统中。 + (pg_xlog包含WAL文件。) + 默认值是当前目录中的pg_test_fsync.out。 + + + + + + + + + + 指定每项测试的秒数。每项测试用时越长,测试结果就越精确,但运行完 + 成所需的时间也越长。默认值是 5 秒,这使程序能够在不到 2 分钟内完 + 成。 + + + + + + + + + + 打印pg_test_fsync版本并退出。 + + + + + + + + + + 显示有关pg_test_fsync命令行参数的帮助信息并退出。 + + + + + + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/pgtesttiming.sgml b/zh/9.6/ref/pgtesttiming.sgml new file mode 100644 index 00000000..bc3e1e1f --- /dev/null +++ b/zh/9.6/ref/pgtesttiming.sgml @@ -0,0 +1,264 @@ + + + + + pg_test_timing + + + + pg_test_timing + 1 + 应用程序 + + + + pg_test_timing + 测量计时开销 + + + + + pg_test_timing + option + + + + + 描述 + + + pg_test_timing是一个用于测量系统计时开销的工具,并确认 + 系统时间不会倒退。收集计时数据较慢的系统,可能会给出不够精确的 + EXPLAIN ANALYZE结果。 + + + + + 选项 + + + pg_test_timing 接受以下命令行选项: + + + + + + + + + 指定测试持续时间,以秒为单位。持续时间越长,精度会略有提高,也更可能发现 + 系统时钟倒退的问题。默认测试持续时间为 3 秒。 + + + + + + + + + + 打印pg_test_timing版本并退出。 + + + + + + + + + + 显示有关pg_test_timing命令行参数的帮助信息并退出。 + + + + + + + + + + + 用法 + + + 结果解读 + + + 良好的结果会显示,大多数(>90%)单次计时调用耗时不到 1 微秒。每轮循环的平均开销还会更低,在 100 纳秒以下。下面这个示例来自使用 TSC 时钟源的 Intel i7-860 系统,表现非常出色: + + +Testing timing overhead for 3 seconds. +Per loop time including overhead: 35.96 nsec +Histogram of timing durations: +< usec % of total count + 1 96.40465 80435604 + 2 3.59518 2999652 + 4 0.00015 126 + 8 0.00002 13 + 16 0.00000 2 + + + + + 注意,每轮循环时间与直方图使用的单位不同。循环的解析度可以达到几纳秒(nsec), + 而单次计时调用的解析度只能达到 1 微秒(usec)。 + + + + + 测量执行器计时开销 + + + 当查询执行器使用EXPLAIN ANALYZE运行语句时,除了显示汇总信 + 息之外,还会对各个操作分别计时。可以使用psql程序统计 + 行数,以检查你的系统上的这类开销: + + +CREATE TABLE t AS SELECT * FROM generate_series(1,100000); +\timing +SELECT COUNT(*) FROM t; +EXPLAIN ANALYZE SELECT COUNT(*) FROM t; + + + + + 在所测的 i7-860 系统上,普通计数查询耗时 9.8 ms,而 + EXPLAIN ANALYZE版本耗时 16.6 ms,两者都处理了略多于 + 100,000 行。这 6.8 ms 的差异意味着每行的计时开销为 68 ns,大约是 + pg_test_timing 估计值的两倍。即便是这样相对较小的开销,也会让完整计时的计数语 + 句耗时增加近 70%。对于更复杂的查询,计时开销带来的问题就不会这么明显。 + + + + + + 更改时钟源 + + 在一些较新的 Linux 系统上,可以随时更改用于收集计时数据的时钟源。第二个示例在上述表现很快的同一系统上,展示了切换到较慢的 acpi_pm 时钟源后可能出现的减速: + + +# cat /sys/devices/system/clocksource/clocksource0/available_clocksource +tsc hpet acpi_pm +# echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource +# pg_test_timing +Per loop time including overhead: 722.92 nsec +Histogram of timing durations: +< usec % of total count + 1 27.84870 1155682 + 2 72.05956 2990371 + 4 0.07810 3241 + 8 0.01357 563 + 16 0.00007 3 + + + + + 在这种配置下,上面的示例EXPLAIN ANALYZE需要 115.9 ms。这 + 意味着计时开销达到 1061 nsec,同样只是本工具直接测得数值的一个小倍数。如此之大的 + 计时开销说明,实际查询本身只占已统计时间的一小部分,其余大多耗费在开销上。在这 + 种配置下,任何包含大量计时操作的EXPLAIN ANALYZE总计值,都会 + 因计时开销而被显著抬高。 + + + + FreeBSD 也允许动态更改时钟源,并会记录启动期间所选计时器的信息: + + +# dmesg | grep "Timecounter" +Timecounter "ACPI-fast" frequency 3579545 Hz quality 900 +Timecounter "i8254" frequency 1193182 Hz quality 0 +Timecounters tick every 10.000 msec +Timecounter "TSC" frequency 2531787134 Hz quality 800 +# sysctl kern.timecounter.hardware=TSC +kern.timecounter.hardware: ACPI-fast -> TSC + + + + + 其他系统可能只允许在启动时设置时钟源。在较旧的 Linux 系统上,“clock”内核设置是进行此类更改的唯一方式。甚至在某些较新的系统上,能看到的时钟源选项也只有“jiffies”。Jiffies 是较旧的 Linux 软件时钟实现;如果底层计时硬件足够快,它也能提供良好的解析度,如下例所示: + + +$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource +jiffies +$ dmesg | grep time.c +time.c: Using 3.579545 MHz WALL PM GTOD PIT/TSC timer. +time.c: Detected 2400.153 MHz processor. +$ pg_test_timing +Testing timing overhead for 3 seconds. +Per timing duration including loop overhead: 97.75 ns +Histogram of timing durations: +< usec % of total count + 1 90.23734 27694571 + 2 9.75277 2993204 + 4 0.00981 3010 + 8 0.00007 22 + 16 0.00000 1 + 32 0.00000 1 + + + + + + 时钟硬件和计时准确性 + + + 在计算机上收集精确的计时信息,通常依赖精度各异的硬件时钟。对于某些硬件,操作系 + 统几乎可以将系统时钟时间直接传递给程序。系统时钟也可能来自一种只提供计时中断、 + 按已知时间间隔周期性地产生滴答信号的芯片。无论哪种情况,操作系统内核都会提供一 + 个屏蔽这些细节的时钟源。不过,这种时钟源的精度以及返回结果的速度,都取决于底层 + 硬件。 + + + + 不准确的计时可能导致系统不稳定。对时钟源的任何更改都应十分谨慎地测试。操作系统 + 的默认设置有时会偏向可靠性,而不是追求最佳精度。如果你使用的是虚拟机,还应了解 + 与其兼容的推荐时钟源。虚拟硬件在模拟计时器时会面临额外困难,厂商通常还会针对不 + 同操作系统给出特定的设置建议。 + + + + 时间戳计数器(TSC)时钟源是当前一代 CPU 上可用的最精确时钟源。当操作系统支持它 + 且 TSC 时钟本身可靠时,它是跟踪系统时间的首选方式。TSC 也可能因多种原因无法提 + 供精确的计时源,从而变得不可靠。较旧的系统中,TSC 时钟可能会随 CPU 温度变化, + 因此不适合用于计时。在某些较旧的多核 CPU 上使用 TSC,还可能导致不同核心报告的 + 时间彼此不一致。这会引起时间倒退,而本程序正是用来检查这类问题的。即便在最新的 + 系统上,若节能配置过于激进,也可能无法提供精确的 TSC 计时。 + + + + 较新的操作系统可能会检测已知的 TSC 问题,并在发现时切换到更慢但更稳定的时钟 + 源。如果你的系统支持 TSC 计时却没有默认使用它,那么禁用它很可能是有充分理由的。 + 某些操作系统也可能无法正确检测所有潜在问题,或者即使已知 TSC 不准确,仍允许继 + 续使用它。 + + + + 如果系统上有高精度事件计时器(HPET),并且 TSC 不准确,那么 HPET 就是系统更倾 + 向使用的计时器。计时器芯片本身可编程,最高可提供 100 纳秒的解析度,但在你的系 + 统时钟中未必能看到这么高的准确性。 + + + + 高级配置与电源接口(ACPI)提供了一种电源管理(PM)计时器,Linux 将其称为 + acpi_pm。由 acpi_pm 派生的时钟在最佳情况下可提供 300 纳秒的解析度。 + + + + 较旧 PC 硬件使用的计时器包括 8254 可编程区间计时器(PIT)、实时时钟(RTC)、高 + 级可编程中断控制器(APIC)计时器以及 Cyclone 计时器。这些计时器的目标解析度是 + 毫秒级。 + + + + + + 参见 + + + + + + diff --git a/zh/9.6/ref/pgupgrade.sgml b/zh/9.6/ref/pgupgrade.sgml new file mode 100644 index 00000000..27b022f9 --- /dev/null +++ b/zh/9.6/ref/pgupgrade.sgml @@ -0,0 +1,576 @@ + + + + + pg_upgrade + + + + pg_upgrade + 1 + 应用程序 + + + + pg_upgrade + 升级一个PostgreSQL服务器实例 + + + + + pg_upgrade + + oldbindir + + newbindir + + olddatadir + + newdatadir + option + + + + + 描述 + + + pg_upgrade(以前称为 pg_migrator)允许将存储在 + PostgreSQL 数据文件中的数据升级到更新的 + PostgreSQL主版本,而无需执行主版本升级通常所需的数据转储/重新装载, + 例如从 8.4.7 升级到 PostgreSQL 的当前主版本。次版本升级不需要使用它, + 例如从 9.0.1 升级到 9.0.4。 + + + + PostgreSQL 主版本会定期增加新特性,这些特性常常会改变系统表的布局, + 但内部数据存储格式很少改变。pg_upgrade利用这一点, + 通过创建新的系统表并直接重用旧的用户数据文件来快速完成升级。如果未来某个 + 主版本改变了数据存储格式,致使旧数据格式无法读取,那么 + pg_upgrade就不能用于这类升级。(社区会尽量避免这种情况。) + + + + pg_upgrade会尽最大努力确保新旧集簇在二进制上兼容, + 例如会检查兼容的编译时设置,包括 32/64 位二进制程序。任何外部模块也同样必须 + 二进制兼容,这一点很重要,但 pg_upgrade 无法检查。 + + + + pg_upgrade 支持从 8.4.X 及更高版本升级到当前 + PostgreSQL主版本,包括快照版和 alpha 版。 + + + + + 选项 + + + pg_upgrade接受下列命令行参数: + + + bindir + bindir + 旧 PostgreSQL 可执行文件目录;环境变量 PGBINOLD + + + + bindir + bindir + 新 PostgreSQL 可执行文件目录;环境变量 PGBINNEW + + + + + + 仅检查集簇,不更改任何数据 + + + + datadir + datadir + 旧集簇数据目录;环境变量 PGDATAOLD + + + + datadir + datadir + 新集簇数据目录;环境变量 PGDATANEW + + + + + + 要使用的并发进程或线程数 + + + + + + + 使用硬链接而不是把文件复制到新集簇 + + + + options + options + 将直接传递给旧 postgres 命令的选项;多次指定会追加 + + + + options + options + 将直接传递给新 postgres 命令的选项;多次指定会追加 + + + + port + port + 旧集簇端口号;环境变量 PGPORTOLD + + + + port + port + 新集簇端口号;环境变量 PGPORTNEW + + + + + + 即使成功完成后也保留 SQL 和日志文件 + + + + + username + username + 集簇安装用户名称;环境变量 PGUSER + + + + + + 启用详细的内部日志记录 + + + + + + 显示版本信息,然后退出 + + + + + + 显示帮助,然后退出 + + + + + + + + + 使用 + + 使用pg_upgrade执行升级的步骤如下: + + + + 移动旧集簇(可选) + + + 如果你使用的是带版本号的安装目录,例如 + /opt/PostgreSQL/9.1,则不必移动旧集簇。 + 图形化安装程序都使用带版本号的安装目录。 + + + + 如果你的安装目录不是带版本号的,例如 /usr/local/pgsql, + 就必须移动当前的 PostgreSQL 安装目录,以免它干扰新的 + PostgreSQL 安装。当前的 + PostgreSQL 服务器关闭后,就可以安全地重命名 + PostgreSQL 安装目录。假设旧目录是 /usr/local/pgsql, + 你可以这样做: + + +mv /usr/local/pgsql /usr/local/pgsql.old + + 以上命令会重命名该目录。 + + + + + 对于源码安装,编译新版本 + + + 使用与旧集簇兼容的 configure 标志编译新的 PostgreSQL 源码。 + pg_upgrade 会在开始升级前检查 pg_controldata, + 以确保所有设置都兼容。 + + + + + 安装新的 PostgreSQL 二进制文件 + + + 安装新服务器的二进制文件和支持文件。默认安装中包含 + pg_upgrade。 + + + + 对于源码安装,如果你希望把新服务器安装到自定义位置,可以使用 + prefix 变量: + + +make prefix=/usr/local/pgsql.new install + + + + + 初始化新的 PostgreSQL 集簇 + + + 使用 initdb 初始化新集簇。这里同样要使用与旧集簇匹配的兼容 + initdb 标志。许多预构建的安装程序会自动执行该步骤。 + 无需启动新集簇。 + + + + + 安装扩展共享对象文件 + + + 许多来自 contrib 或其他来源的扩展和自定义模块都会使用 + 共享对象文件(或 DLL),例如 pgcrypto.so。如果旧集簇使用了这些模块, + 则必须在新集簇中安装与新服务器二进制相匹配的共享对象文件,这通常通过操作系统命令完成。 + 不要装载模式定义,例如 CREATE EXTENSION pgcrypto, + 因为这些会从旧集簇复制过来。如果有可用的扩展更新, + pg_upgrade 会报告这一点,并创建一个稍后可运行的脚本来更新它们。 + + + + + 复制自定义全文检索文件 + + + 将所有自定义全文检索文件(词典、同义词、词库、停用词)从旧集簇复制到新集簇。 + + + + + 调整认证 + + + pg_upgrade 会多次连接旧服务器和新服务器,因此你可能希望将认证设置为 + peer(在 pg_hba.conf 中),或者使用 + ~/.pgpass 文件(见 )。 + + + + + 停止两个服务器 + + 确保两个数据库服务器都已停止。例如,在 Unix 上使用: +pg_ctl -D /opt/PostgreSQL/8.4 stop +pg_ctl -D /opt/PostgreSQL/9.0 stop +或者在 Windows 上使用正确的服务名: +NET STOP postgresql-8.4 +NET STOP postgresql-9.0 + + + + 流复制和日志传送备库可以继续运行,直到后面的步骤。 + + + + 为备库升级做准备 + + + 如果你要使用节概述的方法升级备库, + 请通过对旧主库集簇和备库集簇运行 pg_controldata 来确认旧备库已追上主库。 + 确认所有集簇中的 Latest checkpoint location 值一致。(如果旧备库在旧主库之前关闭,或者旧备库仍在运行,这些值就会不匹配。)另外, + 请确保 wal_level 没有被设置为 minimal, + 这一点可在新主库集簇的 postgresql.conf 文件中检查。 + + + + + 运行 <application>pg_upgrade</application> + + + 始终运行新服务器的 pg_upgrade 二进制,而不是旧服务器的。 + pg_upgrade 需要指定新旧集簇的数据目录和可执行文件 + (bin)目录。你还可以指定用户和端口值,以及是否希望链接数据而不是复制(默认)。 + + + + 如果使用链接模式,升级将快得多(无需复制文件)且占用更少磁盘空间,但一旦在升级后启动 + 新集簇,就无法再访问旧集簇。链接模式还要求新旧集簇的数据目录位于同一文件系统中。 + (表空间和 pg_xlog 可以位于不同文件系统中。)完整选项列表请参阅pg_upgrade --help + + 选项允许使用多个 CPU 核心复制/链接文件,并行转储和重新装载数据库模式;一个不错的起始值是 CPU 核心数与表空间数量中的较大值。对于运行在多处理器机器上的多数据库服务器,此选项可以显著减少升级时间。 + + Windows 用户必须先登录管理员帐号,然后以 postgres 用户身份启动 shell,并设置正确的路径: +RUNAS /USER:postgres "CMD.EXE" +SET PATH=%PATH%;C:\Program Files\PostgreSQL\9.0\bin; +然后运行 pg_upgrade,并用引号括起目录,例如: +pg_upgrade.exe + --old-datadir "C:/Program Files/PostgreSQL/8.4/data" + --new-datadir "C:/Program Files/PostgreSQL/9.0/data" + --old-bindir "C:/Program Files/PostgreSQL/8.4/bin" + --new-bindir "C:/Program Files/PostgreSQL/9.0/bin" +启动后,pg_upgrade会验证两个集簇是否兼容,然后执行升级。你可以使用 pg_upgrade --check 仅执行检查,即使旧服务器仍在运行。pg_upgrade --check还会概述升级后需要手工进行的调整。如果打算使用链接模式,应将 一起使用,以启用链接模式的专用检查。pg_upgrade需要对当前目录具有写权限。 + + + 显然,在升级期间不应有人访问这些集簇。pg_upgrade + 默认会在 50432 端口上运行服务器,以避免意外的客户端连接。升级时可以让两个集簇使用相同 + 的端口号,因为新旧集簇不会同时运行。不过,在检查仍在运行的旧服务器时,新旧端口号必须不同。 + + + + 如果在恢复数据库模式时发生错误,pg_upgrade 将退出,你必须按照下文 + 所述回退到旧集簇。若要再次尝试 + pg_upgrade,你需要修改旧集簇,使 pg_upgrade 的模式恢复步骤能够成功完成。 + 如果问题出在某个 contrib 模块,而该模块并未用于存储用户数据, + 则你可能需要先从旧集簇卸载这个 contrib 模块,并在升级后将其安装到新集簇。 + + + + + 升级流复制和日志传送备库 + + + 如果你使用了链接模式,并且有流复制(见 ) + 或日志传送(见 )备库,可以按照下列步骤快速升级它们。 + 你无需在备库上运行 pg_upgrade,而是要在主库上运行 + rsync。现在先不要启动任何服务器。 + + + + 如果你没有使用链接模式、没有或不想使用 rsync, + 或者想要更简单的方案,请跳过本节说明;待 pg_upgrade 完成且 + 新主库启动后,直接重建备库即可。 + + + + + + 在备库上安装新的 PostgreSQL 二进制文件 + + 确保所有备库都已安装新的二进制文件和支持文件。 + + + + 确保新的备库数据目录<emphasis>不</emphasis>存在 + + + 确保新的备库数据目录存在,或者为空。如果运行过 + initdb,请删除备库的新数据目录。 + + + + + 安装扩展共享对象文件 + + 在新备库上安装与新主库集簇中相同的扩展共享对象文件。 + + + + 停止备库 + + 如果备库仍在运行,请按上述说明立即停止它们。 + + + + 保存配置文件 + + + 保存旧备库配置目录中需要保留的配置文件,例如 postgresql.conf + (以及其包含的所有文件)、postgresql.auto.conf、 + recovery.confpg_hba.conf,因为在下一步中它们会被覆盖或删除。 + + + + + 运行 <application>rsync</application> + + 在使用链接模式时,可以通过 rsync 快速升级备库。为此,在主库上选择一个位于新旧数据库集簇目录之上的目录,并在主库上针对每个备库运行: +rsync --archive --delete --hard-links --size-only --no-inc-recursive old_cluster new_cluster remote_dir +其中, 是相对于主库当前目录的路径,则是备库上位于新旧集簇目录上层的目录。主库和备库在指定目录下的目录结构必须一致。请参阅 rsync 手册页,了解如何指定远程目录,例如: +rsync --archive --delete --hard-links --size-only --no-inc-recursive /opt/PostgreSQL/9.5 \ + /opt/PostgreSQL/9.6 standby.example.com:/opt/PostgreSQL +你可以使用 rsync 的 选项来验证命令将执行什么操作。尽管至少要在主库上为一个备库运行 rsync,但也可以运行 rsync 来升级其他备库,运行位置是某个已升级且尚未启动的备库。 + + + 其原理是记录主库上新旧集簇文件之间由 pg_upgrade + 链接模式创建的硬链接,然后在备库的旧集簇中找到匹配文件,并在其新集簇中为这些文件 + 创建链接。主库上未被链接的文件会从主库复制到备库(通常都很小)。这使得 + 备库能够快速升级。不幸的是,rsync 会无谓地复制与 + 临时表和不记录 WAL 的表相关的文件,因为这些文件通常不存在于备库上。 + + + 如果你有表空间,则需要对每个表空间目录运行类似的 rsync 命令,例如: +rsync --archive --delete --hard-links --size-only --no-inc-recursive /vol1/pg_tblsp/PG_9.5_201510051 \ + /vol1/pg_tblsp/PG_9.6_201608131 standby.example.com:/vol1/pg_tblsp +如果你把 pg_xlog 迁移到了数据目录之外,也必须对这些目录运行 rsync。 + + + + 配置流复制和日志传送备库 + + 为服务器配置日志传送。(由于备库仍与主库保持同步,因此无需运行pg_start_backup()pg_stop_backup(),也无需进行文件系统备份。) + + + + + + + + 恢复 <filename>pg_hba.conf</filename> + + + 如果你修改了 pg_hba.conf,请恢复其原始设置。 + 也可能需要调整新集簇中的其他配置文件以匹配旧集簇,例如 + postgresql.conf(以及其包含的所有文件)和 + postgresql.auto.conf。 + + + + + 启动新服务器 + + + 现在可以安全地启动新服务器,然后启动所有经 rsync 同步的备库。 + + + + + 升级后处理 + + 如果需要任何升级后处理,pg_upgrade 在完成时会发出警告。它还会生成必须由管理员运行的脚本文件。这些脚本会连接到每个需要升级后处理的数据库。每个脚本应通过以下方式运行: +psql --username postgres --file script.sql postgres +这些脚本可以按任意顺序运行,运行完毕后即可删除。 + + + + 一般来说,在重建脚本运行完成之前访问其中引用的表是不安全的;这样做可能产生错误结果 + 或较差的性能。未在重建脚本中引用的表则可以立即访问。 + + + + + + 统计信息 + + 由于优化器统计信息不会由pg_upgrade传输,升级结束时你会被要求运行命令来重新生成这些信息。你可能需要设置连接参数以匹配新集簇。 + + + + 删除旧集簇 + + + 一旦你确认升级结果令人满意,就可以运行 pg_upgrade 完成时提到的脚本, + 删除旧集簇的数据目录。(如果旧数据目录中包含用户定义的表空间,则无法自动删除。) + 你也可以删除旧的安装目录(例如 binshare)。 + + + + + 恢复到旧集簇 + + 如果在运行 pg_upgrade 之后,你想恢复到旧集簇,可以选择以下几种方法: + + + 如果使用了 选项,旧集簇不会被修改;可以重新启动。 + + + + + 如果选项没有被使用,旧集簇就未被修改,可以重新启动。 + + + + 如果使用了 选项,新旧集簇可能共享数据文件: + + + 如果 pg_upgrade 在开始建立链接之前就已中止, + 旧集簇不会被修改;可以重新启动。 + + + + + + 如果你没有启动新集簇,则旧集簇没有被修改,只是在开始建立链接时, + 会把一个 .old 后缀附加到 + $PGDATA/global/pg_control。要重新使用旧集簇,请移除 + .old 这个附加在 + $PGDATA/global/pg_control 上的后缀;随后即可重新启动旧集簇。 + + + + + + 如果你启动了新集簇,它就已经写入了共享文件,此时再使用旧集簇是不安全的。 + 在这种情况下,旧集簇必须从备份恢复。 + + + + + + + + + + + + + + + 注解 + + pg_upgrade不支持升级包含下列reg* OID 引用系统数据类型的数据库:regprocregprocedureregoperregoperatorregconfigregdictionary。(regtype可以升级。) + + + 凡是会影响你安装环境的失败、重建和重新索引情况,pg_upgrade + 都会报告;用于重建表和索引的升级后脚本也会自动生成。如果你试图自动化升级很多集簇, + 你会发现具有相同数据库模式的集簇在所有升级中需要相同的升级后步骤;这是因为升级后步骤 + 依据的是数据库模式,而不是用户数据。 + + + + 为了测试部署,请创建旧集簇的纯模式副本,插入虚拟数据,然后对其升级。 + + + 如果正在升级PostgreSQL 9.2 之前的集簇,而且它使用了只包含配置文件的目录,就必须把实际数据目录的位置传给pg_upgrade,并把配置目录的位置传给服务器,例如-d /real-data-directory -o '-D /configuration-directory' + + 如果旧服务器早于 9.1,而且使用非默认的 Unix 域套接字目录,或者其默认目录与新集簇的默认目录不同,请将PGHOST设置为旧服务器的套接字位置。(这与 Windows 无关。) + + + 如果你想使用链接模式,又不希望在启动新集簇时修改旧集簇,可以先复制旧集簇,再对该副本使用链接模式升级。要创建旧集簇的有效副本, + 可在服务器运行时使用 rsync 创建旧集簇的脏拷贝,然后关闭旧服务器, + 再运行 rsync --checksum,把所有变更同步到该副本,使其达到一致状态。 + (之所以需要 ,是因为 rsync + 对文件修改时间的粒度只有一秒。)你可能还想排除某些文件,例如 + postmaster.pid,如 所述。 + 如果你的文件系统支持文件系统快照或写时复制文件副本, + 也可以用这些方式备份旧集簇和表空间,不过快照和副本必须同时创建,或者在数据库服务器关闭时创建。 + + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/postgres-ref.sgml b/zh/9.6/ref/postgres-ref.sgml new file mode 100644 index 00000000..b5d44c78 --- /dev/null +++ b/zh/9.6/ref/postgres-ref.sgml @@ -0,0 +1,728 @@ + + + + + postgres + + + + postgres + 1 + 应用程序 + + + + postgres + PostgreSQL数据库服务器 + + + + + postgres + option + + + + + 描述 + + + postgres是 + PostgreSQL数据库服务器。客户端应用要访问数据库时,会(通过网络或本地)连接到一个正在运行的postgres实例。 + 该postgres实例随后会启动一个独立的服务器进程来处理该连接。 + + + + 一个postgres实例始终只管理一个数据库集簇的数据。数据库集簇是一组存储在同一文件系统位置(数据区域)中的数据库。只要使用不同的数据区域和不同的通信端口(见下文),一个系统上就可以同时运行多个 + postgres实例。当 + postgres启动时,它需要知道数据区域的位置。该位置必须通过 + 选项或PGDATA环境变量指定;没有默认值。通常,或 + PGDATA会直接指向由创建的数据区域目录。其他可能的文件布局见 + 。 + + + + 默认情况下,postgres在前台启动,并将日志消息打印到标准错误流。在实际应用中,postgres + 应作为后台进程启动,例如在系统启动时启动。 + + + + postgres命令也可以在单用户模式下调用。这种模式的主要用途是在执行引导期间使用。有时也会用它来调试或进行灾难恢复;但请注意,运行单用户服务器其实并不适合调试服务器,因为不会发生真实的进程间通信和锁定。 + 从 shell 以单用户模式调用时,用户可以输入查询,结果会打印到屏幕上,但其形式对开发者更有用,而非面向终端用户。在单用户模式下, + 会话用户会被设置为 ID 为 1 的用户,并向该用户隐式授予超级用户权限。 + 该用户实际上不必存在,因此单用户模式可用于手工恢复系统目录遭受某些意外损坏的情况。 + + + + + 选项 + + + postgres接受以下命令行参数。有关这些选项的详细讨论,请参阅。通过设置配置文件,你可以省去键入其中大多数选项的麻烦。某些(安全的)选项还可以由连接的客户端以应用相关的方式设置,并且仅对该会话生效。例如,如果设置了环境变量PGOPTIONS,那么基于libpq的客户端就会将该字符串传给服务器,服务器会将其解释为 + postgres命令行选项。 + + + + 通用选项 + + + + + + + 设置供服务器进程使用的共享缓冲区数量。该参数的默认值由initdb自动选择。 + 指定此选项等效于设置 + 配置参数。 + + + + + + + + + 设置一个命名的运行时参数。PostgreSQL支持的配置参数见 + 。大多数其他命令行选项实际上都是这种参数赋值的简写形式。可以出现多次 + 以设置多个参数。 + + + + + + + + + 打印指定运行时参数的值并退出。 + (详情见上面的选项。)这个选项可用于正在运行的服务器,它会返回 + postgresql.conf中的值,并应用本次调用中提供的任何参数修改。它不反映 + 集簇启动时提供的参数。 + + + + 该选项供其他与服务器实例交互的程序(例如)查询配置参数值之用。面向用户的应用则应使用pg_settings视图。 + + + + + + + + + 设置调试级别。该值设得越高,写入服务器日志的调试输出就越多。取值范围是 + 1 到 5。还可以针对某个特定会话传入-d + 0,从而阻止父postgres进程的服务器日志级别传播到该会话。 + + + + + + + + + 指定数据库配置文件在文件系统中的位置。详见 + 。 + + + + + + + + + 将默认日期风格设置为European,即输入日期字段采用 + DMY顺序。这也会在某些日期输出格式中使日期显示为日在月前。 + 更多信息见。 + + + + + + + + + 禁用fsync调用以提高性能,但在系统崩溃时会有数据损坏的风险。指定此选项等效于 + 禁用配置 + 参数。使用前请先阅读详细文档! + + + + + + + + + 指定postgres用来侦听来自客户端应用的 TCP/IP + 连接的 IP 主机名或地址。该值也可以是逗号分隔的地址列表,或者用*指定侦听所有可用接口。空值 + 表示不侦听任何 IP 地址,在这种情况下 + 只能使用 Unix 域套接字连接到服务器。默认只侦听 + localhost。 + 指定此选项等效于设置配置参数。 + + + + + + + + + 允许远程客户端通过 TCP/IP(Internet 域) + 连接。没有此选项时,只接受本地连接。 + 该选项等效于在 + postgresql.conf中,或通过将 + listen_addresses设置为*。 + + + 该选项已被弃用,因为它无法访问 + 的全部功能。 + 通常最好直接设置listen_addresses。 + + + + + + + + + 指定postgres用来侦听 + 客户端应用连接的 Unix 域套接字所在目录。该值也可以是逗号分隔的目录列表。空值 + 表示不侦听任何 Unix 域套接字,在这种情况下 + 只能使用 TCP/IP 套接字连接到服务器。 + 默认值通常是 + /tmp,但可以在编译时更改。 + 指定此选项等效于设置配置参数。 + + + + + + + + + 启用使用SSL的安全连接。 + 若要使用此选项,PostgreSQL必须在编译时启用 + SSL支持。关于使用SSL的更多信息, + 请参阅。 + + + + + + + + + 设置该服务器将接受的最大客户端连接数。该参数的默认值由initdb自动选择。 + 指定此选项等效于设置 + 配置参数。 + + + + + + + + extra-options中指定的命令行形式参数,会传给由这个postgres进程启动的所有服务器进程。 + + extra-options中的空格被视为参数分隔符,除非用反斜线(\)转义;写成\\可表示字面的反斜线。也可以多次使用来指定多个参数。 + + 此选项已过时;服务器进程的所有命令行选项都可以直接在postgres命令行上指定。 + + + + + + + + 指定postgres + 用来侦听客户端应用连接的 TCP/IP 端口或本地 Unix 域套接字文件扩展名。 + 默认值取自PGPORT环境 + 变量;如果PGPORT未设置,则 + 默认值就是在编译期间确定的值(通常 + 为 5432)。如果你指定了非默认端口, + 那么所有客户端应用都必须通过 + 命令行选项或PGPORT指定相同的端口。 + + + + + + + + + 在每条命令结束时打印时间信息和其他统计信息。 + 这对基准测试或者调整缓冲区数量很有用。 + + + + + + work-mem + + 指定内部排序和散列操作在转而使用临时磁盘文件之前可使用的内存量。有关work_mem配置参数的说明,请参阅 + + + + + + + + + 打印postgres的版本并退出。 + + + + + + + + + 设置一个命名的运行时参数;这是的较短形式。 + + + + + + + + + 该选项会以制表符分隔的COPY格式导出服务器内部配置变量、说明以及默认值。 + 它主要是为管理工具设计的。 + + + + + + + + + + 显示postgres命令行参数的帮助并退出。 + + + + + + + + 半内部选项 + + + 这里描述的选项主要用于调试,并且在某些情况下可协助恢复严重损坏的数据库。在生产数据库环境中没有理由使用它们。这里列出这些选项,仅供PostgreSQL + 系统开发者使用。此外,这些选项在未来版本中可能会更改或被移除,且不另行通知。 + + + + + { s | i | o | b | t | n | m | h } + + + 禁止使用某些扫描和连接方法: + si + 分别禁用顺序扫描和索引扫描, + obt + 分别禁用仅索引扫描、位图索引扫描和 TID 扫描, + 而nmh + 则分别禁用嵌套循环连接、归并连接和哈希连接。 + + + + 顺序扫描和嵌套循环连接都无法被完全禁用;-fs和 + -fn选项只是在优化器 + 有其他选择时,尽量不使用这些计划类型。 + + + + + + + + 此选项用于调试导致服务器进程异常终止的问题。在这种情况下,通常的策略是通知所有其他服务器进程必须终止,然后重新初始化共享内存和信号量。这是因为出错的服务器进程可能在终止前破坏了某些共享状态。此选项指定postgres不重新初始化共享数据结构。经验丰富的系统程序员随后可以使用调试器检查共享内存和信号量的状态。 + + + + + + + + 允许修改系统表的结构。该选项由 + initdb使用。 + + + + + + + + + 读取系统表时忽略系统索引,但在修改表时仍然更新 + 这些索引。这在从损坏的系统索引中恢复时很有用。 + + + + + + pa[rser] | pl[anner] | e[xecutor] + + + 打印与各个主要系统模块相关的每个查询的时间统计信息。该选项不能与选项一起 + 使用。 + + + + + + + + 此选项用于调试导致服务器进程异常终止的问题。在这种情况下,通常的策略是通知所有其他服务器进程必须终止,然后重新初始化共享内存和信号量。这是因为出错的服务器进程可能在终止前破坏了某些共享状态。此选项指定postgres通过发送SIGSTOP信号暂停所有其他服务器进程,但不使它们终止。这允许系统程序员手工收集所有服务器进程的核心转储。 + + + + + protocol + + + 指定特定会话要使用的前端/后端协议 + 版本号。该选项仅供 + 内部使用。 + + + + + + seconds + + + 新服务器进程在完成认证过程后,会延迟这么多秒。 + 这是为了给调试器附着到该服务器进程提供机会。 + + + + + + + + 用于单用户模式的选项 + + + 单用户模式 + + + 以下选项仅适用于单用户模式(参见)。 + + + + + + + 选择单用户模式。这必须是命令行上的第一个参数。 + + + + + + database + + + 指定要访问的数据库名称。这必须是 + 命令行上的最后一个参数。如果省略,则默认为用户名。 + + + + + + + + + 在执行前将所有命令回显到标准输出。 + + + + + + + + + 使用“分号后跟两个换行符”而不是单个换行符, + 作为命令输入终止符。 + + + + + + filename + + + 将所有服务器日志输出发送到filename。只有 + 将此选项作为命令行选项提供时,它才会生效。 + + + + + + + + + 环境 + + + + PGCLIENTENCODING + + + + 客户端使用的默认字符编码。(客户端可以单独覆盖它。) + 该值也可以在配置文件中设置。 + + + + + + PGDATA + + + + 默认数据目录位置 + + + + + + PGDATESTYLE + + + + 运行时 + 参数的默认值。(该环境变量的用法已被弃用。) + + + + + + PGPORT + + + + 默认端口号(最好在配置文件中设置) + + + + + + + + + 诊断 + + + 提到semget或 + shmget的失败消息,很可能表明你需要配置内核,以提供足够的共享内存和信号量。更多讨论见。你也许可以通过降低来减少PostgreSQL的共享内存 + 消耗,以及/或通过降低 + 来减少信号量 + 消耗,从而推迟重新配置内核。 + + + + 表明另一个服务器已经在运行的失败消息 + 应仔细检查,例如可根据你的系统使用下面的命令: + +$ ps ax | grep postgres + + 或 + +$ ps -ef | grep postgres + + 如果你确信没有冲突的 + 服务器在运行,可以删除消息中提到的锁文件,然后重试。 + + + + 表明无法绑定端口的失败消息,可能 + 意味着该端口已经被某个非PostgreSQL进程占用。你在终止postgres + 后如果立即使用同一端口重新启动它,也可能会 + 得到这个错误;在这种情况下,你 + 只需等待几秒钟,直到操作系统关闭 + 该端口后再重试。最后,如果你 + 指定了一个操作系统认为 + 是保留的端口号,也可能会收到这个错误。例如,许多版本的 Unix + 认为低于 1024 的端口号是受信任的,并且只允许 + Unix 超级用户访问它们。 + + + + + + 注解 + + + 实用命令可用于 + 安全、便捷地启动和关闭postgres服务器。 + + + + 只要有可能,就不要使用 + SIGKILL杀死主 + postgres服务器。这样会阻止 + postgres在终止前释放其持有的系统 + 资源(例如共享内存和信号量)。这可能会导致 + 新的postgres实例在启动时出现问题。 + + + + 要正常终止postgres服务器,可以使用 + SIGTERMSIGINT或 + SIGQUIT信号。第一个会在退出前等待 + 所有客户端终止,第二个会 + 强制断开所有客户端,而第三个会在不进行正确关闭的情况下立即退出, + 从而在重启期间导致一次恢复运行。 + + + + SIGHUP信号会重新加载 + 服务器配置文件。也可以向单个服务器进程发送 + SIGHUP,但这通常没有意义。 + + + + 要取消一个正在运行的查询,可以向执行该命令的进程发送SIGINT信号。要干净地终止一个后端进程, + 可以向该进程发送SIGTERM。这两种操作在 SQL 中可调用的等效形式,见 + 中的pg_cancel_backendpg_terminate_backend。 + + + + postgres服务器使用SIGQUIT + 通知从属服务器进程在不进行正常 + 清理的情况下终止。 + 用户不应该使用这个信号。向某个服务器 + 进程发送SIGKILL也是不明智的 — 主postgres进程会 + 将其解释为一次崩溃,并作为其标准崩溃恢复过程的一部分 + 强制所有同级进程退出。 + + + + + 缺陷 + + 选项在FreeBSDOpenBSD上不起作用。 + 请改用。这是受影响操作系统中的一个缺陷;如果这个问题未被修复,未来版本的PostgreSQL + 将提供变通方案。 + + + + + 单用户模式 + + + 要启动单用户模式服务器,可以使用类似下面的命令: + +postgres --single -D /usr/local/pgsql/data other-options my_database + + 使用提供正确的数据库目录路径,或者 + 确保已经设置了环境变量PGDATA。 + 还要指定你想要操作的那个数据库的名称。 + + + + 通常,单用户模式服务器将换行视为命令 + 输入终止符;它不像psql那样会对分号进行智能处理。要让一个命令 + 跨越多行,必须在除最后一个换行之外的每个 + 换行前输入反斜线。反斜线和紧邻的换行都会 + 从输入命令中删除。注意,即使在字符串字面量或注释中 + 也会如此。 + + + + 但是,如果你使用命令行开关,单个换行 + 并不会终止命令输入;取而代之的是使用 + “分号-换行-换行”这一序列。也就是说,输入一个分号,后面立刻 + 跟着一个完全空白的行。在这种模式下,反斜线-换行不会 + 被特殊处理。同样,对于 + 这种序列出现在字符串字面量或注释中的情况,也不会有特殊处理。 + + + + 在任一种输入模式中,如果你输入的分号既不恰好位于 + 命令输入终止符之前,也不是命令输入终止符的一部分, + 那么它就会被视为命令分隔符。 + 当你输入命令输入终止符时,已输入的多条语句 + 会作为单个事务执行。 + + + + 要退出会话,输入EOF + (通常是ControlD)。 + 如果自上一个命令输入终止符以来你已经输入了任何文本, + 那么EOF会被视为命令输入终止符, + 你还需要再输入一次EOF才能退出。 + + + + 请注意,单用户模式服务器不提供复杂的 + 行编辑功能(例如没有命令历史)。 + 单用户模式也不会执行任何后台处理,例如 + 自动检查点或复制。 + + + + + 示例 + + + 要使用默认值在后台启动postgres, + 输入: + + +$ nohup postgres >logfile 2>&1 </dev/null & + + + + + 要用指定端口启动postgres, + 例如 1234: + +$ postgres -p 1234 + + 要使用psql连接到该服务器,请用选项指定这个端口: + +$ psql -p 1234 + + 或者设置环境变量PGPORT: + +$ export PGPORT=1234 +$ psql + + + + + 命名的运行时参数可以采用以下任一种形式设置: + +$ postgres -c work_mem=1234 +$ postgres --work-mem=1234 + + 两种形式都会覆盖 + postgresql.conf中可能存在的 + work_mem设置。请注意, + 参数名中的下划线在命令行中既可以写成下划线, + 也可以写成连字符。除了短期实验之外, + 与其依赖命令行开关来设置参数, + 通常更好的做法是直接编辑 + postgresql.conf中的设置。 + + + + + 另见 + + + , + + + + diff --git a/zh/9.6/ref/postmaster.sgml b/zh/9.6/ref/postmaster.sgml new file mode 100644 index 00000000..a888b877 --- /dev/null +++ b/zh/9.6/ref/postmaster.sgml @@ -0,0 +1,42 @@ + + + + + postmaster + + + + postmaster + 1 + 应用程序 + + + + postmaster + PostgreSQL数据库服务器 + + + + + postmaster + option + + + + + 描述 + + postmasterpostgres的一个已弃用别名。 + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/prepare.sgml b/zh/9.6/ref/prepare.sgml new file mode 100644 index 00000000..ce84452d --- /dev/null +++ b/zh/9.6/ref/prepare.sgml @@ -0,0 +1,174 @@ + + + + + PREPARE + + + + prepared statements + creating + + + + PREPARE + 7 + SQL - 语言语句 + + + + PREPARE + 为执行准备一个语句 + + + + +PREPARE name [ ( data_type [, ...] ) ] AS statement + + + + + 描述 + + + PREPARE 创建一个预备语句。预备语句是一种服务器端对象, + 可用于优化性能。执行 PREPARE 语句时,指定的语句会被解 + 析、分析并重写。随后发出 EXECUTE 命令时,该预备语句会 + 被规划并执行。这种分工避免了重复的解析分析工作,同时又允许执行计划依赖 + 于所提供的特定参数值。 + + + + 预备语句可以带参数,也就是在执行时会代入语句中的值。创建预备语句时,可 + 以按位置引用参数,例如 $1$2 等。 + 也可以选择指定相应的参数数据类型列表。如果某个参数的数据类型未指定,或 + 被声明为 unknown,则其类型会从该参数第一次被引用时所 + 在的上下文中推断出来(如果可能)。执行该语句时,需要在 + EXECUTE 语句中为这些参数提供实际值。有关详情,参见 + 。 + + + + 预备语句只在当前数据库会话期间存在。会话结束时,预备语句就会被遗忘,因 + 此再次使用前必须重新创建。这也意味着单个预备语句不能由多个并发的数据库 + 客户端共用;不过,每个客户端都可以创建自己要使用的预备语句。预备语句也 + 可以用 + 命令手工释放。 + + + + 当单个会话要执行大量相似语句时,预备语句往往能带来最大的性能优势。如果 + 语句在规划或重写时比较复杂,例如查询涉及许多表的连接,或者需要应用多条 + 规则,那么这种性能差异会特别明显。如果语句相对容易规划和重写,但执行本 + 身代价较高,则预备语句带来的性能优势就不那么明显。 + + + + + 参数 + + + + name + + + 为这个特定预备语句指定的任意名称。它在单个会话内必须唯一,随后用于执 + 行或释放先前已准备的语句。 + + + + + + data_type + + + 预备语句中某个参数的数据类型。如果某个参数的数据类型未指定,或被指定 + 为 unknown,则其类型会从该参数第一次被引用时所在 + 的上下文中推断出来。要在预备语句本身中引用参数,可使用 + $1$2 等。 + + + + + + statement + + + 任何SELECTINSERTUPDATE, + DELETE,或VALUES + 语句。 + + + + + + + + 注解 + + 预备语句可以使用通用计划,而不必为每组提供的EXECUTE值重新规划。对于没有参数的预备语句,会立即使用通用计划;否则,只有在执行五次或更多次后,所生成计划的平均估计代价(包括规划开销)高于通用计划的估计代价时,才会使用通用计划。一旦选定通用计划,在该预备语句的剩余生命周期内都会使用它。如果EXECUTE提供的值在含有大量重复值的列中很少见,生成的自定义计划可能比通用计划便宜得多,即使加上规划开销也是如此,以至于可能永远不会使用通用计划。 + + 通用计划假定提供给EXECUTE的每个值都是该列的不同值之一,并且列值均匀分布。例如,如果统计信息记录该列有三个不同值,通用计划就假定对该列进行相等比较会匹配所处理行的 33%。列统计信息还使通用计划能够准确计算唯一列的选择率。对分布不均匀的列进行比较以及指定不存在的值,会影响平均计划代价,进而影响是否以及何时选用通用计划。 + + 要检查PostgreSQL为预备语句使用的查询计划,请使用,例如EXPLAIN EXECUTE。如果使用的是通用计划,其中会包含参数符号$n,而自定义计划中会代入所提供的参数值。通用计划中的行数估计反映了为这些参数计算的选择率。 + + + 有关查询规划以及 PostgreSQL 为此收集的统计 + 信息的更多内容,请参见文档。 + + + 尽管预备语句的主要目的在于避免对语句重复进行解析分析和规划,但PostgreSQL会在使用前强制重新分析并重新规划该语句,只要其中使用的数据库对象自上次使用该预备语句以来发生了定义(DDL)变更。此外,如果 + 的值在两次使用之间发生变化,该语句也 + 会基于新的 search_path 重新解析。(后一种行为是从 + PostgreSQL 9.3 开始引入的。)这些规则使得 + 使用预备语句在语义上几乎等同于反复重新提交同一段查询文本,同时在对象定 + 义未改变时仍能获得性能收益,特别是在跨多次使用时最佳计划保持不变的情况 + 下。这种语义等价性并不完全成立。举例来说,如果语句以未限定名引用某个表,随 + 后又在 search_path 中更靠前的某个模式里创建了一个同 + 名新表,那么不会自动触发重新解析,因为语句所使用的对象本身并未改变。不 + 过,如果后来由于其他变更而触发了重新解析,后续使用中引用的就会是这个新 + 表。 + + + + 可以通过查询pg_prepared_statements + 系统视图,查看当前会话中所有可用的预备语句。 + + + + + 示例 + 为一个INSERT 语句创建预备语句,然后执行它: +PREPARE fooplan (int, text, bool, numeric) AS + INSERT INTO foo VALUES($1, $2, $3, $4); +EXECUTE fooplan(1, 'Hunter Valley', 't', 200.00); + + + + 为一个 SELECT 语句创建预备语句,然后执行它: +PREPARE usrrptplan (int) AS + SELECT * FROM users u, logs l WHERE u.usrid=$1 AND u.usrid=l.usrid + AND l.date = $2; +EXECUTE usrrptplan(1, current_date); +注意,第二个参数的数据类型未指定,因此会从 $2 所在的使用上下文中推断出来。 + + + 兼容性 + + + SQL 标准包含PREPARE语句,但它只用于嵌入式 SQL。这里的PREPARE语句在语法上也略有不同。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/prepare_transaction.sgml b/zh/9.6/ref/prepare_transaction.sgml new file mode 100644 index 00000000..afaa8173 --- /dev/null +++ b/zh/9.6/ref/prepare_transaction.sgml @@ -0,0 +1,122 @@ + + + + + PREPARE TRANSACTION + + + + PREPARE TRANSACTION + 7 + SQL - 语言语句 + + + + PREPARE TRANSACTION + 为两阶段提交准备当前事务 + + + + +PREPARE TRANSACTION transaction_id + + + + + 描述 + + + PREPARE TRANSACTION为两阶段提交准备当前事务。执行此命令后,该事务将不再与当前会话相关联;相反,它的状态会被完整地存储到磁盘上,因此即使在请求提交之前数据库发生崩溃,它也极有可能成功提交。 + + + 事务进入预备状态后,稍后可以分别使用来提交或回滚。发出这些命令的不必是执行原始事务的那个会话,任何会话都可以这样做。 + + + 从发出该命令的会话来看,PREPARE TRANSACTION颇似ROLLBACK:执行之后,将不再有活动的当前事务,并且该预备事务的效果也不再可见。(如果该事务随后被提交,这些效果会再次可见。) + + + + 如果PREPARE TRANSACTION因任何原因失败,它就等同于执行了一次ROLLBACK:当前事务会被取消。 + + + + + 参数 + + + + transaction_id + + + 一个任意标识符,后续可通过它在COMMIT PREPARED或ROLLBACK PREPARED中标识该事务。该标识符必须写成字符串字面值,长度必须小于 200 字节,并且不能与当前任何已处于预备状态的事务标识符相同。 + + + + + + + + 注解 + + + PREPARE TRANSACTION并非供应用程序或交互式会话使用。它的目的是让外部事务管理器能够跨多个数据库或其他事务性资源执行原子性的全局事务。除非是在编写事务管理器,否则通常不应使用PREPARE TRANSACTION。 + + + + 该命令必须在事务块内部使用。请用启动事务块。 + + + + 当前不允许对执行过以下任一操作的事务执行PREPARE:涉及临时表的操作、创建任何WITH HOLD游标,或执行过LISTENUNLISTENNOTIFY。这些特性与当前会话绑定得过于紧密,因此在要进入预备状态的事务中没有意义。 + + + + 如果该事务曾用SET修改过任何运行时参数(且未使用LOCAL选项),那么这些效果在执行PREPARE TRANSACTION之后仍会保留,并且不会受到后续任何COMMIT PREPAREDROLLBACK PREPARED的影响。因此,仅就这一点而言,PREPARE TRANSACTION的行为更像COMMIT而不是ROLLBACK。 + + + + 当前所有处于预备状态的事务都列在pg_prepared_xacts系统视图中。 + + + + + 让事务长时间停留在预备状态并不明智。这会妨碍VACUUM回收存储空间,在极端情况下甚至可能导致数据库为防止事务 ID 回卷而关闭(见)。还要记住,该事务会继续持有它原本持有的所有锁。此功能的预期用法是:一旦外部事务管理器确认其他数据库也已准备好提交,就尽快提交或回滚该预备事务。 + + + + 如果没有设置外部事务管理器来跟踪预备事务并确保它们能被及时结束,最好将设为零,从而禁用预备事务功能。这样可以防止意外创建预备事务,而这些事务随后可能被遗忘并最终引发问题。 + + + + + + 示例 + + 为两阶段提交准备当前事务,并使用foobar作为事务标识符: + + +PREPARE TRANSACTION 'foobar'; + + + + + 兼容性 + + + PREPARE TRANSACTIONPostgreSQL扩展。它供外部事务管理系统使用,其中有些系统受标准约束(例如 X/Open XA),但这些系统的 SQL 侧并未标准化。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/psql-ref.sgml b/zh/9.6/ref/psql-ref.sgml new file mode 100644 index 00000000..2427a328 --- /dev/null +++ b/zh/9.6/ref/psql-ref.sgml @@ -0,0 +1,2640 @@ + + + + + psql + + + + psql + 1 + 应用程序 + + + + psql + + PostgreSQL的交互式终端 + + + + + + psql + option + dbname + username + + + + + 描述 + + + psqlPostgreSQL的一个基于终端的前端。它使你能够交互式地输入查询,将其发送给PostgreSQL,并查看查询结果。也可以从文件或命令行参数提供输入。此外,psql还提供了若干元命令和多种类似 shell 的特性,以便于编写脚本和自动化执行各种任务。 + + + + + 选项 + + + + + + + + 在读入时将所有非空输入行打印到标准输出(不适用于交互式行读取)。这等效于把变量ECHO设置为 + all。 + + + + + + + + + + 切换到非对齐输出模式(默认输出模式是对齐的)。 + + + + + + + + + + 把失败的 SQL 命令打印到标准错误输出。这等效于把变量ECHO设置为errors。 + + + + + + + + + + 指定psql执行一个给定的命令字符串command。这个选项可以重复多次并且以任何顺序与选项组合在一起。当或者被指定时,psql不会从标准输入读取命令,而是在按顺序处理完所有选项后终止。 + + + command必须是一个服务器完全可解析的命令字符串(即不包含psql专有的特性)或者单个反斜线命令。因此不能在一个选项中混合SQLpsql元命令。要那样做,可以使用多个选项或者把字符串用管道输送到psql中,例如: + +psql -c '\x' -c 'SELECT * FROM foo;' + + 或者 + +echo '\x \\ SELECT * FROM foo;' | psql + + (\\是分隔符元命令)。 + + + 每个SQL命令字符串传递给都作为一个单独的查询发送到服务器。 + 因此,即使字符串包含多个SQL命令,服务器也会将其作为单个事务执行, + 除非字符串中包含显式的BEGIN/COMMIT命令将其分成多个事务。 + + 此外,psql只打印字符串中最后一条SQL命令的结果。这与从文件读取同一字符串或将其送入psql标准输入时的行为不同,因为在这些情况下,psql会分别发送每条SQL命令。 + + + 由于这种行为,在单个 字符串中放入多条 SQL 命令常常会产生意外结果。最好重复使用 命令,或将多条命令送入 psql 的标准输入,可以像上面所示使用 echo,也可以使用 shell 的 here-document,例如: + +psql <<EOF +\x +SELECT * FROM foo; +EOF + + + + + + + + + + + 指定要连接的数据库的名称。这等效于指定dbname为命令行上的第一个非选项参数。dbname 可以是 连接字符串。 如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 也把发送到服务器的所有 SQL 命令复制到标准输出。这等效于把变量ECHO设置为queries。 + + + + + + + + + + 回显\d以及其他反斜线命令生成的实际查询。可以用它来学习psql的内部操作。这等效于把变量ECHO_HIDDEN设置为on。 + + + + + + + + + + 从文件filename而不是标准输入中读取命令。这个选项可以重复指定,也可以按任意顺序与选项组合使用。当指定了时,psql不会从标准输入读取命令,而是在按顺序处理完所有选项后终止。除此之外,这个选项在很大程度上等价于元命令\i。 + + + + 如果filename-(连字符),则会读取标准输入,直到遇到 EOF 指示或\q元命令。这可用于将交互式输入与文件输入交错使用。不过请注意,这种情况下不会使用 Readline(很像指定了时的情况)。 + + + + 使用这个选项与写成psql < filename有细微差别。通常两种形式都会得到你期望的结果,但使用-f可以启用一些有用的特性,例如带行号的错误消息。使用这个选项也还有一点机会降低启动开销。另一方面,使用 shell 输入重定向的形式在理论上能保证得到与你手工逐行输入时完全相同的输出。 + + + + + + + + + + 使用separator作为非对齐输出的字段分隔符。这等效于\pset fieldsep或者\f。 + + + + + + + + + + 指定运行服务器的机器的主机名。如果该值以斜线开头,则它会被用作 Unix 域套接字所在的目录。 + + + + + + + + + + 切换到HTML表格输出模式。这等效于\pset format html或者\H命令。 + + + + + + + + + + 列出所有可用的数据库,然后退出。其他非连接选项会被忽略。这与元命令\list类似。 + + + + + + + + + + + 除了把所有查询输出写到普通输出目标之外,还写到文件filename中。 + + + + + + + + + + 不要使用Readline进行行编辑,也不要使用命令历史记录。这有助于在剪切和粘贴时关闭TAB 补全。 + + + + + + + + + + 把所有查询输出放到文件filename中。这等效于命令\o。 + + + + + + + + + + 指定服务器用于监听连接的 TCP 端口或者本地 Unix 域套接字文件扩展名。默认是PGPORT环境变量的值,如果没有设置,则默认为编译时指定的端口号(通常是5432)。 + + + + + + + + + + 以\pset的形式指定打印选项。注意,这里你必须用一个等号而不是空格来分隔名称和值。例如,要设置输出格式为LaTeX,应该写成-P format=latex。 + + + + + + + + + + 指定psql应该安静地工作。默认情况下,它会打印出欢迎消息和各种提示信息。如果使用了这个选项,以上那些就都不会输出。在使用选项时,配合这个选项很有用。这等效于设置变量QUIETon。 + + + + + + + + + + 把separator用作非对齐输出的记录分隔符。这等效于\pset recordsep命令。 + + + + + + + + + + 运行在单步模式中。这意味着在每个命令被发送给服务器之前都会提示用户,并允许取消执行。使用这个选项可以调试脚本。 + + + + + + + + + + 运行在单行模式中,其中换行符会终止一个 SQL 命令,就像分号的作用一样。 + + + + + 这种模式是为坚持使用它的用户提供的,但并不一定值得推荐。特别是,如果在一行中混合了SQL和元命令,对于没有经验的用户来说,它们的执行顺序未必总是清楚的。 + + + + + + + + + + + 关闭打印列名和结果行计数页脚等。这等效于\t命令。 + + + + + + + + + + 指定要放在HTML table标签内的选项。详见\pset。 + + + + + + + + + + 作为用户username而不是默认用户连接到数据库(当然,你必须具有这样做的权限)。 + + + + + + + + + + + 执行一次变量赋值,和\set元命令相似。注意你必须在命令行上用等号分隔名字和值(如果有)。要取消变量的设置,去掉等号就行。要把一个变量设为空字符串,使用等号但是去掉值。这些赋值在启动的非常早期阶段完成,因此为内部目的保留的变量可能会在稍后被覆盖。 + + + + + + + + + + 打印psql版本并且退出。 + + + + + + + + + + 永不发出密码提示。如果服务器要求密码认证,而密码又无法从其他来源获得,例如.pgpass文件,则连接尝试将失败。这个选项对于批处理任务和脚本很有用,因为那时通常没有用户在场输入密码。 + + + + 请注意,这个选项在整个会话期间都会保持生效,因此它不仅影响初始连接尝试,也会影响元命令\connect。 + + + + + + + + + + 强制psql在连接数据库之前提示输入密码,即使该密码实际上不会被使用。 + + + + 如果服务器要求密码认证,而密码又无法从其他来源获得,例如.pgpass文件,则psql无论如何都会提示输入密码。不过,psql需要先浪费一次连接尝试,才能知道服务器要求密码。在某些情况下,显式指定值得用来避免这次额外的连接尝试。 + + + + 请注意,这个选项在整个会话期间都会保持生效,因此它不仅影响初始连接尝试,也会影响元命令\connect。 + + + + + + + + + + 打开扩展表格式模式。这等效于\x命令。 + + + + + + + + + + 不读取启动文件(既不读取系统范围的psqlrc文件,也不读取用户的~/.psqlrc文件)。 + + + + + + + + + + 设置非对齐输出的字段分隔符为零字节。 + + + + + + + + + + 设置非对齐输出的记录分隔符为零字节。例如,这有助于与xargs -0配合使用。 + + + + + + + + + + 这个选项只能与一个或多个和/或选项结合使用。 + 它会导致psql在第一个这样的选项之前发出一个BEGIN命令, + 并在最后一个选项之后发出一个COMMIT命令,从而将所有命令包装成一个单独的事务。 + 这确保要么所有命令都成功完成,要么不应用任何更改。 + + + + 如果命令本身包含BEGINCOMMITROLLBACK,这个选项就不会产生期望的效果。另外,如果某个单独命令不能在事务块中执行,指定这个选项将导致整个事务失败。 + + + + + + + + + + 显示有关psql的帮助并且退出。可选的topic参数(默认为options)选择要解释哪一部分的psqlcommands描述psql的反斜线命令;options描述可以被传递给psql的命令行选项;而variables则显示有关psql配置变量的帮助。 + + + + + + + + + + 退出状态 + + + 如果psql正常结束,它会向 shell 返回 0;如果它自身发生致命错误(例如内存耗尽、找不到文件),则返回 1;如果到服务器的连接发生故障且该会话不是交互式的,则返回 2;如果脚本中发生错误且变量ON_ERROR_STOP已设置,则返回 3。 + + + + + + 用法 + + + 连接到数据库 + + + psql 是常规的 + PostgreSQL 客户端应用程序。要连接到数据库, + 你需要知道目标数据库名称、服务器的主机名和端口号,以及要以哪个数据库用户名连接。 + psql 可以通过命令行选项 + 和 + 分别指定这些参数。如果遇到一个不属于任何选项的参数, + 它将被解释为数据库名(如果数据库名已经给出,则解释为数据库用户名)。 + 并非所有这些选项都是必需的;它们都有有用的默认值。如果省略主机名, + psql 将通过 Unix 域套接字连接到本地主机上的服务器,而在没有 Unix 域套接字的机器上则通过 TCP/IP 连接到 localhost。默认端口号在编译时确定。 + 由于数据库服务器使用相同的默认值,因此在大多数情况下不必指定端口。 + 默认用户名是你的操作系统用户名,默认数据库名也是如此。 + 请注意,你不能随意以任意数据库用户名连接到任意数据库。数据库管理员应当已经告知你拥有的访问权限。 + + + + 当默认值不完全合适时,可以通过把环境变量PGDATABASEPGHOSTPGPORTPGUSER设置为适当的值来少敲一些键盘(额外的环境变量见)。另外,准备一个~/.pgpass文件也很方便,这样就不必经常手工输入密码。详见。 + + + + 指定连接参数的另一种方法是使用一个conninfo字符串或一个URI,它可用来替代数据库名。这种机制可以让你对连接进行非常细致的控制。例如: + +$ psql "service=myservice sslmode=require" +$ psql postgresql://dbmaster:5433/mydb?sslmode=require + + 用这种方式,你也可以把LDAP用于中描述的连接参数查找。可用连接选项的更多信息请见。 + + + + 如果由于任何原因(例如权限不足、服务器没有在目标主机上运行等)导致连接无法建立,psql将返回一个错误并且终止。 + + + + 如果标准输入和标准输出都是终端,那么psql会把客户端编码设置为auto,从区域设置中检测合适的客户端编码(在 Unix 系统上是LC_CTYPE环境变量)。如果结果不符合预期,可以使用环境变量PGCLIENTENCODING覆盖客户端编码。 + + + + + 输入 SQL 命令 + + + 在正常操作时,psql会提供一个提示符,该提示符是psql当前连接到的数据库名称后面跟上字符串=>。例如: + +$ psql testdb +psql (&version;) +Type "help" for help. + +testdb=> + + + + + 在提示符下,用户可以输入SQL命令。通常,当遇到表示命令结束的分号时,输入的内容就会被发送给服务器。行结束并不会终止一条命令,因此为了提高清晰度,命令可以分布在多行上。如果命令被发送并成功执行,其结果就会显示在屏幕上。 + + + + 如果不受信任的用户能够访问尚未采用模式的安全使用方式的数据库,请在会话开始时从search_path中移除所有公众可写的模式。可以在连接字符串中加入options=-csearch_path=,或者在执行其他 SQL 命令之前发出SELECT pg_catalog.set_config('search_path', '', false)。这种考虑并非psql特有;它适用于任何执行任意 SQL 命令的接口。 + + + 每当执行命令时,psql也会轮询由生成的异步通知事件。 + + + 虽然 C 风格的块注释会被传给服务器处理并移除,但 SQL 标准注释会由psql自行移除。 + + + + + 元命令 + + + 你输入到psql中的任何以未加引号的反斜线开始的东西都是一个psql元命令,它们由psql自行处理。这些命令让psql对管理和编写脚本更有用。元命令常常被称作斜线或者反斜线命令。 + + + + psql命令的格式是用反斜线后面直接跟上一个命令动词,然后是一些参数。参数与命令动词和其他参数之间用任意多个空白字符分隔开。 + + + + 要在参数中包含空白,可以用单引号将它括起来。要在参数中包含一个单引号,可以在单引号文本内写两个单引号。单引号中的内容还会接受类似 C 语言的替换:\n(换行)、\t(制表符)、\b(退格)、\r(回车)、\f(换页)、\digits(八进制)以及\xdigits(十六进制)。在单引号文本内,反斜线出现在任何其他字符前面时,只是对该单个字符进行转义,不论它是什么字符。 + + + + 在参数中,用反引号(`)包围的文本会被视为传给 shell 的命令行。该命令的输出(去掉末尾的换行符)会替换反引号中的文本。 + + + + 如果在参数中出现一个未加引号的冒号(:),后面紧跟一个psql变量名,它就会被该变量的值替换, + 如下面所述。 + + + + 有些命令把SQL标识符(例如表名)作为参数。这些参数遵循SQL的语法规则:未加引号的字母会被强制转换为小写,而双引号(")可以保护字母不发生大小写转换,并允许在标识符中包含空白。在双引号内,成对的双引号会在结果名称中折叠成一个双引号。例如,FOO"BAR"BAZ会被解释为fooBARbaz,而"A weird"" name"会变成A weird" name。 + + + + 参数解析会在行尾或遇到另一个未加引号的反斜线时停止。未加引号的反斜线会被视为新元命令的开始。特殊序列\\(两个反斜线)表示参数结束,并继续解析SQL命令(如果还有)。通过这种方式,SQL命令和psql命令可以自由地混合在同一行中。但无论如何,元命令的参数都不能延续到下一行。 + + + + 定义了以下元命令: + + + + \a + + + 如果当前表格输出格式是非对齐,则切换为对齐;否则切换为非对齐。保留此命令是为了向后兼容。更通用的解决方案请参见\pset。 + + + + + + \c\connect [ -reuse-previous=on|off ] [ dbname [ username ] [ host ] [ port ] | conninfo ] + + + 建立到PostgreSQL服务器的新连接。连接参数既可以使用按位置指定的语法(数据库名、用户、主机和端口中的一个或多个),也可以使用conninfo连接字符串,详见。如果没有给出参数,则使用与之前相同的参数建立新连接。 + + + + 将dbnameusernamehostport中的任何一个指定为-,都等价于省略该参数。 + + + + 新连接可以重用前一个连接的连接参数;不仅包括数据库名称、用户、主机和端口,还包括其他设置,如sslmode。 + 默认情况下,参数在位置语法中被重用,但在给定conninfo字符串时不会被重用。 + 传递-reuse-previous=on-reuse-previous=off作为第一个参数将覆盖该默认设置。 + 如果参数被重用,则任何未明确指定为位置参数或在conninfo字符串中的参数将从现有连接的参数中获取。 + 一个例外是,如果使用位置语法更改host设置,使其不同于先前的值,则现有连接参数中存在的任何hostaddr设置将被删除。 + 此外,仅当用户、主机和端口设置未更改时,才会重用现有连接使用的任何密码。 + 当命令既不指定也不重用特定参数时,将使用libpq的默认值。 + + + + 如果成功建立了新连接,则关闭先前的连接。如果连接尝试失败(用户名错误、访问被拒绝等),当psql处于交互模式时,会保留先前的连接。但在执行非交互式脚本时,会立即报错并停止处理。这一区别一方面使用户能够方便地应对输入错误,另一方面也作为安全机制,防止脚本意外地作用于错误的数据库。 + + + + 例如: + + +=> \c mydb myuser host.dom 6432 +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +=> \c -reuse-previous=on sslmode=require -- 仅更改 sslmode +=> \c postgresql://tom@localhost/mydb?application_name=myapp + + + + + + \C [ title ] + + + 设置作为查询结果打印的表的标题,或取消此类标题。该命令等价于\pset title title。(此命令的名称源自caption,因为它过去只用于设置HTML表的标题。) + + + + + + \cd [ directory ] + + + 将当前工作目录更改为directory。如果没有参数,则切换到当前用户的主目录。 + + + + 要打印当前工作目录,请使用\! pwd + + + + + + \conninfo + + 输出当前数据库连接的信息。 + + + + + \copy { table [ ( column_list ) ] | ( query ) } { from | to } { 'filename' | program 'command' | stdin | stdout | pstdin | pstdout } [ [ with ] ( option [, ...] ) ] + + + + 执行前端(客户端)复制。该操作会运行一个SQL + 命令,但并不是由服务器读取或写入指定文件,而是由psql读取或写入文件,并在服务器与本地文件系统之间转送数据。这意味着文件可访问性和权限取决于本地用户,而不是服务器,也不需要 SQL 超级用户权限。 + + + + 当指定program时,commandpsql执行,传给command的数据或从其中读出的数据都会在服务器与客户端之间转送。再次强调,执行权限属于本地用户,而不是服务器,也不需要 SQL 超级用户权限。 + + + + 对于 \copy ... from stdin,数据行会从发出该命令的同一输入源读取,直到读到仅包含 \. 的一行或流到达 EOF 为止。这个选项适合在 SQL 脚本文件中以内联方式填充表。对于 \copy ... to stdout,输出会发送到与psql命令输出相同的位置,并且不会打印 COPY count 命令状态(因为这可能与数据行混淆)。若要读写psql的标准输入或输出,而不受当前命令来源或 \o 选项影响,请写成 from pstdinto pstdout。 + + + + 该命令的语法与SQL 命令类似。 + 除数据源或目标之外,其他所有选项都与中的指定相同。 + 因此,特殊的解析规则适用于\copy命令。 + 特别是,psql的变量替换规则和反斜线转义在这里不适用。 + + + + + 另一种获得与\copy ... to相同结果的方法是使用SQL + COPY ... TO STDOUT命令,并以\g filename + 或\g |program结束。 + 与\copy不同,这种方法允许命令跨越多行;此外,可以使用变量插值和反引号扩展。 + + + + + 这些操作不如以文件或程序作为数据源或目标的SQL COPY命令高效,因为所有数据都必须经过客户端/服务器连接。对于大量数据,SQL命令可能更合适。 + + + + + + + \copyright + + 显示PostgreSQL的版权和分发条款。 + + + + + + \crosstabview [ colV [ colH [ colD [ sortcolH ] ] ] ] + + + 执行当前查询缓冲区(与\g类似),并以交叉表网格显示结果。查询必须返回至少三列。由colV标识的输出列成为纵向表头,由colH标识的输出列成为横向表头。colD标识要在网格中显示的输出列。sortcolH标识横向表头的可选排序列。 + + + + 每个列指定都可以是列号(从 1 开始)或列名。通常的 SQL 大小写折叠和加引号规则适用于列名。如果省略,colV取第 1 列,colH取第 2 列。colH必须不同于colV。如果未指定colD,查询结果必须恰好有三列,既不是colV也不是colH的那一列被用作colD。 + + + + 纵向表头显示为最左列,包含colV列中的值,其顺序与查询结果中相同,但会移除重复值。 + + + + 横向表头显示为第一行,包含colH列中的值,并移除重复值。默认情况下,它们按查询结果中的相同顺序显示。但如果给出了可选的sortcolH参数,它所标识的列的值必须是整数,而colH中的值会按照对应的sortcolH值排序后显示在横向表头中。 + + + + 在交叉表网格中,对于colH中的每个不同值xcolV中的每个不同值y,交点(x,y)处的单元格包含查询结果中colD列的值,该结果行的colH值为xcolV值为y。如果没有这样的行,单元格为空。如果存在多条这样的行,则报错。 + + + + + + + \d[S+] [ pattern ] + + + + 对于每个匹配pattern的关系(表、视图、物化视图、索引、序列或外部表)或复合类型,显示所有列及其类型、表空间(如果不是默认表空间),以及NOT NULL或默认值等特殊属性。还会显示关联的索引、约束、规则和触发器。对于外部表,还会显示关联的外部服务器。(匹配模式的定义见下面的。) + + + 对于某些关系类型,\d会为每列显示附加信息:序列的列值、索引的索引表达式,以及外部表的外部数据包装器选项。 + + \d+形式的命令与之相同,但会显示更多信息:表各列关联的注释、表中是否存在 OID、视图定义(如果关系是视图),以及非默认的复制标识设置。 + + 默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。 + + + + 如果使用\d而没有pattern参数, + 它等同于\dtvmsE,它将显示所有可见的表、视图、物化视图、序列和外部表的列表。 + 这纯粹是一种便利措施。 + + + + + + + \da[S] [ pattern ] + + + 列出聚合函数及其返回类型和操作的数据类型。如果指定了pattern,则只显示名称匹配该模式的聚合函数。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。 + + + + + \dA[+] [ pattern ] + + + 列出访问方法。如果指定了pattern,则只显示名称匹配该模式的访问方法。如果在命令名后附加+,还会列出每个访问方法关联的处理器函数和描述。 + + + + + \db[+] [ pattern ] + + + 列出表空间。如果指定了pattern,则只显示名称匹配该模式的表空间。如果在命令名后附加+,还会列出每个表空间关联的选项、磁盘大小、权限和描述。 + + + + + + \dc[S+] [ pattern ] + + 列出字符集编码之间的转换。如果指定了pattern,则只列出名称匹配该模式的转换。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果在命令名后附加+,还会列出每个对象关联的描述。 + + + + + + \dC[+] [ pattern ] + + 列出类型转换。如果指定了pattern,则只列出源类型或目标类型匹配该模式的类型转换。如果在命令名后附加+,还会列出每个对象关联的描述。 + + + + + + \dd[S] [ pattern ] + + 显示类型为constraintoperator classoperator familyruletrigger的对象的描述。其他所有注释都可以通过相应对象类型的反斜线命令查看。 + + \dd显示匹配pattern的对象的描述;如果没有给出参数,则显示适当类型的可见对象的描述。但无论哪种情况,都只列出有描述的对象。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。 + + 可以用 SQL命令为对象创建描述。 + + + + + + + + \ddp [ pattern ] + + 列出默认访问权限设置。对于默认权限设置已偏离内置默认值的每个角色(以及模式,如果适用),都会显示一个条目。如果指定了pattern,则只列出角色名或模式名匹配该模式的条目。 + + 命令用于设置默认访问权限。权限显示的含义在中有解释。 + + + + \dD[S+] [ pattern ] + + 列出域。如果指定了pattern,则只显示名称匹配该模式的域。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果在命令名后附加+,还会列出每个对象关联的权限和描述。 + + + + + + \dE[S+] [ pattern ] + \di[S+] [ pattern ] + \dm[S+] [ pattern ] + \ds[S+] [ pattern ] + \dt[S+] [ pattern ] + \dv[S+] [ pattern ] + + + 在这组命令中,字母Eimstv分别代表外部表、索引、物化视图、序列、表和视图。可以按任意顺序指定这些字母中的任意一个或全部,以获取相应类型的对象列表。例如,\dit列出索引和表。如果在命令名后附加+,还会列出每个对象的磁盘上的物理大小以及关联的描述(如果有)。如果指定了pattern,则只列出名称匹配该模式的对象。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。 + + + + + + \des[+] [ pattern ] + + 列出外部服务器(助记词:external servers)。如果指定了pattern,则只列出名称匹配该模式的服务器。如果使用\des+形式,则显示每个服务器的完整说明,包括服务器的ACL、类型、版本、选项和描述。 + + + + + + \det[+] [ pattern ] + + 列出外部表(助记词:external tables)。如果指定了pattern,则只列出表名或模式名匹配该模式的条目。如果使用\det+形式,还会显示通用选项和外部表描述。 + + + + + + \deu[+] [ pattern ] + + 列出用户映射(助记词:external users)。如果指定了pattern,则只列出用户名匹配该模式的映射。如果使用\deu+形式,还会显示每个映射的附加信息。 + + + \deu+还可能显示远程用户的用户名和密码,因此应注意不要泄露它们。 + + + + + + + \dew[+] [ pattern ] + + 列出外部数据包装器(助记词:external wrappers)。如果指定了pattern,则只列出名称匹配该模式的外部数据包装器。如果使用\dew+形式,还会显示外部数据包装器的ACL、选项和描述。 + + + + + + \df[antwS+] [ pattern ] + + + 列出函数及其结果数据类型、参数数据类型和函数类型。函数类型分为agg(聚合)、normaltriggerwindow。要仅显示特定类型的函数,可在命令中添加对应的字母antw。如果指定了pattern,则只显示名称匹配该模式的函数。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果使用\df+形式,还会显示每个函数的附加信息,包括易变性、并行安全性、拥有者、安全分类、访问权限、语言、源代码和描述。 + + + 要查找接受特定数据类型参数或返回特定数据类型值的函数,请使用分页器的搜索功能浏览\df输出。 + + + + + + + \dF[+] [ pattern ] + + 列出全文检索配置。如果指定了pattern,则只显示名称匹配该模式的配置。如果使用\dF+形式,则显示每个配置的完整说明,包括底层全文检索解析器和每种解析器词元类型的词典列表。 + + + + + \dFd[+] [ pattern ] + + 列出全文检索词典。如果指定了pattern,则只显示名称匹配该模式的词典。如果使用\dFd+形式,还会显示每个选中词典的附加信息,包括底层全文检索模板和选项值。 + + + + + \dFp[+] [ pattern ] + + 列出全文检索解析器。如果指定了pattern,则只显示名称匹配该模式的解析器。如果使用\dFp+形式,则显示每个解析器的完整说明,包括底层函数和可识别的词元类型列表。 + + + + + \dFt[+] [ pattern ] + + 列出全文检索模板。如果指定了pattern,则只显示名称匹配该模式的模板。如果使用\dFt+形式,还会显示每个模板的附加信息,包括底层函数名。 + + + + + + \dg[S+] [ pattern ] + + 列出数据库角色。(由于用户的概念已经统一为角色,此命令现在等价于\du。)默认只显示用户创建的角色;提供S修饰符可包含系统角色。如果指定了pattern,则只列出名称匹配该模式的角色。如果使用\dg+形式,还会显示每个角色的附加信息;目前会增加每个角色的注释。 + + + + + + \dl + + 这是\lo_list的别名,用于显示大对象列表。 + + + + + \dL[S+] [ pattern ] + + 列出过程语言。如果指定了pattern,则只列出名称匹配该模式的语言。默认只显示用户创建的语言;提供S修饰符可包含系统对象。如果在命令名后附加+,还会列出每种语言的调用处理器、验证器、访问权限,以及它是否为系统对象。 + + + + + + \dn[S+] [ pattern ] + + + 列出模式(命名空间)。如果指定了pattern,则只列出名称匹配该模式的模式。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果在命令名后附加+,还会列出每个对象关联的权限和描述(如果有)。 + + + + + + \do[S+] [ pattern ] + + 列出操作符及其操作数类型和结果类型。如果指定了pattern,则只列出名称匹配该模式的操作符。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果在命令名后附加+,还会显示每个操作符的附加信息,目前只包括底层函数名。 + + + + + + \dO[S+] [ pattern ] + + 列出排序规则。如果指定了pattern,则只列出名称匹配该模式的排序规则。默认只显示用户创建的对象;提供模式或S修饰符可包含系统对象。如果在命令名后附加+,还会列出每个排序规则关联的描述(如果有)。请注意,只会显示可用于当前数据库编码的排序规则,因此同一安装中的不同数据库可能会得到不同结果。 + + + + + + \dp [ pattern ] + + 列出表、视图和序列及其关联的访问权限。如果指定了pattern,则只列出名称匹配该模式的表、视图和序列。 + + 命令用于设置访问权限。权限显示的含义在中有解释。 + + + + + \drds [ role-pattern [ database-pattern ] ] + + 列出已定义的配置设置。这些设置可以特定于角色、特定于数据库,或同时特定于两者。role-patterndatabase-pattern分别用于选择要列出的角色和数据库。省略某个模式参数或将其指定为*时,不会按该参数筛选,还会分别包含不特定于角色或不特定于数据库的设置。 + + 命令用于定义角色专属和数据库专属的配置设置。 + + + + + + + \dT[S+] [ pattern ] + + + 列出数据类型。如果指定了 pattern,则只列出名称与模式匹配的类型。如果在命令名后追加 +,则每个类型都会连同其内部名称、大小以及相关权限一起列出;对于 enum 类型,还会显示其允许值。默认情况下,只显示用户创建的对象;提供模式或 S 修饰符可包括系统对象。 + + + + + + \du[S+] [ pattern ] + + 列出数据库角色。(由于用户的概念已经统一为角色,此命令现在等价于\dg。)默认只显示用户创建的角色;提供S修饰符可包含系统角色。如果指定了pattern,则只列出名称匹配该模式的角色。如果使用\du+形式,还会显示每个角色的附加信息;目前会增加每个角色的注释。 + + + + + \dx[+] [ pattern ] + + 列出已安装的扩展。如果指定了pattern,则只列出名称匹配该模式的扩展。如果使用\dx+形式,则列出属于每个匹配扩展的所有对象。 + + + + + \dy[+] [ pattern ] + + 列出事件触发器。如果指定了pattern,则只列出名称匹配该模式的事件触发器。如果在命令名后附加+,还会列出每个对象关联的描述。 + + + + + \e\edit filename line_number + + + 如果指定了filename,则编辑该文件;编辑器退出后,将其内容复制回查询缓冲区。如果未给出filename,则将当前查询缓冲区复制到临时文件,再以相同方式编辑。 + + 随后,按照psql的正常规则重新解析新的查询缓冲区,其中整个缓冲区被视为一行。(因此,不能用这种方式编写脚本。应使用\i来处理这类脚本。)这意味着,如果查询以分号结束(或包含分号),它会立即执行;否则它只会在查询缓冲区中等待;键入分号或\g发送它,或键入\r取消。 + + 如果指定了行号,psql会将光标定位到文件或查询缓冲区中的指定行。请注意,如果只给出一个全由数字组成的参数,psql会假定它是行号,而不是文件名。 + + + 有关如何配置和定制编辑器,请参见下面的 + + + + + + \echo text [ ... ] + + 将参数打印到标准输出,参数之间用一个空格分隔,末尾跟随换行符。这可用于在脚本输出中插入信息。例如: +=> \echo `date` +Tue Oct 26 21:40:57 CEST 1999 +如果第一个参数是未加引号的-n,则不输出末尾的换行符。 + + + + 如果使用\o命令重定向查询输出,可能会想用\qecho代替这个命令。 + + + + + + + \ef function_description line_number + + + + 这个命令获取并编辑指定函数的定义,形式为CREATE OR REPLACE FUNCTION命令。 + 编辑方式与\edit相同。 + 编辑器退出后,更新后的命令会在查询缓冲区中等待;键入分号或\g发送它,或用\r取消。 + + + 目标函数可以只用名称指定,也可以同时给出名称和参数,例如foo(integer, text)。如果存在多个同名函数,就必须给出参数类型。 + + + 如果未指定函数,则会呈现一个空的CREATE FUNCTION模板供编辑。 + + + 如果指定了行号,psql会将光标定位到函数体中的指定行。(请注意,函数体通常并不从文件的第一行开始。) + + + 有关如何配置和定制编辑器,请参见下面的 + + + + + + + \encoding [ encoding ] + + + + 设置客户端字符集编码。没有参数时,此命令显示当前编码。 + + + + + + + \errverbose + + + + 以最大的详细程度重复最近的服务器错误消息,就好像VERBOSITY被设置为verbose, + SHOW_CONTEXT被设置为always一样。 + + + + + + + \ev view_name line_number + + + + 这个命令获取并编辑指定视图的定义,形式为CREATE OR REPLACE VIEW命令。 + 编辑方式与\edit相同。 + 编辑器退出后,更新后的命令会在查询缓冲区中等待;输入分号或\g发送它,或用\r取消。 + + + + 如果未指定视图,则会呈现一个空的CREATE VIEW模板供编辑。 + + + 如果指定了行号,psql会将光标定位到视图定义中的指定行。 + + + + + + \f [ string ] + + + + 设置非对齐查询输出的字段分隔符。默认值是竖线(|)。另请参见 + \pset,那里介绍了设置输出选项的通用方法。 + + + + + + + \g [ filename ] + \g [ |command ] + + 将当前查询输入缓冲区发送给服务器,并且可以选择将查询输出存储到filename,或通过管道把输出传给 shell 命令command。只有查询成功返回零个或更多元组时,才会向文件或命令写入内容;如果查询失败或 SQL 命令不返回数据,则不会写入。 + + 单独的\g基本上等同于一个分号。 + 带有参数的\g提供了一个一次性替代\o命令的选择。 + + + + + + \gexec + + + 将当前查询输入缓冲区发送给服务器,然后把查询输出(如果有)的每一行的每一列视为要执行的 SQL 语句。例如,下面为每一列创建一个索引,目标表是 my_table: + +=> SELECT format('create index on my_table(%I)', attname) +-> FROM pg_attribute +-> WHERE attrelid = 'my_table'::regclass AND attnum > 0 +-> ORDER BY attnum +-> \gexec +CREATE INDEX +CREATE INDEX +CREATE INDEX +CREATE INDEX + + + + + 生成的查询按照返回行的顺序执行;如果有多列,则在每行内从左到右执行。NULL 字段会被忽略。生成的查询按原样发送到服务器进行处理,因此不能是psql元命令,也不能包含psql变量引用。如果某个查询失败,仍会继续执行其余查询,除非设置了ON_ERROR_STOP。每个查询的执行都受ECHO处理的影响。(通常,在使用\gexec时,适宜将ECHO设为allqueries。)查询日志、单步模式、计时及其他查询执行功能也适用于每个生成的查询。 + + + + + + \gset [ prefix ] + + + 将当前查询输入缓冲区发送给服务器,并将查询输出存入 psql 变量(参见下面的)。要执行的查询必须恰好返回一行。该行的每一列分别存入一个变量,变量名与列名相同。例如: +=> SELECT 'hello' AS var1, 10 AS var2 +-> \gset +=> \echo :var1 :var2 +hello 10 + + + 如果指定了prefix,则会将该字符串加到查询的列名前面,以构成要使用的变量名: +=> SELECT 'hello' AS var1, 10 AS var2 +-> \gset result_ +=> \echo :result_var1 :result_var2 +hello 10 + + + 如果某一列的结果为 NULL,则取消设置对应的变量,而不是设置它。 + 如果查询失败或没有恰好返回一行,则不会更改任何变量。 + + + + + + + \h\help [ command ] + + + 给出指定SQL命令的语法帮助。如果未指定command, + 则psql将列出所有可用语法帮助的命令。如果command是星号 + (*),则显示所有SQL命令的语法帮助。 + + + + + 为了简化输入,由多个单词组成的命令不需要加引号。因此,可以直接输入\help alter table。 + + + + + + + + \H\html + + + 打开HTML查询输出格式。如果HTML格式已经打开,则切换回默认的对齐文本格式。此命令是为兼容性和便利性而保留的;设置其他输出选项的方法见\pset。 + + + + + + + \i\include filename + + + 从文件filename中读取输入,并像在键盘上输入一样执行它。 + + + 如果filename-(连字符),则从标准输入读取,直到遇到 EOF 指示或\q元命令。这可用于将交互式输入与文件输入交错使用。请注意,只有在最外层启用了 Readline,此处才会使用 Readline 功能。 + + + + 如果想在屏幕上看到被读入的各行,请将变量ECHO设置为all。 + + + + + + + + + + \ir\include_relative filename + + \ir命令与\i相似,但解析相对文件名的方式不同。在交互模式下执行时,这两个命令的行为相同。不过,在脚本中调用时,\ir会相对于脚本所在的目录来解释文件名,而不是相对于当前工作目录。 + + + + + + \l[+]\list[+] [ pattern ] + + 列出服务器中的数据库,并显示其名称、拥有者、字符集编码和访问权限。如果指定了pattern,则只列出名称匹配该模式的数据库。如果在命令名后附加+,还会显示数据库大小、默认表空间和描述。(大小信息只对当前用户能够连接的数据库可用。) + + + + + + \lo_export loid filename + + + + 从数据库中读取具有OIDloid的大对象,并将其写入filename。请注意,这与服务器函数 + lo_export略有不同,后者使用运行数据库服务器的用户的权限, + 并在服务器的文件系统上操作。 + + + + 使用\lo_list命令来查找大对象的OID。 + + + + + + + + \lo_import filename [ comment ] + + + 将文件存储到一个PostgreSQL大对象中。可选地,它将给定的注释与对象关联起来。例如: +foo=> \lo_import '/home/peter/pictures/photo.xcf' 'a picture of me' +lo_import 152801 +响应表明大对象获得了对象 ID 152801,这个 ID 可以用来在将来访问新创建的大对象。为便于阅读,建议始终为每个对象关联一条便于人阅读的注释。查看 OID 和注释时,可以使用\lo_list命令。 + + + 请注意,此命令与服务器端的lo_import略有不同,因为它作为本地用户在本地文件系统上操作,而不是服务器的用户和文件系统。 + + + + + + \lo_list + + 列出当前存储在数据库中的所有PostgreSQL大对象,以及为它们提供的注释。 + + + + + \lo_unlink loid + + + + 从数据库中删除OIDloid的大对象。 + + + + + 使用\lo_list命令来查找大对象的OID。 + + + + + + + + \o\out [ filename ] + \o\out [ |command ] + + 将后续查询结果保存到文件filename,或通过管道传给 shell 命令command。如果没有指定参数,查询输出将恢复为标准输出。 + + 查询结果包括从数据库服务器获取的所有表格、命令响应和通知,以及查询数据库的各种反斜杠命令的输出(例如\d),但不包括错误消息。 + + + + + 要在查询结果之间插入文本输出,使用\qecho。 + + + + + + + + \p\print + + + 将当前查询缓冲区打印到标准输出。 + + + + + + \password [ username ] + + + 更改指定用户(默认为当前用户)的密码。此命令提示输入新密码,对其进行加密, + 并将其作为ALTER ROLE命令发送到服务器。这样可以确保新密码 + 不会以明文形式出现在命令历史记录、服务器日志或其他地方。 + + + + + + \prompt [ text ] name + + + 提示用户提供文本,将其赋值给变量name。 + 可以指定一个可选的提示字符串text。 + (对于多个单词的提示,用单引号括起文本。) + + + + 默认情况下,\prompt 使用终端进行输入和输出。然而,如果使用了 + 命令行开关,\prompt 将使用标准输入和标准输出。 + + + + + + \pset [ option [ value ] ] + + + 这个命令设置影响查询结果表输出的选项。option指定要设置哪个选项。value的含义取决于所选的选项。对于某些选项,省略value会切换或取消设置该选项,具体见各选项的说明。如果没有提及这类行为,那么省略value只会显示当前设置。 + + 不带任何参数的\pset会显示所有打印选项的当前状态。 + + 可调整的打印选项如下: + + border + + value必须是数字。一般来说,数字越大,表格的边框和分隔线就越多,但细节取决于具体格式。在HTML格式中,它会直接转换为border=...属性。在大多数其他格式中,只有值 0(无边框)、1(内部分隔线)和 2(表格外框)有意义,大于 2 的值会与border = 2作相同处理。latexlatex-longtable格式还允许使用值 3,以在数据行之间添加分隔线。 + + + + + columns + + + 设置wrapped格式的目标宽度,同时也是确定输出是否足够宽以 + 需要分页器或在扩展自动模式下切换到垂直显示的宽度限制。 + 零(默认值)会导致目标宽度由环境变量COLUMNS控制,或者如果未设置 + COLUMNS则由检测到的屏幕宽度控制。 + 另外,如果columns为零,则wrapped格式仅影响屏幕输出。 + 如果columns为非零,则文件和管道输出也会按该宽度折行。 + + + + + + expanded(或 x + + 如果指定了value,它必须是onoff(分别启用或禁用扩展模式),或者是auto。如果省略value,该命令会在开启和关闭设置之间切换。启用扩展模式时,查询结果以两列显示,左侧为列名,右侧为数据。如果数据在通常的横向模式下无法适应屏幕,这种模式就很有用。在自动设置下,当查询输出包含多列且宽度超过屏幕时,会使用扩展模式;否则使用常规模式。自动设置只在对齐和折行格式中有效。在其他格式中,它的行为始终与关闭扩展模式相同。 + + + + + fieldsep + + 指定非对齐输出格式使用的字段分隔符。这样可以创建例如制表符或逗号分隔的输出,这可能更符合其他程序的需要。要将制表符设置为字段分隔符,请输入\pset fieldsep '\t'。默认字段分隔符是'|'(竖线)。 + + + + + fieldsep_zero + + 将非对齐输出格式使用的字段分隔符设置为零字节。 + + + + + footer + + 如果指定了value,它必须是onoff,分别启用或禁用表格页脚((n rows)计数)的显示。如果省略value,该命令会切换页脚显示的开关状态。 + + + + + format + + 将输出格式设置为unalignedalignedwrappedhtmlasciidoclatex(使用tabular)、latex-longtabletroff-ms之一。允许使用不产生歧义的缩写。 + + unaligned格式将一行的所有列写在同一行上,以当前生效的字段分隔符分隔。这适合创建供其他程序读取的输出(例如制表符分隔或逗号分隔格式)。 + + aligned格式是标准的、适合人阅读且排版整齐的文本输出;这是默认格式。 + + wrapped格式与aligned相似,但会将较宽的数据值折成多行,使输出适应目标列宽。目标宽度的确定方式见columns选项的说明。请注意,psql不会尝试对列标题折行;因此,如果列标题所需的总宽度超过目标宽度,wrapped格式的行为就与aligned相同。 + + + asciidochtml、 + latexlatex-longtable和 + troff-ms格式生成的表格旨在包含在使用相应标记语言的文档中。 + 它们不是完整的文档!这在HTML中可能不是必需的,但在 + LaTeX中,则必须有一个完整文档的外层结构。 + latex-longtable格式需要LaTeX + 的longtablebooktabs包。 + + + + + + linestyle + + + 设置边框线绘制样式为asciiold-asciiunicode之一。 + 允许使用唯一缩写。(这意味着一个字母就足够了。) + 默认设置为ascii。 + 此选项仅影响alignedwrapped输出格式。 + + + + ascii样式使用普通的ASCII字符。数据中的换行符以右边缘的+符号表示。当wrapped格式在没有换行符的位置把数据折到下一行时,会在第一行的右边缘显示一个点(.),并在下一行的左边缘再次显示。 + + + + old-ascii样式使用普通的ASCII字符,采用PostgreSQL 8.4 及更早版本的格式样式。数据中的换行符以替代左侧列分隔符的:符号表示。当数据在没有换行符的位置折到下一行时,则用;符号替代左侧列分隔符。 + + + + unicode样式使用 Unicode 框线绘制字符。数据中的换行符以右边缘的回车符号表示。当数据在没有换行符的位置折到下一行时,会在第一行的右边缘显示省略号符号,并在下一行的左边缘再次显示。 + + + + 当border设置大于零时,linestyle选项还决定用哪些字符绘制边框线。普通的ASCII字符在任何环境中都可用,但在支持 Unicode 的显示设备上,Unicode 字符更美观。 + + + + + + null + + 设置用于代替空值打印的字符串。默认不打印任何内容,这很容易被误认为空字符串。例如,你可能更喜欢使用\pset null '(null)' + + + + + numericlocale + + 如果指定了value,它必须是onoff,分别启用或禁用使用区域设置特定的字符来分隔小数点左侧的数字组。如果省略value,该命令会在常规数字输出和区域设置特定的数字输出之间切换。 + + + + + pager + + 控制查询和psql帮助输出是否使用分页器程序。如果设置了环境变量PAGER,输出会通过管道传递给指定的程序。否则,使用与平台有关的默认程序(如more)。 + + pager选项为off时,不使用分页器程序。当pager选项为on时,会在适当时使用分页器,即输出目标为终端且内容无法在屏幕上完整显示时。pager选项也可以设为always,这样所有终端输出都会使用分页器,无论内容是否能在屏幕上完整显示。不带value\pset pager会切换分页器的使用状态。 + + + + + pager_min_lines + + 如果将pager_min_lines设置为大于页面高度的数字,那么只有待显示的输出至少达到这么多行时,才会调用分页器程序。默认设置为 0。 + + + + + recordsep + + 指定非对齐输出格式使用的记录(行)分隔符。默认为换行符。 + + + + + recordsep_zero + + 将非对齐输出格式使用的记录分隔符设置为零字节。 + + + + + tableattr(或 T + + HTML格式中,这指定要放在table标签内的属性,例如cellpaddingbgcolor。请注意,你可能不需要在这里指定border,因为\pset border已经负责处理它。如果没有给出value,则取消设置表格属性。 + latex-longtable格式中,这控制每个包含左对齐数据类型的列的宽度比例。它以空白分隔的值列表指定,例如'0.2 0.2 0.6'。未指定的输出列使用最后指定的值。 + + + + + title(或 C + + 设置随后打印的所有表格的标题。这可以为输出提供描述性标签。如果没有给出value,则取消设置标题。 + + + + + tuples_only(或 t + + 如果指定了value,它必须是onoff,分别启用或禁用仅元组模式。如果省略value,该命令会在常规输出和仅元组输出之间切换。常规输出包含列标题、表格标题和各种页脚等附加信息。在仅元组模式下,只显示实际的表格数据。 + + + + + unicode_border_linestyle + + unicode线条样式的边框绘制样式设置为singledouble + + + + + unicode_column_linestyle + + unicode线条样式的列分隔线绘制样式设置为singledouble + + + + + unicode_header_linestyle + + unicode线条样式的表头分隔线绘制样式设置为singledouble + + + + + + 这些不同格式的外观示例可参见一节。 + + + + 有各种快捷命令用于\pset。请参见\a、 + \C\f\H、 + \t\T\x。 + + + + + + + + + \q\quit + + 退出psql程序。在脚本文件中,只会终止该脚本的执行。 + + + + + + \qecho text [ ... ] + + + 这个命令与\echo相同,只是输出会写入由\o设置的查询输出通道。 + + + + + + + \r\reset + + 重置(清空)查询缓冲区。 + + + + + + \s [ filename ] + + + 打印psql的命令行历史记录到filename。 + 如果省略filename,历史记录将被写入标准输出(如果适用,将使用分页器)。 + 如果psql在构建时没有使用Readline支持,则此命令不可用。 + + + + + + + \set [ name [ value [ ... ] ] ] + + + psql变量name设置为value,如果给出多个值,则设置为所有值的串接。如果只给出一个参数,则将变量设置为空值。要取消变量设置,请使用\unset命令。 + + \set没有任何参数时,显示当前设置的所有psql变量的名称和值。 + + 合法的变量名可以包含字母、数字和下划线。详情见下面的。变量名区分大小写。 + + 尽管你可以随意将任何变量设置为任何值,但psql将其中若干变量视为特殊变量。这些变量在下面有关变量的章节中介绍。 + + + + 这个命令与SQL命令无关。 + + + + + + + + \setenv name [ value ] + + + + 设置环境变量namevalue, + 或者如果未提供value,则取消设置环境变量。示例: + +testdb=> \setenv PAGER less +testdb=> \setenv LESS -imx4F + + + + + + \sf[+] function_description + + + + 这个命令获取并显示指定函数的定义,以CREATE OR REPLACE FUNCTION命令的形式呈现。 + 定义将打印到当前查询输出通道,由\o设置。 + + + 目标函数可以只用名称指定,也可以同时给出名称和参数,例如foo(integer, text)。如果存在多个同名函数,就必须给出参数类型。 + + 如果在命令名后附加+,则会为输出行编号,函数体的第一行编号为 1。 + + + + + + \sv[+] view_name + + + + 这个命令获取指定视图的定义,并以CREATE OR REPLACE VIEW命令的形式显示。定义会打印到由\o设置的当前查询输出通道。 + + + + 如果在命令名称后添加+,那么输出行将从1开始编号。 + + + + + + + \t + + 切换输出中的列名标题和行数页脚的显示状态。这个命令等价于\pset tuples_only,提供它是为了使用方便。 + + + + + + \T table_options + + 指定在HTML输出格式中放在table标签内的属性。这个命令等价于\pset tableattr table_options + + + + + + \timing [ on | off ] + + 不带参数时,切换以毫秒为单位显示每条 SQL 语句执行耗时的开关状态。带参数时,设置为相同的值。 + + + + + + \unset name + + + 取消设置(删除)psql变量name + + + + + + \w\write filename + \w\write |command + + 将当前查询缓冲区输出到文件filename,或通过管道传递给 shell 命令command + + + + + + \watch [ seconds ] + + + 重复执行当前查询缓冲区(如同 \g 一样),直到被中断或查询失败。两次执行之间等待指定的秒数(默认 2 秒)。每次查询结果都会带有一个头部,其中包含 \pset title + 字符串(如果有)、查询开始时的时间以及延迟间隔。 + + + + + + + \x [ on | off | auto ] + + 设置或切换扩展表格格式模式。它等价于\pset expanded + + + + + + \z [ pattern ] + + 列出表、视图和序列及其关联的访问权限。如果指定了pattern,则只列出名称匹配该模式的表、视图和序列。 + + + 这是\dp的别名(显示权限)。 + + + + + + + \! [ command ] + + + 进入一个单独的 shell,或执行 shell 命令command。参数不会被进一步解释;shell 会原样看到它们。特别地,变量替换规则和反斜线转义在这里不适用。 + + + + + + + \? [ topic ] + + + 显示帮助信息。可选的topic参数 + (默认为commands)选择要解释的psql的哪个部分: + commands描述psql的反斜线命令; + options描述可以传递给psql的命令行选项; + 而variables显示关于psql配置变量的帮助。 + + + + + + + + + 模式 + + + 模式 + 在 psql 和 pg_dump 中 + + + + 很多\d命令都可以用一个pattern参数来指定要被显示的对象名称。在最简单的情况下,模式正好就是该对象的准确名称。在模式中的字符通常会被变成小写形式(就像在 SQL 名称中那样),例如\dt FOO将会显示名为foo的表。就像在 SQL 名称中那样,把模式放在双引号中可以阻止它被转换成小写形式。如果需要在一个模式中包括一个真正的双引号字符,则需要在双引号包围的文本内把它写成两个相邻的双引号,这同样是符合 SQL 加引号标识符的规则。例如,\dt "FOO""BAR"将显示名为FOO"BAR(不是foo"bar)的表。和普通的 SQL 名称规则不同,你可以只在模式的一部分周围放上双引号,例如\dt FOO"FOO"BAR将会显示名为fooFOObar的表。 + + + + 只要完全省略pattern参数,\d命令就会显示当前模式搜索路径中可见的全部对象 — 这等价于用*作为模式(如果一个对象所在的模式位于搜索路径中,并且在搜索路径中该模式之前没有同类且同名的对象,则该对象就是可见的。这表示可以直接用名称引用该对象,而不需要用模式来限定)。要查看数据库中所有对象而不考虑其可见性,可以把*.*用作模式。 + + + + 如果放在一个模式中,*将匹配任意字符序列(包括空序列),而?会匹配任意的单个字符(这种记号方法就像 Unix shell 的文件名模式一样)。例如,\dt int*会显示名称以int开始的表。但是如果被放在双引号内,*?就会失去这些特殊含义而变成普通的字符。 + + + 包含点号(.)的模式会被解释为模式名称的匹配模式,后接对象名称的匹配模式。例如,\dt foo*.*bar*显示模式名以foo开头且表名包含bar的所有表。如果没有点号,则该匹配模式只匹配当前模式搜索路径中可见的对象。同样,双引号内的点号会失去其特殊含义,而按字面匹配。 + + + 高级用户可以使用字符类等正则表达式记法,如[0-9]可以匹配任意数字。所有的正则表达式特殊字符都按照所说的工作,以下字符除外:.会按照上面所说的作为一种分隔符,*会被翻译成正则表达式记号.*?会被翻译成.,而$则按字面意思匹配。根据需要,可以用?模拟.,用(R+|)模拟R*,或用(R|)模拟R?$不需要作为一个正则表达式字符,因为模式必须匹配整个名称,而不是像正则表达式的常规用法那样解释(换句话说,$会被自动地追加到模式上)。如果不希望该模式的匹配位置被固定,可以在开头或者结尾写上*。注意在双引号内,所有的正则表达式特殊字符会失去其特殊含义并且按照其字面意思进行匹配。还有,在操作符名称模式中(即作为\do的参数),正则表达式特殊字符也按照字面意思进行匹配。 + + + + + + 高级特性 + + + 变量 + + + psql提供了和普通 Unix 命令 shell 相似的变量替换特性。变量简单来说就是一对名称/值,其中值可以是任意长度的任意字符串。名称必须由字母(包括非拉丁字母)、数字和下划线构成。 + + + + 要设置一个变量,可以使用psql的元命令\set。例如, + +testdb=> \set foo bar + + 会将foo设置为值bar。要检索该变量的内容,可以在名称前放一个冒号,例如: + +testdb=> \echo :foo +bar + + 这在常规 SQL 命令和元命令中均有效,下文的中有更多细节。 + + + + 如果调用\set时没有第二个参数,该变量会被设置,其值为空字符串。要取消设置(即删除)一个变量,可以使用命令\unset。要显示所有变量的值,在调用\set时不带任何参数即可。 + + + + + \set的参数服从与其他命令相同的替换规则。因此可以构造有趣的引用,例如\set :foo 'something'以及分别得到Perl或者PHP软链接或者可变变量。不幸的是(或者幸运的是?),这些构造出来的东西并没有什么用处。在另一方面,\set bar :foo是一种很好的拷贝变量的方法。 + + + + + 有一些变量会被psql特殊对待。它们表示特定的选项设置,运行时这类选项设置可以通过修改该变量的值来改变,或者在某些情况下它们表示psql的可更改的状态。尽管你可以把这些变量用于其他目的,但不建议这样做,因为程序的行为可能会很快变得非常奇怪。按照惯例,所有被特殊对待的变量的名称由全部大写形式的 ASCII 字母(还有可能是数字和下划线)组成。为了确保未来最大的兼容性,最好避免把这类变量名用于自己的目的。 + + + + + + AUTOCOMMIT + + autocommit + psql + + + + + 在被设置为on(默认)时,每一个 SQL 命令在成功完成时会被自动提交。在这种模式中要推迟提交,必须输入一个BEGIN或者START TRANSACTION SQL 命令。当被设置为off或者被取消设置时,在显式发出COMMIT或者END之前,SQL 命令不会被提交。自动提交关闭模式会为你发出一个隐式的BEGIN,这会发生在任何不在一个事务块中且本身既不是BEGIN及其他事务控制命令且不是无法在事务块中执行的命令(例如VACUUM)之前。 + + + + + 在自动提交关闭模式中,必须通过ABORT或者ROLLBACK显式地放弃任何失败的事务。还要记住,如果退出会话时没有提交,则所有的工作都会丢失。 + + + + + + 自动提交打开模式是PostgreSQL的传统行为,但是自动提交关闭模式更接近于 SQL 的规范。如果更喜欢自动提交关闭模式,可以在系统级的psqlrc文件或者个人的~/.psqlrc文件中设置它。 + + + + + + + COMP_KEYWORD_CASE + + + 确定在补全一个 SQL 关键词时要使用的大小写形式。如果被设置为lower或者upper,补全后的词将分别是小写或者大写形式。如果被设置为preserve-lower或者preserve-upper(默认),补全后的词将会保持该词已输入部分的大小写形式,但是如果被补全的词还没有被输入,则它会被分别补全成小写或者大写形式。 + + + + + + DBNAME + + + 当前已连接的数据库名称。每次连接到一个数据库时都会设置该变量(包括程序启动时),但可以被取消设置。 + + + + + + ECHO + + + 如果被设置为all,所有非空输入行会在读入时打印到标准输出(不适用于交互式读取的行)。要在程序开始时选择这种行为,可以使用开关。如果被设置为queriespsql会在发送每个查询给服务器时将它们打印到标准输出。选择这种行为的开关是。如果被设置为errors,那么只有失败的查询会被显示在标准错误输出上。对应的开关是。如果未设置,或被设置为none(或上述值以外的其他值),则不会显示任何查询。 + + + + + + ECHO_HIDDEN + + + 当这个变量被设置为on且一个反斜线命令查询数据库时,相应的查询会被先显示。这种特性可以帮助我们学习PostgreSQL的内部并且在自己的程序中提供类似的功能(要在程序开始时选择这种行为,可以使用开关)。如果把这个变量设置为值noexec,则对应的查询只会被显示而并不真正被发送给服务器执行。 + + + + + + ENCODING + + + 当前的客户端字符集编码。 + + + + + + FETCH_COUNT + + + 如果这个变量被设置为一个大于 0 的整数值,SELECT查询的结果会以一组一组的方式取出并且显示(而不是像默认的那样把整个结果集拿到以后再显示),每组包含的行数等于该整数值。因此,这种方式只会使用有限的内存量,而不管整个结果集的大小。在启用这个特性时,通常会使用 100 到 1000 的设置。记住在使用这种特性时,一个查询可能会在已经显示了一些行之后失败。 + + + + 尽管可以把这种特性用于任何的输出格式,但是默认的aligned格式看起来会比较糟糕,因为每一组的FETCH_COUNT行将被单独格式化,这就会导致不同的行组的列宽不同。其他的输出格式会更好。 + + + + + + + HISTCONTROL + + + 如果这个变量被设置为ignorespace,则以一个空格开始的行不会被放入到历史列表中。如果被设置为值ignoredups,则与上一条历史记录相同的行不会被放入。值ignoreboth组合了上述两种值。如果未设置,或被设置为none(或上述值以外的其他值),所有在交互模式中被读入的行都会保存在历史列表中。 + + + + 这个特性是可耻地从Bash抄袭过来的。 + + + + + + + HISTFILE + + 用于存储历史记录列表的文件名。默认值是~/.psql_history。例如,将以下内容: +\set HISTFILE ~/.psql_history- :DBNAME +放入 ~/.psqlrc 会使 psql 为每个数据库维护单独的历史记录。 + + + 这个特性是可耻地从Bash抄袭过来的。 + + + + + + + HISTSIZE + + + 存储在命令历史中的命令数量。默认值是 500。 + + + + 这个特性是可耻地从Bash抄袭过来的。 + + + + + + + HOST + + + 当前连接到的数据库服务器主机。每次连接到数据库时都会设置该变量(包括程序启动时),但可以被取消设置。 + + + + + + IGNOREEOF + + + 如果未设置,向一个psql的交互式会话发送一个EOF字符(通常是ControlD)将会终止应用。如果设置为一个数字值,则会有该数量的EOF字符被忽略,然后应用才会终止。如果该变量被设置但没有数字值,则默认为 10。 + + + + 这个特性是可耻地从Bash抄袭过来的。 + + + + + + + LASTOID + + + 最后被影响的 OID 的值,这可能会由INSERT或者\lo_import命令返回。这个变量只保证在下一个SQL命令的结果被显示完之前有效。 + + + + + + ON_ERROR_ROLLBACK + + rollback + psql + + + + + 当被设置为on时,如果事务块中的一个语句产生一个错误,该错误会被忽略并且该事务会继续。当被设置为interactive时,只在交互式会话中忽略这类错误,而读取脚本文件时则不会忽略错误。当未设置或被设置为off时,事务块中产生错误的一个语句会中止整个事务。错误回滚模式的工作原理是在事务块的每个命令之前都为你发出一个隐式的SAVEPOINT,然后在该命令失败时回滚到该保存点。 + + + + + + ON_ERROR_STOP + + + 默认情况下,发生错误后命令处理会继续。当这个变量被设置为on时,处理将立即停止。在交互模式下,psql会返回到命令提示符;否则,psql会退出,并返回错误代码 3,以便与错误代码 1 所表示的致命错误区分开来。在两种情况下,任何当前正在运行的脚本(顶层脚本以及它可能调用的其他脚本)都会立即中止。如果顶层命令字符串包含多个 SQL 命令,则会在当前命令处停止处理。 + + + + + + PORT + + + 当前连接到的数据库服务器端口。每次连接到数据库时都会设置该变量(包括程序启动时),但可以被取消设置。 + + + + + + PROMPT1 + PROMPT2 + PROMPT3 + + + 这些变量指定psql发出的提示符的模样。见下文的。 + + + + + + QUIET + + + 把这个变量设置为on等效于命令行选项。在交互模式下可能用处不大。 + + + + + + + SHOW_CONTEXT + + + 这个变量可以被设置为值nevererrors或者always来控制是否在来自服务器的消息中显示CONTEXT字段。默认是errors(表示在错误消息中显示上下文,但在通知和警告消息中不显示)。 + 当VERBOSITY被设置为terse时,这个设置无效(另见\errverbose,它可以用来得到刚遇到的错误的详细信息)。 + + + + + + SINGLELINE + + + 设置这个变量为on等效于命令行选项。 + + + + + + SINGLESTEP + + + 设置这个变量为on等效于命令行选项。 + + + + + + USER + + + 当前连接的数据库用户。每次连接到一个数据库时都会设置该变量(包括程序启动时),但可以被取消设置。 + + + + + + VERBOSITY + + + 这个变量可以被设置为值defaultverbose或者terse来控制错误报告的详细程度(另见\errverbose,在想得到刚遇到的错误的详细信息时使用)。 + + + + + + + + + + + <acronym>SQL</acronym> 插值 + + + psql变量的一个关键特性是可以把它们替换(插值)到常规SQL语句中,也可以把它们替换到元命令的参数中。此外,psql还提供了功能来确保被用作 SQL 字面量和标识符的变量值会被正确地加引号。不加引号地插值一个值的语法是在变量名前面加上一个冒号(:)。例如, + +testdb=> \set foo 'my_table' +testdb=> SELECT * FROM :foo; + + 将查询表my_table。注意这可能会不安全:该变量的值会被按字面拷贝,因此它可能包含不平衡的引号甚至反斜线命令。必须确保把它放在那里是有意义的。 + + + + 当一个值要用作 SQL 字面量或标识符时,最安全的做法是为它加上引号。要将变量值作为 SQL 字面量加引号,应写一个冒号,后面跟用单引号括起来的变量名。要将变量值作为 SQL 标识符加引号,则在冒号后面用双引号括起变量名。这些写法能正确处理变量值中嵌入的引号和其他特殊字符。前面的示例可用以下更安全的写法: + +testdb=> \set foo 'my_table' +testdb=> SELECT * FROM :"foo"; + + + + + 在加引号的SQL字面量和标识符内部,不会执行变量插值。因此,':foo'这样的写法不能根据变量值生成加引号的字面量(即使能够生效,也不安全,因为它无法正确处理变量值中嵌入的引号)。 + + + + 使用这种机制的一个示例是把一个文件的内容拷贝到一个表列中。首先把该文件载入到一个变量,然后把该变量的值作为一个加引号的字符串进行插值: + +testdb=> \set content `cat my_file.txt` +testdb=> INSERT INTO my_table VALUES (:'content'); + + (注意如果my_file.txt包含 NUL 字节,这样也不行。psql不支持在变量值中嵌入 NUL 字节)。 + + + + 因为冒号可以合法地出现在 SQL 命令中,一次明显的插值尝试(即:name:'name'或者:"name")不会被替换,除非所指的变量当前已设置。在任何情况下,可以用一个反斜线对冒号进行转义以避免它被替换。 + + + + 变量的冒号语法对嵌入式查询语言(例如ECPG)来说是标准的SQL。用于数组切片和类型转换的冒号语法是PostgreSQL扩展,它有时可能会与标准用法冲突。把一个变量值转义成 SQL 字面量或者标识符的冒号加引号语法是一种psql扩展。 + + + + + + 提示符 + + + psql 发出的提示符可以按你的喜好进行定制。PROMPT1PROMPT2PROMPT3 这三个变量包含描述提示符外观的字符串和特殊转义序列。提示符 1 是 psql 请求新命令时发出的常规提示符。提示符 2 会在录入命令期间还需要更多输入时发出,例如命令尚未以分号结束,或者引号尚未闭合时。在执行 SQL COPY FROM STDIN 命令并需要在终端中输入一行值时,会发出提示符 3。 + + + 所选提示符变量的值会原样打印,除非遇到百分号(%)。此时会根据下一个字符替换为其他文本。已定义的替换项如下: + + %M + + 数据库服务器的完整主机名(含域名);如果通过 Unix 域套接字连接,则为[local];如果 Unix 域套接字不在编译时指定的默认位置,则为[local:/dir/name] + + + + + %m + + 数据库服务器的主机名,在第一个点号处截断;如果通过 Unix 域套接字连接,则为[local] + + + + + %> + 数据库服务器监听的端口号。 + + + + %n + + 数据库会话用户名。(在数据库会话期间,SET SESSION AUTHORIZATION命令可能改变该值的扩展结果。) + + + + + %/ + 当前数据库的名称。 + + + + %~ + 类似 %/,但如果该数据库是你的默认数据库,则输出 ~ + (波浪号)。 + + + + %# + + 如果会话用户是数据库超级用户,则为#,否则为>。(在数据库会话期间,SET SESSION AUTHORIZATION命令可能改变该值的扩展结果。) + + + + + %p + + 当前所连接后端的进程 ID。 + + + + + %R + + + 在提示符 1 中,通常为 =;但如果处于单行模式,则为 ^;如果会话已与数据库断开连接(这可能发生在 \connect 失败时),则为 !。在提示符 2 中,%R 会被替换为一个字符,该字符取决于 psql 为什么还期待更多输入:如果命令只是尚未终止,则为 -;如果存在未结束的 /* ... */ 注释,则为 *;如果存在未结束的带引号字符串,则为单引号;如果存在未结束的带引号标识符,则为双引号;如果存在未结束的 美元引用字符串,则为美元符号;如果存在未匹配的左括号,则为 (。在提示符 3 中,%R 不会产生任何输出。 + + + + + + %x + + + 事务状态:如果当前不在事务块中,则为空字符串;如果处于事务块中,则为 *;如果处于失败的事务块中,则为 !;如果事务状态不确定(例如因为当前没有连接),则为 ?。 + + + + + + %l + + + 当前语句中的行号,从 1 开始。 + + + + + + %digits + + + 替换为指定八进制代码对应的字符。 + + + + + + %:name: + + + psql 变量 + name 的值。详见 + 。 + + + + + + %`command` + + + command 的输出,类似普通的 + 反引号替换。 + + + + + + %[ ... %] + + + 提示符中可以包含终端控制字符,例如改变提示文本的颜色、背景或样式,或者改变终端窗口标题。为了让 Readline 的行编辑功能正常工作,这些不可打印的控制字符必须用 %[ 和 + %] 包围起来,以标记为不可见字符。提示符中可以出现多组这样的标记。例如: + +testdb=> \set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# ' + + 其效果是在兼容 VT100 且支持颜色的终端上,生成一个粗体(1;)的黑底黄字提示符 + (33;40)。 + + + + + 要在提示符中插入百分号,请写为%%。默认情况下,提示符 1 和 2 为'%/%R%# ',提示符 3 为'>> ' + + + + 这个特性是可耻地从tcsh抄袭过来的。 + + + + + + + 命令行编辑 + + + psql支持 Readline 库,便于编辑和检索输入行。命令历史记录会在 psql 退出时自动保存,并在 psql 启动时重新载入。也支持 Tab 补全,不过其补全逻辑并不声称自己是 SQL 解析器。Tab 补全生成的查询还可能干扰其他 SQL 命令,例如 SET + TRANSACTION ISOLATION LEVEL。如果出于某种原因你不喜欢 Tab 补全,可以将以下内容放入主目录下名为 .inputrc 的文件中,将其关闭: +$if psql +set disable-completion on +$endif +(这不是 psql 的功能,而是 Readline 的功能。更多细节请阅读其文档。) + + + + + + + 环境 + + + + + COLUMNS + + + + 如果\pset columns为零,这个环境变量控制用于wrapped格式的宽度以及用来确定是否输出需要用到分页器或者切换到扩展自动模式中的垂直格式的宽度。 + + + + + + PAGER + + + 如果查询结果无法在屏幕上完整显示,则会通过管道传递给这个命令。典型值为moreless。默认值取决于平台。可以将PAGER设置为空,或使用\pset命令中与分页器有关的选项来禁用分页器。 + + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数(见)。 + + + + + + PSQL_EDITOR + EDITOR + VISUAL + + + \e\ef\ev命令使用的编辑器。按列出的顺序检查这些变量,使用第一个已设置的变量。 + + 内置的默认编辑器在 Unix 系统上为vi,在 Windows 系统上为notepad.exe + + + + + PSQL_EDITOR_LINENUMBER_ARG + + + + 当 \e\ef 或 + \ev 带有行号参数使用时,此变量指定将起始行号传给用户编辑器时所用的命令行参数。对于 Emacs 或 + vi 之类的编辑器,这个参数是一个加号。如果选项名和行号之间需要空格,请在变量值中包含末尾空格。例如: + +PSQL_EDITOR_LINENUMBER_ARG='+' +PSQL_EDITOR_LINENUMBER_ARG='--line ' + + + + + 在 Unix 系统上默认是+(对应于默认编辑器vi,且对很多其他常见编辑器可用)。在 Windows 系统上没有默认值。 + + + + + + PSQL_HISTORY + + + + 命令历史文件的替代位置。波浪线(~)扩展会被执行。 + + + + + + PSQLRC + + + + 用户的.psqlrc文件的替代位置。波浪线(~)扩展会被执行。 + + + + + + SHELL + + + + 被\!命令执行的命令。 + + + + + + TMPDIR + + + + 存储临时文件的目录。默认是/tmp。 + + + + + + + 和大部分其他PostgreSQL工具一样,这个工具也使用libpq所支持的环境变量(见)。 + + + + + + + 文件 + + + + psqlrc~/.psqlrc + + + 如果没有选项,在连接到数据库后但在接收正常的命令之前,psql会尝试依次从系统级的启动文件(psqlrc)和用户的个人启动文件(~/.psqlrc)中读取并且执行命令。这些文件可以被用来设置客户端或者服务器,通常是一些\setSET命令。 + + + 系统范围的启动文件名为 psqlrc。默认情况下,会在安装的系统配置目录中查找它,最可靠的识别方式是运行 pg_config + --sysconfdir。通常这个目录是相对于包含 PostgreSQL 可执行文件的目录的 ../etc/。也可以通过 PGSYSCONFDIR 环境变量显式指定查找目录。 + + + 用户的个人启动文件名为.psqlrc,并且在调用用户的主目录中寻找。 + Windows 没有主目录这一概念,在 Windows 上,个人启动文件的名称为%APPDATA%\postgresql\psqlrc.conf。 + 在任何情况下,可以通过设置PSQLRC环境变量来覆盖此默认文件路径。 + + + 系统范围的启动文件和用户个人的启动文件都可以通过在文件名后附加连字符和PostgreSQL的大版本或小版本号来使其与psql版本相关, + 例如~/.psqlrc-9.2~/.psqlrc-9.2.5。 + 最具体版本匹配的文件将优先读取,而不是非特定版本的文件。 + + + + + + .psql_history + + + 命令行历史被存储在文件~/.psql_history中,或者是 Windows 的文件%APPDATA%\postgresql\psql_history中。 + + + 历史文件的位置可以通过PSQL_HISTORY环境变量显式设置。 + + + + + + + + + 注解 + + + + psql最适合与相同或较旧大版本的服务器配合使用。 + 如果服务器的版本比psql本身更新,反斜线命令特别容易失败。 + 然而,\d系列的反斜线命令应该可以在最低至 7.4 版本的服务器上运行, + 但不一定适用于比psql本身更新的服务器。运行SQL命令和显示查询结果的一般功能 + 也应该可以在更新大版本的服务器上运行,但不能保证在所有情况下都能实现。 + + + 如果你想用psql连接到多个具有不同大版本的服务器,推荐使用最新版本的psql。或者,你可以为每一个大版本保留一份psql拷贝,并且针对相应的服务器使用匹配的版本。但实际上,这种额外的麻烦是不必要的。 + + + + + + 在 PostgreSQL 9.6 之前, + 选项意味着 + ();现在已经不是这样了。 + + + + + + 在PostgreSQL 8.4 之前,psql允许一个单字母反斜线命令的第一个参数直接写在该命令后面,中间不需要空白。现在则要求用空白分隔。 + + + + + + + + 给 Windows 用户的注解 + + + psql是一个控制台应用。由于 Windows 的控制台窗口使用的是一种和系统中其他应用不同的编码,在psql中使用 8 位字符时要特别注意。如果psql检测到一个有问题的控制台代码页,它将会在启动时警告你。要更改控制台代码页,有两件事是必要的: + + + + + 输入cmd.exe /c chcp 1252可以设置代码页(1252 是适用于德语的一个代码页,请在这里替换成你的值)。如果正在使用 Cygwin,可以把这个命令放在/etc/profile中。 + + + + + + 把控制台字体设置为Lucida Console,因为栅格字体无法与 ANSI 代码页一起使用。 + + + + + + + + + 示例 + + 第一个示例展示如何将一个命令分散在多行输入中。请注意提示符的变化: +testdb=> CREATE TABLE my_table ( +testdb(> first integer not null default 0, +testdb(> second text) +testdb-> ; +CREATE TABLE +现在再看看表定义: +testdb=> \d my_table + Table "my_table" + Attribute | Type | Modifier +-----------+---------+-------------------- + first | integer | not null default 0 + second | text | + +现在我们把提示符改得更有趣一些: +testdb=> \set PROMPT1 '%n@%m %~%R%# ' +peter@localhost testdb=> +假设你已经在表中填入数据,并想查看一下: +peter@localhost testdb=> SELECT * FROM my_table; + first | second +-------+-------- + 1 | one + 2 | two + 3 | three + 4 | four +(4 rows) + +要以不同方式显示表格,可以使用\pset命令: +peter@localhost testdb=> \pset border 2 +Border style is 2. +peter@localhost testdb=> SELECT * FROM my_table; ++-------+--------+ +| first | second | ++-------+--------+ +| 1 | one | +| 2 | two | +| 3 | three | +| 4 | four | ++-------+--------+ +(4 rows) + +peter@localhost testdb=> \pset border 0 +Border style is 0. +peter@localhost testdb=> SELECT * FROM my_table; +first second +----- ------ + 1 one + 2 two + 3 three + 4 four +(4 rows) + +peter@localhost testdb=> \pset border 1 +Border style is 1. +peter@localhost testdb=> \pset format unaligned +Output format is unaligned. +peter@localhost testdb=> \pset fieldsep "," +Field separator is ",". +peter@localhost testdb=> \pset tuples_only +Showing only tuples. +peter@localhost testdb=> SELECT second, first FROM my_table; +one,1 +two,2 +three,3 +four,4 +也可以使用简短命令: +peter@localhost testdb=> \a \t \x +Output format is aligned. +Tuples only is off. +Expanded display is on. +peter@localhost testdb=> SELECT * FROM my_table; +-[ RECORD 1 ]- +first | 1 +second | one +-[ RECORD 2 ]- +first | 2 +second | two +-[ RECORD 3 ]- +first | 3 +second | three +-[ RECORD 4 ]- +first | 4 +second | four + + +在适当的情况下,要以交叉表形式显示查询结果,可以使用\crosstabview命令: +testdb=> SELECT first, second, first > 2 AS gt2 FROM my_table; + first | second | gt2 +-------+--------+----- + 1 | one | f + 2 | two | f + 3 | three | t + 4 | four | t +(4 rows) + +testdb=> \crosstabview first second + first | one | two | three | four +-------+-----+-----+-------+------ + 1 | f | | | + 2 | | f | | + 3 | | | t | + 4 | | | | t +(4 rows) +第二个示例显示一个乘法表,行按数值降序排列,列则独立地按数值升序排列。 +testdb=> SELECT t1.first as "A", t2.first+100 AS "B", t1.first*(t2.first+100) as "AxB", +testdb-> row_number() over(order by t2.first) AS ord +testdb-> FROM my_table t1 CROSS JOIN my_table t2 ORDER BY 1 DESC +testdb-> \crosstabview "A" "B" "AxB" ord + A | 101 | 102 | 103 | 104 +---+-----+-----+-----+----- + 4 | 404 | 408 | 412 | 416 + 3 | 303 | 306 | 309 | 312 + 2 | 202 | 204 | 206 | 208 + 1 | 101 | 102 | 103 | 104 +(4 rows) + + + + + + + diff --git a/zh/9.6/ref/reassign_owned.sgml b/zh/9.6/ref/reassign_owned.sgml new file mode 100644 index 00000000..9c24c0d1 --- /dev/null +++ b/zh/9.6/ref/reassign_owned.sgml @@ -0,0 +1,113 @@ + + + + + REASSIGN OWNED + + + + REASSIGN OWNED + 7 + SQL - 语言语句 + + + + REASSIGN OWNED + 更改一个数据库角色所拥有的数据库对象的所有权 + + + + +REASSIGN OWNED BY { old_role | CURRENT_USER | SESSION_USER } [, ...] + TO { new_role | CURRENT_USER | SESSION_USER } + + + + + 描述 + + + REASSIGN OWNED指示系统将任何 + old_roles所拥有的数据库对象的所有权更改为 + new_role。 + + + + + 参数 + + + + old_role + + + 一个角色的名称。该角色在当前数据库中拥有的所有对象,以及该角色拥有的 + 所有共享对象(数据库、表空间)的所有权,都将被重新分配给 + new_role。 + + + + + + new_role + + + 将成为受影响对象新拥有者的角色名称。 + + + + + + + + 注解 + + + REASSIGN OWNED常用于为移除一个或多个角色做准备。 + 由于REASSIGN OWNED不会影响其他数据库中的对象, + 因此通常需要在每个包含待移除角色所拥有对象的数据库中执行此命令。 + + + REASSIGN OWNED 要求对源角色和目标角色都具有权限。 + + + 命令提供了另一种选择, + 它会直接删除一个或多个角色所拥有的全部数据库对象。 + + + + REASSIGN OWNED命令不会影响授予 + old_roles的、在非其所拥有的对象上的任何权限。 + 同样,它也不会影响使用ALTER DEFAULT PRIVILEGES创建的默认权限。 + 如需撤销这类权限,请使用DROP OWNED。 + + + + 更多讨论请见。 + + + + + + 兼容性 + + + REASSIGN OWNED命令是 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/refresh_materialized_view.sgml b/zh/9.6/ref/refresh_materialized_view.sgml new file mode 100644 index 00000000..0844ae8e --- /dev/null +++ b/zh/9.6/ref/refresh_materialized_view.sgml @@ -0,0 +1,134 @@ + + + + + REFRESH MATERIALIZED VIEW + + + + REFRESH MATERIALIZED VIEW + 7 + SQL - 语言语句 + + + + REFRESH MATERIALIZED VIEW + 替换物化视图的内容 + + + + +REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] name + [ WITH [ NO ] DATA ] + + + + + 描述 + + + REFRESH MATERIALIZED VIEW会完全替换物化视图的内容。 + 要执行此命令,必须是该物化视图的拥有者。旧 + 内容会被丢弃。如果指定了WITH DATA(或者默认如 + 此),则会执行其底层查询以生成新数据,并使物化视图处于可扫描状态。如 + 果指定了WITH NO DATA,则不会生成新数据,并使物化视 + 图处于不可扫描状态。 + + + 不得同时指定CONCURRENTLYWITH NO + DATA。 + + + + + 参数 + + + + CONCURRENTLY + + + 刷新物化视图时,不会阻塞针对该物化视图的并发查询。如果不使用此选 + 项,一次影响很多行的刷新通常会占用更少的资源并且更快完成,但可能会 + 阻塞其他试图从该物化视图读取的连接。在只影响少量行的情况下,此选项 + 可能更快。 + + + 只有当该物化视图上至少有一个仅使用列名且涵盖所有行的 + UNIQUE索引时,才允许使用此选项;也就是说,它不 + 能是表达式索引,也不能带有WHERE子句。 + + + 只有在该物化视图已经填充数据时,才能使用此选项。 + + + 即使使用了此选项,对于任意一个物化视图,一次也只能运行一个 + REFRESH。 + + + + + + name + + + 要刷新的物化视图名称(可以带模式限定)。 + + + + + + + + 注解 + + + 虽然会保留未来操作使用的默认索引, + 但REFRESH MATERIALIZED VIEW不会基于该属性对生成 + 的行排序。如果希望数据在生成时就已排序,必须在底层查询中使用 + ORDER BY子句。 + + + + + 示例 + + + 这个命令会使用物化视图order_summary定义中的查询来 + 替换其内容,并使其处于可扫描状态: + +REFRESH MATERIALIZED VIEW order_summary; + + + + + 这个命令会释放与物化视图annual_statistics_basis相关 + 的存储,并使其处于不可扫描状态: + +REFRESH MATERIALIZED VIEW annual_statistics_basis WITH NO DATA; + + + + + 兼容性 + + + REFRESH MATERIALIZED VIEW是 + PostgreSQL的扩展。 + + + + + 另见 + + + + + + + + + diff --git a/zh/9.6/ref/reindex.sgml b/zh/9.6/ref/reindex.sgml new file mode 100644 index 00000000..ec27e801 --- /dev/null +++ b/zh/9.6/ref/reindex.sgml @@ -0,0 +1,236 @@ + + + + + REINDEX + + + + REINDEX + 7 + SQL - 语言语句 + + + + REINDEX + 重建索引 + + + + +REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } name + + + + + 描述 + + + REINDEX使用索引所属表中存储的数据重建索引, + 并替换索引的旧副本。以下几种场景适合使用REINDEX: + + + + + 一个索引已经损坏,不再包含有效数据。尽管理论上这不该发生, + 但在实践中索引可能因软件缺陷或硬件故障而损坏。 + REINDEX提供了一种恢复方法。 + + + + + + 一个索引已经膨胀,也就是说其中包含许多空页或几乎为空 + 的页。在 PostgreSQL 中,B-树索引在某些不常见 + 的访问模式下可能出现这种情况。REINDEX可通过写入一个 + 不含死页的新版本索引来减少索引的空间消耗。详见。 + + + + + + 你修改了某个索引的存储参数(例如 fillfactor),并希望确保该更改已经 + 完全生效。 + + + + + 使用 CONCURRENTLY 选项构建索引失败后,会留下一个无效索引。这类索引没有用处,但用 REINDEX 重建它们可能很方便。注意,REINDEX 不会进行并发构建。要在不干扰生产运行的情况下构建索引,应删除该索引并重新执行 CREATE INDEX CONCURRENTLY 命令。 + + + + + + + 参数 + + + + INDEX + + 重新创建指定的索引。 + + + + + TABLE + + 重新创建指定表的所有索引。如果该表有一个辅助TOAST表,也会对其重新索引。 + + + + + SCHEMA + + + + 重新创建指定模式中的所有索引。如果该模式中的某个表有一个辅助 + TOAST表,也会对其重新索引。共享系统目录上的索引也会被 + 处理。这种形式的REINDEX不能在事务块内执行。 + + + + + + DATABASE + + + + 重新创建当前数据库中的所有索引。 + 共享系统目录上的索引也会被处理。这种形式的REINDEX不能 + 在事务块内执行。 + + + + + + SYSTEM + + + + 重新创建当前数据库内系统目录上的所有索引。共享系统目录上的索引也包 + 含在内。用户表上的索引不会被处理。这种形式的 + REINDEX不能在事务块内执行。 + + + + + + name + + + + 要重新索引的特定索引、表或数据库的名称。索引名和表名可以带模式限 + 定。目前,REINDEX DATABASE和 + REINDEX SYSTEM只能对当前数据库重新索引,所以其参数必须与当前数据库名匹配。 + + + + + + VERBOSE + + 在每个索引被重建时打印进度报告。 + + + + + + + 注解 + + + 如果怀疑某个用户表上的索引已经损坏,可以使用 + REINDEX INDEXREINDEX TABLE + 直接重建该索引,或者重建该表上的所有索引。 + + + + 如果需要从系统表上的索引损坏中恢复,情况就更复杂了。在这种情况下, + 重要的是系统本身没有使用任何可疑索引。(事实上,在这种场景下,你可 + 能会发现服务器进程在启动时立即崩溃,因为它依赖损坏的索引。)要安 + 全恢复,必须用选项启动服务器,该选项会阻止服务器 + 在查找系统目录时使用索引。 + + + + 一种做法是关闭服务器,并在命令行中包含选项来启 + 动单用户 PostgreSQL 服务器。然后可以根据 + 希望重建的范围,执行REINDEX DATABASE、 + REINDEX SYSTEMREINDEX TABLE + 或REINDEX INDEX。如果拿不准,就使用 + REINDEX SYSTEM来重建该数据库中的所有系统索引。然 + 后退出单用户服务器会话并重新启动常规服务器。关于如何与单用户服务器接 + 口交互的更多信息,参见参考页。 + + + + 另一种方法是启动一个常规服务器会话,并在其命令行选项中包含 + 。具体做法因客户端而异,但对于所有基于 + libpq的客户端,都可以在启动客户端之前将环 + 境变量PGOPTIONS设置为-P。注意,尽 + 管这种方法不需要阻止其他客户端,但在修复完成之前,阻止其他用户连接到 + 受损数据库可能仍然更稳妥。 + + + + REINDEX类似于删除并重新创建索引,因为索引内容都是 + 从头重建的。不过,两者在锁方面的考量相当不同。 + REINDEX会阻止该索引所属表上的写入,但不阻止读取。它 + 还会对正在处理的特定索引获取ACCESS EXCLUSIVE锁, + 从而阻塞试图使用该索引的读取。相比之下, + DROP INDEX会短暂地对父表获取 + ACCESS EXCLUSIVE锁,同时阻塞写入和读取。随后的 + CREATE INDEX会阻止写入但不阻止读取;由于索引不存 + 在,读取不会尝试使用它,因此不会发生阻塞,但读取可能被迫使用代价高昂 + 的顺序扫描。 + + + 对单个索引或表重新索引,要求用户是该索引或表的拥有者。对数据库重新索引,则要求用户是该数据库的拥有者(因此,拥有者可以重建其他用户所拥有表的索引)。当然,超级用户始终可以重新索引任何对象。 + + + + + 示例 + + + 重建单个索引: + + +REINDEX INDEX my_index; + + + + + 重建表my_table上的所有索引: + + +REINDEX TABLE my_table; + + + + + 在不假定系统索引已经有效的情况下,重建某个数据库中的所有索引: + + +$ export PGOPTIONS="-P" +$ psql broken_db +... +broken_db=> REINDEX DATABASE broken_db; +broken_db=> \q + + + + + + 兼容性 + + + 在 SQL 标准中没有REINDEX命令。 + + + diff --git a/zh/9.6/ref/reindexdb.sgml b/zh/9.6/ref/reindexdb.sgml new file mode 100644 index 00000000..34e9bf27 --- /dev/null +++ b/zh/9.6/ref/reindexdb.sgml @@ -0,0 +1,357 @@ + + + + + reindexdb + + + + reindexdb + 1 + 应用程序 + + + + reindexdb + 重建一个PostgreSQL数据库中的索引 + + + + + reindexdb + connection-option + option + + + + + + + + schema + + + + + + + + + + table + + + + + + + + + + index + + + + dbname + + + + reindexdb + connection-option + option + + + + + + + + + reindexdb + connection-option + option + + + + + + dbname + + + + + + 描述 + + + reindexdb是用于重建PostgreSQL数据库中索引的工具。 + + + reindexdb 是 SQL 命令 的一个包装器。通过该工具重建数据库索引与通过其他方法访问服务器来重建索引,在效果上没有区别。 + + + + + + 选项 + + + reindexdb接受以下命令行参数: + + + + + + 重建所有数据库的索引。 + + + + + + + + + + 在未使用/时,指定要重建索引的数据库名称。 + 如果未指定该选项,则从环境变量PGDATABASE中读取数据库名称。 + 如果该变量未设置,则使用为连接指定的用户名。 + dbname可以是一个连接字符串。 + 如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 回显reindexdb生成并发送给服务器的命令。 + + + + + + + + + 仅重建 index。可以重建多个索引,方法是多次指定 开关。 + + + + + + + + + 不显示进度消息。 + + + + + + + + + + 仅重建数据库的系统目录上的索引。 + + + + + + + + + 仅重建以下对象的索引:schema。可以重建多个模式中的索引,方法是多次指定 开关。 + + + + + + + + 仅重建以下对象的索引:table。可以重建多个表的索引,方法是多次指定 开关。 + + + + + + + + + 在处理过程中打印详细信息。 + + + + + + + + + + 打印reindexdb的版本并退出。 + + + + + + + + + + 显示关于reindexdb命令行参数的帮助信息,并退出。 + + + + + + + + + + reindexdb还接受以下用于连接参数的命令行参数: + + + + + + 指定服务器所在机器的主机名。如果该值以斜杠开头,则它将被用作 Unix 域套接字的目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 要作为其身份连接的用户名。 + + + + + + + + + + 永远不发出密码提示。如果服务器要求密码认证,而又无法通过其他方式获取密码,例如通过.pgpass文件,则连接尝试会失败。此选项可用于没有用户可输入密码的批处理作业和脚本。 + + + + + + + + + + 强制reindexdb在连接数据库前提示输入密码。 + + + + 该选项并非必需,因为如果服务器要求密码认证,reindexdb会自动提示输入密码。不过,reindexdb会先浪费一次连接尝试来确认服务器需要密码。在某些场景下,使用值得,以避免这次额外的连接尝试。 + + + + + + + + + 当使用/时,连接到该数据库以收集要重建索引的数据库列表。 + 如果未指定,则使用postgres数据库;如果它不存在,则使用template1。 + 这可以是一个连接字符串。 + 如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + 此外,除数据库名称本身以外的连接字符串参数,在连接到其他数据库时也会被重复使用。 + + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他PostgreSQL工具一样,该工具也使用libpq支持的环境变量(见)。 + + + + + + + 诊断 + + + 如果遇到困难,请参阅中关于潜在问题和错误消息的讨论。数据库服务器必须运行在目标主机上。此外, + libpq前端库所使用的任何默认连接设置和环境变量也都会生效。 + + + + + + + 注解 + + reindexdb 可能需要多次连接到 PostgreSQL 服务器,每次都要求输入密码。在这种情况下,使用 ~/.pgpass 文件会很方便。更多信息见 + + + + + 示例 + + + 要重建数据库test中的索引: + +$ reindexdb test + + + + + 要重建名为abcd的数据库中表foo的索引和索引bar: + +$ reindexdb --table foo --index bar abcd + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/release_savepoint.sgml b/zh/9.6/ref/release_savepoint.sgml new file mode 100644 index 00000000..e7107ee3 --- /dev/null +++ b/zh/9.6/ref/release_savepoint.sgml @@ -0,0 +1,111 @@ + + + + + RELEASE SAVEPOINT + + + + 保存点 + 释放 + + + + RELEASE SAVEPOINT + 7 + SQL - 语言语句 + + + + RELEASE SAVEPOINT + 销毁一个先前定义的保存点 + + + + +RELEASE [ SAVEPOINT ] savepoint_name + + + + + 描述 + + RELEASE SAVEPOINT 销毁先前在当前事务中定义的保存点。 + + 销毁保存点后,就不能再将它用作回滚点,但除此之外没有其他用户可见的行为。它不会撤销保存点建立之后执行的命令的效果。(要这样做,请参见 。)在不再需要保存点时将其销毁,可以让系统在事务结束之前回收一些资源。 + + RELEASE SAVEPOINT 还会销毁在指定保存点建立之后建立的所有保存点。 + + + + 参数 + + + + savepoint_name + + + 要销毁的保存点名称。 + + + + + + + + 注解 + + + 指定一个此前未定义的保存点名称是一种错误。 + + + 当事务处于已中止状态时,不能释放保存点。 + + + 如果有多个保存点使用相同名称,则只会释放最近定义的那个。 + + + + + + 示例 + + + 要建立保存点,并在稍后销毁它: + +BEGIN; + INSERT INTO table1 VALUES (3); + SAVEPOINT my_savepoint; + INSERT INTO table1 VALUES (4); + RELEASE SAVEPOINT my_savepoint; +COMMIT; + + 上述事务将同时插入 3 和 4。 + + + + + 兼容性 + + + 该命令符合SQL标准。 + 标准规定关键字SAVEPOINT是强制的, + 但PostgreSQL允许省略它。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/reset.sgml b/zh/9.6/ref/reset.sgml new file mode 100644 index 00000000..cc3b5237 --- /dev/null +++ b/zh/9.6/ref/reset.sgml @@ -0,0 +1,107 @@ + + + + + RESET + + + + RESET + 7 + SQL - 语言语句 + + + + RESET + 将一个运行时参数的值恢复为默认值 + + + + +RESET configuration_parameter +RESET ALL + + + + + 描述 + + + RESET将运行时参数恢复为其默认值。 + RESET是 + +SET configuration_parameter TO DEFAULT + + 的另一种拼写。详见。 + + + + 默认值指的是:在当前会话中,如果从未对该参数执行过SET, + 它本应具有的值。该值的实际来源可能是编译时内置的默认值、配置文件、 + 命令行选项,或者针对特定数据库或特定用户的默认设置。这与将其定义为 + 该参数在会话开始时的值略有不同,因为如果该值来自配置文件, + 那么它会被重置为配置文件当前指定的值。详见。 + + + + RESET的事务行为和SET相同: + 它的效果会在事务回滚时被撤销。 + + + + + 参数 + + + + configuration_parameter + + + 可设置的运行时参数名称。可用参数见以及 + 参考页。 + + + + + + ALL + + + 将所有可设置的运行时参数重置为默认值。 + + + + + + + + 示例 + + + 将timezone配置参数设置为其默认值: + +RESET timezone; + + + + + 兼容性 + + + RESET是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/revoke.sgml b/zh/9.6/ref/revoke.sgml new file mode 100644 index 00000000..6610445e --- /dev/null +++ b/zh/9.6/ref/revoke.sgml @@ -0,0 +1,269 @@ + + + + + REVOKE + + + + REVOKE + 7 + SQL - 语言语句 + + + + REVOKE + 撤销访问权限 + + + + +REVOKE [ GRANT OPTION FOR ] + { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } + [, ...] | ALL [ PRIVILEGES ] } + ON { [ TABLE ] table_name [, ...] + | ALL TABLES IN SCHEMA schema_name [, ...] } + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { SELECT | INSERT | UPDATE | REFERENCES } ( column_name [, ...] ) + [, ...] | ALL [ PRIVILEGES ] ( column_name [, ...] ) } + ON [ TABLE ] table_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { USAGE | SELECT | UPDATE } + [, ...] | ALL [ PRIVILEGES ] } + ON { SEQUENCE sequence_name [, ...] + | ALL SEQUENCES IN SCHEMA schema_name [, ...] } + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] } + ON DATABASE database_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON DOMAIN domain_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN DATA WRAPPER fdw_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON FOREIGN SERVER server_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { EXECUTE | ALL [ PRIVILEGES ] } + ON { FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ) [, ...] + | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON LANGUAGE lang_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] } + ON LARGE OBJECT loid [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] } + ON SCHEMA schema_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { CREATE | ALL [ PRIVILEGES ] } + ON TABLESPACE tablespace_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ GRANT OPTION FOR ] + { USAGE | ALL [ PRIVILEGES ] } + ON TYPE type_name [, ...] + FROM role_specification [, ...] + [ CASCADE | RESTRICT ] + +REVOKE [ ADMIN OPTION FOR ] + role_name [, ...] FROM role_specification [, ...] + [ GRANTED BY role_specification ] + [ CASCADE | RESTRICT ] + +其中role_specification可以是: + + [ GROUP ] role_name + | PUBLIC + | CURRENT_USER + | SESSION_USER + + + + + 描述 + + + REVOKE 命令从一个或多个角色那里撤销先前授予的权限。 + 关键字 PUBLIC 指的是由所有角色组成的隐式定义组。 + + + + 有关各种权限类型的含义,请参见 命令的描述。 + + + + 请注意,任何特定角色实际拥有的权限,是直接授予给它的权限、授予给它当前 + 所属任一角色的权限,以及授予给 PUBLIC 的权限之和。 + 因此,例如,从 PUBLIC 撤销 SELECT 权限, + 并不一定意味着所有角色都失去了该对象上的 SELECT 权限: + 那些被直接授予该权限或通过其他角色获得该权限的角色仍然拥有它。类似地, + 从某个用户撤销 SELECT 权限,如果 PUBLIC + 或其所属的其他角色仍然拥有 SELECT 权限,该用户仍可能 + 使用 SELECT。 + + + + 如果指定了 GRANT OPTION FOR,则只撤销该权限的授予选项, + 而不撤销权限本身。否则,权限和授予选项都会被撤销。 + + + + 如果某个用户持有带授予选项的权限,并且已经将其授予其他用户,那么其他 + 用户持有的这些权限称为依赖权限。若正在撤销第一个用户持有的该权限或其 + 授予选项,并且存在依赖权限,则在指定 CASCADE 时这些 + 依赖权限也会被一并撤销;否则,该撤销操作会失败。此递归撤销只影响那些 + 通过某条可追溯到本 REVOKE 命令目标用户的用户链授予的权限。 + 因此,如果受影响用户还通过其他用户获得了同一权限,那么他们实际上可能 + 仍保有该权限。 + + + + 在撤销某个表上的权限时,该表每一列上的对应列权限(若有)也会自动被 + 撤销。反过来,如果某个角色已被授予表级权限,那么从单独列上撤销同一 + 权限不会产生任何效果。 + + + 在撤销角色成员资格时,GRANT OPTION 改称为 ADMIN OPTION,但行为类似。这种形式的命令还允许使用 GRANTED BY 选项,但目前会忽略该选项(只检查所指定角色是否存在)。另请注意,这种形式的命令不允许把噪声词 GROUP 写在 role_specification 中。 + + + + 注解 + + 使用 \dp 命令可以显示现有表和列上授予的权限。有关格式的信息,参见 。对于非表对象,也有其他 \d 命令可以显示它们的权限。 + + + 用户只能撤销由自己直接授予的权限。例如,如果用户 A 已将某项带授予选项 + 的权限授予用户 B,而用户 B 又将其授予用户 C,则用户 A 不能直接从 C + 撤销该权限。相反,用户 A 可以从 B 撤销授予选项,并使用 + CASCADE 选项,这样该权限就会进一步从 C 撤销。再例如, + 如果 A 和 B 都将同一权限授予了 C,A 可以撤销自己授予的那份,但不能撤销 + B 授予的那份,因此 C 实际上仍会拥有该权限。 + + + + 当对象的非拥有者尝试对该对象执行 REVOKE 时,如果该用户 + 在该对象上完全没有任何权限,命令将立即失败。只要该用户至少拥有某项 + 权限,命令就会继续执行,但只会撤销那些该用户持有授予选项的权限。 + 如果未持有任何授予选项,REVOKE ALL PRIVILEGES 形式 + 会发出警告;而其他形式如果命令中特别列出的任一权限未持有其授予选项, + 也会发出警告。(原则上,这些说明也适用于对象拥有者;但由于拥有者始终 + 被视为持有全部授予选项,这种情况实际上不会发生。) + + + + 如果超级用户选择执行 GRANTREVOKE + 命令,则该命令会像由受影响对象的拥有者发出那样执行。由于所有权限最终都来自对象拥有者 + (可能经由授予选项链间接传递),超级用户可以撤销所有权限,但如上所述, + 这可能需要使用 CASCADE。 + + + + REVOKE 也可以由并非受影响对象拥有者的角色执行,只要 + 该角色是拥有该对象之角色的成员,或者是持有该对象上 + WITH GRANT OPTION 权限之角色的成员。在这种情况下, + 该命令会视同由实际拥有该对象的角色,或持有该对象上 + WITH GRANT OPTION 权限的角色发出。例如,如果表 + t1 由角色 g1 拥有,而角色 + u1 是其成员,那么 + u1 可以撤销 t1 上那些记录为由 + g1 授出的权限。这包括由 u1 以及角色 + g1 的其他成员作出的授权。 + + + + 如果执行 REVOKE 的角色通过多条角色成员资格路径间接 + 持有权限,则系统不会指明将使用哪一个上层角色来执行该命令。在这种情况 + 下,最佳做法是使用 SET ROLE 切换成你希望作为其身份执行 + REVOKE 的那个具体角色。否则,可能会撤销掉并非你本意 + 要撤销的权限,或者根本没有撤销任何权限。 + + + + + 示例 + + + 撤销表 films 上授予所有用户的插入权限: + + +REVOKE INSERT ON films FROM PUBLIC; + + + + + 撤销用户 manuel 在视图 kinds 上的所有权限: + + +REVOKE ALL PRIVILEGES ON kinds FROM manuel; + + + 请注意,这实际上意味着 撤销所有由我授予的权限。 + + + + 撤销用户 joe 在角色 admins 中的成员资格: + + +REVOKE admins FROM joe; + + + + + 兼容性 + + + 命令的兼容性注解同样适用于 + REVOKE。按照标准,关键词 + RESTRICTCASCADE + 是必需的,但 PostgreSQL 默认假定为 + RESTRICT。 + + + + + 另见 + + + + + diff --git a/zh/9.6/ref/rollback.sgml b/zh/9.6/ref/rollback.sgml new file mode 100644 index 00000000..c6df0b43 --- /dev/null +++ b/zh/9.6/ref/rollback.sgml @@ -0,0 +1,85 @@ + + + + + ROLLBACK + + + + ROLLBACK + 7 + SQL - 语言语句 + + + + ROLLBACK + 中止当前事务 + + + + +ROLLBACK [ WORK | TRANSACTION ] + + + + + 描述 + + + ROLLBACK回滚当前事务,并丢弃该事务所做的全部更新。 + + + + + 参数 + + + + WORK + TRANSACTION + + + 可选关键字,没有任何作用。 + + + + + + + + 注解 + + 使用 成功结束事务。 + + 在事务块外执行 ROLLBACK 会发出警告,除此之外没有任何效果。 + + + + 示例 + + + 要中止所有更改: + +ROLLBACK; + + + + + 兼容性 + + SQL 标准只规定了 ROLLBACKROLLBACK WORK 两种形式。除此之外,该命令完全符合标准。 + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/rollback_prepared.sgml b/zh/9.6/ref/rollback_prepared.sgml new file mode 100644 index 00000000..4929a72c --- /dev/null +++ b/zh/9.6/ref/rollback_prepared.sgml @@ -0,0 +1,100 @@ + + + + + ROLLBACK PREPARED + + + + ROLLBACK PREPARED + 7 + SQL - 语言语句 + + + + ROLLBACK PREPARED + 回滚一个先前为两阶段提交而预备的事务 + + + + +ROLLBACK PREPARED transaction_id + + + + + 描述 + + + ROLLBACK PREPARED回滚一个处于预备状态的事务。 + + + + + 参数 + + + + transaction_id + + + 要回滚的事务的事务标识符。 + + + + + + + + 注解 + + + 要回滚预备事务,执行者必须是最初执行该事务的同一用户,或者是超级用户; + 但不必处在执行该事务的同一会话中。 + + + + 这个命令不能在事务块内执行。该预备事务会被立即回滚。 + + + + 当前所有处于预备状态的事务都列在 + pg_prepared_xacts + 系统视图中。 + + + + + 示例 + + 回滚事务标识符为foobar的事务: + + +ROLLBACK PREPARED 'foobar'; + + + + + + 兼容性 + + + ROLLBACK PREPARED是 + PostgreSQL扩展。它旨在供外部事务管理系统使用, + 其中有些系统已被标准覆盖(例如 X/Open XA),但这些系统的 SQL 侧并未标准化。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/rollback_to.sgml b/zh/9.6/ref/rollback_to.sgml new file mode 100644 index 00000000..592c0a7c --- /dev/null +++ b/zh/9.6/ref/rollback_to.sgml @@ -0,0 +1,145 @@ + + + + + ROLLBACK TO SAVEPOINT + + + + 保存点 + 回滚 + + + + ROLLBACK TO SAVEPOINT + 7 + SQL - 语言语句 + + + + ROLLBACK TO SAVEPOINT + 回滚到一个保存点 + + + + +ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name + + + + + 描述 + + + 回滚该保存点建立后执行的所有命令。该保存点仍然有效,如有需要,之后还可以再次回滚到它。 + + + + ROLLBACK TO SAVEPOINT会隐式销毁在指定保存点之后建立的所有保存点。 + + + + + 参数 + + + + savepoint_name + + + 要回滚到的保存点名称。 + + + + + + + + 注解 + + + 使用可以销毁一个保存点, + 而不丢弃在它建立之后执行的命令所产生的效果。 + + + + 指定一个尚未建立的保存点名称是一种错误。 + + + + 就保存点而言,游标带有一些非事务性的行为。凡是在某个保存点内打开的游标, + 在回滚该保存点时都会被关闭。如果先前打开的游标在某个随后又被回滚的保存点内受到了 + FETCHMOVE命令的影响,那么该游标会保留在FETCH使其指向的位置上 + (也就是说,由FETCH引起的游标移动不会被回滚)。 + 关闭游标同样不会因回滚而撤销。不过,如果游标查询导致了其他副作用(例如该查询调用的 + 易变函数带来的副作用),且这些副作用发生在后来被回滚的保存点期间,那么它们 + 被回滚。如果某个游标的执行导致事务中止,该游标会进入不可执行状态, + 因此即使事务可以通过ROLLBACK TO SAVEPOINT恢复,该游标也不能再使用。 + + + + + 示例 + + + 要撤销在my_savepoint建立后执行的命令的效果: + +ROLLBACK TO SAVEPOINT my_savepoint; + + + + + 游标位置不受保存点回滚的影响: + +BEGIN; + +DECLARE foo CURSOR FOR SELECT 1 UNION SELECT 2; + +SAVEPOINT foo; + +FETCH 1 FROM foo; + ?column? +---------- + 1 + +ROLLBACK TO SAVEPOINT foo; + +FETCH 1 FROM foo; + ?column? +---------- + 2 + +COMMIT; + + + + + + + 兼容性 + + + SQL标准规定关键字SAVEPOINT是强制的,但 + PostgreSQLOracle允许省略它。 + SQL 只允许在ROLLBACK之后使用WORK作为噪声词, + 不允许使用TRANSACTION。此外,SQL 还有一个可选子句 + AND [ NO ] CHAIN,而PostgreSQL当前尚不支持。 + 除此之外,该命令符合 SQL 标准。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/savepoint.sgml b/zh/9.6/ref/savepoint.sgml new file mode 100644 index 00000000..7d2cbbf0 --- /dev/null +++ b/zh/9.6/ref/savepoint.sgml @@ -0,0 +1,132 @@ + + + + + SAVEPOINT + + + + 保存点 + 定义 + + + + SAVEPOINT + 7 + SQL - 语言语句 + + + + SAVEPOINT + 在当前事务中定义一个新的保存点 + + + + +SAVEPOINT savepoint_name + + + + + 描述 + + + SAVEPOINT在当前事务中建立一个新的保存点。 + + + + 保存点是事务中的一种特殊标记,它允许回滚在其建立之后执行的所有命令, + 从而将事务状态恢复到建立保存点时的状态。 + + + + + 参数 + + + + savepoint_name + + + 赋予新保存点的名称。 + + + + + + + + 注解 + + + 使用回滚到保存点。使用 + 销毁保存点,同时保留其建立后执行的命令所产生的效果。 + + + + 保存点只能在事务块内部建立。一个事务中可以定义多个保存点。 + + + + + 示例 + + + 要建立一个保存点,并在稍后撤销其建立后执行的所有命令的效果: + +BEGIN; + INSERT INTO table1 VALUES (1); + SAVEPOINT my_savepoint; + INSERT INTO table1 VALUES (2); + ROLLBACK TO SAVEPOINT my_savepoint; + INSERT INTO table1 VALUES (3); +COMMIT; + + 上述事务将插入值 1 和 3,而不会插入 2。 + + + + 要建立保存点,并在稍后销毁它: + +BEGIN; + INSERT INTO table1 VALUES (3); + SAVEPOINT my_savepoint; + INSERT INTO table1 VALUES (4); + RELEASE SAVEPOINT my_savepoint; +COMMIT; + + 上述事务将同时插入 3 和 4。 + + + + + + + 兼容性 + + + SQL 要求在建立另一个同名保存点时自动销毁原有保存点。在 + PostgreSQL中,旧保存点会被保留,不过在回滚或 + 释放时只会使用最近建立的那个。(如果使用 + RELEASE SAVEPOINT释放较新的保存点,较旧的保存点将再次 + 可供 ROLLBACK TO SAVEPOINT 和 + RELEASE SAVEPOINT 使用。)除此之外, + SAVEPOINT 完全符合 SQL。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/security_label.sgml b/zh/9.6/ref/security_label.sgml new file mode 100644 index 00000000..b0a83419 --- /dev/null +++ b/zh/9.6/ref/security_label.sgml @@ -0,0 +1,188 @@ + + + + + SECURITY LABEL + + + + SECURITY LABEL + 7 + SQL - 语言语句 + + + + SECURITY LABEL + 定义或更改应用于对象的安全标签 + + + + +SECURITY LABEL [ FOR provider ] ON +{ + TABLE object_name | + COLUMN table_name.column_name | + AGGREGATE aggregate_name ( aggregate_signature ) | + DATABASE object_name | + DOMAIN object_name | + EVENT TRIGGER object_name | + FOREIGN TABLE object_name + FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + LARGE OBJECT large_object_oid | + MATERIALIZED VIEW object_name | + [ PROCEDURAL ] LANGUAGE object_name | + ROLE object_name | + SCHEMA object_name | + SEQUENCE object_name | + TABLESPACE object_name | + TYPE object_name | + VIEW object_name +} IS 'label' + +其中aggregate_signature为: + +* | +[ argmode ] [ argname ] argtype [ , ... ] | +[ [ argmode ] [ argname ] argtype [ , ... ] ] ORDER BY [ argmode ] [ argname ] argtype [ , ... ] + + + + + 描述 + + + SECURITY LABEL为数据库对象设置安全标签。一个给定的数据库 + 对象可以关联任意数量的安全标签,每个标签提供者对应一个。标签提供者是使用 + 函数register_label_provider注册自身的可加载模块。 + + + + + register_label_provider不是一个 SQL 函数;它只能从加载到 + 后端的 C 代码中调用。 + + + + + 标签提供者决定给定标签是否有效,以及是否允许将该标签赋给给定对象。给定标 + 签的含义同样由标签提供者自行决定。PostgreSQL不 + 限制标签提供者是否解释安全标签,也不限制其如何解释;它仅提供一种存储安全 + 标签的机制。实际上,此功能旨在支持与基于标签的强制访问控制(MAC)系统 + (例如SELinux)集成。这类系统基于对象标签,而不 + 是基于用户和组等传统的自主访问控制(DAC)概念,做出所有访问控制决策。 + + + + + 参数 + + + + object_name + table_name.column_name + aggregate_name + function_name + + 要加上安全标签的对象名称。表、聚合、域、外部表、函数、序列、类型和视图的名称可以带模式限定。 + + + + + provider + + + 要与该标签关联的标签提供者名称。指定的提供者必须已加载,并且必须同意所 + 提议的标签设置操作。若只加载了一个提供者,则为简洁起见可以省略其名称。 + + + + + + argmode + + + + 函数或聚合函数参数的模式:INOUT、 + INOUTVARIADIC。如果省略,默认值是 + IN。注意SECURITY LABEL实际上并不关 + 心OUT参数,因为确定函数身份只需要输入参数。因此,列出 + ININOUTVARIADIC参数就足够了。 + + + + + + argname + + + + 函数或聚合函数参数的名称。注意SECURITY LABEL + 实际上并不关心参数名称,因为确定函数身份只需要参数数据类型。 + + + + + + argtype + + + + 函数或聚合函数参数的数据类型。 + + + + + + large_object_oid + + + 大对象的 OID。 + + + + + + PROCEDURAL + + + + 这是一个噪声词。 + + + + + + label + + 新的安全标签,写成字符串字面量;也可以使用 NULL 来删除安全标签。 + + + + + + + 示例 + + 下面的示例展示了如何更改表的安全标签。 +SECURITY LABEL FOR selinux ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0'; + + + + + 兼容性 + + 在 SQL 标准中没有SECURITY LABEL命令。 + + + + + 另见 + + + src/test/modules/dummy_seclabel + + + diff --git a/zh/9.6/ref/select.sgml b/zh/9.6/ref/select.sgml new file mode 100644 index 00000000..28a02b51 --- /dev/null +++ b/zh/9.6/ref/select.sgml @@ -0,0 +1,1711 @@ + + + + + SELECT + + + + TABLE command + + + + WITH + in SELECT + + + + SELECT + 7 + SQL - 语言语句 + + + + SELECT + TABLE + WITH + 从表或视图中检索行 + + + + +[ WITH [ RECURSIVE ] with_query [, ...] ] +SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] + [ * | expression [ [ AS ] output_name ] [, ...] ] + [ FROM from_item [, ...] ] + [ WHERE condition ] + [ GROUP BY grouping_element [, ...] ] + [ HAVING condition ] + [ WINDOW window_name AS ( window_definition ) [, ...] ] + [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ] + [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] + [ LIMIT { count | ALL } ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] + [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] + +其中from_item为以下之一: + + [ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] + [ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ] + [ LATERAL ] ( select ) [ AS ] alias [ ( column_alias [, ...] ) ] + with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] + [ LATERAL ] function_name ( [ argument [, ...] ] ) + [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] + [ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] ) + [ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] ) + [ LATERAL ] ROWS FROM( function_name ( [ argument [, ...] ] ) [ AS ( column_definition [, ...] ) ] [, ...] ) + [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] + from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ] + +grouping_element为以下之一: + + ( ) + expression + ( expression [, ...] ) + ROLLUP ( { expression | ( expression [, ...] ) } [, ...] ) + CUBE ( { expression | ( expression [, ...] ) } [, ...] ) + GROUPING SETS ( grouping_element [, ...] ) + +with_query为: + + with_query_name [ ( column_name [, ...] ) ] AS ( select | values | insert | update | delete ) + +TABLE [ ONLY ] table_name [ * ] + + + + + + 描述 + + + SELECT 从零个或多个表中检索行。SELECT 的一般处理流程如下: + + + + + WITH列表中的所有查询都会被计算。 + 这些实际上充当临时表,可以在FROM列表中引用。 + 在FROM列表中多次引用的WITH查询只会计算一次。 + (参见下面的。) + + + + + + + 所有FROM列表中的元素都会被计算。 + (FROM列表中的每个元素都是一个真实或虚拟表。) + 如果在FROM列表中指定了多个元素,则它们会被交叉连接在一起。 + (参见下面的。) + + + + + + + 如果指定了WHERE子句,则不满足条件的所有行将从输出中删除。 + (请参见下面的。) + + + + + + 如果指定了GROUP BY子句, + 或者存在聚合函数调用, + 输出将被组合成在一个或多个值上匹配的行组, + 并计算聚合函数的结果。 + 如果存在HAVING子句, + 它将消除不满足给定条件的组。(参见 + 和 + 。) + + + + + + + + 实际输出行是使用每个选定行或行组的SELECT输出表达式计算的。 + (参见下面的。) + + + + + + SELECT DISTINCT消除结果中的重复行。 + SELECT DISTINCT ON会消除在所有指定表达式上匹配的行,只保留每组中的第一行。 + SELECT ALL(默认)将返回所有候选行,包括重复行。 + (参见下面的。) + + + + + + 使用操作符UNIONINTERSECTEXCEPT, + 可以将多个SELECT语句的输出合并成一个结果集。 + UNION操作符返回在一个或两个结果集中的所有行。 + INTERSECT操作符返回同时出现在两个结果集中的所有行。 + EXCEPT操作符返回在第一个结果集中但不在第二个结果集中的行。 + 在这三种情况下,除非指定ALL,否则将消除重复行。 + 还可以添加噪声词DISTINCT,以明确指定去重。 + 请注意,这里的默认行为是DISTINCT,即使SELECT本身的默认行为是ALL。 + (请参见下面的。) + + + + + + + 如果指定了ORDER BY子句,则返回的行按指定顺序排序。 + 如果没有给出ORDER BY,则按系统认为最快的顺序返回行。 + (参见下面的。) + + + + + + + 如果指定了LIMIT(或FETCH FIRST)或OFFSET子句, + SELECT语句只返回结果行的子集。(参见下面的。) + + + + + + + 如果指定了FOR UPDATE、FOR NO KEY UPDATEFOR SHARE + 或FOR KEY SHARE, + SELECT语句将选定的行锁定,防止并发更新。(参见下面的。) + + + + + + + 你必须拥有SELECT命令中使用到的每一列上的 + SELECT权限。FOR NO KEY UPDATE、 + FOR UPDATE、 + FOR SHARE或者FOR KEY SHARE + 还要求具备UPDATE权限(对这样选中的每个表至少一列)。 + + + + + 参数 + + + <literal>WITH</literal> 子句 + + + WITH子句允许你指定一个或多个可在主查询中按名称 + 引用的子查询。这些子查询在主查询执行期间实际上充当临时表或 + 视图。每个子查询都可以是SELECT、 + TABLEVALUES、 + INSERT、 + UPDATE、 + DELETE语句。在WITH中编写 + 数据修改语句(INSERT、 + UPDATE、 + DELETE)时,通常要包括一个 + RETURNING子句。被主查询读取并构成临时表的是 + RETURNING的输出,而不是该语句所修改的 + 底层表。如果省略RETURNING,该语句仍会执行,但不会 + 产生输出,因此主查询无法把它当作表来引用。 + + + + 对于每个WITH查询,都必须指定一个名称(不带模式限定)。 + 还可以指定一个列名列表;如果省略,则列名将从子查询中推导出来。 + + + + 如果指定了RECURSIVE,则允许一个 + SELECT子查询使用名称引用自身。 + 这样一个子查询的形式必须是 + +non_recursive_term UNION [ ALL | DISTINCT ] recursive_term + + 其中递归自引用必须出现在UNION的右手边。每个 + 查询中只允许一个递归自引用。不支持递归数据修改语句,但是 + 可以在一个数据查询语句中使用一个递归 + SELECT查询的结果。示例可见 + 。 + + + + RECURSIVE的另一个效果是 + WITH查询不需要被排序:一个查询可以引用另一个 + 在列表中比它靠后的查询(不过,循环引用或者互递归没有实现)。 + 如果没有RECURSIVEWITH + 查询只能引用在WITH列表中位置更前面的兄弟 + WITH查询。 + + + + WITH查询的一个关键特性是,每次执行主查询时,它们都只会求值一次,即使主查询多次引用它们。特别是,无论主查询是否读取了它们的全部输出或任何输出,数据修改语句都保证执行一次且仅执行一次。 + + + + 当WITH子句中有多个查询时,RECURSIVE应只编写一次,紧跟在WITH之后。 + 它适用于WITH子句中的所有查询,尽管它对不使用递归或前向引用的查询没有影响。 + + + + 主查询和WITH查询(概念上)都在同一时间执行。 + 这意味着,除了读取其RETURNING输出之外,查询的其 + 他部分都看不到WITH中数据修改语句的效果。如果两个这 + 样的数据修改语句试图修改同一行,结果未指定。 + + + + 更多信息请见。 + + + + + <literal>FROM</literal> 子句 + + + FROM子句为SELECT + 指定一个或者更多源表。如果指定了多个源表,结果将是所有源表的 + 笛卡尔积(交叉连接)。但是通常会增加限定条件(通过 + WHERE)来把返回的行限制为该笛卡尔积的一个小子集。 + + + + 该 FROM 子句可以包含以下元素: + + + + table_name + + + + 要扫描的现有表或视图的名称(可选模式限定符)。如果在表名之前指定ONLY, + 则仅扫描该表。如果未指定ONLY,则扫描该表及其所有后代表(如果有)。 + 可选地,可以在表名后指定*,以明确指示包括后代表。 + + + + + + alias + + + + 包含该别名的FROM项的替代名称。别名可用于简写, + 或者消除自连接(同一张表被扫描多次)中的歧义。提供别名后,它会 + 完全隐藏表或函数的实际名称;例如给定FROM foo AS f, + SELECT的其余部分必须把这个FROM + 项写成f而不是foo。如果写了别名, + 还可以写列别名列表,为该表的一个或多个列提供替代名称。 + + + + + + TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] + + + + 跟在table_name之后的 + TABLESAMPLE子句表示,应使用指定的 + sampling_method从该表中 + 取回行的一个子集。这种抽样先于任何其他过滤条件(例如 + WHERE子句)的应用。标准 + PostgreSQL发行版包含两种抽样方法, + 即BERNOULLISYSTEM;其他抽样 + 方法可以通过扩展安装到数据库中。 + + + + BERNOULLISYSTEM抽样方法 + 各接受一个argument, + 表示要抽样的表的比例,以 0 到 100 之间的百分比表示。 + 该参数可以是任何返回real的表达式。 + (其他抽样方法可能接受更多或不同的参数。) + 这两种方法都会返回表的一个随机样本,其中大约包含表中指定百分比的 + 行。BERNOULLI方法扫描整个表,并 + 以指定概率独立选择或忽略单个行。 + SYSTEM方法进行块级抽样, + 每个块有指定的选择机会;返回每个选定块中的所有行。 + 当指定小的抽样百分比时,SYSTEM方法比 + BERNOULLI方法快得多,但由于聚类效应, + 它返回的表样本随机性可能稍差一些。 + + + + 可选的REPEATABLE子句指定一个 + seed数字或表达式,用于在 + 抽样方法内部生成随机数。种子值可以是任意非空浮点值。如果两个查询 + 指定了相同的种子和argument值, + 且该表在期间未被修改,它们会选出相同的表样本;但不同的种子值通常会 + 产生不同的样本。如果未给出REPEATABLE,则每次查询 + 都会基于系统生成的种子选取新的随机样本。注意,某些附加抽样方法并不 + 接受REPEATABLE,因此每次使用时都会生成新的样本。 + + + + + + select + + + 子SELECT可以出现在FROM子句中, + 它的作用就像在这个SELECT命令的执行期间创建了一个 + 临时表。注意,子SELECT必须用圆括号括起来,并且 + 必须为其提供别名。这里也可以使用 + 命令。 + + + + + + with_query_name + + + + WITH查询通过写出它的名称来引用,就像该查询名是表名 + 一样。(事实上,对于主查询而言,WITH查询会遮蔽任何同名的 + 真实表;如有必要,可以通过模式限定表名来引用该同名真实表。) + 也可以像对待表一样为它提供别名。 + + + + + + function_name + + + 函数调用可以出现在FROM子句中。(这对于返回结果集的函数尤其有用,但任何函数都可以使用。)其效果就像在这条SELECT命令执行期间,将函数的输出创建成了一张临时表。在函数调用后加上可选的WITH ORDINALITY子句时,会在函数的所有输出列之后追加一列,为每一行编号。 + + + + 可以像对表一样提供别名。如果写了别名,还可以写一个列别名列表,为函数复合返回类型中的一个或多个属性提供替代名称,其中也包括ORDINALITY所添加的列(如果有)。 + + + + 多个函数调用可以通过用ROWS FROM( ... )括起来, + 合并成单个FROM子句项。这样一个项的输出会先拼接每个 + 函数的第一行,再拼接每个函数的第二行,依此类推。如果某些函数产生 + 的行数少于其他函数,则会用空值替代缺失的数据,以确保返回的总行数 + 始终与产生最多行的那个函数相同。 + + + + 如果函数被定义为返回record数据类型,则必须给出别名 + 或关键字AS,后面跟一个如下形式的列定义列表: + ( column_name + data_type , ... )。 + 列定义列表必须与该函数实际返回的列数和列类型相匹配。 + + + + 当使用ROWS FROM( ... )语法时,如果其中某个函数需要 + 列定义列表,最好将该列定义列表放在ROWS FROM( ... ) + 内部、紧跟在函数调用之后。只有在只有一个函数且没有 + WITH ORDINALITY子句时,才能把列定义列表放在 + ROWS FROM( ... )结构之后。 + + + + 要在列定义列表中使用ORDINALITY,必须使用ROWS FROM( ... )语法, + 并将列定义列表放在ROWS FROM( ... )内部。 + + + + + + join_type + + + 以下之一: + + + [ INNER ] JOIN + + + LEFT [ OUTER ] JOIN + + + RIGHT [ OUTER ] JOIN + + + FULL [ OUTER ] JOIN + + + CROSS JOIN + + + + 对于 INNEROUTER 连接类型,必须指定连接条件,即以下三者之一: + NATURALON join_conditionUSING (join_column [, ...])。其含义见下文。对于 CROSS JOIN,这些子句都不能出现。 + + + + JOIN子句组合两个FROM项。为方便起见,我们将它们称为,但实际上它们可以是任意类型的FROM项。必要时可使用圆括号来确定嵌套顺序。如果没有圆括号,JOIN会从左到右嵌套。无论如何,JOIN的结合都比用于分隔FROM列表项的逗号更紧密。 + + + + CROSS JOININNER JOIN产生简单的笛卡尔积,与在FROM顶层列出这两个表得到的结果相同,但会受到连接条件(如果有)的限制。CROSS JOIN等价于INNER JOIN ON (TRUE),也就是说,没有行会被条件过滤掉。这些连接类型只是提供了一种方便的记法,因为它们所做的一切都可以用普通的FROMWHERE完成。 + + + LEFT OUTER JOIN返回已限定笛卡尔积中的所有行(即,通过其连接条件的所有组合行),外加左侧表中每一行的一个副本;对于这些行,不存在通过连接条件的右侧行。这样的左侧行会通过在右侧列中插入空值而扩展到连接表的完整宽度。注意,在决定哪些行有匹配项时,只考虑JOIN子句自身的条件;外层条件是在之后应用的。 + + + 相反,RIGHT OUTER JOIN返回所有连接后的行,再加上每个未匹配右侧行对应的一行 + (左侧用空值扩展)。这只是一种记法上的便利,因为你可以通过交换左右表 + 将其改写成LEFT + OUTER JOIN。 + + + FULL OUTER JOIN返回所有连接后的行,再加上每个未匹配的左侧行(右侧用空值扩展),以及每个未匹配的右侧行(左侧用空值扩展)。 + + + + + ON join_condition + + join_condition 是一个表达式,其结果类型为 boolean(类似于 WHERE 子句),用于指定连接中哪些行被视为匹配。 + + + + + USING ( join_column [, ...] ) + + + 一个形如USING ( a, b, ... )的子句是ON left_table.a = right_table.a AND + left_table.b = right_table.b ...的简写。此外, + USING意味着只有每对等价列中的一个会包含在连接输出中,而不是两者都包含。 + + + + + + NATURAL + + + + NATURAL是一个简写,表示一个包含两个表中所有具有相同名称的列的USING列表。 + 如果没有共同的列名,NATURAL等同于ON TRUE。 + + + + + + LATERAL + + + + LATERAL关键字可以在子SELECT FROM项之前出现。 + 这允许子SELECT引用在FROM列表中出现在其前面的FROM项的列。 + (没有LATERAL,每个子SELECT都是独立评估的,因此不能交叉引用任何其他FROM项。) + + + LATERAL也可以放在函数调用形式的FROM项之前,但在这种情况下它只是一个噪声词,因为函数表达式无论如何都可以引用更早出现的FROM项。 + + + LATERAL项既可以出现在FROM列表的顶层, + 也可以出现在JOIN树中。在后一种情况下,它还可以引用 + 它所处右侧JOIN左边的任何项。 + + + + 当FROM项包含LATERAL交叉引用时,求值过程如下: + 对于提供被交叉引用列的FROM项的每一行,或者对于提供这些 + 列的多个FROM项的一组行,都会使用该行或行集中的列值来 + 计算LATERAL项。得到的行随后照常与生成它们的那些行 + 连接。这个过程会针对列源表中的每一行或每一组行重复执行。 + + + + 列源表必须通过INNERLEFT连接到 + LATERAL项,否则就无法得到一个定义良好的行集合,用来 + 计算该LATERAL项的每一组结果行。因此,尽管像 + X RIGHT JOIN LATERAL Y + 这样的结构在语法上有效,但实际上并不允许Y + 引用X。 + + + + + + + + + <literal>WHERE</literal> 子句 + + + 可选的WHERE子句的形式 + +WHERE condition + + 其中condition + 是任一计算得到boolean类型结果的表达式。任何不满足 + 这个条件的行都会从输出中被消除。如果用一行的实际值替换其中的 + 变量引用后,该表达式返回真,则该行符合条件。 + + + + + <literal>GROUP BY</literal> 子句 + + + 可选的 GROUP BY 子句的一般形式为 + +GROUP BY grouping_element [, ...] + + + + + GROUP BY会把所有在分组表达式上具有相同值的已选中 + 行压缩成单独一行。用于 + grouping_element中的 + expression可以是输入列名、输出列 + (SELECT列表项)的名称或序号或者由输入列 + 值构成的任意表达式。在出现歧义时,GROUP BY名称 + 将被解释为输入列名而不是输出列名。 + + + + 如果GROUPING SETSROLLUP或 + CUBE中的任何一个作为分组元素出现,那么整个 + GROUP BY子句就定义了若干个相互独立的 + 分组集。其效果等价于在多个子查询之间构造一 + 个UNION ALL,各个子查询分别以各自的分组集作为它们 + 的GROUP BY子句。 关于分组集处理的更多细节,见。 + + + + 聚合函数(如果使用)会在组成每一个分组的所有行上进行计算,从而为每 + 一个分组产生一个单独的值(如果有聚合函数但是没有 + GROUP BY子句,则查询会被当成是由所有选中行构成 + 的一个单一分组)。传递给每一个聚合函数的行集合可以通过在聚合函数调 + 用附加一个FILTER子句来进一步过滤,详见 + 。当存在一个 + FILTER子句时,只有那些匹配它的行才会被包括在该聚 + 集函数的输入中。 + + + + 当存在GROUP BY子句或者任何聚合函数时, + SELECT列表表达式不能引用非分组列,除非它 + 出现在聚合函数中,或者该非分组列函数依赖于分组列,因为这样做会导致返回 + 非分组列的值时会有多种可能的值。如果分组列是包含非分组列的表的主键( + 或者主键的子集),则存在函数依赖。 + + + + 记住所有的聚合函数都是在HAVING子句或者 + SELECT列表中的任何标量表达式之前被计算。 + 这意味着CASE表达式不能被用来跳过聚合表达式的 + 计算,见。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHAREFOR KEY SHARE不能和 + GROUP BY一起指定。 + + + + + <literal>HAVING</literal> 子句 + + + 可选的HAVING子句的形式 + +HAVING condition + + 其中condition与 + WHERE子句中指定的条件相同。 + + + + HAVING消除不满足该条件的分组行。 + HAVINGWHERE不同: + WHERE会在应用GROUP + BY之前过滤个体行,而HAVING过滤由 + GROUP BY创建的分组行。 + condition中引用 + 的每一列都必须无歧义地引用某个分组列,除非该引用出现在聚合 + 函数中,或者该非分组列函数依赖于分组列。 + + + + 即使没有GROUP BY子句,HAVING + 的存在也会把一个查询转变成一个分组查询。这和查询中包含聚合函数但没有 + GROUP BY子句时的情况相同。所有被选择的行都被认为是一个 + 单一分组,并且SELECT列表和 + HAVING子句只能从聚合函数内部引用表列。如果该 + HAVING条件为真,这样的查询将输出单独一行; + 否则不返回行。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHAREFOR KEY SHARE不能与 + HAVING一起指定。 + + + + + <literal>WINDOW</literal> 子句 + + + 可选的 WINDOW 子句的一般形式为: +WINDOW window_name AS ( window_definition ) [, ...] + + + 其中,window_name 是一个名称,可从 OVER 子句或后续窗口定义中引用,而 window_definition 的定义为: +[ existing_window_name ] +[ PARTITION BY expression [, ...] ] +[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] +[ frame_clause ] + + + + + 如果指定了一个existing_window_name, + 它必须引用WINDOW列表中一个更早出现的项。新窗口将从 + 该项中复制它的分区子句,以及排序子句(如果有)。在这种情况下,新窗口 + 不能指定它自己的PARTITION BY子句,并且只有在被复制 + 的窗口没有排序子句时,才可以指定 + ORDER BY。新窗口总是使用自己的帧子句,而被复制的 + 窗口不得指定帧子句。 + + + + PARTITION BY列表元素的解释以 + 元素的方式 + 进行,不过它们总是简单表达式并且绝不能是输出列的名称或编号。另一个区 + 别是这些表达式可以包含聚合函数调用,而这在常规GROUP BY + 子句中是不被允许的。它们被允许的原因是窗口是出现在分组和聚合之后的。 + + + + 类似地,ORDER BY列表元素的解释也以语句级 + 元素的方式进行, + 不过该表达式总是被当做简单表达式并且绝不会是输出列的名称或编号。 + + + + 可选的 frame_clause 定义窗口帧,供依赖于帧的窗口函数使用(并非所有窗口函数都依赖帧)。窗口帧是与查询中每一行相关联的一组行;查询中的这一行称为当前行frame_clause 可以是以下之一: + + +{ RANGE | ROWS } frame_start +{ RANGE | ROWS } BETWEEN frame_start AND frame_end + + + 其中,frame_start 和 frame_end 可以是以下之一: + + +UNBOUNDED PRECEDING +value PRECEDING +CURRENT ROW +value FOLLOWING +UNBOUNDED FOLLOWING + + + 如果省略 frame_end,则默认为 CURRENT ROW。其限制是:frame_start 不能是 UNBOUNDED FOLLOWING, + frame_end 不能是 UNBOUNDED PRECEDING,并且 frame_end 在上面列表中的位置不能早于 frame_start 的位置 — 例如,RANGE BETWEEN CURRENT ROW AND value + PRECEDING 是不允许的。 + + + + 默认的帧选项是RANGE UNBOUNDED PRECEDING,它等同于RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;它将帧设置为从分区起点到当前行最后一个同等行的所有行(同等行是ORDER BY认为与当前行等价的行;如果没有ORDER BY,则所有行都属于同等行)。一般而言,UNBOUNDED PRECEDING表示帧从分区的第一行开始,而UNBOUNDED FOLLOWING同样表示帧在分区的最后一行结束(无论采用RANGE还是ROWS模式)。在ROWS模式下,CURRENT ROW表示帧从当前行开始或在当前行结束;但在RANGE模式下,它表示帧从ORDER BY排序中当前行的第一个同等行开始,或在最后一个同等行结束。目前,value PRECEDINGvalue FOLLOWING仅允许用于ROWS模式。它们表示帧从当前行之前或之后相应行数的那一行开始或结束。value必须是不包含任何变量、聚合函数或窗口函数的整数表达式。该值不能为空值或负数,但可以为零,此时选择当前行本身。 + + + + 请注意,如果ORDER BY排序没有将行排成唯一顺序,ROWS选项可能产生不可预测的结果。RANGE选项旨在确保ORDER BY排序中的同等行得到相同处理;所有同等行都会位于同一帧中。 + + + + WINDOW子句的目的是指定出现在查询的 + 或 + 中的 + 窗口函数的行为。这些函数可以在它们的 + OVER子句中用名称引用WINDOW + 子句项。不过,WINDOW子句项不是必须被引用。 + 如果在查询中没有用到它,它会被简单地忽略。可以使用根本没有任何 + WINDOW子句的窗口函数,因为窗口函数调用可 + 以直接在其OVER子句中指定它的窗口定义。不过,当多 + 个窗口函数都需要相同的窗口定义时, + WINDOW子句能够减少输入量。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHAREFOR KEY SHARE不能和 + WINDOW一起被指定。 + + + + 窗口函数的详细描述在 + 、 + 以及 + 中。 + + + + + <command>SELECT</command> 列表 + + + SELECT列表(位于关键词 + SELECTFROM之间)指定构成 + SELECT语句输出行的表达式。这些表达式 + 可以(并且通常确实会)引用FROM子句中计算得到的列。 + + + + 正如在表中一样,SELECT的每一个输出列都有一个名称。 + 在一个简单的SELECT中,这个名称只是被用来标记要显 + 示的列,但是当SELECT是一个大型查询的一个子查询时,大型查询 + 会把该名称看做子查询产生的虚表的列名。要指定用于输出列的名称,在该列的表达式 + 后面写上 + AS output_name( + 你可以省略AS,但只能在期望的输出名称不匹配任何 + PostgreSQL关键词(见)时省略。为了避免和未来增加的关键词冲突, + 推荐总是写上AS或者用双引号引用输出名称)。如果你不指定列名, + PostgreSQL会自动选择一个名称。如果列的表达式 + 是一个简单的列引用,那么被选择的名称就和该列的名称相同。在使用函数或者类型名称 + 的更复杂的情况中,系统可能会生成诸如 + ?column?之类的名称。 + + + + 一个输出列的名称可以被用来在ORDER BY以及 + GROUP BY子句中引用该列的值,但是不能用于 + WHEREHAVING子句(在其中 + 必须写出表达式)。 + + + + 可以在输出列表中写*来取代表达式,它是被选中 + 行的所有列的一种简写方式。还可以写 + table_name.*,它 + 是只来自那个表的所有列的简写形式。在这些情况中无法用 + AS指定新的名称,输出行的名称将和表列的名称相同。 + + + + 按照 SQL 标准,输出列表中的表达式应当在应用DISTINCT、 + ORDER BYLIMIT之前计算。对于 + DISTINCT来说,这显然是必要的,否则就不清楚究竟要对哪 + 些值去重。不过,在很多情况下,如果先执行ORDER BY和 + LIMIT再计算输出表达式会更方便,特别是当输出列表中包含 + 可变函数或代价高昂的函数时。那样一来,函数求值的顺序更符合直觉,也不 + 会去计算那些根本不会出现在输出中的行。只要输出表达式没有被 + DISTINCTORDER BY或 + GROUP BY引用,PostgreSQL + 实际上就会在排序和限制行数之后再计算它们。(反例是 + SELECT f(x) FROM tab ORDER BY 1,它显然必须在排序前 + 计算f(x)。)包含集合返回函数的输出表达式则实际上会在 + 排序之后、限制之前计算,这样LIMIT才能截断该集合返回函数 + 产生的输出。 + + + + + + 9.6 版本之前的PostgreSQL不对执行输出表达式、排序、限制行数的时间顺序做任何保证,那将取决于被选中的查询计划的形式。 + + + + + + <literal>DISTINCT</literal> 子句 + + + 如果指定了SELECT DISTINCT,所有重复的行会被从结果 + 集中移除(为每一组重复的行保留一行)。SELECT ALL则 + 指定相反的行为:所有行都会被保留,这也是默认情况。 + + + + SELECT DISTINCT ON ( expression [, ...] ) + 只保留在给定表达式上计算相等的行集合中的第一行。 + DISTINCT ON表达式使用和 + ORDER BY相同的规则(见上文)解释。注意,除非用 + ORDER BY来确保所期望的行出现在第一位,每一个集 + 合的第一行是不可预测的。例如: + +SELECT DISTINCT ON (location) location, time, report + FROM weather_reports + ORDER BY location, time DESC; + + 为每个地点检索最近的天气报告。但是如果我们不使用 + ORDER BY来强制对每个地点的时间值进行降序排序, + 我们为每个地点得到的报告的时间可能是无法预测的。 + + + + DISTINCT ON表达式必须匹配最左边的 + ORDER BY表达式。ORDER BY子句通常 + 将包含额外的表达式,这些额外的表达式用于决定在每一个 + DISTINCT ON分组内行的优先级。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHAREFOR KEY SHARE不能和 + DISTINCT一起使用。 + + + + + <literal>UNION</literal> 子句 + + + UNION子句的一般形式如下: + +select_statement UNION [ ALL | DISTINCT ] select_statement +select_statement + 是任何没有ORDER BYLIMIT、 + FOR NO KEY UPDATEFOR UPDATE、 + FOR SHAREFOR KEY SHARE子句的 + SELECT语句。(如果某个子表达式被圆括号括起来, + ORDER BYLIMIT可以附加在它上面。 + 如果没有圆括号,这些子句会被视为作用于UNION的结果, + 而不是作用于其右侧输入表达式。) + + + + UNION操作符计算相关 + SELECT语句所返回的行的并集。如果一行 + 至少出现在两个结果集中的一个内,它就会在并集中。作为 + UNION两个操作数的 + SELECT语句必须产生相同数量的列并且 + 对应位置上的列必须具有兼容的数据类型。 + + + + UNION的结果不会包含重复行,除非指定了 + ALL选项。ALL会阻止消除重复(因此, + UNION ALL通常显著地快于UNION, + 尽量使用ALL)。也可以写上DISTINCT, + 以显式指定默认的去重行为。 + + + + 除非用圆括号指定计算顺序, + 同一个SELECT语句中的多个 + UNION操作符会从左至右计算。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHARE和 + FOR KEY SHARE不能用于UNION结果或者 + UNION的任何输入。 + + + + + <literal>INTERSECT</literal> 子句 + + + INTERSECT子句的一般形式如下: + +select_statement INTERSECT [ ALL | DISTINCT ] select_statement +select_statement + 是任何没有ORDER + BYLIMITFOR NO KEY UPDATEFOR UPDATE、 + FOR SHARE以及FOR KEY SHARE子句的 + SELECT语句。 + + + + INTERSECT操作符计算相关 + SELECT语句返回的行的交集。如果 + 一行同时出现在两个结果集中,它就在交集中。 + + + + INTERSECT的结果不会包含重复行,除非指定了 + ALL选项。如果有ALL,一个在左表中有 + m次重复并且在右表中有n + 次重复的行将会在结果中出现 + min(m,n) 次。 + 也可以写上DISTINCT,以显式指定默认的去重行为。 + + + + 除非用圆括号指定计算顺序, + 同一个SELECT语句中的多个 + INTERSECT操作符会从左至右计算。 + INTERSECT的优先级比 + UNION更高。也就是说, + A UNION B INTERSECT + C将被读成A UNION (B INTERSECT + C)。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHARE和 + FOR KEY SHARE不能用于INTERSECT结果或者 + INTERSECT的任何输入。 + + + + + <literal>EXCEPT</literal> 子句 + + + EXCEPT子句的一般形式如下: + +select_statement EXCEPT [ ALL | DISTINCT ] select_statement +select_statement + 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATE、 + FOR SHARE以及FOR KEY SHARE子句的 + SELECT语句。 + + + + EXCEPT操作符计算位于左侧 + SELECT语句的结果中但不在右侧语句结果中的行集合。 + + + + EXCEPT的结果不会包含重复行,除非指定了 + ALL选项。如果有ALL,一个在左表中有 + m次重复并且在右表中有 + n次重复的行将会在结果集中出现 + max(m-n,0) 次。 + 也可以写上DISTINCT,以显式指定默认的去重行为。 + + + + 除非用圆括号指定计算顺序, + 同一个SELECT语句中的多个 + EXCEPT操作符会从左至右计算。 + EXCEPT的优先级与 + UNION相同。 + + + + 当前,FOR NO KEY UPDATEFOR UPDATE、 + FOR SHARE和 + FOR KEY SHARE不能用于EXCEPT结果或者 + EXCEPT的任何输入。 + + + + + <literal>ORDER BY</literal> 子句 + + + 可选的ORDER BY子句的形式如下: + +ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] + + ORDER BY子句导致结果行被按照指定的表达式排序。 + 如果两行按照最左边的表达式是相等的,则会根据下一个表达式比较它们, + 依次类推。如果按照所有指定的表达式它们都是相等的,则它们被返回的 + 顺序取决于实现。 + + + + 每一个expression + 可以是输出列(SELECT列表项)的名称或 + 者序号,它也可以是由输入列值构成的任意表达式。 + + + + 序号指的是输出列的顺序(从左至右)位置。这种特性可以为不具有唯一 + 名称的列定义一个顺序。这不是绝对必要的,因为总是可以使用 + AS子句为输出列赋予一个名称。 + + + + 也可以在ORDER BY子句中使用任意表达式,包括没 + 有出现在SELECT输出列表中的列。因此, + 下面的语句是合法的: + +SELECT name FROM distributors ORDER BY code; + + 这种特性的一个限制是一个应用在UNION、 + INTERSECTEXCEPT子句结果上的 + ORDER BY只能指定输出列名称或序号,但不能指定表达式。 + + + + 如果一个ORDER BY表达式是一个既匹配输出列名称又匹配 + 输入列名称的简单名称,ORDER BY将把它解读成输出列名 + 称。这与在同样情况下GROUP BY会做出的选择相反。这种 + 不一致是为了与 SQL 标准兼容。 + + + + 可以在ORDER BY子句中任一表达式之后附加关键字 + ASC(升序)或DESC(降序)。如果没有指定, + ASC被假定为默认值。或者,可以在USING + 子句中指定一个特定的排序操作符名称。一个排序操作符必须是某个 + B-树操作符族的小于或者大于成员。ASC通常等价于 + USING <DESC通常等价于 + USING >(但是一种用户定义数据类型的创建者可以 + 准确地定义默认排序顺序是什么,并且它可能会对应于其他名称的操作符)。 + + + + 如果指定NULLS LAST,空值会排在非空值之后;如果指定 + NULLS FIRST,空值会排在非空值之前。如果都没有指定, + 在指定或者隐含ASC时的默认行为是NULLS LAST, + 而指定或者隐含DESC时的默认行为是 + NULLS FIRST(因此,默认行为是空值大于非空值)。 + 当指定USING时,默认的空值顺序取决于该操作符是否为 + 小于或者大于操作符。 + + + + 注意顺序选项只应用到它们所跟随的表达式上。例如 + ORDER BY x, y DESC和 + ORDER BY x DESC, y DESC是不同的。 + + + + 字符串数据会被根据引用到被排序列上的排序规则排序。根据需要可以通过在 + expression中包括一个 + COLLATE子句来覆盖,例如 + ORDER BY mycolumn COLLATE "en_US"。更多信息请见 + 和 + 。 + + + + + <literal>LIMIT</literal> 子句 + + + 该 LIMIT 子句由两个独立的子句组成: + +LIMIT { count | ALL } +OFFSET start + + count 指定最多返回多少行,而 start 指定开始返回行之前要跳过的行数。如果两者都指定,则先跳过 start 行,然后开始计数并返回 count 行。 + + + 如果 count 表达式计算结果为 NULL,则视为 LIMIT ALL,即不限制。如果 start 计算结果为 NULL,则等同于 OFFSET 0。 + + + + SQL:2008 引入了另一种实现相同结果的语法,PostgreSQL 也支持它。其形式如下: + +OFFSET start { ROW | ROWS } +FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY + + 在这种语法中,标准要求 startcount 的值必须是字面常量、参数或变量名;作为 PostgreSQL 的扩展,也允许其他表达式,但通常需要用圆括号将其括起以避免歧义。如果 countFETCH 子句中被省略,则默认为 1。 + ROWROWS 以及 FIRSTNEXT 都是噪声词,不会影响这些子句的效果。按照标准,OFFSET 子句必须出现在 FETCH 子句之前(如果两者都存在);而 PostgreSQL 更宽松,允许任意顺序。 + + + + 在使用LIMIT时,用一个ORDER BY子句把 + 结果行约束到一个唯一顺序是个好办法。否则你将得到该查询结果行的 + 一个不可预测的子集 — 你可能要求从第 10 到第 20 行,但是在 + 什么顺序下的第 10 到第 20 呢?除非指定ORDER BY,你 + 是不知道顺序的。 + + + + 查询规划器在生成一个查询计划时会考虑LIMIT,因此 + 根据你使用的LIMITOFFSET,你很可能 + 得到不同的计划(得到不同的行序)。所以,使用不同的 + LIMIT/OFFSET值来选择一个查询结果的 + 不同子集将会给出不一致的结果,除非你 + 用ORDER BY强制一种可预测的结果顺序。这不是一个 + 缺陷,它是 SQL 不承诺以任何特定顺序(除非使用 + ORDER BY来约束顺序)给出一个查询结果这一事实造 + 成的必然后果。 + + + + 如果没有一个ORDER BY来强制选择一个确定的子集, + 重复执行同样的LIMIT查询甚至可能会返回一个表中行 + 的不同子集。同样,这也不是一种缺陷,在这种情况下也无法 + 保证结果的确定性。 + + + + + 锁定子句 + + + FOR UPDATEFOR NO KEY UPDATE、 + FOR SHAREFOR KEY SHARE + 是锁定子句,它们影响SELECT + 把行从表中取得时如何对它们加锁。 + + + 锁定子句的一般形式为: +FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] +其中,lock_strength可以是以下值之一: +UPDATE +NO KEY UPDATE +SHARE +KEY SHARE + + + + + 更多关于每一种行级锁模式的信息可见。 + + + + 为了防止该操作等待其他事务提交,可使用NOWAIT或 + SKIP LOCKED选项。使用NOWAIT时,如果 + 选中的行不能被立即锁定,该语句会直接报错而不是等待。使用 + SKIP LOCKED时,任何无法立即锁定的已选中行都会被跳过。 + 跳过已锁定行会提供数据的不一致视图,因此不适合一般用途的工作,但可用 + 于避免多个消费者访问类似队列表时的锁竞争。注意, + NOWAITSKIP LOCKED只适用于行级锁; + 所需的ROW SHARE表级锁仍会按常规方式取得(见)。 + 如果想要不等待的表级锁,你可以先使用带NOWAIT。 + + + + 如果在一个锁定子句中提到了特定的表,则只有来自于那些表的 + 行会被锁定,任何SELECT中用到的 + 其他表还是被简单地照常读取。一个没有表列表的锁定子句会影响 + 该语句中用到的所有表。如果一个锁定子句被应用到一个视图或者 + 子查询,它会影响在该视图或子查询中用到的所有表。不过,这些 + 子句不适用于主查询引用的WITH查询。如果你希望 + 在一个WITH查询中发生行锁定,应该在该 + WITH查询内指定一个锁定子句。 + + + + 如果有必要对不同的表指定不同的锁定行为,可以写多个锁定子句。 + 如果同一个表在多于一个锁定子句中被提到(或者被隐式的影响到), + 那么会按照所指定的最强的锁定行为来处理它。类似地,如果在任何 + 影响一个表的子句中指定了NOWAIT,就会按此行为来处理该表。否则如果 + SKIP LOCKED在任何影响该表的子句中被指定, + 该表就会被按此行为处理。 + + + + 如果返回的行无法清楚地与表中的各个行对应起来,就不能使用锁定子句。 + 例如,锁定子句不能与聚合一起使用。 + + + + 当一个锁定子句出现在一个SELECT查询的顶层时, + 被锁定的行正好就是该查询返回的行。在连接查询的情况下,被锁定 + 的行是那些对返回的连接行有贡献的行。此外,自该查询的快照起满足 + 查询条件的行将被锁定,如果它们在该快照后被更新并且不再满足 + 查询条件,它们将不会被返回。如果使用了LIMIT,只要 + 已经返回的行数满足了限制,锁定就会停止(但注意被 + OFFSET跳过的行将被锁定)。类似地,如果在一个游标 + 的查询中使用锁定子句,只有被该游标实际取出或者跳过的行才将被 + 锁定。 + + + + 当锁定子句出现在一个子SELECT中时,被锁定 + 行是那些该子查询返回给外层查询的行。这些被锁定的行的数量可能比 + 从子查询自身的角度看到的要少,因为来自外层查询的条件可能会被用 + 来优化子查询的执行。例如: + +SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5; + + 将只锁定具有col1 = 5的行(虽然在子查询中并没有写上 + 该条件)。 + + + + 较早的版本无法保持一个随后在保存点中被升级的锁。例如,下面这段代码: + +BEGIN; +SELECT * FROM mytable WHERE key = 1 FOR UPDATE; +SAVEPOINT s; +UPDATE mytable SET ... WHERE key = 1; +ROLLBACK TO s; + + 在执行ROLLBACK TO之后将无法保持 + FOR UPDATE锁。这个问题已在 9.3 版本中修复。 + + + + + + 一个运行在READ + COMMITTED事务隔离级别并且使用ORDER + BY和锁定子句的SELECT命令有可能返回无序的行。 + 这是因为ORDER BY会被首先应用。该命令对结果排序,但是可能 + 接着在尝试获得一行或多行上的锁时阻塞。一旦SELECT解除 + 阻塞,某些排序列值可能已经被修改,从而导致那些行变成无序的(尽管它们根 + 据原始列值是有序的)。根据需要,可以通过在子查询中放置 + FOR UPDATE/SHARE来解决这一问题,例如 + +SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1; + + 注意这将导致锁定mytable的所有行,而顶层的 + FOR UPDATE只会锁定实际被返回的行。这可能会导致显著的 + 性能差异,特别是把ORDER BYLIMIT或者其他 + 限制组合使用时。因此只有在并发更新排序列并且要求严格的排序结果时才推 + 荐使用这种技术。 + + + + 在REPEATABLE READSERIALIZABLE事务隔离级别下, + 这将导致串行化失败(SQLSTATE'40001'), + 因此在这些隔离级别下不可能接收到无序的行。 + + + + + + <literal>TABLE</literal> 命令 + + + 命令 + +TABLE name + + 等价于 + +SELECT * FROM name + + 它可以作为顶层命令使用,也可以作为复杂查询中一种节省空间的语法变体。只有 + WITH、 + UNIONINTERSECTEXCEPT、 + ORDER BYLIMITOFFSET、 + FETCH以及FOR锁定子句可以用于 + TABLE。不能使用WHERE子句和任何形式 + 的聚合。 + + + + + + 示例 + + + 连接表 films 和表 + distributors: + + +SELECT f.title, f.did, d.name, f.date_prod, f.kind + FROM distributors d, films f + WHERE f.did = d.did + + title | did | name | date_prod | kind +-------------------+-----+--------------+------------+---------- + The Third Man | 101 | British Lion | 1949-12-23 | Drama + The African Queen | 101 | British Lion | 1951-08-11 | Romantic + ... + + + + + 要对所有电影的len列求和并且用 + kind对结果分组: + + +SELECT kind, sum(len) AS total FROM films GROUP BY kind; + + kind | total +----------+------- + Action | 07:34 + Comedy | 02:58 + Drama | 14:28 + Musical | 06:42 + Romantic | 04:38 + + + + + 要对所有电影的len列求和、对结果按照 + kind分组并且显示总长小于 5 小时的分组: + + +SELECT kind, sum(len) AS total + FROM films + GROUP BY kind + HAVING sum(len) < interval '5 hours'; + + kind | total +----------+------- + Comedy | 02:58 + Romantic | 04:38 + + + + + 下面两个示例都是根据第二列(name)的内容来排序结果: + + +SELECT * FROM distributors ORDER BY name; +SELECT * FROM distributors ORDER BY 2; + + did | name +-----+------------------ + 109 | 20th Century Fox + 110 | Bavaria Atelier + 101 | British Lion + 107 | Columbia + 102 | Jean Luc Godard + 113 | Luso films + 104 | Mosfilm + 103 | Paramount + 106 | Toho + 105 | United Artists + 111 | Walt Disney + 112 | Warner Bros. + 108 | Westward + + + + + 接下来的示例展示了如何得到表distributors和 + actors的并集,把结果限制为那些在每个表中以 + 字母 W 开始的行。这里只需要不重复的行,因此省略了关键词 + ALL。 + + +distributors: actors: + did | name id | name +-----+-------------- ----+---------------- + 108 | Westward 1 | Woody Allen + 111 | Walt Disney 2 | Warren Beatty + 112 | Warner Bros. 3 | Walter Matthau + ... ... + +SELECT distributors.name + FROM distributors + WHERE distributors.name LIKE 'W%' +UNION +SELECT actors.name + FROM actors + WHERE actors.name LIKE 'W%'; + + name +---------------- + Walt Disney + Walter Matthau + Warner Bros. + Warren Beatty + Westward + Woody Allen + + + + + 这个示例展示了如何在FROM子句中使用函数, + 分别使用和不使用列定义列表: + + +CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$ + SELECT * FROM distributors WHERE did = $1; +$$ LANGUAGE SQL; + +SELECT * FROM distributors(111); + did | name +-----+------------- + 111 | Walt Disney + +CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$ + SELECT * FROM distributors WHERE did = $1; +$$ LANGUAGE SQL; + +SELECT * FROM distributors_2(111) AS (f1 int, f2 text); + f1 | f2 +-----+------------- + 111 | Walt Disney + + + + + 下面是为函数结果增加序号列的示例: + + +SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY; + unnest | ordinality +--------+---------- + a | 1 + b | 2 + c | 3 + d | 4 + e | 5 + f | 6 +(6 rows) + + + + + 这个例子展示了如何使用一个简单的 WITH 子句: + + +WITH t AS ( + SELECT random() as x FROM generate_series(1, 3) + ) +SELECT * FROM t +UNION ALL +SELECT * FROM t + + x +-------------------- + 0.534150459803641 + 0.520092216785997 + 0.0735620250925422 + 0.534150459803641 + 0.520092216785997 + 0.0735620250925422 + + + 请注意,WITH 查询只被求值了一次,因此我们得到了两组相同的三个随机值。 + + + + 这个示例使用WITH RECURSIVE从一个只显示 + 直接下属的表中寻找雇员 Mary + 的所有下属(直接的或者间接的)以及他们的间接层数: + + +WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS ( + SELECT 1, employee_name, manager_name + FROM employee + WHERE manager_name = 'Mary' + UNION ALL + SELECT er.distance + 1, e.employee_name, e.manager_name + FROM employee_recursive er, employee e + WHERE er.employee_name = e.manager_name + ) +SELECT distance, employee_name FROM employee_recursive; + + + 注意这种递归查询的典型形式:一个初始条件,后面跟着 + UNION,然后是查询的递归部分。要确保 + 查询的递归部分最终将不返回任何行,否则该查询将无限循环( + 更多示例见)。 + + + + 这个示例使用LATERALmanufacturers + 表的每一行应用一个集合返回函数get_product_names(): + + +SELECT m.name AS mname, pname +FROM manufacturers m, LATERAL get_product_names(m.id) pname; + + + 当前没有任何产品的制造商不会出现在结果中,因为这是一个内连接。 + 如果我们希望把这类制造商的名称包括在结果中,我们可以: + + +SELECT m.name AS mname, pname +FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true; + + + + + 兼容性 + + + 当然,SELECT语句与 SQL 标准兼容。 + 但它也有一些扩展和缺失的特性。 + + + + 省略的<literal>FROM</literal>子句 + + + PostgreSQL 允许省略 FROM 子句。它可以直接用于计算简单表达式的结果: + +SELECT 2+2; + + ?column? +---------- + 4 + + 其他一些 SQL 数据库必须引入一个只有一行的虚拟表,才能从该表执行 SELECT。 + + + + 请注意,如果未指定 FROM 子句,查询就不能引用任何数据库表。例如,以下查询是无效的: + +SELECT distributors.* WHERE distributors.name = 'Westward'; + + PostgreSQL 在 8.1 之前的版本会接受这种形式的查询,并为查询引用的每个表在该查询的 FROM 子句中添加一个隐式项。现在不再允许这种做法。 + + + + + 空<literal>SELECT</literal>列表 + + + SELECT之后的输出表达式列表可以为空, + 这会产生一个零列的结果表。按照 SQL 标准,这不是合法的 + 语法。PostgreSQL允许 + 这样做,是为了与允许零列表保持一致。不过在使用 + DISTINCT时不允许空列表。 + + + + + 省略<literal>AS</literal>关键词 + + + 在 SQL 标准中,只要新列名是一个合法的列名(就是说与任何保留关键词不同), + 就可以省略输出列名之前的可选关键词AS。 + PostgreSQL要稍微严格些:只要新列名匹配 + 任何关键词(保留或者非保留)就需要AS。推荐的习惯是使用 + AS或者带双引号的输出列名来防止与未来增加的关键词可能的冲突。 + + + + 在FROM项中,标准和 + PostgreSQL都允许在非保留关键字别名前 + 省略AS。但是由于语法歧义,这种写法无法 + 用于输出列名。 + + + + + <literal>ONLY</literal>与继承 + + + SQL 标准要求在使用ONLY时用圆括号括起表名,例如SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...PostgreSQL认为这些圆括号是可选的。 + + + + PostgreSQL允许在末尾写上*,显式指定包含子表的非ONLY行为。标准不允许这种写法。 + + + + (这些要点同样适用于所有支持ONLY选项的 SQL 命令。) + + + + + <literal>TABLESAMPLE</literal>子句的限制 + + + 目前只有普通表和物化视图可以使用TABLESAMPLE子句。按照 SQL 标准,它应当能够应用于任何FROM项。 + + + + + + <literal>FROM</literal>中的函数调用 + + + PostgreSQL允许把函数调用直接写成 + FROM列表中的一个成员。在 SQL 标准中,必须把这样的函数 + 调用包装在一个子SELECT中。也就是说,语法 + FROM func(...) alias + 近似等价于 + FROM LATERAL (SELECT func(...)) alias。 + 注意该LATERAL被认为是隐式的,这是因为标准对于 + FROM中的一个UNNEST()项要求 + LATERAL语义。PostgreSQL会把 + UNNEST()和其他集合返回函数同样对待。 + + + + + + <literal>GROUP BY</literal>和<literal>ORDER BY</literal>可用的名字空间 + + + 在 SQL-92 标准中,一个ORDER BY子句只能使用输出 + 列名或者序号,而一个GROUP BY子句只能使用基于输 + 入列名的表达式。PostgreSQL扩展了 + 这两种子句以允许它们使用其他的选择(但如果有歧义时还是使用标准的 + 解释)。PostgreSQL也允许两种子句 + 指定任意表达式。注意出现在一个表达式中的名称将总是被当做输入列名而 + 不是输出列名。 + + + + SQL:1999 及其后的标准使用了一种略微不同的定义,它并不完全向后兼容 + SQL-92。不过,在大部分的情况下, + PostgreSQL会以与 SQL:1999 相同的 + 方式解释ORDER BYGROUP + BY表达式。 + + + + + + 函数依赖 + + + 只有当一个表的主键被包括在GROUP BY列表中时, + PostgreSQL才识别函数依赖(允许 + 从GROUP BY中省略列)。SQL 标准指定了应该要识别 + 的额外情况。 + + + + + <literal>WINDOW</literal>子句的限制 + + + SQL 标准为窗口frame_clause提供了更多选项。PostgreSQL目前仅支持上面列出的选项。 + + + + + <literal>LIMIT</literal>和<literal>OFFSET</literal> + + + LIMITOFFSET子句是PostgreSQL特有的语法,MySQL也使用这种语法。SQL:2008 标准引入了OFFSET ... FETCH {FIRST|NEXT} ...子句来实现相同功能,如上面的所示。IBM DB2也使用这种语法。(为Oracle编写的应用经常采用一种变通办法,通过自动生成的rownum列实现这些子句的效果,而 PostgreSQL 中没有这一列。) + + + + + + <literal>FOR NO KEY UPDATE</literal>、<literal>FOR UPDATE</literal>、<literal>FOR SHARE</literal>、<literal>FOR KEY SHARE</literal> + + + 尽管 SQL 标准中出现了FOR UPDATE,但标准只允许它作为 + DECLARE CURSOR的一个选项。 + PostgreSQL允许它出现在任何 + SELECT查询以及子SELECT中,但这是 + 一种扩展。FOR NO KEY UPDATEFOR SHARE + 以及FOR KEY SHARE变体以及NOWAIT + 和SKIP LOCKED选项没有在标准中出现。 + + + + + <literal>WITH</literal>中的数据修改语句 + + + PostgreSQL允许将INSERTUPDATEDELETE用作WITH查询。SQL 标准中没有这种用法。 + + + + + 非标准子句 + + + DISTINCT ON ( ... )是 SQL 标准的扩展。 + + + + ROWS FROM( ... )是 SQL 标准的扩展。 + + + + + diff --git a/zh/9.6/ref/select_into.sgml b/zh/9.6/ref/select_into.sgml new file mode 100644 index 00000000..936bfe69 --- /dev/null +++ b/zh/9.6/ref/select_into.sgml @@ -0,0 +1,142 @@ + + + + + SELECT INTO + + + + SELECT INTO + 7 + SQL - 语言语句 + + + + SELECT INTO + 根据查询结果定义一个新表 + + + + +[ WITH [ RECURSIVE ] with_query [, ...] ] +SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] + * | expression [ [ AS ] output_name ] [, ...] + INTO [ TEMPORARY | TEMP | UNLOGGED ] [ TABLE ] new_table + [ FROM from_item [, ...] ] + [ WHERE condition ] + [ GROUP BY expression [, ...] ] + [ HAVING condition ] + [ WINDOW window_name AS ( window_definition ) [, ...] ] + [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ] + [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] + [ LIMIT { count | ALL } ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] + [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] + + + + + 描述 + + + SELECT INTO创建一个新表,并用查询计算得到的 + 数据填充该表。与普通的SELECT不同,这些数据不 + 会返回给客户端。新表的列具有与SELECT输出列对 + 应的名称和数据类型。 + + + + + 参数 + + + + TEMPORARYTEMP + + + 如果指定,该表将创建为临时表。详见 + 。 + + + + + + UNLOGGED + + + 如果指定,该表将创建为不记录 WAL 的表。详见 + 。 + + + + + + new_table + + + 要创建的表的名称(可选地带模式限定)。 + + + + + + + 所有其他参数都在中有详细说明。 + + + + + 注解 + + + 在功能上与 + SELECT INTO类似。CREATE TABLE AS + 是推荐使用的语法,因为这种形式的SELECT + INTOECPG + 或PL/pgSQL中不可用,因为它们对 + INTO子句有不同的解释。此外, + CREATE TABLE AS提供的功能是 + SELECT INTO所提供功能的超集。 + + + 要为 SELECT INTO 创建的表添加 OID,请启用 配置变量。也可以在 CREATE TABLE AS 中使用 WITH OIDS 子句。 + + + + 示例 + + + 创建一个新表films_recent,它只包含表 + films中的最近条目: + + +SELECT * INTO films_recent FROM films WHERE date_prod >= '2002-01-01'; + + + + + 兼容性 + + + SQL 标准使用SELECT INTO表示将值选入宿主程序 + 的标量变量,而不是创建一个新表。这确实是 + ECPG(见)和 + PL/pgSQL(见) + 中的用法。PostgreSQL使用SELECT + INTO表示创建表则是历史遗留用法。对于新代码,最好为此目的使用CREATE TABLE AS。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/set.sgml b/zh/9.6/ref/set.sgml new file mode 100644 index 00000000..f9ed3490 --- /dev/null +++ b/zh/9.6/ref/set.sgml @@ -0,0 +1,288 @@ + + + + + SET + + + + SET + 7 + SQL - 语言语句 + + + + SET + 更改运行时参数 + + + + +SET [ SESSION | LOCAL ] configuration_parameter { TO | = } { value | 'value' | DEFAULT } +SET [ SESSION | LOCAL ] TIME ZONE { timezone | LOCAL | DEFAULT } + + + + + 描述 + + + SET命令用于更改运行时配置参数。许多在 + 中列出的运行时参数都可以通过 + SET即时更改。 + (有些参数需要超级用户权限才能更改, + 还有一些参数在服务器启动或会话开始之后便无法再更改。) + SET只影响当前会话所使用的值。 + + + + 如果在一个随后被中止的事务中发出SET + (或者等效的SET SESSION),那么在事务回滚时, + SET命令的效果就会消失。一旦该事务提交,这些效果 + 就会持续到会话结束,除非被另一个SET覆盖。 + + + + SET LOCAL的效果只持续到当前事务结束, + 无论事务是否提交。一种特殊情况是:在同一个事务中先执行 + SET,再执行SET LOCAL。 + 这样SET LOCAL设置的值会一直生效到事务结束, + 但在此之后(如果该事务被提交),SET设置的值 + 将生效。 + + + + SETSET LOCAL的效果, + 也会因为回滚到早于该命令的保存点而被取消。 + + + + 如果在一个针对同一变量带有SET选项的函数内使用 + SET LOCAL(见), + 那么SET LOCAL命令的效果会在函数退出时消失;也就是说, + 无论如何都会恢复函数被调用时生效的值。这使得SET LOCAL + 可以在函数内部用于动态或重复地更改某个参数,同时仍能方便地利用 + SET选项保存并恢复调用者的值。不过,普通的 + SET命令会覆盖任何外围函数的SET选项; + 除非回滚,否则它的效果会一直保留。 + + + + + 在PostgreSQL 8.0 到 8.2 版本中, + SET LOCAL的效果会因为释放一个更早的保存点, + 或者成功退出某个PL/pgSQL异常块而被取消。 + 由于这种行为被认为不够直观,因此后来对其进行了更改。 + + + + + + 参数 + + + + SESSION + + + 指定该命令对当前会话生效。(如果既没有出现SESSION, + 也没有出现LOCAL,这就是默认情况。) + + + + + + LOCAL + + + 指定该命令只对当前事务生效。在COMMIT或 + ROLLBACK之后,会话级设置会再次生效。 + 在事务块外发出该命令会发出警告,但除此之外没有效果。 + + + + + + configuration_parameter + + + 可设置的运行时参数名称。可用参数见 + 以及下文。 + + + + + + value + + + 参数的新值。根据具体参数的不同,值可以指定为字符串常量、标识符、 + 数字,或由这些构成的逗号分隔列表。也可以写成DEFAULT, + 表示把该参数重置为其默认值(即当前会话中如果从未执行过 + SET,它本应具有的值)。 + + + + + + 除了中记载的配置参数外,还有几个参数只能通过SET命令调整,或者具有特殊语法: + + SCHEMA + + SET SCHEMA 'value'是 + SET search_path TO value的别名。 + 使用这种语法时只能指定一个模式。 + + + + + + NAMES + + SET NAMES value是 + SET client_encoding TO value的别名。 + + + + + + SEED + + 设置随机数生成器(即函数 random)的内部种子。允许的值是 -1 到 1 之间的浮点数,这些值随后会乘以 231-1。 + + + 种子也可以通过调用函数setseed来设置: + +SELECT setseed(value); + + + + + + TIME ZONE + + SET TIME ZONE value是以下写法的别名:SET timezone TO value。语法SET TIME ZONE允许使用特殊语法指定时区。下面是一些有效值的示例: + + 'PST8PDT' + + + 加利福尼亚州伯克利所使用的时区。 + + + + + 'Europe/Rome' + + + 意大利所使用的时区。 + + + + + -7 + + + 比 UTC 向西 7 小时的时区(等同于 PDT)。正值表示位于 UTC 以东。 + + + + + INTERVAL '-08:00' HOUR TO MINUTE + + + 比 UTC 向西 8 小时的时区(等同于 PST)。 + + + + + LOCAL + DEFAULT + + + 将时区设置为本地时区(即服务器timezone的默认值)。 + + + + + + + + 以数字或时间间隔给出的时区设置在内部会被转换为 POSIX 时区语法。 + 例如,在SET TIME ZONE -7之后,SHOW TIME ZONE + 会报告<-07>+07。 + + + + 关于时区的更多信息,见 + 。 + + + + + + + + + 注解 + + + 函数set_config提供了等效功能;见 + 。此外,也可以对 + pg_settings + 系统视图执行 UPDATE,以实现与SET等效的操作。 + + + + + 示例 + + + 设置模式搜索路径: + +SET search_path TO my_schema, public; + + + + + 将日期风格设置为传统POSTGRES风格,并采用 + 日在月之前的输入惯例: + +SET datestyle TO postgres, dmy; + + + + 设置加利福尼亚州伯克利的时区: +SET TIME ZONE 'PST8PDT'; + + + + + 将时区设置为意大利所使用的时区: + +SET TIME ZONE 'Europe/Rome'; + + + + + 兼容性 + + + SET TIME ZONE扩展了 SQL 标准定义的语法。标准 + 只允许使用数值形式的时区偏移量,而PostgreSQL + 允许更灵活的时区指定方式。其他所有SET特性都是 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/set_constraints.sgml b/zh/9.6/ref/set_constraints.sgml new file mode 100644 index 00000000..7701efbf --- /dev/null +++ b/zh/9.6/ref/set_constraints.sgml @@ -0,0 +1,110 @@ + + + + + SET CONSTRAINTS + + + + SET CONSTRAINTS + 7 + SQL - 语言语句 + + + + SET CONSTRAINTS + 为当前事务设置约束检查时机 + + + + +SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE } + + + + + 描述 + + + SET CONSTRAINTS设置当前事务中约束检查 + 的行为。IMMEDIATE约束会在每条语句结束时检查。 + DEFERRED约束要等到事务提交时才检查。每个约束都 + 有各自的IMMEDIATEDEFERRED模式。 + + + + 创建约束时,它会被赋予以下三种特性之一: + DEFERRABLE INITIALLY DEFERRED、 + DEFERRABLE INITIALLY IMMEDIATE或者 + NOT DEFERRABLE。第三类始终是 + IMMEDIATE,不会受到 + SET CONSTRAINTS命令的影响。前两类在每个 + 事务开始时都处于其指定的模式,但其行为可以在事务中通过 + SET CONSTRAINTS修改。 + + + + 带约束名称列表的SET CONSTRAINTS只会修改这些 + 约束的模式(它们都必须是可延迟的)。每个约束名称都可以带模式限定。 + 如果未指定模式名称,就会使用当前模式搜索路径查找第一个匹配的名称。 + SET CONSTRAINTS ALL会修改所有可延迟约束的模式。 + + + + 当SET CONSTRAINTS将某个约束的模式从 + DEFERRED改为IMMEDIATE时, + 新模式具有追溯效力:任何原本会在事务结束时才检查的未决数据修改, + 都会改为在执行SET CONSTRAINTS命令期间检查。 + 如果违反了任何此类约束,SET CONSTRAINTS就会失败 + (并且不会改变该约束的模式)。因此,可以利用SET + CONSTRAINTS强制在事务中的特定点执行约束检查。 + + + + 当前,只有UNIQUEPRIMARY KEY、 + REFERENCES(外键)以及EXCLUDE + 约束受到这个设置的影响。 + NOT NULLCHECK约束总是在一行 + 被插入或修改时立即检查(不是在语句结束时)。 + 未声明为DEFERRABLE的唯一约束和排他约束也会立即检查。 + + + + 被声明为约束触发器的触发器,其引发也受此设置控制 + — 它们会在相关约束应当被检查的同时引发。 + + + + + 注解 + + + 因为PostgreSQL并不要求约束名称在同一模式内 + 唯一(只要求在每个表内唯一),所以指定的约束名称有可能匹配到多个约束。 + 在这种情况下,SET CONSTRAINTS会作用于所有匹配项。 + 对于未带模式限定的名称,一旦在搜索路径中的某个模式里找到一个或多个匹配项, + 就不会再搜索路径中更靠后的模式。 + + + + 这个命令只会改变当前事务中约束的行为。在事务块之外发出该命令会产生一条 + 警告,除此之外不会有任何效果。 + + + + + 兼容性 + + + 这个命令符合 SQL 标准定义的行为,但有一个限制:在 + PostgreSQL中,它不会应用在 + NOT NULLCHECK约束上。此外, + PostgreSQL会立即检查不可延迟的 + 唯一约束,而不是像标准所暗示的那样在语句结束时检查。 + + + + diff --git a/zh/9.6/ref/set_role.sgml b/zh/9.6/ref/set_role.sgml new file mode 100644 index 00000000..c2695218 --- /dev/null +++ b/zh/9.6/ref/set_role.sgml @@ -0,0 +1,120 @@ + + + + + SET ROLE + + + + SET ROLE + 7 + SQL - 语言语句 + + + + SET ROLE + 设置当前会话的当前用户标识符 + + + + +SET [ SESSION | LOCAL ] ROLE role_name +SET [ SESSION | LOCAL ] ROLE NONE +RESET ROLE + + + + + 描述 + + 该命令把当前 SQL 会话的当前用户标识符设置为 role_name。角色名可以写成标识符或字符串字面量。在 SET ROLE 之后,SQL 命令的权限检查会视同指定角色是最初登录的角色。 + + 当前会话用户必须是指定的 role_name 角色的成员。(如果会话用户是超级用户,则可以选择任意角色。) + + SESSIONLOCAL 修饰符的作用与常规的 命令相同。 + + + SET ROLE NONE将当前用户标识符设置为当前会话用户标识符, + 即session_user返回的值。 + 如果存在此类设置,RESET ROLE会将当前用户标识符设置为 + 连接建立时由命令行选项、 + ALTER ROLE或 + ALTER DATABASE指定的设置。 + 否则,RESET ROLE会将当前用户标识符设置为当前会话用户标识符。 + 这些形式可以由任何用户执行。 + + + + + 注解 + + 使用这个命令,既可以增加权限,也可以限制自己的权限。如果会话用户角色具有 INHERIT 属性,它会自动拥有每个可通过 SET ROLE 切换到的角色的全部权限;在这种情况下,SET ROLE 实际上会去掉直接分配给会话用户及其所属其他角色的全部权限,只保留指定角色可用的权限。另一方面,如果会话用户角色具有 NOINHERIT 属性,SET ROLE 会放弃直接分配给会话用户的权限,转而获得指定角色可用的权限。 + + 特别是,当超级用户选择通过 SET ROLE 切换到非超级用户角色时,会失去其超级用户权限。 + + + SET ROLE的效果与 + 相近, + 但其中涉及的权限检查完全不同。此外, + SET SESSION AUTHORIZATION会决定后续 + SET ROLE命令允许使用哪些角色,而通过 + SET ROLE更改角色并不会改变后续 + SET ROLE允许使用的角色集合。 + + + + SET ROLE不会处理角色的 + 设置所指定的会话变量; + 这只会在登录期间发生。 + + + + SET ROLE不能在 + SECURITY DEFINER函数中使用。 + + + + + 示例 + + +SELECT SESSION_USER, CURRENT_USER; + + session_user | current_user +--------------+-------------- + peter | peter + +SET ROLE 'paul'; + +SELECT SESSION_USER, CURRENT_USER; + + session_user | current_user +--------------+-------------- + peter | paul + + + + + 兼容性 + + + PostgreSQL允许使用标识符语法 + ("rolename"),而 SQL 标准要求将角色名写成字符串字面量。 + SQL 不允许在事务中执行这个命令; + PostgreSQL不施加这一限制,因为没有理由这样做。 + SESSIONLOCAL修饰符,以及 + RESET语法,都是PostgreSQL扩展。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/set_session_auth.sgml b/zh/9.6/ref/set_session_auth.sgml new file mode 100644 index 00000000..8cb55d2d --- /dev/null +++ b/zh/9.6/ref/set_session_auth.sgml @@ -0,0 +1,115 @@ + + + + + SET SESSION AUTHORIZATION + + + + SET SESSION AUTHORIZATION + 7 + SQL - 语言语句 + + + + SET SESSION AUTHORIZATION + 设置当前会话的会话用户标识符和当前用户标识符 + + + + +SET [ SESSION | LOCAL ] SESSION AUTHORIZATION user_name +SET [ SESSION | LOCAL ] SESSION AUTHORIZATION DEFAULT +RESET SESSION AUTHORIZATION + + + + + 描述 + + + 这个命令将当前 SQL 会话的会话用户标识符和当前用户标识符设置为 + user_name。 + 用户名可以写成一个标识符或者一个字符串字面量。例如,可以使用这个 + 命令临时变为一个非特权用户,之后再切换回超级用户。 + + + + 会话用户标识符初始时被设置为客户端提供的(可能已认证的)用户名。 + 当前用户标识符通常与会话用户标识符相同,但可能在 + SECURITY DEFINER函数和类似机制的上下文中临时更改; + 它也可以通过更改。 + 当前用户标识符用于权限检查。 + + + + 只有当初始会话用户(即已认证用户)最初具有超级用户权限时, + 才能更改会话用户标识符。否则,只有当该命令指定的是已认证用户名时, + 才会被接受。 + + + SESSIONLOCAL 修饰符的作用与常规的 命令相同。 + + + DEFAULTRESET形式会将会话用户标识符和 + 当前用户标识符重置为最初已认证的用户名。这些形式可以由任何用户执行。 + + + + + 注解 + + + SET SESSION AUTHORIZATION不能在一个 + SECURITY DEFINER函数中使用。 + + + + + 示例 + + +SELECT SESSION_USER, CURRENT_USER; + + session_user | current_user +--------------+-------------- + peter | peter + +SET SESSION AUTHORIZATION 'paul'; + +SELECT SESSION_USER, CURRENT_USER; + + session_user | current_user +--------------+-------------- + paul | paul + + + + + 兼容性 + + + SQL 标准允许在字面值user_name的位置上使用某些其他表达式, + 但这些选项在实践中并不重要。PostgreSQL允许使用标识符语法 + ("username"),而 SQL 标准不允许。 + SQL 不允许在事务中使用这个命令;PostgreSQL并不做此限制, + 因为没有理由这样做。SESSIONLOCAL修饰符 + 以及RESET语法都是PostgreSQL扩展。 + + + + 标准把执行这个命令所需的权限留给实现定义。 + + + + + 另见 + + + + + + diff --git a/zh/9.6/ref/set_transaction.sgml b/zh/9.6/ref/set_transaction.sgml new file mode 100644 index 00000000..d23d51d2 --- /dev/null +++ b/zh/9.6/ref/set_transaction.sgml @@ -0,0 +1,260 @@ + + + + + SET TRANSACTION + + + + 事务隔离级别 + 设置 + + + + 只读事务 + 设置 + + + + 可延迟事务 + 设置 + + + + SET TRANSACTION + 7 + SQL - 语言语句 + + + + SET TRANSACTION + 设置当前事务的特性 + + + + +SET TRANSACTION transaction_mode [, ...] +SET TRANSACTION SNAPSHOT snapshot_id +SET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...] + +其中 transaction_mode 是下列之一: + + ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } + READ WRITE | READ ONLY + [ NOT ] DEFERRABLE + + + + + 描述 + + + SET TRANSACTION命令设置当前事务的特性。 + 它对任何后续事务都没有影响。SET SESSION + CHARACTERISTICS设置一个会话中后续事务的默认 + 事务特性。对于单个事务,这些默认值可以用 + SET TRANSACTION覆盖。 + + + + 可用的事务特性是事务隔离级别、事务访问模式(读/写或只读)以及 + 可延迟模式。此外,还可以选择一个快照,不过它只能用于当前事务, + 不能作为会话默认值。 + + + + 一个事务的隔离级别决定当其他事务并发运行时该事务能看见什么数据: + + + + READ COMMITTED + + + 一条语句只能看到在它开始之前已提交的行。这是默认值。 + + + + + + REPEATABLE READ + + + 当前事务中的所有语句都只能看到在该事务中执行第一条查询或 + 数据修改语句之前已提交的行。 + + + + + + SERIALIZABLE + + + 当前事务中的所有语句都只能看到在该事务中执行第一条查询或 + 数据修改语句之前已提交的行。如果并发的可串行化事务之间出现 + 某种读写模式,而这种情况不可能在这些事务的任何串行 + (一次执行一个)执行中发生,那么其中一个事务将以 + serialization_failure错误回滚。 + + + + + + SQL 标准还定义了一个额外的级别:READ + UNCOMMITTED。在 + PostgreSQL中,READ + UNCOMMITTED被视为 + READ COMMITTED。 + + + + 在事务执行第一条查询或数据修改语句(SELECT, + INSERTDELETE, + UPDATE, + FETCH,或 + COPY)之后,事务隔离级别就不能再更改。有关事务隔离和并发控制的更多信息,请参见 + 。 + + + + 事务访问模式决定事务是读/写还是只读。读/写是默认值。当事务为只读时, + 下列 SQL 命令会被禁止:INSERTUPDATE、 + DELETE 以及 + COPY FROM,前提是它们要写入的表不是临时表; + 所有 CREATEALTER 和 + DROP 命令;COMMENT、 + GRANTREVOKE、 + TRUNCATE;以及 EXPLAIN ANALYZE + 和 EXECUTE,前提是它们要执行的命令属于上述列表。 + 这是一个较高层面的只读概念,并不会阻止所有写入磁盘的行为。 + + + + 只有当事务同时是SERIALIZABLE和 + READ ONLY时,DEFERRABLE + 事务属性才会生效。当为一个事务同时选择这三个属性时,该事务在 + 首次获取其快照时可能会阻塞;在此之后,它便可以运行,而无需承担普通 + SERIALIZABLE事务的常规开销,也不会有促成 + 串行化失败或因串行化失败而被取消的风险。这种模式非常适合长时间运行的 + 报表或备份。 + + + + SET TRANSACTION SNAPSHOT命令允许一个新事务使用与现有事务 + 相同的快照运行。已有事务必须用 + pg_export_snapshot函数导出其快照(参见 + )。该函数会返回一个 + 快照标识符,必须将其提供给SET TRANSACTION + SNAPSHOT以指定要导入的快照。该标识符在此命令中必须写成 + 字符串字面量,例如 + '000003A1-1'。 + SET TRANSACTION SNAPSHOT只能在事务开始时执行,也就是在 + 事务的第一条查询或数据修改语句(SELECT、 + INSERTDELETE、 + UPDATE、 + FETCHCOPY)之前。 + 此外,事务还必须已经设置为 SERIALIZABLE 或 + REPEATABLE READ 隔离级别(否则,快照会被立即丢弃, + 因为 READ COMMITTED 模式会为每条命令获取一个新快照)。 + 如果导入事务使用 SERIALIZABLE 隔离级别,则导出快照 + 的事务也必须使用该隔离级别。此外,非只读的可串行化事务不能从只读事务 + 导入快照。 + + + + + + 注解 + + + 如果执行SET TRANSACTION之前没有 + START TRANSACTION或者 + BEGIN,它会发出一条警告,除此之外没有任何效果。 + + + + 可以不使用SET TRANSACTION,而是在 + BEGINSTART TRANSACTION中指定所需的 + transaction_modes。但是, + 对于SET TRANSACTION SNAPSHOT,这种方式不可用。 + + + + 会话默认的事务模式也可以通过配置参数 + 、 + 和 + 来设置或检查(实际上, + SET SESSION CHARACTERISTICS只是用 + SET设置这些变量的一种更冗长的等价写法)。这意味着可以通过配置文件、 + ALTER DATABASE等方式设置默认值。详见 + 。 + + + + 当前事务的模式也可以类似地通过配置参数 + 、 + 和 + 来设置或检查。 + 设置其中任一参数的作用都与对应的SET TRANSACTION选项相同, + 并且在可设置时机方面有相同的限制。但是,这些参数不能在配置文件中设置, + 也不能通过实时执行的 SQL 之外的任何来源来设置。 + + + + + 示例 + + + 要以与某个现有事务相同的快照开始一个新事务,先从该现有事务导出 + 快照。这样会返回快照标识符,例如: + + +BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; +SELECT pg_export_snapshot(); + pg_export_snapshot +-------------------- + 000003A1-1 +(1 row) + + + 然后在新开启事务的开始处,通过SET TRANSACTION + SNAPSHOT命令给出该快照标识符: + + +BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; +SET TRANSACTION SNAPSHOT '000003A1-1'; + + + + + 兼容性 + + + 这些命令由SQL标准定义,但 + DEFERRABLE事务模式和 + SET TRANSACTION SNAPSHOT这种形式除外,它们是 + PostgreSQL扩展。 + + + + SERIALIZABLE是标准中的默认事务隔离级别。在 + PostgreSQL中,默认值通常是 + READ COMMITTED,但你可以按上述方式修改。 + + + + 在 SQL 标准中,还可以用这些命令设置另一项事务特性:诊断区域 + 的大小。这个概念特定于嵌入式 SQL,因此没有在 + PostgreSQL服务器中实现。 + + + + SQL 标准要求在连续的transaction_modes之间有逗号, + 但出于历史原因, + PostgreSQL允许省略逗号。 + + + diff --git a/zh/9.6/ref/show.sgml b/zh/9.6/ref/show.sgml new file mode 100644 index 00000000..08538428 --- /dev/null +++ b/zh/9.6/ref/show.sgml @@ -0,0 +1,174 @@ + + + + + SHOW + + + + SHOW + 7 + SQL - 语言语句 + + + + SHOW + 显示一个运行时参数的值 + + + + +SHOW name +SHOW ALL + + + + + 描述 + + + SHOW将显示运行时参数的当前设置。 + 这些变量可以使用SET语句来设置,也可以通过编辑 + postgresql.conf配置文件、使用 + PGOPTIONS环境变量(在使用 + libpq或基于libpq的应用时), + 或者在启动postgres服务器时通过命令行 + 标志来设置。详见。 + + + + + 参数 + + + + name + + 运行时参数的名称。可用参数的说明见以及参考页。此外,还有几个可以显示但不能设置的参数: + + SERVER_VERSION + + 显示服务器的版本号。 + + + + + SERVER_ENCODING + + 显示服务器端字符集编码。目前,该参数可以显示但不能设置,因为编码在数据库创建时就已确定。 + + + + + LC_COLLATE + + 显示数据库用于排序规则(文本排序)的区域设置。目前,该参数可以显示但不能设置,因为该设置在数据库创建时就已确定。 + + + + + LC_CTYPE + + 显示数据库用于字符分类的区域设置。目前,该参数可以显示但不能设置,因为该设置在数据库创建时就已确定。 + + + + + IS_SUPERUSER + + 如果当前角色具有超级用户权限,则为真。 + + + + + + + + ALL + + + 显示所有配置参数的值及其描述。 + + + + + + + + 注解 + + + 函数current_setting也会产生等效的输出,见 + 。另外, + pg_settings + 系统视图也会给出相同的信息。 + + + + + + 示例 + + + 显示参数DateStyle的当前设置: + + +SHOW DateStyle; + DateStyle +----------- + ISO, MDY +(1 row) + + + + + 显示参数geqo的当前设置: + +SHOW geqo; + geqo +------ + on +(1 row) + + + + + 显示所有设置: + +SHOW ALL; + name | setting | description +-------------------------+---------+------------------------------------------------- + allow_system_table_mods | off | Allows modifications of the structure of ... + . + . + . + xmloption | content | Sets whether XML data in implicit parsing ... + zero_damaged_pages | off | Continues processing past damaged page headers. +(196 rows) + + + + + 兼容性 + + + SHOW命令是一种 + PostgreSQL扩展。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/start_transaction.sgml b/zh/9.6/ref/start_transaction.sgml new file mode 100644 index 00000000..bcbdc548 --- /dev/null +++ b/zh/9.6/ref/start_transaction.sgml @@ -0,0 +1,95 @@ + + + + + START TRANSACTION + + + + START TRANSACTION + 7 + SQL - 语言语句 + + + + START TRANSACTION + 开始一个事务块 + + + + +START TRANSACTION [ transaction_mode [, ...] ] + +其中 transaction_mode 是下列之一: + + ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } + READ WRITE | READ ONLY + [ NOT ] DEFERRABLE + + + + + 描述 + + + 该命令开始一个新的事务块。如果指定了隔离级别、读写模式或可延迟模式, + 新事务就会具有这些特性,就像执行了 + 一样。 + 它与命令具有相同功能。 + + + + + 参数 + + + 关于本语句参数含义的信息,参见。 + + + + + 兼容性 + + + 在 SQL 标准中,并不需要发出START TRANSACTION + 来开始一个事务块:任何 SQL 命令都会隐式开始一个事务块。 + PostgreSQL的行为可以看作是:对每条不跟在 + START TRANSACTION(或BEGIN)之后的命令, + 都会在其后隐式发出一个COMMIT,因此这种行为通常被称为 + 自动提交。其他关系型数据库系统也可能为了方便而提供 + 自动提交特性。 + + + + DEFERRABLE + transaction_mode + 是PostgreSQL的一种语言扩展。 + + + + SQL 标准要求在连续的transaction_modes之间有逗号, + 但出于历史原因, + PostgreSQL允许省略逗号。 + + + + 另见的兼容性小节。 + + + + + 另见 + + + + + + + + + + diff --git a/zh/9.6/ref/truncate.sgml b/zh/9.6/ref/truncate.sgml new file mode 100644 index 00000000..958620c9 --- /dev/null +++ b/zh/9.6/ref/truncate.sgml @@ -0,0 +1,197 @@ + + + + + TRUNCATE + + + + TRUNCATE + 7 + SQL - 语言语句 + + + + TRUNCATE + 清空一个表或一组表 + + + + +TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ] + [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ] + + + + + 描述 + + + TRUNCATE可以快速移除一组表中的所有行。 + 它的效果与对每个表执行不带条件的DELETE相同, + 但由于它并不实际扫描这些表,因此速度更快。此外,它会立即回收磁盘空间, + 而不是要求后续再执行一次VACUUM操作。这一点对于大表尤其有用。 + + + + + 参数 + + + + name + + + 要截断的表名(可以是模式限定的)。如果在表名前指定了 + ONLY,则只截断该表。如果未指定ONLY, + 则该表及其所有后代表(如果有)都会被截断。也可以在表名后指定 + *,以显式表明包含后代表。 + + + + + + RESTART IDENTITY + + + 自动重新启动由被截断表列拥有的 sequence。 + + + + + + CONTINUE IDENTITY + + + 不更改 sequence 的值。这是默认值。 + + + + + + CASCADE + + + 自动截断所有以外键引用任一已命名表的表,以及任何由于 + CASCADE而被加入该组的表。 + + + + + + RESTRICT + + + 如果任一表具有来自命令中未列出表的外键引用,则拒绝截断。这是默认值。 + + + + + + + + 注解 + + + 要截断一个表,你必须拥有该表上的TRUNCATE权限。 + + + + TRUNCATE会在其操作的每个表上获取 + ACCESS EXCLUSIVE锁,这会阻塞该表上的所有其他并发操作。 + 当指定RESTART IDENTITY时,任何需要重新启动的 sequence + 也会同样被排他锁定。如果需要对某个表进行并发访问,则应改用 + DELETE命令。 + + + + TRUNCATE不能用于被其他表通过外键引用的表, + 除非所有这类表也在同一条命令中被截断。因为在这种情况下检查有效性将需要 + 扫描表,而该命令的意义恰恰在于避免这样做。CASCADE + 选项可用于自动包含所有依赖表 — 但使用该选项时一定要非常小心, + 否则你可能会丢失并非有意删除的数据! + + + + TRUNCATE不会引发这些表上可能存在的任何 + ON DELETE触发器,但会引发 + ON TRUNCATE触发器。如果这些表中的任何一个 + 定义了ON TRUNCATE触发器,那么所有 + BEFORE TRUNCATE触发器都会在任何截断发生之前 + 引发,而所有AFTER TRUNCATE触发器都会在最后一次 + 截断完成且所有 sequence 被重置之后引发。触发器会按照表的处理顺序 + 引发(先是命令中列出的表,然后是由于级联而加入的表)。 + + + + TRUNCATE不是多版本并发控制(MVCC)安全的。截断之后, + 如果并发事务使用的是在截断发生前取得的快照, + 该表对这些并发事务而言将表现为空。详见。 + + + + 就表中数据而言,TRUNCATE是事务安全的: + 如果外围事务没有提交,截断将被安全地回滚。 + + + + 当指定RESTART IDENTITY时,隐含的 + ALTER SEQUENCE RESTART操作也会以事务方式执行; + 也就是说,如果外围事务没有提交,它们也会被回滚。这与 + ALTER SEQUENCE RESTART通常的行为不同。请注意,如果在 + 事务回滚前又对这些重启后的 sequence 执行了额外操作,这些操作对 sequence + 本身的影响会被回滚,但对currval()的影响不会被回滚。 + 也就是说,事务结束后,currval()仍将反映失败事务内取得的 + 最后一个 sequence 值,即使 sequence 本身可能已经不再与之保持一致。 + 这与失败事务之后currval()的通常行为类似。 + + + 目前,TRUNCATE 不支持外部表。这意味着,如果指定表的后代表中有外部表,该命令就会失败。 + + + + 示例 + + + 截断表bigtable和 + fattable: + + +TRUNCATE bigtable, fattable; + + + + + 同样的操作,并重置所有相关联的 sequence 生成器: + + +TRUNCATE bigtable, fattable RESTART IDENTITY; + + + + + 截断表othertable,并级联到任何通过 + 外键约束引用othertable的表: + + +TRUNCATE othertable CASCADE; + + + + + 兼容性 + + + SQL:2008 标准包括了一个TRUNCATE命令, + 语法是TRUNCATE TABLE + tablename。子句 + CONTINUE IDENTITY/RESTART IDENTITY + 在该标准中也有出现,但其含义虽相关却略有不同。该命令的某些并发行为在标准中 + 被留作实现定义,因此必要时应结合上述注解并与其他实现进行比较。 + + + + diff --git a/zh/9.6/ref/unlisten.sgml b/zh/9.6/ref/unlisten.sgml new file mode 100644 index 00000000..f28790c8 --- /dev/null +++ b/zh/9.6/ref/unlisten.sgml @@ -0,0 +1,124 @@ + + + + + UNLISTEN + + + + UNLISTEN + 7 + SQL - 语言语句 + + + + UNLISTEN + 停止监听通知 + + + + +UNLISTEN { channel | * } + + + + + 描述 + + + UNLISTEN用于移除现有的 + NOTIFY事件注册。 + UNLISTEN会取消当前 + PostgreSQL会话作为名为channel的通知通道监听者的任何现有注册。 + 特殊通配符*会取消当前会话的所有监听注册。 + + + + 中对LISTEN和 + NOTIFY的使用有更详细的讨论。 + + + + + 参数 + + + + channel + + + 通知通道的名称(任意标识符)。 + + + + + + * + + + 该会话当前的所有监听注册都会被清除。 + + + + + + + + 注解 + + 可以取消监听自己并未监听的通道,不会出现警告或错误。 + + + 每个会话结束时,都会自动执行UNLISTEN *。 + + + + 执行过UNLISTEN的事务不能为两阶段提交做准备。 + + + + + 示例 + + + 建立一个监听注册: + + +LISTEN virtual; +NOTIFY virtual; +Asynchronous notification "virtual" received from server process with PID 8448. + + + + + 执行UNLISTEN之后,后续的NOTIFY + 消息将被忽略: + + +UNLISTEN virtual; +NOTIFY virtual; +-- no NOTIFY event is received + + + + + 兼容性 + + + SQL 标准中没有UNLISTEN语句。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/update.sgml b/zh/9.6/ref/update.sgml new file mode 100644 index 00000000..5d49ff45 --- /dev/null +++ b/zh/9.6/ref/update.sgml @@ -0,0 +1,356 @@ + + + + + UPDATE + + + + UPDATE + 7 + SQL - 语言语句 + + + + UPDATE + 更新表中的行 + + + + +[ WITH [ RECURSIVE ] with_query [, ...] ] +UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ] + SET { column_name = { expression | DEFAULT } | + ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) | + ( column_name [, ...] ) = ( sub-SELECT ) + } [, ...] + [ FROM from_item [, ...] ] + [ WHERE condition | WHERE CURRENT OF cursor_name ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] + + + + + 描述 + + + UPDATE会更改所有满足条件的行中指定列 + 的值。只需在SET子句中提及需要修改的列; + 未被显式修改的列会保留其原有值。 + + + + 有两种方法可以利用数据库中其他表所包含的信息来修改一个表:使用子选择, + 或者在FROM子句中指定附加表。哪种技术更合适取决 + 于具体情况。 + + + + 可选的RETURNING子句使UPDATE + 基于每个实际更新的行计算并返回一个或多个值。可以计算任何使用该表列和/或 + FROM中提到的其他表列的表达式。默认使用该 + 表列的新值(更新后值),但也可以请求旧值(更新前值)。 + RETURNING列表的语法与SELECT + 的输出列表相同。 + + + 必须拥有该表上的UPDATE权限,或者至少拥有所列待更新列的该权限。还必须拥有SELECT权限,覆盖在expressionscondition中读取其值的所有列。 + + + + 参数 + + + + with_query + + + + WITH子句允许你指定一个或多个子查询,这些子查 + 询可在UPDATE查询中按名称引用。详见 + 。 + + + + + + table_name + + + + 要更新的表名(可以是模式限定的)。如果在表名前指定了 + ONLY,只会更新所提及表中的匹配行。如果未指定 + ONLY,还会更新继承自该表的任何表中的匹配行。 + 可选地,可以在表名后指定*,以显式指示包含后代 + 表。 + + + + + + alias + + + + 目标表的替代名称。提供别名时,它会完全隐藏该表的实际名称。 + 例如,给定UPDATE foo AS f, + UPDATE语句的其余部分必须将该表称为 + f,而不是foo。 + + + + + + column_name + + + + 由table_name命名 + 的表中的列名。如有需要,列名可以使用子字段名或数组下标 + 进行限定。指定目标列时不要包含表名 — 例如, + UPDATE table_name SET table_name.col = 1是无 + 效的。 + + + + + + expression + + + + 要赋给该列的表达式。该表达式可以使用该表中这一列和其他列的 + 旧值。 + + + + + + DEFAULT + + 把该列设置为其默认值(如果没有为它指定特定的默认表达式,则该值为 NULL)。 + + + + + sub-SELECT + + + + 一个SELECT子查询,其输出列数必须与它前面圆括号中 + 列列表的列数相同。执行时,该子查询返回的行数不得超过一行。如 + 果返回一行,则其列值会赋给目标列;如果不返回任何行,则把 NULL + 值赋给目标列。该子查询可以引用正在更新的表的当前行的旧值。 + + + + + + from_item + + 一个表表达式,允许其他表的列出现在WHERE条件和更新表达式中。这里使用的语法与相同,后者属于SELECT语句;例如,可以为表名指定别名。不要把目标表重复写成from_item,除非你打算进行自连接(这种情况下它必须在from_item中带有别名出现)。 + + + + + condition + + + + 一个返回boolean类型值的表达式。只有使这个表达式返 + 回true的行才会被更新。 + + + + + + cursor_name + + 要在WHERE CURRENT OF条件中使用的游标名。要被更新的行是最近一次从该游标中取出的那一行。该游标必须是针对UPDATE目标表的非分组查询。注意,WHERE CURRENT OF不能与布尔条件同时指定。参见,了解将游标用于WHERE CURRENT OF的更多信息。 + + + + + output_expression + + + 在每一行被更新后,由UPDATE命令计算并返回的 + 表达式。该表达式可以使用由table_name + 命名的表或FROM中列出的表中的任何列名。写成 + *可返回所有列。 + + + + + + output_name + + + 用于返回列的名称。 + + + + + + + + + 输出 + + + 在成功完成时,一个UPDATE命令会返回以下形式 + 的命令标签: + +UPDATE count + + count是被更新的行数, + 包括值没有改变的匹配行。注意,当更新被BEFORE UPDATE + 触发器抑制时,这个数量可能小于匹配 + condition的行数。如果 + count为 0,则该查询没有更 + 新任何行(这不被视为错误)。 + + + + 如果UPDATE命令包含RETURNING + 子句,其结果将类似于一个SELECT语句,其中包含 + RETURNING列表中定义的列和值,并在该命令更新的 + 行上进行计算。 + + + + + 注解 + + + 当存在FROM子句时,本质上会将目标表与 + from_item列表中提到的表连接起来,而连接 + 的每一条输出行都代表对目标表的一次更新操作。使用 + FROM时,应确保对每一个要修改的目标行,连接至多 + 生成一条输出行。换言之,一条目标行不应与其他表中的多于一行成功 + 连接。如果发生这种情况,则只会使用其中某一条连接行来更新目标行, + 但具体使用哪一条并不容易预测。 + + + + 因为存在这种不确定性,所以仅在子选择中引用其他表会更安全,尽管 + 这种写法通常比使用连接更难阅读、也更慢。 + + + + + + 示例 + + + 把表filmskind + 列中的单词Drama改为Dramatic: + + +UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama'; + + + + + 在表weather的一行中调整温度项,并将降水量 + 重置为其默认值: + + +UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT + WHERE city = 'San Francisco' AND date = '2003-07-03'; + + + + 执行同样的操作,并返回更新后的各项: +UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT + WHERE city = 'San Francisco' AND date = '2003-07-03' + RETURNING temp_lo, temp_hi, prcp; + + + + 使用另一种列列表语法完成同样的更新: +UPDATE weather SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT) + WHERE city = 'San Francisco' AND date = '2003-07-03'; + + + + 将负责 Acme Corporation 账户的销售人员的销量计数加一,使用 FROM 子句语法: +UPDATE employees SET sales_count = sales_count + 1 FROM accounts + WHERE accounts.name = 'Acme Corporation' + AND employees.id = accounts.sales_person; + + + + 执行同样的操作,在 WHERE 子句中使用子选择: +UPDATE employees SET sales_count = sales_count + 1 WHERE id = + (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation'); + + + + 更新账户表中的联系人姓名,使其与当前分配的销售人员保持一致: +UPDATE accounts SET (contact_first_name, contact_last_name) = + (SELECT first_name, last_name FROM salesmen + WHERE salesmen.id = accounts.sales_id); +使用连接也可以得到类似结果: +UPDATE accounts SET contact_first_name = first_name, + contact_last_name = last_name + FROM salesmen WHERE salesmen.id = accounts.sales_id; +但是,第二个查询可能会给出意外结果,如果 salesmen.id 不是唯一键的话;而第一个查询在存在多个 id 匹配时保证会报错。此外,如果某个特定的 accounts.sales_id 条目没有匹配项,第一个查询会把相应的姓名字段设为 NULL,而第二个查询则根本不会更新该行。 + + 更新汇总表中的统计数据以匹配当前数据: +UPDATE summary s SET (sum_x, sum_y, avg_x, avg_y) = + (SELECT sum(x), sum(y), avg(x), avg(y) FROM data d + WHERE d.group_id = s.group_id); + + + + 尝试插入一个新库存项及其库存量。如果该项已存在,则改为更新现有项的库存量。要在不致使整个事务失败的情况下做到这一点,请使用保存点: +BEGIN; +-- 其他操作 +SAVEPOINT sp1; +INSERT INTO wines VALUES('Chateau Lafite 2003', '24'); +-- 假设上面的语句因唯一键冲突而失败, +-- 那么现在发出这些命令: +ROLLBACK TO sp1; +UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003'; +-- 继续执行其他操作,最后 +COMMIT; + + + + 更改 kind 列,该列位于表 films 中,所更改的行是游标 c_films 当前定位的行: +UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films; + + + + + + 兼容性 + + + 这个命令符合SQL标准,不过 + FROMRETURNING子句是 + PostgreSQL扩展,把WITH + 与UPDATE一起使用的能力也是扩展。 + + + + 某些其他数据库系统提供一种FROM选项,要求在 + FROM中再次列出目标表。PostgreSQL + 并不是这样解释FROM的。在移植使用这种扩展的应 + 用时要小心。 + + + + 根据标准,列名的圆括号子列表的源值可以是任何能够产生正确列数的 + 行值表达式。PostgreSQL只允许该源值是一个 + 圆括号表达式列表或子SELECT。在表达式列表的情况下, + 单个列的更新值可以指定为DEFAULT,但在子 + SELECT中则不能这样做。 + + + diff --git a/zh/9.6/ref/vacuum.sgml b/zh/9.6/ref/vacuum.sgml new file mode 100644 index 00000000..17d5366d --- /dev/null +++ b/zh/9.6/ref/vacuum.sgml @@ -0,0 +1,209 @@ + + + + + VACUUM + + + + VACUUM + 7 + SQL - 语言语句 + + + + VACUUM + 垃圾收集并按需分析数据库 + + + + +VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ] +VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table_name ] +VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name [ (column_name [, ...] ) ] ] + + + + + 描述 + + + VACUUM回收死元组占用的存储空间。在正常的 + PostgreSQL运行中,被删除或因更新而过时的元组 + 并不会从其表中物理移除;它们会一直保留,直到执行 + VACUUM。因此有必要定期执行 + VACUUM,尤其是对频繁更新的表。 + + + 不带参数时,VACUUM会处理当前数据库中当前用户有权清理的每个表。带参数时,VACUUM则只处理该表。 + + + VACUUM ANALYZE会对每个选定表先执行 + VACUUM,再执行ANALYZE。 + 这种便捷的组合形式很适合例行维护脚本使用。关于其处理细节,参见 + 。 + + + + 普通的VACUUM(不带FULL)只是回收空间并使其可被重用。 + 这种形式的命令可以与表的正常读写并行运行,因为它不会获得独占锁。 + 不过,在大多数情况下,额外空间不会返还给操作系统;它只是保留在同一张表内供再次使用。 + VACUUM FULL会把表的全部内容重写到一个没有额外空闲空间的新磁盘文件中, + 从而让未使用的空间能够返还给操作系统。这种形式要慢得多,并且在处理每个表时都需要 + ACCESS EXCLUSIVE锁。 + + + 当选项列表用圆括号括起来时,选项可以按任意顺序书写。如果不加圆括号,则必须严格按照上面所示的顺序指定选项。带圆括号的语法是在PostgreSQL 9.0 中加入的;不带圆括号的语法已被弃用。 + + + + 参数 + + + + FULL + + + 选择完全清理,它可以回收更多空间,但耗时更长,并且会独占锁定该表。 + 这种方法还需要额外的磁盘空间,因为它会写出该表的一个新副本,并且在操作完成之前 + 不会释放旧副本。通常,只有当需要从表内回收大量空间时才应使用这种方法。 + + + + + + FREEZE + + + 选择激进的元组冻结。指定FREEZE + 等价于执行一个将和 + 参数设为零的 + VACUUM。在表被重写时总会执行激进冻结,因此指定了 + FULL时,这个选项就是多余的。 + + + + + + VERBOSE + + 为每个表输出详细的清理活动报告。 + + + + + ANALYZE + + + 更新规划器用来确定查询最高效执行方式的统计信息。 + + + + + + DISABLE_PAGE_SKIPPING + + + 通常,VACUUM会根据可见性映射跳过某些页面。已知其中所有元组都已冻结的页面总是可以跳过, + 而已知其中所有元组都对所有事务可见的页面,也可以跳过,除非正在执行激进清理。 + 此外,除非正在执行激进清理,为了避免等待其他会话结束对页面的使用,也可能会跳过某些页面。 + 此选项会禁用所有跳页行为,只应在怀疑可见性映射内容存在问题时使用;而这种情况通常只会在 + 硬件或软件问题导致数据库损坏时发生。 + + + + + + table_name + + 要清理的特定表的名称(可选地带模式限定)。默认为当前数据库中的所有表。 + + + + + column_name + + 要分析的特定列名。默认会分析所有列。如果指定了列列表,则隐含指定ANALYZE + + + + + + + 输出 + + + 指定VERBOSE时,VACUUM会输出进度消息, + 指示当前正在处理哪个表,同时还会打印这些表的各种统计信息。 + + + + + 注解 + + + 要清理一个表,通常调用者必须是该表的拥有者或超级用户。 + 不过,数据库拥有者可以清理其数据库中的所有表,但共享系统目录除外。 + (对共享系统目录的这一限制意味着,真正意义上的全数据库 + VACUUM 只能由超级用户执行。) + VACUUM 会跳过调用用户无权清理的任何表。 + + + + VACUUM不能在一个事务块内被执行。 + + + + 对于带有GIN索引的表,VACUUM(任何形式)还会通过把待处理的索引项移动到主 + GIN索引结构中的适当位置,来完成所有挂起的索引插入。详见 + 。 + + + 我们建议对活跃的生产数据库频繁执行清理(至少每晚一次),以移除死行。在添加或删除大量行之后,对受影响的表执行VACUUM ANALYZE命令可能是个好主意。这会用所有近期变更的结果更新系统目录,使PostgreSQL查询规划器在规划查询时能够做出更好的选择。 + + + 选项不建议在日常场景中使用,但在某些特殊情况下可能很有用。 + 例如,当你删除或更新了表中的绝大多数行,并希望该表在物理上收缩以占用更少磁盘空间、 + 让表扫描更快时,这个选项就比较合适。VACUUM FULL通常会比普通 + VACUUM更大幅度地收缩表。 + + + + VACUUM会显著增加 I/O 流量,这可能导致其他活动会话性能变差。 + 因此,有时建议使用基于代价的清理延迟特性。详见 + + PostgreSQL提供了一个自动清理(autovacuum)机制,可以自动执行常规清理维护。有关自动与手动清理的更多信息,参见 + + + + 示例 + + + 清理单个表onek,对其进行分析以供优化器使用,并打印详细的清理活动报告: + + +VACUUM (VERBOSE, ANALYZE) onek; + + + + + 兼容性 + + + 在SQL标准中没有VACUUM语句。 + + + + + 另见 + + + + + + + + diff --git a/zh/9.6/ref/vacuumdb.sgml b/zh/9.6/ref/vacuumdb.sgml new file mode 100644 index 00000000..a013988c --- /dev/null +++ b/zh/9.6/ref/vacuumdb.sgml @@ -0,0 +1,384 @@ + + + + + vacuumdb + + + + vacuumdb + 1 + 应用程序 + + + + vacuumdb + 清理并分析一个 PostgreSQL 数据库 + + + + + vacuumdb + connection-option + option + + + + + + + + table + ( column [,...] ) + + + + dbname + + + + vacuumdb + connection-option + option + + + + + + + + + 描述 + + + vacuumdb 是用于清理 PostgreSQL + 数据库的工具。vacuumdb 还会生成供 + PostgreSQL 查询优化器使用的内部统计信息。 + + + vacuumdb是 SQL 命令的包装器。通过该工具或通过访问服务器的其他方式来清理和分析数据库,在效果上没有区别。 + + + + + + 选项 + + + vacuumdb接受下列命令行参数: + + + + + + 清理所有数据库。 + + + + + + + + + + 当未使用 / 时,指定要清理或分析的数据库名称。 + 如果未指定,则从环境变量 PGDATABASE 中读取数据库名。如果该变量未设置, + 则使用连接时指定的用户名。dbname 可以是 + 连接字符串。如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + + + + + + + + + + 回显 vacuumdb 生成并发送给服务器的命令。 + + + + + + + + + + 执行完全清理。 + + + + + + + + + + 激进地冻结元组。 + + + + + + + + + + 通过同时运行 njobs 条命令,并行执行清理或分析命令。 + 该选项会缩短处理时间,但也会增加数据库服务器上的负载。 + + + vacuumdb 将打开 + njobs 个到数据库的连接,因此请确保 + 的设置足够高,能够容纳所有这些连接。 + + + 注意,如果将此模式与 FULL)选项一起使用, + 某些系统目录被并行处理时可能会发生死锁失败。 + + + + + + + + + + 不显示进度消息。 + + + + + + + + + 仅清理或分析 table。只有结合 选项时才能指定列名。可以清理多个表,方法是多次指定 开关。 + + + 如果指定了列,你很可能需要在 shell 中转义括号。(见下面的示例。) + + + + + + + + + + + 在处理过程中输出详细信息。 + + + + + + + + + + 输出 vacuumdb 的版本并退出。 + + + + + + + + + + 同时计算供优化器使用的统计信息。 + + + + + + + + + + 仅计算供优化器使用的统计信息(不执行清理)。 + + + + + + + + 仅计算供优化器使用的统计信息(不执行清理),类似。使用不同的配置设置运行多个(目前为三个)分析阶段,以便更快地生成可用统计信息。 + + + 此选项适合分析刚通过恢复转储或pg_upgrade填充的数据库。 + 它会尽快尝试生成一些统计信息,让数据库能够使用,然后在后续阶段生成完整统计信息。 + + + + + + + + + + 显示 vacuumdb 命令行参数的帮助并退出。 + + + + + + + + + vacuumdb还接受下列用于连接参数的命令行参数: + + + + + + 指定服务器所在机器的主机名。如果该值以斜杠开头,则它会被用作 Unix 域套接字目录。 + + + + + + + + + + 指定服务器监听连接所用的 TCP 端口,或本地 Unix 域套接字文件扩展名。 + + + + + + + + + + 连接时使用的用户名。 + + + + + + + + + + 绝不提示输入密码。如果服务器要求密码认证,而又无法通过其他方式获得密码,例如 + .pgpass 文件,则连接尝试将失败。此选项可用于无人输入密码的批处理作业和脚本。 + + + + + + + + + + 强制 vacuumdb 在连接数据库之前提示输入密码。 + + + + 这个选项绝非必需,因为如果服务器要求密码认证, + vacuumdb 会自动提示输入密码。不过, + vacuumdb 会先浪费一次连接尝试,才得知服务器需要密码。 + 在某些情况下,输入 以避免这次额外的连接尝试是值得的。 + + + + + + + + + 当使用 / 时,连接到该数据库以收集要清理的数据库列表。 + 如果未指定,则使用 postgres 数据库;若该数据库不存在,则使用 + template1。这可以是一个 + 连接字符串。如果是这样,连接字符串参数将覆盖任何冲突的命令行选项。 + 此外,除数据库名本身外,连接字符串中的其他参数在连接到其他数据库时也会被复用。 + + + + + + + + + + 环境 + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + 默认连接参数 + + + + + + + 与大多数其他 PostgreSQL 工具一样,此工具也使用 + libpq 支持的环境变量(见 )。 + + + + + + + 诊断 + + + 如遇困难,请参见 中 + 关于潜在问题和错误消息的讨论。数据库服务器必须在目标主机上运行。此外, + libpq 前端库所使用的任何默认连接设置和环境变量也都会生效。 + + + + + + + 注解 + + vacuumdb可能需要多次连接到PostgreSQL服务器,每次都要询问密码。在这种情况下,使用~/.pgpass文件会比较方便。更多信息见 + + + + 示例 + + 清理数据库 test: + +$ vacuumdb test + + + + 清理并为优化器分析数据库 bigdb: + +$ vacuumdb --analyze bigdb + + + + 清理单个表 foo,它位于数据库 xyzzy 中,并为优化器分析该表的单列 bar +$ vacuumdb --analyze --verbose --table 'foo(bar)' xyzzy + + + + + + 另见 + + + + + + + diff --git a/zh/9.6/ref/values.sgml b/zh/9.6/ref/values.sgml new file mode 100644 index 00000000..ba17afe2 --- /dev/null +++ b/zh/9.6/ref/values.sgml @@ -0,0 +1,237 @@ + + + + + VALUES + + + + VALUES + 7 + SQL - 语言语句 + + + + VALUES + 计算一组行 + + + + +VALUES ( expression [, ...] ) [, ...] + [ ORDER BY sort_expression [ ASC | DESC | USING operator ] [, ...] ] + [ LIMIT { count | ALL } ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] + + + + + 描述 + + + VALUES计算由值表达式指定的一个行值或一组行值。 + 它最常用于在更大的命令中生成一个常量表,但也可以单独使用。 + + + + 当指定多于一行时,所有行都必须具有相同数量的元素。结果表各列的数据类型 + 由出现在该列中的表达式的显式类型或推断类型组合决定,所用规则与 + UNION相同(见)。 + + + + 在更大的命令中,从语法上说,凡是允许出现SELECT的地方, + 也都允许出现VALUES。因为在语法中它被当作 + SELECT处理,所以可以对VALUES + 命令使用ORDER BY、 + LIMIT(或者等效的FETCH FIRST) + 以及OFFSET子句。 + + + + + 参数 + + + + expression + + + 要在结果表(行集合)中相应位置计算并插入的常量或表达式。 + 在出现在INSERT顶层的 + VALUES列表中, + expression可以用 + DEFAULT替代,以表示应插入目标列的默认值。 + 当VALUES出现在其他上下文中时,不能使用 + DEFAULT。 + + + + + + sort_expression + + + 一个指示结果行如何排序的表达式或整数常量。该表达式 + 可以将VALUES结果的列引用为column1、 + column2等。详细信息参见 + 。 + + + + + + operator + + + 一个排序操作符。详细信息参见 + 。 + + + + + + count + + + 返回的最大行数。详细信息参见 + 。 + + + + + + start + + + 开始返回行之前要跳过的行数。详细信息参见 + 。 + + + + + + + + 注解 + + + 应避免使用行数非常多的VALUES列表,因为这可能导致 + 内存不足错误或性能不佳。出现在INSERT中的 + VALUES属于一种特殊情况(因为所需的列类型 + 可以从INSERT的目标表得知,并且无须通过扫描 + VALUES列表来推断),因此它能处理比其他上下文中 + 实际可行的更大的列表。 + + + + + 示例 + + + 一个独立的VALUES命令: + + +VALUES (1, 'one'), (2, 'two'), (3, 'three'); + + + 这将返回一个两列三行的表。它实际上等效于: + + +SELECT 1 AS column1, 'one' AS column2 +UNION ALL +SELECT 2, 'two' +UNION ALL +SELECT 3, 'three'; + + + + + + 更常见的是,VALUES会用在更大的 SQL 命令中。 + 最常见的用法是在INSERT中: + + +INSERT INTO films (code, title, did, date_prod, kind) + VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama'); + + + + + 在INSERT的上下文中,VALUES列表中 + 的项可以写成DEFAULT,表示此处应使用该列的默认值, + 而不是显式指定一个值: + + +INSERT INTO films VALUES + ('UA502', 'Bananas', 105, DEFAULT, 'Comedy', '82 minutes'), + ('T_601', 'Yojimbo', 106, DEFAULT, 'Drama', DEFAULT); + + + + + VALUES也可以用在原本可以编写子SELECT的地方, + 例如在FROM子句中: + + +SELECT f.* + FROM films f, (VALUES('MGM', 'Horror'), ('UA', 'Sci-Fi')) AS t (studio, kind) + WHERE f.studio = t.studio AND f.kind = t.kind; + +UPDATE employees SET salary = salary * v.increase + FROM (VALUES(1, 200000, 1.2), (2, 400000, 1.4)) AS v (depno, target, increase) + WHERE employees.depno = v.depno AND employees.sales >= v.target; + + + 注意,在FROM子句中使用VALUES时, + 需要有一个AS子句,这一点与SELECT相同。 + 虽然AS子句不要求为所有列都指定名称,但这样做是一个好习惯。 + (在PostgreSQL中,VALUES的默认列名是 + column1column2等,但在其他数据库系统中, + 这些名称可能不同。) + + + + 当在INSERT中使用VALUES时,所有值都会 + 自动转换为相应目标列的数据类型。当它用于其他上下文时,可能有必要指定 + 正确的数据类型。如果各项都是带引号的字符串常量,只需转换第一项, + 就足以为所有项确定假定的数据类型: + + +SELECT * FROM machines +WHERE ip_address IN (VALUES('192.168.0.1'::inet), ('192.168.0.10'), ('192.168.1.43')); + + + + + 对于简单的IN测试,最好使用IN的 + 标量列表形式, + 而不是写一个像上面那样的VALUES查询。标量列表方法 + 写法更简洁,而且通常效率更高。 + + + + + + 兼容性 + + VALUES符合 SQL 标准。 + LIMITOFFSET是 + PostgreSQL扩展,另见 + 。 + + + + + 另见 + + + + + + + diff --git a/zh/9.6/reference.sgml b/zh/9.6/reference.sgml new file mode 100644 index 00000000..83525117 --- /dev/null +++ b/zh/9.6/reference.sgml @@ -0,0 +1,259 @@ + + + + 参考 + + + + + 本参考中的各条目旨在以适当的篇幅,对各自的主题给出权威、完整且正式的总结。关于如何使用PostgreSQL的更多信息,例如叙述性说明、教程或示例,可在本书的其他部分找到。另见各参考页列出的交叉引用。 + + + + 这些参考条目也提供传统的man手册页形式。 + + + + + SQL 命令 + + + + + 本部分包含PostgreSQL支持的SQL命令的参考信息。这里的SQL指的是一般意义上的该语言;每条命令的标准符合性和兼容性信息可在相应的参考页中找到。 + + + + &abort; + &alterAggregate; + &alterCollation; + &alterConversion; + &alterDatabase; + &alterDefaultPrivileges; + &alterDomain; + &alterEventTrigger; + &alterExtension; + &alterForeignDataWrapper; + &alterForeignTable; + &alterFunction; + &alterGroup; + &alterIndex; + &alterLanguage; + &alterLargeObject; + &alterMaterializedView; + &alterOperator; + &alterOperatorClass; + &alterOperatorFamily; + &alterPolicy; + &alterRole; + &alterRule; + &alterSchema; + &alterSequence; + &alterServer; + &alterSystem; + &alterTable; + &alterTableSpace; + &alterTSConfig; + &alterTSDictionary; + &alterTSParser; + &alterTSTemplate; + &alterTrigger; + &alterType; + &alterUser; + &alterUserMapping; + &alterView; + &analyze; + &begin; + &checkpoint; + &close; + &cluster; + &commentOn; + &commit; + &commitPrepared; + ©Table; + &createAccessMethod; + &createAggregate; + &createCast; + &createCollation; + &createConversion; + &createDatabase; + &createDomain; + &createEventTrigger; + &createExtension; + &createForeignDataWrapper; + &createForeignTable; + &createFunction; + &createGroup; + &createIndex; + &createLanguage; + &createMaterializedView; + &createOperator; + &createOperatorClass; + &createOperatorFamily; + &createPolicy; + &createRole; + &createRule; + &createSchema; + &createSequence; + &createServer; + &createTable; + &createTableAs; + &createTableSpace; + &createTSConfig; + &createTSDictionary; + &createTSParser; + &createTSTemplate; + &createTransform; + &createTrigger; + &createType; + &createUser; + &createUserMapping; + &createView; + &deallocate; + &declare; + &delete; + &discard; + &do; + &dropAccessMethod; + &dropAggregate; + &dropCast; + &dropCollation; + &dropConversion; + &dropDatabase; + &dropDomain; + &dropEventTrigger; + &dropExtension; + &dropForeignDataWrapper; + &dropForeignTable; + &dropFunction; + &dropGroup; + &dropIndex; + &dropLanguage; + &dropMaterializedView; + &dropOperator; + &dropOperatorClass; + &dropOperatorFamily; + &dropOwned; + &dropPolicy; + &dropRole; + &dropRule; + &dropSchema; + &dropSequence; + &dropServer; + &dropTable; + &dropTableSpace; + &dropTSConfig; + &dropTSDictionary; + &dropTSParser; + &dropTSTemplate; + &dropTransform; + &dropTrigger; + &dropType; + &dropUser; + &dropUserMapping; + &dropView; + &end; + &execute; + &explain; + &fetch; + &grant; + &importForeignSchema; + &insert; + &listen; + &load; + &lock; + &move; + ¬ify; + &prepare; + &prepareTransaction; + &reassignOwned; + &refreshMaterializedView; + &reindex; + &releaseSavepoint; + &reset; + &revoke; + &rollback; + &rollbackPrepared; + &rollbackTo; + &savepoint; + &securityLabel; + &select; + &selectInto; + &set; + &setConstraints; + &setRole; + &setSessionAuth; + &setTransaction; + &show; + &startTransaction; + &truncate; + &unlisten; + &update; + &vacuum; + &values; + + + + + PostgreSQL 客户端应用 + + + + + 本部分包含PostgreSQL客户端应用和工具的参考信息。并非所有这些命令都适用于一般用途;有些可能需要特殊权限。这些应用的共同点是,它们都可以在任意主机上运行,而不受数据库服务器所在位置的限制。 + + + + 在命令行中指定用户名和数据库名时,其大小写会被保留 — 如果其中包含空格或特殊字符,可能需要加引号。除非文档另有说明,表名和其他标识符的大小写不会被保留,而且也可能需要加引号。 + + + + &clusterdb; + &createdb; + &createlang; + &createuser; + &dropdb; + &droplang; + &dropuser; + &ecpgRef; + &pgBasebackup; + &pgbench; + &pgConfig; + &pgDump; + &pgDumpall; + &pgIsready; + &pgReceivexlog; + &pgRecvlogical; + &pgRestore; + &psqlRef; + &reindexdb; + &vacuumdb; + + + + + PostgreSQL 服务器端应用 + + + + + 本部分包含PostgreSQL服务器端应用和支持工具的参考信息。这些命令只有在数据库服务器所在的主机上运行时才有意义。其他工具程序列在中。 + + + + &initdb; + &pgarchivecleanup; + &pgControldata; + &pgCtl; + &pgResetxlog; + &pgRewind; + &pgtestfsync; + &pgtesttiming; + &pgupgrade; + &pgxlogdump; + &postgres; + &postmaster; + + + + diff --git a/zh/9.6/regress.sgml b/zh/9.6/regress.sgml new file mode 100644 index 00000000..c51ce157 --- /dev/null +++ b/zh/9.6/regress.sgml @@ -0,0 +1,521 @@ + + + + 回归测试 + + + regression tests + + + + test + + + + 回归测试是针对PostgreSQL中 SQL + 实现的一套全面测试。它们既测试标准 SQL 操作,也测试 + PostgreSQL的扩展能力。 + + + + 运行测试 + + + 回归测试既可以针对已经安装并正在运行的服务器执行,也可以在构建树中 + 使用临时安装来执行。此外,运行测试时还有parallel和 + sequential两种模式。顺序方式一次只运行一个测试脚本, + 而并行方式会启动多个服务器进程,以并行方式运行一组测试。并行测试能 + 进一步确认进程间通信和锁定机制是否正常工作。 + + + + 针对临时安装运行测试 + + 要在构建完成后、安装之前运行并行回归测试,请输入: +make check +运行位置为顶层目录。(也可以切换到src/test/regress并在那里运行此命令。)结束时,应该会看到类似下面的结果: + +======================= + All 115 tests passed. +======================= + +否则会显示哪些测试失败的说明。请参阅中的下文说明,不要过早认定一次失败就意味着存在严重问题。 + + + 由于这种测试方法会运行一个临时服务器,如果你以 root 用户身份进行构建, + 它将无法工作,因为服务器不会以 root 身份启动。推荐做法是不要以 root + 身份构建;否则应在安装完成后再执行测试。 + + + + 如果你把PostgreSQL配置为安装到某个已经存在旧版 + PostgreSQL安装的位置,并且在安装新版本之前执行 + make check,可能会发现测试失败,因为新程序会尝试使用 + 已安装的共享库。(典型症状是报出未定义符号。)如果你希望在覆盖旧安装 + 之前运行测试,就需要使用configure --disable-rpath + 进行构建。不过,不建议在最终安装时使用这个选项。 + + + + 并行回归测试会在你的用户 ID 下启动相当多的进程。目前最大并发度为二十个 + 并行测试脚本,也就是四十个进程:每个测试脚本都有一个服务器进程和一个 + psql进程。因此,如果你的系统对每个用户可创建的 + 进程数施加限制,请确保该限制至少有五十个左右,否则并行测试中可能会出现 + 看似随机的失败。如果无法提高该限制,可以通过设置MAX_CONNECTIONS + 参数来降低并行度。例如: + +make MAX_CONNECTIONS=10 check + + 这样一次最多只会并发运行十个测试。 + + + + + 针对现有安装运行测试 + + 要在安装之后运行测试(见),请初始化一个数据区域并启动服务器,具体说明见,然后输入: +make installcheck +或者,要并行测试: +make installcheck-parallel +测试默认通过本地主机和默认端口号连接服务器,除非通过PGHOSTPGPORT环境变量另行指定。测试将在名为regression的数据库中运行;任何现有的同名数据库都会被删除。 + + 这些测试还会临时创建一些集簇级对象,例如角色和表空间。这些对象的名称都会以 regress_ 开头。在存在这样命名的真实用户或表空间的安装中,应谨慎使用 installcheck 模式。 + + + + 附加测试套件 + + make checkmake installcheck 命令只运行核心回归测试,这些测试用于检验 PostgreSQL 服务器的内置功能。源码发行版还包含其他测试套件,其中大部分与附加功能有关,例如可选的过程语言。 + + 要运行适用于已选定构建模块的全部测试套件(包括核心测试),请在构建树顶层输入下列命令之一: +make check-world +make installcheck-world +这些命令分别使用临时服务器或已安装的服务器运行测试,与前面对make checkmake installcheck的说明相同。其他注意事项也与前面对各个方法的说明相同。注意,make check-world会为每个被测试的模块构建一个独立的临时安装树,因此需要多得多的时间和磁盘空间,相比之下耗用较少的是make installcheck-world。 + + + + 或者,也可以在构建树相应的子目录中执行make check + 或make installcheck来运行单个测试套件。请记住, + make installcheck假定你已经安装了相关模块,而不仅仅 + 是核心服务器。 + + + + 可以按这种方式调用的附加测试包括: + + + + + 可选过程语言的回归测试(PL/pgSQL 除外,它由核心测试负责)。这些测试位于 src/pl 下。 + + + + contrib模块的回归测试,位于 + contrib下。并非所有contrib + 模块都有测试。 + + + + ECPG 接口库的回归测试,位于 src/interfaces/ecpg/test + + + + 针对并发会话行为的压力测试,位于src/test/isolation。 + + + + 客户端程序的测试,位于 src/bin 下。另见 + + + + 使用 installcheck 模式时,这些测试会销毁任何名为 pl_regressioncontrib_regressionisolation_regressionecpg1_regressionecpg2_regression 的现有数据库,以及名为 regression 的现有数据库。 + + + + + 区域设置和编码 + + + 默认情况下,使用临时安装的测试会采用当前环境中定义的区域设置,以及由 + initdb决定的相应数据库编码。通过设置适当的环境变量 + 来测试不同的区域设置可能很有用,例如: + +make check LANG=C +make check LC_COLLATE=en_US.utf8 LC_CTYPE=fr_CA.utf8 + + 出于实现原因,设置LC_ALL不能用于此目的;其他所有与 + 区域设置相关的环境变量都可以。 + + + + 在针对现有安装测试时,区域设置由现有数据库集簇决定,不能为测试单独设置。 + + + + 也可以通过设置变量ENCODING来显式选择数据库编码,例如: + +make check LANG=C ENCODING=EUC_JP + + 只有在区域设置为 C 时,这样设置数据库编码通常才有意义;否则编码会从 + 区域设置自动选择,而指定与区域设置不匹配的编码会导致错误。 + + + + 无论是针对临时安装还是现有安装测试,都可以设置数据库编码;不过在后一种 + 情况下,它必须与该安装的区域设置兼容。 + + + + + 额外测试 + + 核心回归测试套件包含几个默认不运行的测试文件,因为它们可能依赖于平台,或者运行时间很长。可以通过设置以下变量来运行这些或其他额外的测试文件:EXTRA_TESTS。例如,要运行numeric_big测试: +make check EXTRA_TESTS=numeric_big +要运行排序规则测试: +make check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8 +其中,collate.linux.utf8测试仅适用于 Linux/glibc 平台,且只有在使用 UTF-8 编码的数据库中运行时才会成功。 + + + + 测试热备 + + + 源代码分发包中还包含针对热备静态行为的回归测试。这些测试需要一个正在运行 + 的主库和一个正在运行的备库,备库正在从主库接收新的 WAL 更改(使用基于文件 + 的日志传送或流复制均可)。这些服务器不会自动创建,复制的设置也不在此处文档 + 说明。请查阅文档中有关所需命令和相关问题的各个章节。 + + + + 要运行热备测试,首先在主库上创建一个名为regression + 的数据库: + +psql -h primary -c "CREATE DATABASE regression" + + 接下来,在主库上的 regression 数据库中运行准备脚本 + src/test/regress/sql/hs_primary_setup.sql,例如: + +psql -h primary -f src/test/regress/sql/hs_primary_setup.sql regression + + 等待这些更改传播到备库。 + + + 现在将默认数据库连接指向要测试的备库(例如,设置 PGHOSTPGPORT 环境变量)。最后,在回归测试目录中运行 make standbycheck +cd src/test/regress +make standbycheck + + + + + 还可以使用脚本 + src/test/regress/sql/hs_primary_extremes.sql + 在主库上生成一些极端行为,以测试备库的行为。 + + + + + + 测试结果评估 + + + 某些安装正确且功能完备的PostgreSQL实例, + 可能会因为平台特有因素而在部分回归测试中失败,例如浮点 + 表示或消息措辞不同。目前这些测试是通过把输出与参考系统生成的输出做 + 简单的diff比较来评估的,因此结果会对细微的系统差异 + 很敏感。报告某项测试失败时,请务必检查预期结果与实际结果 + 之间的差异;你可能会发现这些差异并不重要。尽管如此,我们仍努力在所有 + 受支持平台上维护准确的参考文件,因此原则上应期待所有测试都能通过。 + + + + 回归测试的实际输出位于src/test/regress/results + 目录中的文件里。测试脚本使用diff将每个输出文件 + 与存放在src/test/regress/expected目录中的参考输出 + 进行比较。所有差异都会保存在 + src/test/regress/regression.diffs中供你检查。 + (运行核心测试以外的测试套件时,这些文件当然会出现在相应的子目录中, + 而不是src/test/regress。) + + + + 如果不喜欢默认使用的diff选项,可设置环境变量 + PG_REGRESS_DIFF_OPTS,例如 + PG_REGRESS_DIFF_OPTS='-u'。(或者如果你愿意, + 也可以自己运行diff。) + + + + 如果由于某种原因,某个特定平台在某个测试上产生了失败, + 但对输出的检查令你相信结果是有效的,那么可以新增一个比较文件,让后续 + 测试运行不再报告该失败。详情见。 + + + + 错误消息差异 + + + 某些回归测试包含故意构造的非法输入值。错误消息可能来自 + PostgreSQL代码,也可能来自宿主平台的系统例程。 + 在后一种情况下,消息会因平台而异,但应反映相近的信息。这类消息差异 + 会导致回归测试显示为失败,但可以通过检查确认其有效性。 + + + + + 区域设置差异 + + + 如果你针对一个使用 C 以外排序规则顺序的区域设置初始化的服务器运行测试, + 就可能因为排序顺序不同而出现差异,并导致后续失败。回归测试套件通过 + 提供备用结果文件来处理这个问题;这些文件已知可以覆盖大量区域设置。 + + + + 使用临时安装方法时,要在不同区域设置下运行测试,可在 + make命令行上传递合适的区域设置相关环境变量,例如: + +make check LANG=de_DE.utf8 + + (回归测试驱动器会取消设置LC_ALL,因此不能用该变量 + 选择区域设置。)如果不使用区域设置,要么取消所有与区域设置相关的 + 环境变量(或将它们设为C),要么使用下面这个特殊调用: + +make check NO_LOCALE=1 + + 针对现有安装运行测试时,区域设置由现有安装决定。要改变它,需要在 + 初始化数据库集簇时向initdb传入合适选项,使用 + 另一种区域设置。 + + + + 一般来说,建议在计划用于生产环境的区域设置下运行回归测试,因为这样 + 可以覆盖实际将在生产中使用的区域设置和编码相关代码部分。根据操作系统 + 环境不同,可能会出现失败,但至少你会知道在真实应用运行时应当预期哪些 + 与区域设置相关的行为。 + + + + + 日期和时间差异 + + + 大多数日期和时间结果依赖于时区环境。参考文件是在时区 + PST8PDT(加利福尼亚州伯克利)下生成的,如果测试未在该时区设置 + 下运行,就会出现表面上的失败。回归测试驱动器会将环境变量 + PGTZ设为PST8PDT, + 这通常能够确保获得正确结果。 + + + + + 浮点差异 + + + 某些测试涉及根据表列计算 64 位浮点数(double + precision)。我们已经观察到涉及double + precision列数学函数的结果存在差异。float8和 + geometry测试尤其容易在不同平台之间,甚至在不同 + 编译器优化设置下出现细微差异。要判断这些通常出现在小数点右侧第 10 位 + 附近的差异究竟是否重要,需要人工目测比较。 + + + + 某些系统把负零显示为-0,而另一些只显示 + 0。 + + + + 某些系统对pow()exp() + 发出错误的方式,与当前PostgreSQL代码所 + 预期的机制不同。 + + + + + 行顺序差异 + + +你可能会看到这样的差异:同样的行在输出中的顺序与预期文件中的顺序不同。 +在大多数情况下,严格说来这并不是缺陷。大多数回归测试脚本并没有细致到 +为每一个SELECT都使用ORDER BY, +因此按 SQL 规范,它们的结果行顺序并没有良好定义。实际上,由于我们看到的 +是同一软件在同一数据上执行相同查询,通常在所有平台上都会得到相同的结果 +顺序,所以缺少ORDER BY并不是问题。不过,有些查询 +确实会表现出跨平台的顺序差异。针对已安装服务器测试时,非 C 区域设置或 +非默认参数设置,例如自定义的work_mem值或规划器代价 +参数,也可能导致顺序差异。 + + + +因此,如果你看到顺序差异,一般无需担心,除非查询确实包含 +ORDER BY而你的结果违反了它。不过,仍请报告该问题, +这样我们可以为那个特定查询加上ORDER BY,以在后续 +版本中消除这种虚假的失败。 + + + +你可能会好奇,为什么我们不显式地为所有回归测试查询排序,从而一劳永逸地 +解决这个问题。原因在于,那样反而会降低回归测试的价值,因为测试会倾向于 +覆盖能产生有序结果的查询计划类型,而排除那些不能产生有序结果的计划类型。 + + + + + 栈深度不足 + + + 如果errors测试在执行 + select infinite_recurse()命令时导致服务器崩溃, + 这意味着平台对进程栈大小的限制比 + 参数所表明的更小。可通过在更高的栈大小限制下运行服务器来修复 + (对于max_stack_depth的默认值,建议 4MB)。 + 如果做不到,另一种办法是减小max_stack_depth + 的值。 + + + + 在支持getrlimit()的平台上,服务器应自动选择 + max_stack_depth的安全值;因此,除非你手工覆盖了 + 该设置,否则这类失败就是一个应报告的缺陷。 + + + + + <quote>random</quote> 测试 + + + random测试脚本本来就是要产生随机结果的。在极少数 + 情况下,这会导致该回归测试失败。输入: + +diff results/random.out expected/random.out + + 通常只应产生一两行差异。除非 random 测试反复失败, + 否则不必担心。 + + + + + 配置参数 + + + 在针对现有安装运行测试时,某些非默认参数设置可能导致测试失败。例如, + 改变enable_seqscan或 + enable_indexscan等参数,可能导致计划发生变化, + 进而影响使用EXPLAIN的测试结果。 + + + + + + + 变体比较文件 + + + 由于某些测试天生会产生依赖环境的结果,我们提供了指定备用 + 预期结果文件的方法。每个回归测试都可以有多个比较文件, + 用来展示不同平台上的可能结果。对于每个测试应使用哪个比较文件,有两种 + 相互独立的判定机制。 + + + + 第一种机制允许针对特定平台选择比较文件。有一个映射文件 + src/test/regress/resultmap,用于定义每个平台应使用 + 哪个比较文件。要为某个特定平台消除虚假的测试失败, + 你需要先选择或创建一个变体结果文件,然后在resultmap + 文件中增加一行。 + + + + 映射文件中的每一行都采用如下形式: + +testname:output:platformpattern=comparisonfilename + + 测试名就是相应回归测试模块的名称。输出值指明要检查哪个输出文件。 + 对标准回归测试而言,这里始终是out。该值对应于 + 输出文件的扩展名。平台模式是 Unix 工具expr风格 + 的模式(也就是在开头隐含^锚点的正则表达式)。 + 它会与config.guess打印出的平台名进行匹配。 + 比较文件名则是替代结果比较文件的基名。 + + + 例如:某些系统会把极小的浮点数值解释为零,而不是报告下溢错误。这会导致float8回归测试出现一些差异。因此,我们提供了一个变体比较文件float8-small-is-zero.out,其中包含这些系统上的预期结果。为了消除虚假的失败消息,在OpenBSD平台上,resultmap包含以下内容: +float8:out:i.86-.*-openbsd=float8-small-is-zero.out +如果一台机器上的config.guess输出匹配i.86-.*-openbsd,就会触发该规则。resultmap中的其他行会为其他适用的平台选择变体比较文件。 + + + 第二种用于选择变体比较文件的机制更加自动化:它只是在多个提供的比较文件 + 中选取最佳匹配。回归测试驱动脚本既考虑某个测试的标准 + 比较文件testname.out, + 也考虑名为 + testname_digit.out + 的变体文件(其中digit可以是任一单个数字 + 0-9)。如果其中任何一个文件 + 完全匹配,就认为测试通过;否则,会使用生成最短 diff 的那个文件来创建 + 失败报告。(如果resultmap为该特定测试包含一项, + 那么基础testname就是 + resultmap中给出的替代名称。) + + + + 例如,对于char测试,比较文件 + char.out包含在C和 + POSIX区域设置中应当出现的结果,而文件 + char_1.out则包含在许多其他区域设置中出现的排序结果。 + + + + 最佳匹配机制原本是为了解决依赖区域设置的结果问题,但它也可以用于任何 + 仅凭平台名无法轻易预测测试结果的场景。该机制的一个局限是,测试驱动器 + 无法判断哪一个变体对当前环境才真正正确;它只会选择 + 看起来最合适的那个。因此,最安全的做法是只对那些你愿意在所有上下文中 + 都视为同样有效的变体结果使用这种机制。 + + + + + + TAP 测试 + + + src/bin下的客户端程序测试使用 Perl TAP 工具,并由 + prove运行。你可以通过设置make变量 + PROVE_FLAGS来向prove传递命令行选项, + 例如: + +make -C src/bin check PROVE_FLAGS='--reverse' + + 默认值是--verbose。更多信息请参见prove的手册页。 + + + + 这些用 Perl 编写的测试需要 Perl 模块IPC::Run。该模块可从 CPAN + 或操作系统软件包获得。此外,这些测试还要求 + PostgreSQL在配置时使用 + 选项。 + + + + + 测试覆盖率检查 + + + 可以用覆盖率测试插装编译 PostgreSQL 源代码,从而检查回归测试或任何 + 其他与该代码一起运行的测试套件覆盖了代码的哪些部分。目前,这只在使用 + GCC 编译时受支持,并且需要gcov和 + lcov程序。 + + + 一个典型的工作流程如下: +./configure --enable-coverage ... OTHER OPTIONS ... +make +make check # or other test suite +make coverage-html +然后将 HTML 浏览器指向coverage/index.html。这些make命令在子目录中也可以使用。 + + 要在各次测试运行之间重置执行计数,请运行: +make coverage-clean + + + + + diff --git a/zh/9.6/release-9.6.sgml b/zh/9.6/release-9.6.sgml new file mode 100644 index 00000000..cc68c2cc --- /dev/null +++ b/zh/9.6/release-9.6.sgml @@ -0,0 +1,21793 @@ + + + + + Release 9.6.24 + + + 发布日期: + 2021-11-11 + + + + 此版本包含自 9.6.23 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 这预计是 9.6.X 系列的最后一个 PostgreSQL 版本。 + 建议用户尽快更新到较新的版本分支。 + + + + 迁移到版本 9.6.24 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + 不过请注意,使用物理复制的安装应先更新备库服务器再更新主库服务器, + 如下面第三条变更日志条目所述。 + + + + 此外,还发现了几个可能导致索引损坏的 bug,如接下来几条变更日志条目所述。 + 如果其中任何情况适用于你,建议在更新之后对可能受影响的索引执行 + REINDEX。 + + + + 另外,如果你是从 9.6.21 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + 让服务器拒绝 SSL 或 GSS 加密握手之后的多余数据(Tom Lane) + + + + 能够向 TCP 连接注入数据的中间人,可以在本应受加密保护的数据库会话的 + 开头塞入一些明文数据。这可能被滥用以向服务器发送伪造的 SQL 命令, + 不过只有当服务器不要求任何认证数据时才会奏效。(然而,依赖 SSL 证书 + 认证的服务器很可能确实不要求。) + + + + PostgreSQL 项目感谢 Jacob Champion 报告此问题。 + (CVE-2021-23214) + + + + + + + 让 libpq 拒绝 SSL 或 GSS 加密握手之后 + 的多余数据(Tom Lane) + + + + 能够向 TCP 连接注入数据的中间人,可以在本应受加密保护的数据库会话的 + 开头塞入一些明文数据。这也许可被滥用以向客户端的前几个查询注入伪造 + 的响应,不过 libpq 行为的其他细节使这比听起来更难。另一种攻击思路 + 是窃取客户端的密码或会话早期可能发送的其他敏感数据。已经证明,对于 + 存在 CVE-2021-23214 漏洞的服务器,这是可能的。 + + + + PostgreSQL 项目感谢 Jacob Champion 报告此问题。 + (CVE-2021-23222) + + + + + + + Fix physical replication for cases where the primary crashes + after shipping a WAL segment that ends with a partial WAL record + (Álvaro Herrera) + + + + If the primary did not survive long enough to finish writing the + rest of the incomplete WAL record, then the previous crash-recovery + logic had it back up and overwrite WAL starting from the beginning + of the incomplete WAL record. This is problematic since standby + servers may already have copies of that WAL segment. They will then + see an inconsistent next segment, and will not be able to recover + without manual intervention. To fix, do not back up over a WAL + segment boundary when restarting after a crash. Instead write a new + type of WAL record at the start of the next WAL segment, informing + readers that the incomplete WAL record will never be finished and + must be disregarded. + + + + When applying this update, it's best to update standby servers + before the primary, so that they will be ready to handle this new + WAL record type if the primary happens to crash. + + + + + + + Fix CREATE INDEX CONCURRENTLY to wait for + the latest prepared transactions (Andrey Borodin) + + + + Rows inserted by just-prepared transactions might be omitted from + the new index, causing queries relying on the index to miss such + rows. The previous fix for this type of problem failed to account + for PREPARE TRANSACTION commands that were still + in progress when CREATE INDEX CONCURRENTLY + checked for them. As before, in installations that have enabled + prepared transactions (max_prepared_transactions + > 0), it's recommended to reindex any concurrently-built indexes + in case this problem occurred when they were built. + + + + + + + Avoid race condition that can cause backends to fail to add entries + for new rows to an index being built concurrently (Noah Misch, + Andrey Borodin) + + + + While it's apparently rare in the field, this case could potentially + affect any index built or reindexed with + the CONCURRENTLY option. It is recommended to + reindex any such indexes to make sure they are correct. + + + + + + + Fix float4 and float8 hash functions to + produce uniform results for NaNs (Tom Lane) + + + + Since PostgreSQL's floating-point types + deem all NaNs to be equal, it's important for the hash functions to + produce the same hash code for all bit-patterns that are NaNs + according to the IEEE 754 standard. This failed to happen before, + meaning that hash indexes and hash-based query plans might produce + incorrect results for non-canonical NaN values. + ('-NaN'::float8 is one way to produce such a + value on most machines.) It is advisable to reindex hash indexes + on floating-point columns, if there is any possibility that they + might contain such values. + + + + + + + Prevent data loss during crash recovery of CREATE + TABLESPACE, when wal_level + = minimal (Noah Misch) + + + + If the server crashed between CREATE TABLESPACE + and the next checkpoint, replay would fully remove the contents of + the new tablespace's directory, relying on subsequent WAL replay + to restore everything within that directory. This interacts badly + with optimizations that skip writing WAL (one example + is COPY into a just-created table). Such + optimizations are applied only when wal_level + is minimal, which is not the default in v10 and + later. + + + + + + + 不要丢弃目标为未指定类型修饰符的同一类型的转换(Tom Lane) + + + + 例如,如果列 f1 的类型是 + numeric(18,3),分析器过去会以它不会产生运行时 + 效果为由,直接丢弃 f1::numeric 这样的强制转换。 + 这没错,但表达式呈现出的类型仍应被视为普通 + numeric,而不是 numeric(18,3)。 + 这对于正确解析更大的构造(例如递归 UNION)的类型 + 很重要。 + + + + + + + Fix corner-case loss of precision in + numeric power() (Dean Rasheed) + + + + 当第一个参数非常接近 1 时,结果可能不准确。 + + + + + + + 避免捕获圆括号位于 {0} 内部时引发的正则表达式 + 错误(Tom Lane) + + + + 形如 (.){0}...\1 的正则表达式会报 + invalid backreference number。其他正则引擎(例如 TCL) + 在这里不报错,因此这种模式可能在应用中被使用。 + + + + + + + Prevent regular expression back-references from sometimes matching + when they shouldn't (Tom Lane) + + + + The regexp engine was careless about clearing match data + for capturing parentheses after rejecting a partial match. This + could allow a later back-reference to match in places where it + should fail for lack of a defined referent. + + + + + + + Fix regular expression performance bug with back-references inside + iteration nodes (Tom Lane) + + + + Incorrect back-tracking logic could result in exponential time spent + looking for a match. Fortunately the problem is masked in most + cases by other optimizations. + + + + + + + Fix incorrect results from AT TIME ZONE applied + to a time with time zone value (Tom Lane) + + + + The results were incorrect if the target time zone was specified by + a dynamic timezone abbreviation (that is, one that is defined as + equivalent to a full time zone name, rather than a fixed UTC offset). + + + + + + + Clean up correctly if a transaction fails after exporting its + snapshot (Dilip Kumar) + + + + This oversight would only cause a problem if the same session + attempted to export a snapshot again. The most likely scenario for + that is creation of a replication slot (followed by rollback) + and then creation of another replication slot. + + + + + + + Prevent wraparound of overflowed-subtransaction tracking on standby + servers (Kyotaro Horiguchi, Alexander Korotkov) + + + + This oversight could cause significant performance degradation + (manifesting as excessive SubtransSLRU traffic) on standby servers. + + + + + + + Ensure that prepared transactions are properly accounted for during + promotion of a standby server (Michael Paquier, Andres Freund) + + + + There was a narrow window where a prepared transaction could be + omitted from a snapshot taken by a concurrently-running session. + If that session then used the snapshot to perform data updates, + erroneous results or data corruption could occur. + + + + + + + Fix detection of a relation that has grown to the maximum allowed + length (Tom Lane) + + + + An attempt to extend a table or index past the limit of 2^32-1 + blocks was rejected, but not soon enough to prevent inconsistent + internal state from being created. + + + + + + + Correctly track the presence of data-modifying CTEs when expanding + a DO INSTEAD rule (Greg Nancarrow, Tom Lane) + + + + The previous failure to do this could lead to problems such as + unsafely choosing a parallel plan. + + + + + + + Ensure that walreceiver processes create all required archive + notification files before exiting (Fujii Masao) + + + + If a walreceiver exited exactly at a WAL segment boundary, it failed + to make a notification file for the last-received segment, thus + delaying archiving of that segment on the standby. + + + + + + + Avoid trying to lock the OLD + and NEW pseudo-relations in a rule + that uses SELECT FOR UPDATE + (Masahiko Sawada, Tom Lane) + + + + + + + Fix parser's processing of aggregate FILTER + clauses (Tom Lane) + + + + If the FILTER expression is a plain boolean column, + the semantic level of the aggregate could be mis-determined, leading + to not-per-spec behavior. If the FILTER + expression is itself a boolean-returning aggregate, an error should + be thrown but was not, likely resulting in a crash at execution. + + + + + + + Avoid null-pointer-dereference crash when dropping a role that owns + objects being dropped concurrently (Álvaro Herrera) + + + + + + + Prevent snapshot reference leak warning + when lo_export() or a related function fails + (Heikki Linnakangas) + + + + + + + Ensure that scans of SP-GiST indexes are counted in the statistics + views (Tom Lane) + + + + Incrementing the number-of-index-scans counter was overlooked in the + SP-GiST code, although per-tuple counters were advanced correctly. + + + + + + + Recalculate relevant wait intervals + if recovery_min_apply_delay is changed during + recovery (Soumyadeep Chakraborty, Ashwin Agrawal) + + + + + + + Fix ecpg to recover correctly + after malloc() failure while establishing a + connection (Michael Paquier) + + + + + + + Allow EXIT out of the outermost block in a + PL/pgSQL routine (Tom Lane) + + + + If the routine does not require an explicit RETURN, + this usage should be valid, but it was rejected. + + + + + + + Remove pg_ctl's hard-coded limits on the + total length of generated commands (Phil Krylov) + + + + For example, this removes a restriction on how many command-line + options can be passed through to the postmaster. Individual path + names that pg_ctl deals with, such as the + postmaster executable's name or the data directory name, are still + limited to MAXPGPATH bytes in most cases. + + + + + + + Fix pg_dump to dump non-global default + privileges correctly (Neil Chen, Masahiko Sawada) + + + + If a global (unrestricted) ALTER DEFAULT + PRIVILEGES command revoked some present-by-default + privilege, for example EXECUTE for functions, and + then a restricted ALTER DEFAULT PRIVILEGES + command granted that privilege again for a selected role or + schema, pg_dump failed to dump the + restricted privilege grant correctly. + + + + + + + Improve pg_dump's performance by avoiding + making per-table queries for RLS policies, and by avoiding repetitive + calls to format_type() (Tom Lane) + + + + These changes provide only marginal improvement when dumping from a + local server, but a dump from a remote server can benefit + substantially due to fewer network round-trips. + + + + + + + Fix incorrect filename in pg_restore's + error message about an invalid large object TOC file (Daniel + Gustafsson) + + + + + + + Fix failure of contrib/btree_gin indexes + on "char" + (not char(n)) columns, + when an indexscan using the < + or <= operator is performed (Tom Lane) + + + + Such an indexscan failed to return all the entries it should. + + + + + + + Change contrib/pg_stat_statements to read + its query texts file in units of at most 1GB + (Tom Lane) + + + + Such large query text files are very unusual, but if they do occur, + the previous coding would fail on Windows 64 (which rejects + individual read requests of more than 2GB). + + + + + + + Fix null-pointer crash + when contrib/postgres_fdw tries to report a + data conversion error (Tom Lane) + + + + + + + Add spinlock support for the RISC-V architecture (Marek Szuba) + + + + This is essential for reasonable performance on that platform. + + + + + + + Set correct type identifier on OpenSSL BIO (I/O abstraction) + objects created by PostgreSQL + (Itamar Gafni) + + + + This oversight probably only matters for code that is doing + tasks like auditing the OpenSSL installation. But it's + nominally a violation of the OpenSSL API, so fix it. + + + + + + + Make pg_regexec() robust against an + out-of-range search_start parameter + (Tom Lane) + + + + Return REG_NOMATCH, instead of possibly crashing, + when search_start is past the end of the + string. This case is probably unreachable within + core PostgreSQL, but extensions might be + more careless about the parameter value. + + + + + + + Ensure that GetSharedSecurityLabel() can be + used in a newly-started session that has not yet built its critical + relation cache entries (Jeff Davis) + + + + + + + Use the CLDR project's data to map Windows time zone names to IANA + time zones (Tom Lane) + + + + When running on Windows, initdb attempts + to set the new cluster's timezone parameter to + the IANA time zone matching the system's prevailing time zone. + We were using a mapping table that we'd generated years ago and + updated only fitfully; unsurprisingly, it contained a number of + errors as well as omissions of recently-added zones. It turns out + that CLDR has been tracking the most appropriate mappings, so start + using their data. This change will not affect any existing + installation, only newly-initialized clusters. + + + + + + + Update time zone data files to tzdata + release 2021e for DST law changes in Fiji, Jordan, Palestine, and + Samoa, plus historical corrections for Barbados, Cook Islands, + Guyana, Niue, Portugal, and Tonga. + + + + Also, the Pacific/Enderbury zone has been renamed to Pacific/Kanton. + Also, the following zones have been merged into nearby, more-populous + zones whose clocks have agreed with them since 1970: Africa/Accra, + America/Atikokan, America/Blanc-Sablon, America/Creston, + America/Curacao, America/Nassau, America/Port_of_Spain, + Antarctica/DumontDUrville, and Antarctica/Syowa. + In all these cases, the previous zone name remains as an alias. + + + + + + + + + + Release 9.6.23 + + + 发布日期: + 2021-08-12 + + + + 此版本包含自 9.6.22 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + The PostgreSQL community will stop + releasing updates for the 9.6.X release series in November 2021. + Users are encouraged to update to a newer release branch soon. + + + + 迁移到版本 9.6.23 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.21, + see . + + + + + 变更 + + + + + + + Disallow SSL renegotiation more completely (Michael Paquier) + + + + SSL renegotiation has been disabled for some time, but the server + would still cooperate with a client-initiated renegotiation request. + A maliciously crafted renegotiation request could result in a server + crash (see OpenSSL issue CVE-2021-3449). Disable the feature + altogether on OpenSSL versions that permit doing so, which are + 1.1.0h and newer. + + + + + + + Reject SELECT ... GROUP BY GROUPING SETS (()) FOR + UPDATE (Tom Lane) + + + + This should be disallowed, just as FOR UPDATE + with a plain GROUP BY is disallowed, but the test + for that failed to handle empty grouping sets correctly. + The end result would be a null-pointer dereference in the executor. + + + + + + + Reject cases where a query in WITH + rewrites to just NOTIFY (Tom Lane) + + + + Such cases previously crashed. + + + + + + + In numeric multiplication, round the result rather than + failing if it would have more than 16383 digits after the decimal + point (Dean Rasheed) + + + + + + + Fix corner-case errors and loss of precision when + raising numeric values to very large powers + (Dean Rasheed) + + + + + + + Fix division-by-zero failure in to_char() + with EEEE format and a numeric input + value less than 10^(-1001) (Dean Rasheed) + + + + + + + Fix pg_size_pretty(bigint) to round negative + values consistently with the way it rounds positive ones (and + consistently with the numeric version) (Dean Rasheed, + David Rowley) + + + + + + + Make pg_filenode_relation(0, 0) return NULL + rather than failing (Justin Pryzby) + + + + + + + Make ALTER EXTENSION lock the extension when + adding or removing a member object (Tom Lane) + + + + The previous coding allowed ALTER EXTENSION + ADD/DROP to occur concurrently with DROP + EXTENSION, leading to a crash or corrupt catalog entries. + + + + + + + Avoid alias conflicts in queries generated + for REFRESH MATERIALIZED VIEW CONCURRENTLY + (Tom Lane, Bharath Rupireddy) + + + + This command failed on materialized views containing columns with + certain names, notably mv + and newdata. + + + + + + + Fix PREPARE TRANSACTION to check correctly + for conflicting session-lifespan and transaction-lifespan locks + (Tom Lane) + + + + A transaction cannot be prepared if it has both session-lifespan and + transaction-lifespan locks on the same advisory-lock ID value. This + restriction was not fully checked, which could lead to a PANIC + during PREPARE TRANSACTION. + + + + + + + Fix misbehavior of DROP OWNED BY when the target + role is listed more than once in an RLS policy (Tom Lane) + + + + + + + Skip unnecessary error tests when removing a role from an RLS policy + during DROP OWNED BY (Tom Lane) + + + + Notably, this fixes some cases where it was necessary to be a + superuser to use DROP OWNED BY. + + + + + + + Allow index state flags to be updated transactionally + (Michael Paquier, Andrey Lepikhov) + + + + This avoids failures when dealing with index predicates that aren't + really immutable. While that's not considered a supported case, the + original reason for using a non-transactional update here is long + gone, so we may as well change it. + + + + + + + Avoid corrupting the plan cache entry when CREATE + DOMAIN or ALTER DOMAIN appears + in a cached plan (Tom Lane) + + + + + + + Make + pg_settings.pending_restart + show as true when the pertinent entry + in postgresql.conf has been removed + (Álvaro Herrera) + + + + pending_restart correctly showed the case + where an entry that cannot be changed without a postmaster restart + has been modified, but not where the entry had been removed + altogether. + + + + + + + Fix corner-case failure of a new standby to follow a new primary + (Dilip Kumar, Robert Haas) + + + + Under a narrow combination of conditions, the standby could wind up + trying to follow the wrong WAL timeline. + + + + + + + Update minimum recovery point when WAL replay of a transaction abort + record causes file truncation (Fujii Masao) + + + + File truncation is irreversible, so it's no longer safe to stop + recovery at a point earlier than that record. The corresponding + case for transaction commit was fixed years ago, but this one was + overlooked. + + + + + + + Ensure that a standby server's startup process will respond to a + shutdown signal promptly while waiting for WAL to arrive (Fujii + Masao, Soumyadeep Chakraborty) + + + + + + + Add locking to avoid reading incorrect relmapper data in the face of + a concurrent write from another process (Heikki Linnakangas) + + + + + + + Fix error cases and memory leaks in logical decoding of speculative + insertions (Dilip Kumar) + + + + + + + Fix plan cache reference leaks in some error cases in + CREATE TABLE ... AS EXECUTE (Tom Lane) + + + + + + + Fix possible race condition when releasing BackgroundWorkerSlots + (Tom Lane) + + + + It's likely that this doesn't fix any observable bug on Intel + hardware, but machines with weaker memory ordering rules could + have problems. + + + + + + + Fix latent crash in sorting code (Ronan Dunklau) + + + + One code path could attempt to free a null pointer. The case + appears unreachable in the core server's use of sorting, but perhaps + it could be triggered by extensions. + + + + + + + Prevent infinite loops in SP-GiST index insertion (Tom Lane) + + + + In the event that INCLUDE columns take up enough space to prevent a + leaf index tuple from ever fitting on a page, the text_ops operator + class would get into an infinite loop vainly trying to make the + tuple fit. + While pre-v11 versions don't have INCLUDE columns, back-patch this + anti-looping fix to them anyway, as it seems like a good defense + against bugs in operator classes. + + + + + + + Ensure that SP-GiST index insertion can be terminated by a query + cancel request (Tom Lane, Álvaro Herrera) + + + + + + + Fix uninitialized-variable bug that could + cause PL/pgSQL to act as though + an INTO clause + specified STRICT, even though it didn't + (Tom Lane) + + + + + + + Don't abort the process for an out-of-memory failure in libpq's + printing functions (Tom Lane) + + + + + + + In ecpg, allow the numeric + value INT_MIN (usually -2147483648) to be + converted to integer (John Naylor) + + + + + + + In psql and other client programs, avoid + overrunning the ends of strings when dealing with invalidly-encoded + data (Tom Lane) + + + + An incorrectly-encoded multibyte character near the end of a string + could cause various processing loops to run past the string's + terminating NUL, with results ranging from no detectable issue to + a program crash, depending on what happens to be in the following + memory. This is reminiscent of CVE-2006-2313, although these + particular cases do not appear to have interesting security + consequences. + + + + + + + Avoid invalid creation date in header warnings + observed when running pg_restore on an + archive file created in a different time zone (Tom Lane) + + + + + + + Make pg_upgrade carry forward the old + installation's oldestXID value (Bertrand Drouvot) + + + + Previously, the new installation's oldestXID was + set to a value old enough to (usually) force immediate + anti-wraparound autovacuuming. That's not desirable from a + performance standpoint; what's worse, installations using large + values of autovacuum_freeze_max_age could suffer + unwanted forced shutdowns soon after an upgrade. + + + + + + + Extend pg_upgrade to detect and warn + about extensions that should be upgraded (Bruce Momjian) + + + + A script file is now produced containing the ALTER + EXTENSION UPDATE commands needed to bring extensions up to + the versions that are considered default in the new installation. + + + + + + + In contrib/postgres_fdw, avoid attempting + catalog lookups after an error (Tom Lane) + + + + While this usually worked, it's not very safe since the error might + have been one that made catalog access nonfunctional. A side effect + of the fix is that messages about data conversion errors will now + mention the query's table and column aliases (if used) rather than + the true underlying name of a foreign table or column. + + + + + + + In contrib/pgcrypto, avoid symbol name + conflicts with OpenSSL (Tom Lane) + + + + Operations using SHA224 hashing could show failures under valgrind + checking. It appears that this is only a stomp of alignment-padding + bytes and so has no real consequences, but let's fix it to be sure. + + + + + + + Improve the isolation-test infrastructure (Tom Lane, Michael Paquier) + + + + Allow isolation test steps to be annotated to show the expected + completion order. This allows getting stable results from + otherwise-racy test cases, without the long delays that we + previously used (not entirely successfully) to fend off race + conditions. + Allow non-quoted identifiers as isolation test session/step names + (formerly, all such names had to be double-quoted). + Detect and warn about unused steps in isolation tests. + Improve display of query results in isolation tests. + Remove isolationtester's dry-run mode. + Remove memory leaks in isolationtester itself. + + + + + + + Reduce overhead of cache-clobber testing (Tom Lane) + + + + + + + Fix PL/Python's regression tests to pass + with Python 3.10 (Honza Horak) + + + + + + + Make printf("%s", NULL) + print (null) instead of crashing (Tom Lane) + + + + This should improve server robustness in corner cases, and it syncs + our printf implementation with common libraries. + + + + + + + Fix incorrect log message when point-in-time recovery stops at + a ROLLBACK PREPARED record (Simon Riggs) + + + + + + + Clarify error messages referring to non-negative + values (Bharath Rupireddy) + + + + + + + Fix configure to work with OpenLDAP 2.5, + which no longer has a separate libldap_r + library (Adrian Ho, Tom Lane) + + + + If there is no libldap_r library, we now + silently assume that libldap is thread-safe. + + + + + + + Add new make targets world-bin + and install-world-bin (Andrew Dunstan) + + + + These are the same as world + and install-world respectively, except that they + do not build or install the documentation. + + + + + + + Fix make rule for TAP tests (prove_installcheck) + to work in PGXS usage (Andrew Dunstan) + + + + + + + Avoid assuming that strings returned by GSSAPI libraries are + null-terminated (Tom Lane) + + + + The GSSAPI spec provides for a string pointer and length. It seems + that in practice the next byte after the string is usually zero, + so that our previous coding didn't actually fail; but we do have + a report of AddressSanitizer complaints. + + + + + + + Enable building with GSSAPI on MSVC (Michael Paquier) + + + + Fix various incompatibilities with modern Kerberos builds. + + + + + + + In MSVC builds, include in the set of + configure options reported by pg_config, + if it had been specified (Andrew Dunstan) + + + + + + + + + + Release 9.6.22 + + + 发布日期: + 2021-05-13 + + + + 此版本包含自 9.6.21 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + The PostgreSQL community will stop + releasing updates for the 9.6.X release series in November 2021. + Users are encouraged to update to a newer release branch soon. + + + + 迁移到版本 9.6.22 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.21, + see . + + + + + 变更 + + + + + + + Prevent integer overflows in array subscripting calculations + (Tom Lane) + + + + The array code previously did not complain about cases where an + array's lower bound plus length overflows an integer. This resulted + in later entries in the array becoming inaccessible (since their + subscripts could not be written as integers), but more importantly + it confused subsequent assignment operations. This could lead to + memory overwrites, with ensuing crashes or unwanted data + modifications. + (CVE-2021-32027) + + + + + + + Fix mishandling of junk columns in INSERT + ... ON CONFLICT ... UPDATE target lists (Tom Lane) + + + + If the UPDATE list contains any multi-column + sub-selects (which give rise to junk columns in addition to the + results proper), the UPDATE path would end up + storing tuples that include the values of the extra junk columns. + That's fairly harmless in the short run, but if new columns are + added to the table then the values would become accessible, possibly + leading to malfunctions if they don't match the datatypes of the + added columns. + + + + In addition, in versions supporting cross-partition updates, + a cross-partition update triggered by such a case had the reverse + problem: the junk columns were removed from the target list, + typically causing an immediate crash due to malfunction of the + multi-column sub-select mechanism. + (CVE-2021-32028) + + + + + + + Allow ALTER ROLE/DATABASE ... SET to set + the role, session_authorization, + and temp_buffers parameters (Tom Lane) + + + + Previously, over-eager validity checks might reject these commands, + even if the values would have worked when used later. This created + a command ordering hazard for dump/reload and upgrade scenarios. + + + + + + + Fix bug with coercing the result of a COLLATE + expression to a non-collatable type (Tom Lane) + + + + This led to a parse tree in which the COLLATE + appears to be applied to a non-collatable value. While that + normally has no real impact (since COLLATE has no + effect at runtime), it was possible to construct views that would be + rejected during dump/reload. + + + + + + + Disallow calling window functions and procedures via + the fast path wire protocol message (Tom Lane) + + + + Only plain functions are supported here. While trying to call + an aggregate function failed already, calling a window function + would crash, and calling a procedure would work only if the + procedure did no transaction control. + + + + + + + Extend pg_identify_object_as_address() + to support event triggers (Joel Jacobson) + + + + + + + Fix to_char()'s handling of Roman-numeral month + format codes with negative intervals (Julien Rouhaud) + + + + Previously, such cases would usually cause a crash. + + + + + + + Fix use of uninitialized value while parsing an + \{m,n\} + quantifier in a BRE-mode regular expression (Tom Lane) + + + + This error could cause the quantifier to act non-greedy, that is + behave like an + {m,n}? + quantifier would do in full regular expressions. + + + + + + + Avoid divide-by-zero when estimating selectivity of a regular + expression with a very long fixed prefix (Tom Lane) + + + + This typically led to a NaN selectivity value, + causing assertion failures or strange planner behavior. + + + + + + + Fix access-off-the-end-of-the-table error in BRIN index bitmap scans + (Tomas Vondra) + + + + If the page range size used by a BRIN index isn't a power of two, + there were corner cases in which a bitmap scan could try to fetch + pages past the actual end of the table, leading to could not + open file errors. + + + + + + + Ensure that locks are released while shutting down a standby + server's startup process (Fujii Masao) + + + + When a standby server is shut down while still in recovery, some + locks might be left held. This causes assertion failures in debug + builds; it's unclear whether any serious consequence could occur + in production builds. + + + + + + + Ensure we default to wal_sync_method + = fdatasync on recent FreeBSD (Thomas Munro) + + + + FreeBSD 13 supports open_datasync, which would + normally become the default choice. However, it's unclear whether + that is actually an improvement for Postgres, so preserve the + existing default for now. + + + + + + + Ensure we finish cleaning up when interrupted while detaching a DSM + segment (Thomas Munro) + + + + This error could result in temporary files not being cleaned up + promptly after a parallel query. + + + + + + + Fix assorted minor memory leaks in the server (Tom Lane, Andres Freund) + + + + + + + Prevent infinite loop in libpq + if a ParameterDescription message with a corrupt length is received + (Tom Lane) + + + + + + + Fix psql to restore the previous behavior + of \connect + service=something (Tom Lane) + + + + A previous bug fix caused environment variables (such + as PGPORT) to override entries in the service + file in this context. Restore the previous behavior, in which the + priority is the other way around. + + + + + + + Fix race condition in detection of file modification by + psql's \e and related + commands (Laurenz Albe) + + + + A very fast typist could fool the code's file-timestamp-based + detection of whether the temporary edit file was changed. + + + + + + + Fix missed file version check + in pg_restore (Tom Lane) + + + + When reading a custom-format archive from a non-seekable source, + pg_restore neglected to check the + archive version. If it was fed a newer archive version than it + can support, it would fail messily later on. + + + + + + + Add some more checks to pg_upgrade for + user tables containing non-upgradable data types (Tom Lane) + + + + Fix detection of some cases where a non-upgradable data type is + embedded within a container type (such as an array or range). + Also disallow upgrading when user tables contain columns of + system-defined composite types, since those types' OIDs are not + stable across versions. + + + + + + + Fix pg_waldump to + count XACT records correctly when generating + per-record statistics (Kyotaro Horiguchi) + + + + + + + Fix contrib/amcheck to not complain about the + tuple flags HEAP_XMAX_LOCK_ONLY + and HEAP_KEYS_UPDATED both being set + (Julien Rouhaud) + + + + This is a valid state after SELECT FOR UPDATE. + + + + + + + Adjust VPATH build rules to support recent Oracle Developer Studio + compiler versions (Noah Misch) + + + + + + + Fix testing of PL/Python for Python 3 on Solaris (Noah Misch) + + + + + + + + + + Release 9.6.21 + + + 发布日期: + 2021-02-11 + + + + 此版本包含自 9.6.20 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + The PostgreSQL community will stop + releasing updates for the 9.6.X release series in November 2021. + Users are encouraged to update to a newer release branch soon. + + + + 迁移到版本 9.6.21 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, see the first changelog item below, + which describes cases in which reindexing indexes after the upgrade + may be advisable. + + + + 另外,如果你是从 9.6.16 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + Fix CREATE INDEX CONCURRENTLY to wait for + concurrent prepared transactions (Andrey Borodin) + + + + At the point where CREATE INDEX CONCURRENTLY + waits for all concurrent transactions to complete so that it can see + rows they inserted, it must also wait for all prepared transactions + to complete, for the same reason. Its failure to do so meant that + rows inserted by prepared transactions might be omitted from the new + index, causing queries relying on the index to miss such rows. + In installations that have enabled prepared transactions + (max_prepared_transactions > 0), + it's recommended to reindex any concurrently-built indexes in + case this problem occurred when they were built. + + + + + + + Avoid incorrect results when WHERE CURRENT OF is + applied to a cursor whose plan contains a MergeAppend node (Tom + Lane) + + + + This case is unsupported (in general, a cursor using ORDER + BY is not guaranteed to be simply updatable); but the code + previously did not reject it, and could silently give false matches. + + + + + + + Fix crash when WHERE CURRENT OF is applied to a + cursor whose plan contains a custom scan node (David Geier) + + + + + + + Fix planner's handling of a placeholder that is computed at some + join level and used only at that same level (Tom Lane) + + + + This oversight could lead to failed to build + any N-way joins planner errors. + + + + + + + Be more careful about whether index AMs support mark/restore + (Andrew Gierth) + + + + This prevents errors about missing support functions in rare edge + cases. + + + + + + + Fix ALTER DEFAULT PRIVILEGES to handle duplicated + arguments safely (Michael Paquier) + + + + Duplicate role or schema names within the same command could lead + to tuple already updated by self errors or + unique-constraint violations. + + + + + + + Flush ACL-related caches when pg_authid + changes (Noah Misch) + + + + This change ensures that permissions-related decisions will promptly + reflect the results of ALTER ROLE ... [NO] INHERIT. + + + + + + + Prevent misprocessing of ambiguous CREATE TABLE + LIKE clauses (Tom Lane) + + + + A LIKE clause is re-examined after initial + creation of the new table, to handle importation of indexes and + such. It was possible for this re-examination to find a different + table of the same name, causing unexpected behavior; one example is + where the new table is a temporary table of the same name as + the LIKE target. + + + + + + + Rearrange order of operations in CREATE TABLE + LIKE so that indexes are cloned before building foreign + key constraints (Tom Lane) + + + + This fixes the case where a self-referential foreign key constraint + declared in the outer CREATE TABLE depends on an + index that's coming from the LIKE clause. + + + + + + + Disallow converting an inheritance child table to a view + (Tom Lane) + + + + + + + Ensure that disk space allocated for a dropped relation is released + promptly at commit (Thomas Munro) + + + + Previously, if the dropped relation spanned multiple 1GB segments, + only the first segment was truncated immediately. Other segments + were simply unlinked, which doesn't authorize the kernel to release + the storage so long as any other backends still have the files open. + + + + + + + Fix handling of backslash-escaped multibyte characters + in COPY FROM (Heikki Linnakangas) + + + + A backslash followed by a multibyte character was not handled + correctly. In some client character encodings, this could lead to + misinterpreting part of a multibyte character as a field separator + or end-of-copy-data marker. + + + + + + + Avoid preallocating executor hash tables + in EXPLAIN without ANALYZE + (Alexey Bashtanov) + + + + + + + Fix recently-introduced race conditions + in LISTEN/NOTIFY queue + handling (Tom Lane) + + + + A newly-listening backend could attempt to read SLRU pages that + were in process of being truncated, possibly causing an error. + + + + The queue tail pointer could become + set to a value that's not equal to the queue position of any + backend, resulting in effective disabling of the queue truncation + logic. Continued use of NOTIFY then led to + queue-fill warnings, and eventually to inability to send any more + notifies until the server is restarted. + + + + + + + Allow the jsonb concatenation operator to handle all + combinations of JSON data types (Tom Lane) + + + + We can concatenate two JSON objects or two JSON arrays. Handle + other cases by wrapping non-array inputs in one-element arrays, + then performing an array concatenation. Previously, some + combinations of inputs followed this rule but others arbitrarily + threw an error. + + + + + + + Fix use of uninitialized value while parsing a * + quantifier in a BRE-mode regular expression (Tom Lane) + + + + This error could cause the quantifier to act non-greedy, that is + behave like a *? quantifier would do in full + regular expressions. + + + + + + + Fix numeric power() for the case where the + exponent is exactly INT_MIN (-2147483648) + (Dean Rasheed) + + + + Previously, a result with no significant digits was produced. + + + + + + + Prevent possible data loss from incorrect detection of the + wraparound point of an SLRU log + (Noah Misch) + + + + The wraparound point typically falls in the middle of a page, which + must be rounded off to a page boundary, and that was not done + correctly. No issue could arise unless an installation had gotten + to within one page of SLRU overflow, which is unlikely in a + properly-functioning system. If this did happen, it would manifest + in later apparent wraparound or could not + access status of transaction errors. + + + + + + + Fix memory leak in walsender processes while sending new snapshots + for logical decoding (Amit Kapila) + + + + + + + Fix walsender to accept additional commands after + terminating replication (Jeff Davis) + + + + + + + Ensure detection of deadlocks between hot standby backends and the + startup (WAL-application) process (Fujii Masao) + + + + The startup process did not run the deadlock detection code, so that + in situations where the startup process is last to join a circular + wait situation, the deadlock might never be recognized. + + + + + + + Fix portability problem in parsing + of recovery_target_xid values (Michael Paquier) + + + + The target XID is potentially 64 bits wide, but it was parsed + with strtoul(), causing misbehavior on + platforms where long is 32 bits (such as Windows). + + + + + + + Avoid assertion failure in pg_get_functiondef() + when examining a function with a TRANSFORM option + (Tom Lane) + + + + + + + In psql, re-allow including a password + in a connection_string argument of a + \connect command (Tom Lane) + + + + This used to work, but a recent bug fix caused the password to be + ignored (resulting in prompting for a password). + + + + + + + Fix assorted bugs + in psql's \help + command (Kyotaro Horiguchi, Tom Lane) + + + + \help with two argument words failed to find a + command description using only the first word, for + example \help reset all should show the help + for RESET but did not. + Also, \help often failed to invoke the pager when + it should. It also leaked memory. + + + + + + + Fix pg_dump to handle WITH + GRANT OPTION in an extension's initial privileges + (Noah Misch) + + + + If an extension's script creates an object and grants privileges + on it with grant option, then later the user revokes such + privileges, pg_dump would generate + incorrect SQL for reproducing the situation. (Few if any extensions + do this today.) + + + + + + + In pg_rewind, ensure that all WAL is + accounted for when rewinding a standby server + (Ian Barwick, Heikki Linnakangas) + + + + + + + Report the correct database name in connection failure error + messages from some client programs (Álvaro Herrera) + + + + If the database name was defaulted rather than given on the command + line, pg_dumpall, + pgbench, oid2name, + and vacuumlo would produce misleading + error messages after a connection failure. + + + + + + + Fix memory leak in contrib/auto_explain + (Japin Li) + + + + Memory consumed while producing the EXPLAIN + output was not freed until the end of the current transaction (for a + top-level statement) or the end of the surrounding statement (for a + nested statement). This was particularly a problem + with log_nested_statements enabled. + + + + + + + In contrib/postgres_fdw, avoid leaking open + connections to remote servers when a user mapping or foreign server + object is dropped (Bharath Rupireddy) + + + + Open connections that depend on a dropped user mapping or foreign + server can no longer be referenced, but formerly they were kept + around anyway for the duration of the local session. + + + + + + + In contrib/pgcrypto, check for error returns + from OpenSSL's EVP functions (Michael Paquier) + + + + We do not really expect errors here, but this change silences + warnings from static analysis tools. + + + + + + + In contrib/pg_trgm's GiST index support, avoid + crash in the rare case that picksplit is called on exactly two index + items (Andrew Gierth, Alexander Korotkov) + + + + + + + Fix miscalculation of timeouts + in contrib/pg_prewarm + and contrib/postgres_fdw + (Alexey Kondratov, Tom Lane) + + + + The main loop in contrib/pg_prewarm's + autoprewarm parent process underestimated its desired sleep time by + a factor of 1000, causing it to consume much more CPU than intended. + When waiting for a result from a remote + server, contrib/postgres_fdw overestimated the + desired timeout by a factor of 1000 (though this error had been + mitigated by imposing a clamp to 60 seconds). + + + + Both of these errors stemmed from incorrectly converting + seconds-and-microseconds to milliseconds. Introduce a new + API TimestampDifferenceMilliseconds() + to make it easier to get this right in the future. + + + + + + + Improve configure's heuristics for + selecting PG_SYSROOT on macOS (Tom Lane) + + + + The new method is more likely to produce desirable results when + Xcode is newer than the underlying operating system. Choosing + a sysroot that does not match the OS version may result in + nonfunctional executables. + + + + + + + While building on macOS, specify in + link steps as well as compile steps (James Hilliard) + + + + This likewise improves the results when Xcode is out of sync with + the operating system. + + + + + + + Update time zone data files to tzdata + release 2021a for DST law changes in Russia (Volgograd zone) and + South Sudan, plus historical corrections for Australia, Bahamas, + Belize, Bermuda, Ghana, Israel, Kenya, Nigeria, Palestine, + Seychelles, and Vanuatu. + + + + Notably, the Australia/Currie zone has been corrected to the point + where it is identical to Australia/Hobart. + + + + + + + + + + Release 9.6.20 + + + 发布日期: + 2020-11-12 + + + + 此版本包含自 9.6.19 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.20 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.16, + see . + + + + + 变更 + + + + + + + Block DECLARE CURSOR ... WITH HOLD and firing of + deferred triggers within index expressions and materialized view + queries (Noah Misch) + + + + This is essentially a leak in the security restricted + operation sandbox mechanism. An attacker having permission + to create non-temporary SQL objects could parlay this leak to + execute arbitrary SQL code as a superuser. + + + + PostgreSQL 项目感谢 Etienne Stalmans 报告此问题。 + (CVE-2020-25695) + + + + + + + Fix usage of complex connection-string parameters + in pg_dump, + pg_restore, + clusterdb, + reindexdb, + and vacuumdb (Tom Lane) + + + + The parameter + of pg_dump + and pg_restore, or + the parameter of the other + programs mentioned, can be a connection string + containing multiple connection parameters rather than just a + database name. In cases where these programs need to initiate + additional connections, such as parallel processing or processing of + multiple databases, the connection string was forgotten and just the + basic connection parameters (database name, host, port, and + username) were used for the additional connections. This could lead + to connection failures if the connection string included any other + essential information, such as non-default SSL or GSS parameters. + Worse, the connection might succeed but not be encrypted as + intended, or be vulnerable to man-in-the-middle attacks that the + intended connection parameters would have prevented. + (CVE-2020-25694) + + + + + + + When psql's \connect + command re-uses connection parameters, ensure that all + non-overridden parameters from a previous connection string are + re-used (Tom Lane) + + + + This avoids cases where reconnection might fail due to omission of + relevant parameters, such as non-default SSL or GSS options. + Worse, the reconnection might succeed but not be encrypted as + intended, or be vulnerable to man-in-the-middle attacks that the + intended connection parameters would have prevented. + This is largely the same problem as just cited + for pg_dump et al, + although psql's behavior is more complex + since the user may intentionally override some connection + parameters. + (CVE-2020-25694) + + + + + + + Prevent psql's \gset + command from modifying specially-treated variables (Noah Misch) + + + + \gset without a prefix would overwrite whatever + variables the server told it to. Thus, a compromised server could + set specially-treated variables such as PROMPT1, + giving the ability to execute arbitrary shell code in the user's + session. + + + + PostgreSQL 项目感谢 Nick Cleaton 报告此问题。 + (CVE-2020-25696) + + + + + + + Prevent possible data loss from concurrent truncations of SLRU logs + (Noah Misch) + + + + This rare problem would manifest in later apparent + wraparound or could not access status of + transaction errors. + + + + + + + Ensure that SLRU directories are properly fsync'd during checkpoints + (Thomas Munro) + + + + This prevents possible data loss in a subsequent operating system + crash. + + + + + + + Fix ALTER ROLE for users with + the BYPASSRLS attribute (Tom Lane, Stephen Frost) + + + + The BYPASSRLS attribute is only allowed to be + changed by superusers, but other ALTER ROLE + operations, such as password changes, should be allowed with only + ordinary permission checks. The previous coding erroneously + restricted all changes on such a role to superusers. + + + + + + + Fix handling of expressions in CREATE TABLE LIKE + with inheritance (Tom Lane) + + + + If a CREATE TABLE command uses + both LIKE and traditional inheritance, column + references in CHECK constraints and expression + indexes that came from a LIKE parent table tended + to get mis-numbered, resulting in wrong answers and/or bizarre error + messages. The same could happen in GENERATED + expressions, in branches that have that feature. + + + + + + + Fix off-by-one conversion of negative years to BC dates + in to_date() + and to_timestamp() (Dar Alathar-Yemen, Tom Lane) + + + + Also, arrange for the combination of a negative year and an + explicit BC marker to cancel out and produce AD. + + + + + + + Ensure that standby servers will archive WAL timeline history files + when archive_mode is set + to always (Grigory Smolkin, Fujii Masao) + + + + This oversight could lead to failure of subsequent PITR recovery + attempts. + + + + + + + During smart shutdown, don't terminate background + processes until all client (foreground) sessions are done (Tom Lane) + + + + The previous behavior broke parallel query processing, since the + postmaster would terminate parallel workers and refuse to launch any + new ones. It also caused autovacuum to cease functioning, which + could have dire long-term effects if the surviving client sessions + make a lot of data changes. + + + + + + + Avoid recursive consumption of stack space while processing signals + in the postmaster (Tom Lane) + + + + Heavy use of parallel processing has been observed to cause + postmaster crashes due to too many concurrent signals requesting + creation of a parallel worker process. + + + + + + + Avoid running atexit handlers when exiting + due to SIGQUIT (Kyotaro Horiguchi, Tom Lane) + + + + Most server processes followed this practice already, but the + archiver process was overlooked. Backends that were still waiting + for a client startup packet got it wrong, too. + + + + + + + Avoid misoptimization of subquery qualifications that reference + apparently-constant grouping columns (Tom Lane) + + + + A constant subquery output column isn't really + constant if it is a grouping column that appears in only some of the + grouping sets. + + + + + + + Avoid failure when SQL function inlining changes the shape of a + potentially-hashable subplan comparison expression (Tom Lane) + + + + + + + While building or re-building an index, tolerate the appearance of + new HOT chains due to concurrent updates + (Anastasia Lubennikova, Álvaro Herrera) + + + + This oversight could lead to failed to find parent tuple for + heap-only tuple errors. + + + + + + + Ensure that data is detoasted before being inserted into a BRIN + index (Tomas Vondra) + + + + Index entries are not supposed to contain out-of-line TOAST + pointers, but BRIN didn't get that memo. This could lead to errors + like missing chunk number 0 for toast value NNN. + (If you are faced with such an error from an existing + index, REINDEX should be enough to fix it.) + + + + + + + Handle concurrent desummarization correctly during BRIN index scans + (Alexander Lakhin, Álvaro Herrera) + + + + Previously, if a page range was desummarized at just the wrong time, + an index scan might falsely raise an error indicating index + corruption. + + + + + + + Fix rare lost saved point in index errors in scans of + multicolumn GIN indexes (Tom Lane) + + + + + + + Fix use-after-free hazard when an event trigger monitors + an ALTER TABLE operation (Jehan-Guillaume de + Rorthais) + + + + + + + Fix incorrect error message about inconsistent moving-aggregate + data types (Jeff Janes) + + + + + + + Avoid lockup when a parallel worker reports a very long error + message (Vignesh C) + + + + + + + Avoid unnecessary failure when transferring very large payloads + through shared memory queues (Markus Wanner) + + + + + + + Fix relation cache memory leaks with RLS policies (Tom Lane) + + + + + + + Fix small memory leak when SIGHUP processing decides that a new GUC + variable value cannot be applied without a restart (Tom Lane) + + + + + + + Make libpq support arbitrary-length lines + in .pgpass files (Tom Lane) + + + + This is mostly useful to allow using very long security tokens as + passwords. + + + + + + + In libpq for Windows, + call WSAStartup() once per process + and WSACleanup() not at all (Tom Lane, + Alexander Lakhin) + + + + Previously, libpq + invoked WSAStartup() at connection start + and WSACleanup() at connection cleanup. + However, it appears that calling WSACleanup() + can interfere with other program operations; notably, we have + observed rare failures to emit expected output to stdout. There + appear to be no ill effects from omitting the call, so do that. + (This also eliminates a performance issue from repeated DLL loads and + unloads when a program performs a series of database connections.) + + + + + + + Fix ecpg library's per-thread + initialization logic for Windows (Tom Lane, Alexander Lakhin) + + + + Multi-threaded ecpg applications could + suffer rare misbehavior due to incorrect locking. + + + + + + + On Windows, make psql read the output of + a backtick command in text mode, not binary mode (Tom Lane) + + + + This ensures proper handling of newlines. + + + + + + + Ensure that pg_dump collects per-column + information about extension configuration tables (Fabrízio de + Royes Mello, Tom Lane) + + + + Failure to do this led to crashes when + specifying , or underspecified (though + usually correct) COPY commands when + using COPY to reload the tables' data. + + + + + + + Make pg_upgrade check for pre-existence + of tablespace directories in the target cluster (Bruce Momjian) + + + + + + + Fix potential memory leak in contrib/pgcrypto + (Michael Paquier) + + + + + + + Add check for an unlikely failure case + in contrib/pgcrypto (Daniel Gustafsson) + + + + + + + Use return not exit() in + configure's + test programs (Peter Eisentraut) + + + + This avoids failures with pickier compilers. + + + + + + + Update time zone data files to tzdata + release 2020d for DST law changes in Fiji, Morocco, Palestine, the + Canadian Yukon, Macquarie Island, and Casey Station (Antarctica); + plus historical corrections for France, Hungary, Monaco, and + Palestine. + + + + + + + Sync our copy of the timezone library with IANA tzcode release 2020d + (Tom Lane) + + + + This absorbs upstream's change of zic's + default output option from fat + to slim. That's just cosmetic for our purposes, as + we continue to select the fat mode in pre-v13 + branches. This change also ensures + that strftime() does not + change errno unless it fails. + + + + + + + + + + Release 9.6.19 + + + 发布日期: + 2020-08-13 + + + + 此版本包含自 9.6.18 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.19 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.16, + see . + + + + + 变更 + + + + + + + Make contrib modules' installation scripts more secure (Tom Lane) + + + + Attacks similar to those described in CVE-2018-1058 could be carried + out against an extension installation script, if the attacker can + create objects in either the extension's target schema or the schema + of some prerequisite extension. Since extensions often require + superuser privilege to install, this can open a path to obtaining + superuser privilege. To mitigate this risk, be more careful about + the search_path used to run an installation + script; disable check_function_bodies within the + script; and fix catalog-adjustment queries used in some contrib + modules to ensure they are secure. Also provide documentation to + help third-party extension authors make their installation scripts + secure. This is not a complete solution; extensions that depend on + other extensions can still be at risk if installed carelessly. + (CVE-2020-14350) + + + + + + + In logical replication walsender, fix failure to send feedback + messages after sending a keepalive message (Álvaro Herrera) + + + + This is a relatively minor problem when using built-in logical + replication, because the built-in walreceiver will send a feedback + reply (which clears the incorrect state) fairly frequently anyway. + But with some other replication systems, such + as pglogical, it causes significant + performance issues. + + + + + + + Fix slow execution of ts_headline() (Tom Lane) + + + + The phrase-search fix added in our previous set of minor releases + could cause ts_headline() to take unreasonable + amounts of time for long documents; to make matters worse, the query + was not cancellable within the troublesome loop. + + + + + + + Ensure the repeat() function can be interrupted + by query cancel (Joe Conway) + + + + + + + Fix mis-handling of NaN inputs during parallel + aggregation on numeric-type columns (Tom Lane) + + + + If some partial aggregation workers found only NaNs + while others found only non-NaNs, the results + were combined incorrectly, possibly leading to the wrong overall + result (i.e., not NaN when it should be). + + + + + + + Undo double-quoting of index names in EXPLAIN's + non-text output formats (Tom Lane, Euler Taveira) + + + + + + + Fix timing of constraint revalidation in ALTER + TABLE (David Rowley) + + + + If ALTER TABLE needs to fully rewrite the table's + contents (for example, due to change of a column's data type) and + also needs to scan the table to re-validate foreign keys + or CHECK constraints, it sometimes did things in + the wrong order, leading to odd errors such as could not read + block 0 in file "base/nnnnn/nnnnn": read only 0 of 8192 bytes. + + + + + + + Cope with LATERAL references in restriction + clauses attached to an un-flattened sub-SELECT in + the FROM clause (Tom Lane) + + + + This oversight could result in assertion failures or crashes at + query execution. + + + + + + + Avoid believing that a never-analyzed foreign table has zero tuples + (Tom Lane) + + + + This primarily affected the planner's estimate of the number of + groups that would be obtained by GROUP BY. + + + + + + + Improve error handling in the server's buffile + module (Thomas Munro) + + + + Fix some cases where I/O errors were indistinguishable from reaching + EOF, or were not reported at all. Also add details such as block + numbers and byte counts where appropriate. + + + + + + + Fix conflict-checking anomalies in SERIALIZABLE + isolation mode (Peter Geoghegan) + + + + If a concurrently-inserted tuple was updated by a different + concurrent transaction, and neither tuple version was visible to the + current transaction's snapshot, serialization conflict checking + could draw the wrong conclusions about whether the tuple was relevant + to the results of the current transaction. This could allow a + serializable transaction to commit when it should have failed with a + serialization error. + + + + + + + Avoid repeated marking of dead btree index entries as dead (Masahiko + Sawada) + + + + While functionally harmless, this led to useless WAL traffic when + checksums are enabled or wal_log_hints is on. + + + + + + + Fix failure of some code paths to acquire the correct lock before + modifying pg_control (Nathan Bossart, Fujii + Masao) + + + + This oversight could allow pg_control to be + written out with an inconsistent checksum, possibly causing trouble + later, including inability to restart the database if it crashed + before the next pg_control update. + + + + + + + Fix errors in currtid() + and currtid2() (Michael Paquier) + + + + These functions (which are undocumented and used only by ancient + versions of the ODBC driver) contained coding errors that could + result in crashes, or in confusing error messages such as could + not open file when applied to a relation having no storage. + + + + + + + Avoid calling elog() + or palloc() while holding a spinlock (Michael + Paquier, Tom Lane) + + + + Logic associated with replication slots had several violations of + this coding rule. While the odds of trouble are quite low, an error + in the called function would lead to a stuck spinlock. + + + + + + + Report out-of-disk-space errors properly + in pg_dump + and pg_basebackup (Justin Pryzby, Tom + Lane, Álvaro Herrera) + + + + Some code paths could produce silly reports like could not + write file: Success. + + + + + + + Fix parallel restore of tables having both table-level privileges + and per-column privileges (Tom Lane) + + + + The table-level privilege grants have to be applied first, but a + parallel restore did not reliably order them that way; this could + lead to tuple concurrently updated errors, or to + disappearance of some per-column privilege grants. The fix for this + is to include dependency links between such entries in the archive + file, meaning that a new dump has to be taken with a + corrected pg_dump to ensure that the + problem will not recur. + + + + + + + Ensure that pg_upgrade runs + with vacuum_defer_cleanup_age set to zero in the + target cluster (Bruce Momjian) + + + + If the target cluster's configuration has been modified to + set vacuum_defer_cleanup_age to a nonzero value, + that prevented freezing of the system catalogs from working properly, + which caused the upgrade to fail in confusing ways. Ensure that any + such setting is overridden for the duration of the upgrade. + + + + + + + Fix pg_recvlogical to drain pending + messages before exiting (Noah Misch) + + + + Without this, the replication sender might detect a send failure and + exit without making the expected final update to the replication + slot's LSN position. That led to re-transmitting data after the + next connection. It was also possible to miss error messages sent + after the last data that pg_recvlogical + wants to consume. + + + + + + + Fix pg_rewind's handling of just-deleted + files in the source data directory (Justin Pryzby, Michael Paquier) + + + + When working with an on-line source database, concurrent file + deletions are possible, but pg_rewind + would get confused if deletion happened between seeing a file's + directory entry and examining it with stat(). + + + + + + + Make pg_test_fsync use binary I/O mode on + Windows (Michael Paquier) + + + + Previously it wrote the test file in text mode, which is not an + accurate reflection of PostgreSQL's + actual usage. + + + + + + + Fix failure to initialize local state correctly + in contrib/dblink (Joe Conway) + + + + With the right combination of circumstances, this could lead to + dblink_close() issuing an unexpected + remote COMMIT. + + + + + + + Fix contrib/pgcrypto's misuse + of deflate() (Tom Lane) + + + + The pgp_sym_encrypt functions could produce + incorrect compressed data due to mishandling + of zlib's API requirements. We have no + reports of this error manifesting with + stock zlib, but it can be seen when using + IBM's zlibNX implementation. + + + + + + + Fix corner case in decompression logic + in contrib/pgcrypto's + pgp_sym_decrypt functions (Kyotaro Horiguchi, + Michael Paquier) + + + + A compressed stream can validly end with an empty packet, but the + decompressor failed to handle this and would complain about corrupt + data. + + + + + + + Use POSIX-standard strsignal() in place of the + BSD-ish sys_siglist[] (Tom Lane) + + + + This avoids build failures with very recent versions + of glibc. + + + + + + + Support building our NLS code with Microsoft Visual Studio 2015 or + later (Juan José Santamaría Flecha, Davinder Singh, + Amit Kapila) + + + + + + + Avoid possible failure of our MSVC install script when there is a + file named configure several levels above the + source code tree (Arnold Müller) + + + + This could confuse some logic that looked + for configure to identify the top level of the + source tree. + + + + + + + + + + Release 9.6.18 + + + 发布日期: + 2020-05-14 + + + + 此版本包含自 9.6.17 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.18 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.16, + see . + + + + + 变更 + + + + + + + Preserve the indisclustered setting of + indexes rewritten by ALTER TABLE (Amit Langote, + Justin Pryzby) + + + + Previously, ALTER TABLE lost track of which index + had been used for CLUSTER. + + + + + + + Preserve the replica identity properties of indexes rewritten + by ALTER TABLE (Quan Zongliang, Peter Eisentraut) + + + + + + + Lock objects sooner during DROP OWNED BY + (Álvaro Herrera) + + + + This avoids failures in race-condition cases where another session is + deleting some of the same objects. + + + + + + + Fix error-case processing for CREATE ROLE ... IN + ROLE (Andrew Gierth) + + + + Some error cases would be reported as unexpected node + type or the like, instead of the intended message. + + + + + + + Fix full text search to handle NOT above a phrase search correctly + (Tom Lane) + + + + Queries such as !(foo<->bar) failed to find + matching rows when implemented as a GiST or GIN index search. + + + + + + + Fix full text search for cases where a phrase search includes an + item with both prefix matching and a weight restriction (Tom Lane) + + + + + + + Fix ts_headline() to make better headline + selections when working with phrase queries (Tom Lane) + + + + + + + Fix bugs in gin_fuzzy_search_limit processing + (Adé Heyward, Tom Lane) + + + + A small value of gin_fuzzy_search_limit could + result in unexpected slowness due to unintentionally rescanning the + same index page many times. Another code path failed to apply the + intended filtering at all, possibly returning too many values. + + + + + + + Allow input of type circle to accept the format + (x,y),r + as the documentation says it does (David Zhang) + + + + + + + Make the get_bit() + and set_bit() functions cope + with bytea strings longer than 256MB (Movead Li) + + + + Since the bit number argument is only int4, it's + impossible to use these functions to access bits beyond the first + 256MB of a long bytea. We'll widen the argument + to int8 in v13, but in the meantime, allow these + functions to work on the initial substring of a + long bytea. + + + + + + + Avoid possibly leaking an open-file descriptor for a directory + in pg_ls_dir(), + pg_timezone_names(), + pg_tablespace_databases(), and allied functions + (Justin Pryzby) + + + + + + + Fix polymorphic-function type resolution to correctly infer the + actual type of an anyarray output when given only + an anyrange input (Tom Lane) + + + + + + + Avoid unlikely crash when REINDEX is terminated + by a session-shutdown signal (Tom Lane) + + + + + + + Prevent printout of possibly-incorrect hash join table statistics + in EXPLAIN (Konstantin Knizhnik, Tom Lane, Thomas + Munro) + + + + + + + Fix reporting of elapsed time for heap truncation steps + in VACUUM VERBOSE (Tatsuhito Kasahara) + + + + + + + Avoid possibly showing waiting twice in a process's + PS status (Masahiko Sawada) + + + + + + + Avoid premature recycling of WAL segments during crash recovery + (Jehan-Guillaume de Rorthais) + + + + WAL segments that become ready to be archived during crash recovery + were potentially recycled without being archived. + + + + + + + Avoid scanning irrelevant timelines during archive recovery (Kyotaro + Horiguchi) + + + + This can eliminate many attempts to fetch non-existent WAL files from + archive storage, which is helpful if archive access is slow. + + + + + + + Remove bogus subtransaction logged without previous top-level + txn record error check in logical decoding (Arseny Sher, + Amit Kapila) + + + + This condition is legitimately reachable in various scenarios, so + remove the check. + + + + + + + Ensure that a replication + slot's io_in_progress_lock is released in failure + code paths (Pavan Deolasee) + + + + This could result in a walsender later becoming stuck waiting for + the lock. + + + + + + + Fix race conditions in synchronous standby management (Tom Lane) + + + + During a change in the synchronous_standby_names + setting, there was a window in which wrong decisions could be made + about whether it is OK to release transactions that are waiting for + synchronous commit. Another hazard for similarly wrong decisions + existed if a walsender process exited and was immediately replaced + by another. + + + + + + + Ensure nextXid can't go backwards on a standby + server (Eka Palamadai) + + + + This race condition could allow incorrect hot standby feedback + messages to be sent back to the primary server, potentially allowing + VACUUM to run too soon on the primary. + + + + + + + Add missing SQLSTATE values to a few error reports (Sawada Masahiko) + + + + + + + Fix PL/pgSQL to reliably refuse to execute an event trigger function + as a plain function (Tom Lane) + + + + + + + Fix memory leak in libpq when + using sslmode=verify-full (Roman Peshkurov) + + + + Certificate verification during connection startup could leak some + memory. This would become an issue if a client process opened many + database connections during its lifetime. + + + + + + + Fix ecpg to treat an argument of + just - as meaning read + from stdin on all platforms (Tom Lane) + + + + + + + Add pg_dump support for ALTER + ... DEPENDS ON EXTENSION (Álvaro Herrera) + + + + pg_dump previously ignored dependencies added + this way, causing them to be forgotten during dump/restore or + pg_upgrade. + + + + + + + Fix pg_dump to dump comments on RLS + policy objects (Tom Lane) + + + + + + + In pg_dump, postpone restore of event + triggers till the end (Fabrízio de Royes Mello, Hamid Akhtar, + Tom Lane) + + + + This minimizes the risk that an event trigger could interfere with + the restoration of other objects. + + + + + + + Fix quoting of , + and values + in createdb utility (Michael Paquier) + + + + + + + contrib/lo's lo_manage() + function crashed if called directly rather than as a trigger (Tom + Lane) + + + + + + + In contrib/ltree, + protect against overflow of ltree + and lquery length fields (Nikita Glukhov) + + + + + + + Fix cache reference leak in contrib/sepgsql + (Michael Luo) + + + + + + + Avoid failures when dealing with Unix-style locale names on + Windows (Juan José Santamaría Flecha) + + + + + + + In MSVC builds, cope with spaces in the path name for Python + (Victor Wagner) + + + + + + + In MSVC builds, fix detection of Visual Studio version to work with + more language settings (Andrew Dunstan) + + + + + + + In MSVC builds, use -Wno-deprecated with bison + versions newer than 3.0, as non-Windows builds already do (Andrew + Dunstan) + + + + + + + Update time zone data files to tzdata + release 2020a for DST law changes in Morocco and the Canadian Yukon, + plus historical corrections for Shanghai. + + + + The America/Godthab zone has been renamed to America/Nuuk to reflect + current English usage; however, the old name remains available as a + compatibility link. + + + + Also, update initdb's list of known + Windows time zone names to include recent additions, improving the + odds that it will correctly translate the system time zone setting + on that platform. + + + + + + + + + + Release 9.6.17 + + + 发布日期: + 2020-02-13 + + + + 此版本包含自 9.6.16 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.17 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.16, + see . + + + + + 变更 + + + + + + + Add missing permissions checks for ALTER ... DEPENDS ON + EXTENSION (Álvaro Herrera) + + + + Marking an object as dependent on an extension did not have any + privilege check whatsoever. This oversight allowed any user to mark + routines, triggers, materialized views, or indexes as droppable by + anyone able to drop an extension. Require that the calling user own + the specified object (and hence have privilege to drop it). + (CVE-2020-1720) + + + + + + + Avoid failure in logical decoding when a large transaction must be + spilled into many separate temporary files (Amit Khandekar) + + + + + + + Fix failure in logical replication publisher after a database crash + and restart (Vignesh C) + + + + + + + Prevent premature shutdown of a Gather or GatherMerge plan node that + is underneath a Limit node (Amit Kapila) + + + + This avoids failure if such a plan node needs to be scanned more + than once, as for instance if it is on the inside of a nestloop. + + + + + + + Avoid memory leak when there are no free dynamic shared memory slots + (Thomas Munro) + + + + + + + Ignore the CONCURRENTLY option when performing an + index creation, drop, or rebuild on a temporary table (Michael + Paquier, Heikki Linnakangas, Andres Freund) + + + + This avoids strange failures if the temporary table has + an ON COMMIT action. There is no benefit in + using CONCURRENTLY for a temporary table anyway, + since other sessions cannot access the table, making the extra + processing pointless. + + + + + + + Fix possible failure when resetting expression indexes on temporary + tables that are marked ON COMMIT DELETE ROWS + (Tom Lane) + + + + + + + Fix possible crash in BRIN index operations + with box, range and inet data + types (Heikki Linnakangas) + + + + + + + Fix handling of deleted pages in GIN indexes (Alexander Korotkov) + + + + Avoid possible deadlocks, incorrect updates of a deleted page's + state, and failure to traverse through a recently-deleted page. + + + + + + + Fix possible crash with a SubPlan (sub-SELECT) + within a multi-row VALUES list (Tom Lane) + + + + + + + Fix unlikely crash with pass-by-reference aggregate transition + states (Andres Freund, Teodor Sigaev) + + + + + + + Improve error reporting in to_date() + and to_timestamp() + (Tom Lane, Álvaro Herrera) + + + + Reports about incorrect month or day names in input strings could + truncate the input in the middle of a multi-byte character, leading + to an improperly encoded error message that could cause follow-on + failures. Truncate at the next whitespace instead. + + + + + + + Fix off-by-one result for EXTRACT(ISOYEAR + FROM timestamp) for BC dates + (Tom Lane) + + + + + + + Avoid stack overflow in information_schema views + when a self-referential view exists in the system catalogs + (Tom Lane) + + + + A self-referential view can't work; it will always result in + infinite recursion. We handled that situation correctly when + trying to execute the view, but not when inquiring whether it is + automatically updatable. + + + + + + + Improve performance of hash joins with very large inner relations + (Thomas Munro) + + + + + + + Fix edge-case crashes and misestimations in selectivity calculations + for the <@ and @> range + operators (Michael Paquier, Andrey Borodin, Tom Lane) + + + + + + + Improve error reporting for attempts to use automatic updating of + views with conditional INSTEAD rules (Dean Rasheed) + + + + This has never been supported, but previously the error was thrown + only at execution time, so that it could be masked by planner errors. + + + + + + + Prevent a composite type from being included in itself indirectly + via a range type (Tom Lane, Julien Rouhaud) + + + + + + + Fix error reporting for index expressions of prohibited types + (Amit Langote) + + + + + + + Fix dumping of views that contain only a VALUES + list to handle cases where a view output column has been renamed + (Tom Lane) + + + + + + + Transmit incoming NOTIFY messages to the client + before sending ReadyForQuery, rather than after + (Tom Lane) + + + + This change ensures that, with libpq and other client libraries that + act similarly to it, any notifications received during a transaction + will be available by the time the client thinks the transaction is + complete. This probably makes no difference in practical + applications (which would need to cope with asynchronous + notifications in any case); but it makes it easier to build test + cases with reproducible behavior. + + + + + + + Allow libpq to parse all GSS-related + connection parameters even when the GSSAPI code hasn't been compiled + in (Tom Lane) + + + + This makes the behavior similar to our SSL support, where it was + long ago deemed to be a good idea to always accept all the related + parameters, even if some are ignored or restricted due to lack of + the feature in a particular build. + + + + + + + Fix incorrect handling of %b + and %B format codes + in ecpg's + PGTYPEStimestamp_fmt_asc() function + (Tomas Vondra) + + + + Due to an off-by-one error, these codes would print the wrong month + name, or possibly crash. + + + + + + + Fix + parallel pg_dump/pg_restore + to more gracefully handle failure to create worker processes + (Tom Lane) + + + + + + + Prevent possible crash or lockup when attempting to terminate a + parallel pg_dump/pg_restore + run via a signal (Tom Lane) + + + + + + + In pg_upgrade, look inside arrays and + ranges while searching for non-upgradable data types in tables + (Tom Lane) + + + + + + + Apply more thorough syntax checking + to createuser's + option (Álvaro Herrera) + + + + + + + Avoid crash in postgres_fdw when trying to + send a command like UPDATE remote_tab SET (x,y) = (SELECT + ...) to the remote server (Tom Lane) + + + + + + + In contrib/dict_int, + reject maxlen settings less than one + (Tomas Vondra) + + + + This prevents a possible crash with silly settings for that parameter. + + + + + + + Disallow NULL category values + in contrib/tablefunc's + crosstab() function (Joe Conway) + + + + This case never worked usefully, and it would crash on some + platforms. + + + + + + + Mark some timeout and statistics-tracking GUC variables + as PGDLLIMPORT, to allow extensions to access + them on Windows (Pascal Legrand) + + + + This applies to + idle_in_transaction_session_timeout, + lock_timeout, + statement_timeout, + track_activities, + track_counts, and + track_functions. + + + + + + + Fix race condition that led to delayed delivery of interprocess + signals on Windows (Amit Kapila) + + + + This caused visible timing oddities in NOTIFY, + and perhaps other misbehavior. + + + + + + + On Windows, retry a few times after + an ERROR_ACCESS_DENIED file access failure + (Alexander Lakhin, Tom Lane) + + + + This helps cope with cases where a file open attempt fails because + the targeted file is flagged for deletion but not yet actually gone. + pg_ctl, for example, frequently failed + with such an error when probing to see if the postmaster had shut + down yet. + + + + + + + + + + Release 9.6.16 + + + 发布日期: + 2019-11-14 + + + + 此版本包含自 9.6.15 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.16 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you use the contrib/intarray + extension with a GiST index, and you rely on indexed searches + for the <@ operator, see the entry below + about that. + + + + 另外,如果你是从 9.6.9 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + Fix failure of ALTER TABLE SET with a custom + relation option (Michael Paquier) + + + + + + + Disallow changing a multiply-inherited column's type if not all + parent tables were changed (Tom Lane) + + + + Previously, this was allowed, whereupon queries on the + now-out-of-sync parent would fail. + + + + + + + Prevent VACUUM from trying to freeze + an old multixact ID involving a still-running transaction + (Nathan Bossart, Jeremy Schneider) + + + + This case would lead to VACUUM failing until the + old transaction terminates. + + + + + + + Ensure that offset expressions in WINDOW clauses + are processed when a query's expressions are manipulated (Andrew Gierth) + + + + This oversight could result in assorted failures when the offsets + are nontrivial expressions. One example is that a function + parameter reference in such an expression would fail if the function + was inlined. + + + + + + + Fix handling of whole-row variables in WITH CHECK + OPTION expressions and row-level-security policy expressions + (Andres Freund) + + + + Previously, such usage might result in bogus errors about row type + mismatches. + + + + + + + Avoid postmaster failure if a parallel query requests a background + worker when no postmaster child process array slots remain free + (Tom Lane) + + + + + + + Prevent possible double-free if a BEFORE UPDATE + trigger returns the old tuple as-is, and it is not the last such + trigger (Thomas Munro) + + + + + + + Provide a relevant error context line when an error occurs while + setting GUC parameters during parallel worker startup (Thomas Munro) + + + + + + + In serializable mode, ensure that row-level predicate locks are + acquired on the correct version of the row (Thomas Munro, Heikki + Linnakangas) + + + + If the visible version of the row is HOT-updated, the lock might be + taken on its now-dead predecessor, resulting in subtle failures to + guarantee serialization. + + + + + + + Ensure that fsync() is applied only to files + that are opened read/write (Andres Freund, Michael Paquier) + + + + Some code paths tried to do this after opening a file read-only, + but on some platforms that causes bad file descriptor + or similar errors. + + + + + + + Allow encoding conversion to succeed on longer strings than before + (Álvaro Herrera, Tom Lane) + + + + Previously, there was a hard limit of 0.25GB on the input string, + but now it will work as long as the converted output is not over 1GB. + + + + + + + Avoid creating unnecessarily-bulky tuple stores for window functions + (Andrew Gierth) + + + + In some cases the tuple storage would include all columns of the + source table(s), not just the ones that are needed by the query. + + + + + + + Allow repalloc() to give back space when a + large chunk is reduced in size (Tom Lane) + + + + + + + Ensure that temporary WAL and history files are removed at the end + of archive recovery (Sawada Masahiko) + + + + + + + Avoid failure in archive recovery + if recovery_min_apply_delay is enabled + (Fujii Masao) + + + + recovery_min_apply_delay is not typically used in + this configuration, but it should work. + + + + + + + Avoid unwanted delay during shutdown of a logical replication + walsender (Craig Ringer, Álvaro Herrera) + + + + + + + Correctly time-stamp replication messages for logical + decoding (Jeff Janes) + + + + This oversight resulted, for example, + in pg_stat_subscription.last_msg_send_time + usually reading as NULL. + + + + + + + In logical decoding, ensure that sub-transactions are correctly + accounted for when reconstructing a snapshot (Masahiko Sawada) + + + + This error leads to assertion failures; it's unclear whether any + bad effects exist in production builds. + + + + + + + Fix race condition during backend exit, when the backend process has + previously waited for synchronous replication to occur (Dongming Liu) + + + + + + + Fix ALTER SYSTEM to cope with duplicate entries + in postgresql.auto.conf (Ian Barwick) + + + + ALTER SYSTEM itself will not generate such a state, + but external tools that modify postgresql.auto.conf + could do so. Duplicate entries for the target variable will now be + removed, and then the new setting (if any) will be appended at the end. + + + + + + + Reject include directives with empty file names in configuration + files, and report include-file recursion more clearly + (Ian Barwick, Tom Lane) + + + + + + + Avoid logging complaints about abandoned connections when using PAM + authentication (Tom Lane) + + + + libpq-based clients will typically make two connection attempts when + a password is required, since they don't prompt their user for a + password until their first connection attempt fails. Therefore the + server is coded not to generate useless log spam when a client + closes the connection upon being asked for a password. However, + the PAM authentication code hadn't gotten that memo, and would + generate several messages about a phantom authentication failure. + + + + + + + Fix some cases where an incomplete date specification is not + detected in time with time zone input (Alexander Lakhin) + + + + If a time zone with a time-varying UTC offset is specified, then a + date must be as well, so that the offset can be resolved. Depending + on the syntax used, this check was not enforced in some cases, + allowing bogus output to be produced. + + + + + + + Fix misbehavior of bitshiftright() (Tom Lane) + + + + The bitstring right shift operator failed to zero out padding space + that exists in the last byte of the result when the bitstring length + is not a multiple of 8. While invisible to most operations, any + nonzero bits there would result in unexpected comparison behavior, + since bitstring comparisons don't bother to ignore the extra bits, + expecting them to always be zero. + + + + If you have inconsistent data as a result of saving the output + of bitshiftright() in a table, it's possible to + fix it with something like + +UPDATE mytab SET bitcol = ~(~bitcol) WHERE bitcol != ~(~bitcol); + + + + + + + + Fix detection of edge-case integer overflow in interval + multiplication (Yuya Watari) + + + + + + + Avoid crashes if ispell text search dictionaries + contain wrong affix data (Arthur Zakirov) + + + + + + + Fix incorrect compression logic for GIN posting lists + (Heikki Linnakangas) + + + + A GIN posting list item can require 7 bytes if the distance between + adjacent indexed TIDs exceeds 16TB. One step in the logic was out + of sync with that, and might try to write the value into a 6-byte + buffer. In principle this could cause a stack overrun, but on most + architectures it's likely that the next byte would be unused + alignment padding, making the bug harmless. In any case the bug + would be very difficult to hit. + + + + + + + Fix handling of infinity, NaN, and NULL values in KNN-GiST + (Alexander Korotkov) + + + + The query's output order could be wrong (different from a plain + sort's result) if some distances computed for non-null column values + are infinity or NaN. + + + + + + + Fix handling of searches for NULL in KNN-SP-GiST (Nikita Glukhov) + + + + + + + On Windows, recognize additional spellings of the Norwegian + (Bokmål) locale name (Tom Lane) + + + + + + + Avoid compile failure if an ECPG client + includes ecpglib.h while + having ENABLE_NLS defined (Tom Lane) + + + + This risk was created by a misplaced + declaration: ecpg_gettext() should not be + visible to client code. + + + + + + + In psql, resynchronize internal state + about the server after an unexpected connection loss and successful + reconnection (Peter Billen, Tom Lane) + + + + Ordinarily this is unnecessary since the state would be the same + anyway. But it can matter in corner cases, such as where the + connection might lead to one of several servers. This change + causes psql to re-issue any interactive + messages that it would have issued at startup, for example about + whether SSL is in use. + + + + + + + Avoid platform-specific null pointer dereference + in psql (Quentin Rameau) + + + + + + + Fix pg_dump's handling of circular + dependencies in views (Tom Lane) + + + + In some cases a view may depend on an object + that pg_dump needs to dump later than the + view; the most common example is that a query using GROUP + BY on a primary-key column may be semantically invalid + without the primary key. This is now handled by emitting a + dummy CREATE VIEW command that just establishes + the view's column names and types, and then later + emitting CREATE OR REPLACE VIEW with the full + view definition. Previously, the dummy definition was actually + a CREATE TABLE command, and this was + automagically converted to a view by a later CREATE + RULE command. The new approach has been used successfully + in PostgreSQL version 10 and later. We + are back-patching it into older releases now because of reports that + the previous method causes bogus error messages about the view's + replica identity status. This change also avoids problems when + trying to use the option during a restore + involving such a view. + + + + + + + In pg_dump, ensure stable output order + for similarly-named triggers and row-level-security policy objects + (Benjie Gillam) + + + + Previously, if two triggers on different tables had the same names, + they would be sorted in OID-based order, which is less desirable + than sorting them by table name. Likewise for RLS policies. + + + + + + + Fix pg_dump to work again with pre-8.3 + source servers (Tom Lane) + + + + A previous fix caused pg_dump to always + try to query pg_opfamily, but that catalog + doesn't exist before version 8.3. + + + + + + + In pg_restore, treat + as meaning output to stdout + (Álvaro Herrera) + + + + This synchronizes pg_restore's behavior + with some other applications, and in particular makes pre-v12 branches + act similarly to version 12's pg_restore, + simplifying creation of dump/restore scripts that work across + multiple PostgreSQL versions. Before this + change, pg_restore interpreted such a + switch as meaning output to a file + named -, but few people would want that. + + + + + + + Improve pg_upgrade's checks for the use + of a data type that has changed representation, such + as line (Tomas Vondra) + + + + The previous coding could be fooled by cases where the data type of + interest underlies a stored column of a domain or composite type. + + + + + + + Detect file read errors + during pg_basebackup (Jeevan Chalke) + + + + + + + In pg_rewind with an online source + cluster, disable timeouts, much + as pg_dump does (Alexander Kukushkin) + + + + + + + Fix failure in pg_waldump with + the option, when a continuation WAL record ends + exactly at a page boundary (Andrey Lepikhov) + + + + + + + In pg_waldump, + include the newitemoff field in btree page split + records (Peter Geoghegan) + + + + + + + In pg_waldump with + the option, avoid emitting extra + newlines for WAL records involving full-page writes (Andres Freund) + + + + + + + Fix small memory leak in pg_waldump + (Andres Freund) + + + + + + + Fix vacuumdb with a + high option to handle running out of file + descriptors better (Michael Paquier) + + + + + + + Fix contrib/intarray's GiST opclasses to not + fail for empty arrays with <@ (Tom Lane) + + + + A clause like array_column + <@ constant_array is + considered indexable, but the index search may not find empty array + values; of course, such entries should trivially match the search. + + + + The only practical back-patchable fix for this requires + making <@ index searches scan the whole index, + which is what this patch does. This is unfortunate: it means that + the query performance is likely worse than a plain sequential scan + would be. + + + + Applications whose performance is adversely impacted by this change + have a couple of options. They could switch to a GIN index, which + doesn't have this bug, or they could replace + array_column + <@ constant_array + with array_column + <@ constant_array + AND array_column + && constant_array. + That will provide about the same performance as before, and it will + find all non-empty subsets of the given constant array, which is all + that could reliably be expected of the query before. + + + + + + + Allow configure --with-python to succeed when + only python3 or + only python2 can be found (Peter Eisentraut, + Tom Lane) + + + + Search for python, + then python3, + then python2, so + that configure can succeed in the + increasingly-more-common situation where there is no executable + named simply python. It's still possible to + override this choice by setting the PYTHON + environment variable. + + + + + + + Fix configure's test for presence of + libperl so that it works on recent Red Hat releases (Tom Lane) + + + + Previously, it could fail if the user sets CFLAGS + to -O0. + + + + + + + Ensure correct code generation for spinlocks on PowerPC (Noah Misch) + + + + The previous spinlock coding allowed the compiler to select register + zero for use with an assembly instruction that does not accept that + register, causing a build failure. We have seen only one long-ago + report that matches this bug, but it could cause problems for people + trying to build modified PostgreSQL code + or use atypical compiler options. + + + + + + + On PowerPC, avoid depending on the xlc + compiler's __fetch_and_add() function + (Noah Misch) + + + + xlc 13 and newer interpret this function in a way incompatible with + our usage, resulting in an unusable build + of PostgreSQL. Fix by using custom + assembly code instead. + + + + + + + On AIX, don't use the compiler option + (Noah Misch) + + + + This avoids an internal compiler error with xlc v16.1.0, with little + consequence other than changing the format of compiler error messages. + + + + + + + Fix MSVC build process to cope with spaces in the file path of + OpenSSL (Andrew Dunstan) + + + + + + + Update time zone data files to tzdata + release 2019c for DST law changes in Fiji and Norfolk Island, plus + historical corrections for Alberta, Austria, Belgium, British + Columbia, Cambodia, Hong Kong, Indiana (Perry County), Kaliningrad, + Kentucky, Michigan, Norfolk Island, South Korea, and Turkey. + + + + + + + + + + Release 9.6.15 + + + 发布日期: + 2019-08-08 + + + + 此版本包含自 9.6.14 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.15 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + + Require schema qualification to cast to a temporary type when using + functional cast syntax (Noah Misch) + + + + We have long required invocations of temporary functions to + explicitly specify the temporary schema, that + is pg_temp.func_name(args). + Require this as well for casting to temporary types using functional + notation, for + example pg_temp.type_name(arg). + Otherwise it's possible to capture a function call using a temporary + object, allowing privilege escalation in much the same ways that we + blocked in CVE-2007-2138. + (CVE-2019-10208) + + + + + + + Fix failure of ALTER TABLE ... ALTER COLUMN TYPE + when altering multiple columns' types in one command (Tom Lane) + + + + This fixes a regression introduced in the most recent minor releases: + indexes using the altered columns were not processed correctly, + leading to strange failures during ALTER TABLE. + + + + + + + Don't optimize away GROUP BY columns when the + table involved is an inheritance parent (David Rowley) + + + + Normally, if a table's primary key column(s) are included + in GROUP BY, it's safe to drop any other grouping + columns, since the primary key columns are enough to make the groups + unique. This rule does not work if the query is also reading + inheritance child tables, though; the parent's uniqueness does not + extend to the children. + + + + + + + Avoid using unnecessary sort steps for some queries + with GROUPING SETS (Andrew Gierth, Richard Guo) + + + + + + + Fix mishandling of multi-column foreign keys when rebuilding a + foreign key constraint (Tom Lane) + + + + ALTER TABLE could make an incorrect decision about + whether revalidation of a foreign key is necessary, if not all + columns of the key are of the same type. It seems likely that the + error would always have been in the conservative direction, that is + revalidating unnecessarily. + + + + + + + Avoid spurious deadlock errors when upgrading a tuple lock + (Oleksii Kliukin) + + + + When two or more transactions are waiting for a transaction T1 to + release a tuple-level lock, and T1 upgrades its lock to a higher + level, a spurious deadlock among the waiting transactions could be + reported when T1 finishes. + + + + + + + Fix failure to resolve deadlocks involving multiple parallel worker + processes (Rui Hai Jiang) + + + + It is not clear whether this bug is reachable with non-artificial + queries, but if it did happen, the queries involved in an + otherwise-resolvable deadlock would block until canceled. + + + + + + + Prevent incorrect canonicalization of date ranges + with infinity endpoints (Laurenz Albe) + + + + It's incorrect to try to convert an open range to a closed one or + vice versa by incrementing or decrementing the endpoint value, if + the endpoint is infinite; so leave the range alone in such cases. + + + + + + + Fix loss of fractional digits when converting very + large money values to numeric (Tom Lane) + + + + + + + Fix spinlock assembly code for MIPS CPUs so that it works on + MIPS r6 (YunQiang Su) + + + + + + + Make libpq ignore carriage return + (\r) in connection service files + (Tom Lane, Michael Paquier) + + + + In some corner cases, service files containing Windows-style + newlines could be mis-parsed, resulting in connection failures. + + + + + + + In psql, avoid offering incorrect tab + completion options + after SET variable = + (Tom Lane) + + + + + + + Fix pg_dump to ensure that custom operator + classes are dumped in the right order (Tom Lane) + + + + If a user-defined opclass is the subtype opclass of a user-defined + range type, related objects were dumped in the wrong order, + producing an unrestorable dump. (The underlying failure to handle + opclass dependencies might manifest in other cases too, but this is + the only known case.) + + + + + + + Fix contrib/passwordcheck to coexist with other + users of check_password_hook (Michael Paquier) + + + + + + + Fix contrib/sepgsql tests to work under recent + SELinux releases (Mike Palmiotto) + + + + + + + Improve stability of src/test/recovery + regression tests (Michael Paquier) + + + + + + + Reduce stderr output + from pg_upgrade's test script (Tom Lane) + + + + + + + Fix TAP tests to work with msys Perl, in cases where the build + directory is on a non-root msys mount point (Noah Misch) + + + + + + + Support building Postgres with Microsoft Visual Studio 2019 + (Haribabu Kommi) + + + + + + + In Visual Studio builds, honor WindowsSDKVersion + environment variable, if that's set (Peifeng Qiu) + + + + This fixes build failures in some configurations. + + + + + + + Support OpenSSL 1.1.0 and newer in Visual Studio builds + (Juan José Santamaría Flecha, Michael Paquier) + + + + + + + Allow make options to be passed down + to gmake when non-GNU make is invoked at + the top level (Thomas Munro) + + + + + + + Avoid choosing localtime + or posixrules as TimeZone + during initdb (Tom Lane) + + + + In some cases initdb would choose one of + these artificial zone names over the real zone name. + Prefer any other match to the C library's timezone behavior over + these two. + + + + + + + Adjust pg_timezone_names view to show + the Factory time zone if and only if it has a + short abbreviation (Tom Lane) + + + + Historically, IANA set up this artificial zone with + an abbreviation like Local time zone must be + set--see zic manual page. Modern versions of the tzdb + database show -00 instead, but some platforms + alter the data to show one or another of the historical phrases. + Show this zone only if it uses the modern abbreviation. + + + + + + + Sync our copy of the timezone library with IANA tzcode release 2019b + (Tom Lane) + + + + This adds support for zic's new option to reduce the size of the installed zone files. + We are not currently using that, but may enable it in future. + + + + + + + Update time zone data files to tzdata + release 2019b for DST law changes in Brazil, plus + historical corrections for Hong Kong, Italy, and Palestine. + + + + + + + + + + Release 9.6.14 + + + 发布日期: + 2019-06-20 + + + + 此版本包含自 9.6.13 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.14 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + + Fix failure of ALTER TABLE ... ALTER COLUMN TYPE + when the table has a partial exclusion constraint (Tom Lane) + + + + + + + Fix failure of COMMENT command for comments on + domain constraints (Daniel Gustafsson, Michael Paquier) + + + + + + + Fix faulty generation of merge-append plans (Tom Lane) + + + + This mistake could lead to could not find pathkey item to + sort errors. + + + + + + + Fix incorrect printing of queries with duplicate join names + (Philip Dubé) + + + + This oversight caused a dump/restore failure for views containing + such queries. + + + + + + + Fix misoptimization of {1,1} quantifiers in + regular expressions (Tom Lane) + + + + Such quantifiers were treated as no-ops and optimized away; + but the documentation specifies that they impose greediness, or + non-greediness in the case of the non-greedy + variant {1,1}?, on the subexpression they're + attached to, and this did not happen. The misbehavior occurred + only if the subexpression contained capturing parentheses or a + back-reference. + + + + + + + Avoid possible failures while initializing a new + process's pg_stat_activity data (Tom Lane) + + + + Certain operations that could fail, such as converting strings + extracted from an SSL certificate into the database encoding, were + being performed inside a critical section. Failure there would + result in database-wide lockup due to violating the access protocol + for shared pg_stat_activity data. + + + + + + + Fix race condition in check to see whether a pre-existing shared + memory segment is still in use by a conflicting postmaster (Tom Lane) + + + + + + + Avoid attempting to do database accesses for parameter checking in + processes that are not connected to a specific database (Vignesh C, + Andres Freund) + + + + This error could result in failures like cannot read pg_class + without having selected a database. + + + + + + + Avoid possible hang in libpq if using SSL + and OpenSSL's pending-data buffer contains an exact multiple of 256 + bytes (David Binderman) + + + + + + + Improve initdb's handling of multiple + equivalent names for the system time zone (Tom Lane, Andrew Gierth) + + + + Make initdb examine + the /etc/localtime symbolic link, if that + exists, to break ties between equivalent names for the system time + zone. This makes initdb more likely to + select the time zone name that the user would expect when multiple + identical time zones exist. It will not change the behavior + if /etc/localtime is not a symlink to a zone + data file, nor if the time zone is determined from + the TZ environment variable. + + + + Separately, prefer UTC over other spellings of + that time zone, when neither TZ + nor /etc/localtime provide a hint. This fixes + an annoyance introduced by tzdata 2019a's + change to make the UCT and UTC + zone names equivalent: initdb was then + preferring UCT, which almost nobody wants. + + + + + + + Fix ordering of GRANT commands emitted + by pg_dump + and pg_dumpall for databases and + tablespaces (Nathan Bossart, Michael Paquier) + + + + If cascading grants had been issued, restore might fail due to + the GRANT commands being given in an order that + didn't respect their interdependencies. + + + + + + + Fix misleading error reports + from reindexdb (Julien Rouhaud) + + + + + + + Ensure that vacuumdb returns correct + status if an error occurs while using parallel jobs + (Julien Rouhaud) + + + + + + + Fix contrib/auto_explain to not cause problems + in parallel queries (Tom Lane) + + + + Previously, a parallel worker might try to log its query even if the + parent query were not being logged + by auto_explain. This would work sometimes, but + it's confusing, and in some cases it resulted in failures + like could not find key N in shm TOC. + + + + Also, fix an off-by-one error that resulted in not necessarily + logging every query even when the sampling rate is set to 1.0. + + + + + + + In contrib/postgres_fdw, account for possible + data modifications by local BEFORE ROW UPDATE + triggers (Shohei Mochizuki) + + + + If a trigger modified a column that was otherwise not changed by the + UPDATE, the new value was not transmitted to the + remote server. + + + + + + + On Windows, avoid failure when the database encoding is set to + SQL_ASCII and we attempt to log a non-ASCII string (Noah Misch) + + + + The code had been assuming that such strings must be in UTF-8, and + would throw an error if they didn't appear to be validly encoded. + Now, just transmit the untranslated bytes to the log. + + + + + + + Make PL/pgSQL's header files C++-safe + (George Tarasov) + + + + + + + + + + Release 9.6.13 + + + 发布日期: + 2019-05-09 + + + + 此版本包含自 9.6.12 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.13 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + Prevent row-level security policies from being bypassed via + selectivity estimators (Dean Rasheed) + + + + Some of the planner's selectivity estimators apply user-defined + operators to values found in pg_statistic + (e.g., most-common values). A leaky operator therefore can disclose + some of the entries in a data column, even if the calling user lacks + permission to read that column. In CVE-2017-7484 we added + restrictions to forestall that, but we failed to consider the + effects of row-level security. A user who has SQL permission to + read a column, but who is forbidden to see certain rows due to RLS + policy, might still learn something about those rows' contents via a + leaky operator. This patch further tightens the rules, allowing + leaky operators to be applied to statistics data only when there is + no relevant RLS policy. (CVE-2019-10130) + + + + + + Fix behavior for an UPDATE + or DELETE on an inheritance tree or partitioned + table in which every table can be excluded (Amit Langote, Tom Lane) + + + + In such cases, the query did not report the correct set of output + columns when a RETURNING clause was present, and + if there were any statement-level triggers that should be fired, it + didn't fire them. + + + + + + Fix handling of explicit DEFAULT items in + an INSERT ... VALUES command with + multiple VALUES rows, if the target relation is + an updatable view (Amit Langote, Dean Rasheed) + + + + When the updatable view has no default for the column but its + underlying table has one, a single-row INSERT + ... VALUES will use the underlying table's default. + In the multi-row case, however, NULL was always used. Correct it to + act like the single-row case. + + + + + + Fix CREATE VIEW to allow zero-column views + (Ashutosh Sharma) + + + + We should allow this for consistency with allowing zero-column + tables. Since a table can be converted to a view, zero-column views + could be created even with the restriction in place, leading to + dump/reload failures. + + + + + + Add missing support for CREATE TABLE IF NOT EXISTS ... AS + EXECUTE ... (Andreas Karlsson) + + + + The combination of IF NOT EXISTS + and EXECUTE should work, but the grammar omitted + it. + + + + + + Ensure that sub-SELECTs appearing in + row-level-security policy expressions are executed with the correct + user's permissions (Dean Rasheed) + + + + Previously, if the table having the RLS policy was accessed via a + view, such checks might be executed as the user calling the view, + not as the view owner as they should be. + + + + + + Accept XML documents as valid values of type xml + when xmloption is set + to content, as required by SQL:2006 and later + (Chapman Flack) + + + + Previously PostgreSQL followed the + SQL:2003 definition, which doesn't allow this. But that creates a + serious problem for dump/restore: there is no setting + of xmloption that will accept all valid XML data. + Hence, switch to the 2006 definition. + + + + pg_dump is also modified to emit + SET xmloption = content while restoring data, + ensuring that dump/restore works even if the prevailing + setting is document. + + + + + + Improve server's startup-time checks for whether a pre-existing + shared memory segment is still in use (Noah Misch) + + + + The postmaster is now more likely to detect that there are still + active processes from a previous postmaster incarnation, even if + the postmaster.pid file has been removed. + + + + + + Avoid counting parallel workers' transactions as separate + transactions (Haribabu Kommi) + + + + + + Fix incompatibility of GIN-index WAL records (Alexander Korotkov) + + + + A fix applied in February's minor releases was not sufficiently + careful about backwards compatibility, leading to problems if a + standby server of that vintage reads GIN page-deletion WAL records + generated by a primary server of a previous minor release. + + + + + + Tolerate EINVAL and ENOSYS + error results, where appropriate, for fsync + and sync_file_range calls + (Thomas Munro, James Sewell) + + + + The previous change to panic on file synchronization failures turns + out to have been excessively paranoid for certain cases where a + failure is predictable and essentially means operation not + supported. + + + + + + Fix failed to build any N-way + joins planner failures with lateral references leading out + of FULL outer joins (Tom Lane) + + + + + + Check the appropriate user's permissions when enforcing rules about + letting a leaky operator see pg_statistic + data (Dean Rasheed) + + + + When an underlying table is being accessed via a view, consider the + privileges of the view owner while deciding whether leaky operators + may be applied to the table's statistics data, rather than the + privileges of the user making the query. This makes the planner's + rules about what data is visible match up with the executor's, + avoiding unnecessarily-poor plans. + + + + + + Speed up planning when there are many equality conditions and many + potentially-relevant foreign key constraints (David Rowley) + + + + + + Avoid O(N^2) performance issue when rolling back a transaction that + created many tables (Tomas Vondra) + + + + + + Fix race conditions in management of dynamic shared memory + (Thomas Munro) + + + These could lead to dsa_area could not attach to + segment or cannot unpin a segment that is not + pinned errors. + + + + + + Fix race condition in which a hot-standby postmaster could fail to + shut down after receiving a smart-shutdown request (Tom Lane) + + + + + + Fix possible crash + when pg_identify_object_as_address() is given + invalid input (Álvaro Herrera) + + + + + + Tighten validation of encoded SCRAM-SHA-256 and MD5 passwords + (Jonathan Katz) + + + + A password string that had the right initial characters could be + mistaken for one that is correctly hashed into SCRAM-SHA-256 or MD5 + format. The password would be accepted but would be unusable later. + + + + + + Fix handling of lc_time settings that imply an + encoding different from the database's encoding (Juan José + Santamaría Flecha, Tom Lane) + + + + Localized month or day names that include non-ASCII characters + previously caused unexpected errors or wrong output in such locales. + + + + + + Fix incorrect operator_precedence_warning checks + involving unary minus operators (Rikard Falkeborn) + + + + + + Disallow NaN as a value for floating-point server + parameters (Tom Lane) + + + + + + Rearrange REINDEX processing to avoid assertion + failures when reindexing individual indexes + of pg_class (Andres Freund, Tom Lane) + + + + + + Fix planner assertion failure for parameterized dummy paths (Tom Lane) + + + + + + Insert correct test function in the result + of SnapBuildInitialSnapshot() (Antonin Houska) + + + + No core code cares about this, but some extensions do. + + + + + + Fix intermittent could not reattach to shared memory + session startup failures on Windows (Noah Misch) + + + + A previously unrecognized source of these failures is creation of + thread stacks for a process's default thread pool. Arrange for such + stacks to be allocated in a different memory region. + + + + + + Fix error detection in directory scanning on Windows (Konstantin + Knizhnik) + + + + Errors, such as lack of permissions to read the directory, were not + detected or reported correctly; instead the code silently acted as + though the directory were empty. + + + + + + Fix grammar problems in ecpg (Tom Lane) + + + + A missing semicolon led to mistranslation + of SET variable = + DEFAULT (but + not SET variable TO + DEFAULT) in ecpg programs, + producing syntactically invalid output that the server would reject. + Additionally, in a DROP TYPE or DROP + DOMAIN command that listed multiple type names, only the + first type name was actually processed. + + + + + + Sync ecpg's syntax for CREATE + TABLE AS with the server's (Daisuke Higuchi) + + + + + + Fix possible buffer overruns in ecpg's + processing of include filenames (Liu Huailing, Fei Wu) + + + + + + Avoid crash in contrib/vacuumlo if + an lo_unlink() call failed (Tom Lane) + + + + + + Sync our copy of the timezone library with IANA tzcode release 2019a + (Tom Lane) + + + + This corrects a small bug in zic that + caused it to output an incorrect year-2440 transition in + the Africa/Casablanca zone, and adds support + for zic's new option. + + + + + + Update time zone data files to tzdata + release 2019a for DST law changes in Palestine and Metlakatla, + plus historical corrections for Israel. + + + + Etc/UCT is now a backward-compatibility link + to Etc/UTC, instead of being a separate zone that + generates the abbreviation UCT, which nowadays is + typically a typo. PostgreSQL will still + accept UCT as an input zone abbreviation, but it + won't output it. + + + + + + + + + + Release 9.6.12 + + + 发布日期: + 2019-02-14 + + + + 此版本包含自 9.6.11 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.12 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + + By default, panic instead of retrying + after fsync() failure, to avoid possible data + corruption (Craig Ringer, Thomas Munro) + + + + Some popular operating systems discard kernel data buffers when + unable to write them out, reporting this + as fsync() failure. If we reissue + the fsync() request it will succeed, but in + fact the data has been lost, so continuing risks database + corruption. By raising a panic condition instead, we can replay + from WAL, which may contain the only remaining copy of the data in + such a situation. While this is surely ugly and inefficient, there + are few alternatives, and fortunately the case happens very rarely. + + + + A new server parameter + has been added to control this; if you are certain that your + kernel does not discard dirty data buffers in such scenarios, + you can set data_sync_retry + to on to restore the old behavior. + + + + + + + Include each major release branch's release notes in the + documentation for only that branch, rather than that branch and all + later ones (Tom Lane) + + + + The duplication induced by the previous policy was getting out of + hand. Our plan is to provide a full archive of release notes on + the project's web site, but not duplicate it within each release. + + + + + + + Avoid possible deadlock when acquiring multiple buffer locks + (Nishant Fnu) + + + + + + + Avoid deadlock between hot-standby queries and replay of GIN index + page deletion (Alexander Korotkov) + + + + + + + Fix possible crashes in logical replication when index expressions + or predicates are in use (Peter Eisentraut) + + + + + + + Avoid useless and expensive logical decoding of TOAST data during a + table rewrite (Tomas Vondra) + + + + + + + Fix logic for stopping a subset of WAL senders when synchronous + replication is enabled (Paul Guo, Michael Paquier) + + + + + + + Avoid possibly writing an incorrect replica identity field in a + tuple deletion WAL record (Stas Kelvich) + + + + + + + Make the archiver prioritize WAL history files over WAL data files + while choosing which file to archive next (David Steele) + + + + + + + Fix possible crash in UPDATE with a + multiple SET clause using a + sub-SELECT as source (Tom Lane) + + + + + + + Avoid crash if libxml2 returns a null + error message (Sergio Conde Gómez) + + + + + + + Fix spurious grouping-related parser errors caused by inconsistent + handling of collation assignment (Andrew Gierth) + + + + In some cases, expressions that should be considered to match + were not seen as matching, if they included operations on collatable + data types. + + + + + + + Check whether the comparison function + underlying LEAST() + or GREATEST() is leakproof, rather than just + assuming it is (Tom Lane) + + + + Actual information leaks from btree comparison functions are + typically hard to provoke, but in principle they could happen. + + + + + + + Fix incorrect planning of queries involving nested loops both above + and below a Gather plan node (Tom Lane) + + + + If both levels of nestloop needed to pass the same variable into + their right-hand sides, an incorrect plan would be generated. + + + + + + + Fix incorrect planning of queries in which a lateral reference must + be evaluated at a foreign table scan (Tom Lane) + + + + + + + Fix corner-case underestimation of the cost of a merge join (Tom Lane) + + + + The planner could prefer a merge join when the outer key range is + much smaller than the inner key range, even if there are so many + duplicate keys on the inner side that this is a poor choice. + + + + + + + Avoid O(N^2) planning time growth when a query contains many + thousand indexable clauses (Tom Lane) + + + + + + + Improve ANALYZE's handling of + concurrently-updated rows (Jeff Janes, Tom Lane) + + + + Previously, rows deleted by an in-progress transaction were omitted + from ANALYZE's sample, but this has been found to + lead to more inconsistency than including them would do. In effect, + the sample now corresponds to an MVCC snapshot as + of ANALYZE's start time. + + + + + + + Make TRUNCATE ignore inheritance child tables + that are temporary tables of other sessions (Amit Langote, Michael + Paquier) + + + + This brings TRUNCATE into line with the behavior + of other commands. Previously, such cases usually ended in failure. + + + + + + + Fix TRUNCATE to update the statistics counters + for the right table (Tom Lane) + + + + If the truncated table had a TOAST table, that table's counters were + reset instead. + + + + + + + Process ALTER TABLE ONLY ADD COLUMN IF NOT EXISTS + correctly (Greg Stark) + + + + + + + Allow UNLISTEN in hot-standby mode + (Shay Rojansky) + + + + This is necessarily a no-op, because LISTEN + isn't allowed in hot-standby mode; but allowing the dummy operation + simplifies session-state-reset logic in clients. + + + + + + + Fix missing role dependencies in some schema and data type + permissions lists (Tom Lane) + + + + In some cases it was possible to drop a role to which permissions + had been granted. This caused no immediate problem, but a + subsequent dump/reload or upgrade would fail, with symptoms + involving attempts to grant privileges to all-numeric role names. + + + + + + + Ensure relation caches are updated properly after adding or removing + foreign key constraints (Álvaro Herrera) + + + + This oversight could result in existing sessions failing to enforce + a newly-created constraint, or continuing to enforce a dropped one. + + + + + + + Ensure relation caches are updated properly after renaming + constraints (Amit Langote) + + + + + + + Make autovacuum more aggressive about removing leftover temporary + tables, and also remove leftover temporary tables + during DISCARD TEMP (Álvaro Herrera) + + + + This helps ensure that remnants from a crashed session are cleaned + up more promptly. + + + + + + + Fix replay of GiST index micro-vacuum operations so that concurrent + hot-standby queries do not see inconsistent state (Alexander + Korotkov) + + + + + + + Prevent empty GIN index pages from being reclaimed too quickly, + causing failures of concurrent searches + (Andrey Borodin, Alexander Korotkov) + + + + + + + Fix edge-case failures in float-to-integer coercions (Andrew + Gierth, Tom Lane) + + + + Values very slightly above the maximum valid integer value might not + be rejected, and then would overflow, producing the minimum valid + integer instead. Also, values that should round to the minimum or + maximum integer value might be incorrectly rejected. + + + + + + + When making a PAM authentication request, don't set + the PAM_RHOST variable if the connection is via + a Unix socket (Thomas Munro) + + + + Previously that variable would be set to [local], + which is at best unhelpful, since it's supposed to be a host name. + + + + + + + Disallow setting client_min_messages higher + than ERROR (Jonah Harris, Tom Lane) + + + + Previously, it was possible to set this variable + to FATAL or PANIC, which had + the effect of suppressing transmission of ordinary error messages to + the client. However, that's contrary to guarantees that are given + in the PostgreSQL wire protocol + specification, and it caused some clients to become very confused. + In released branches, fix this by silently treating such settings as + meaning ERROR instead. Version 12 and later will + reject those alternatives altogether. + + + + + + + Fix ecpglib to + use uselocale() + or _configthreadlocale() in preference + to setlocale() (Michael Meskes, Tom Lane) + + + + Since setlocale() is not thread-local, and + might not even be thread-safe, the previous coding caused problems + in multi-threaded ecpg applications. + + + + + + + Fix incorrect results for numeric data passed through + an ecpg SQLDA + (SQL Descriptor Area) (Daisuke Higuchi) + + + + Values with leading zeroes were not copied correctly. + + + + + + + Fix psql's \g + target meta-command to work + with COPY TO STDOUT + (Daniel Vérité) + + + + Previously, the target option was + ignored, so that the copy data always went to the current query + output target. + + + + + + + Make psql's LaTeX output formats render + special characters properly (Tom Lane) + + + + Backslash and some other ASCII punctuation characters were not + rendered correctly, leading to document syntax errors or wrong + characters in the output. + + + + + + + Fix pg_dump's handling of materialized + views with indirect dependencies on primary keys (Tom Lane) + + + + This led to mis-labeling of such views' dump archive entries, + causing harmless warnings about archive items not in correct + section order; less harmlessly, selective-restore options + depending on those labels, such as , might + misbehave. + + + + + + + Avoid null-pointer-dereference crash on some platforms + when pg_dump + or pg_restore tries to report an error + (Tom Lane) + + + + + + + Fix contrib/hstore to calculate correct hash + values for empty hstore values that were created in + version 8.4 or before (Andrew Gierth) + + + + The previous coding did not give the same result as for an + empty hstore value created by a newer version, thus + potentially causing wrong results in hash joins or hash + aggregation. It is advisable to reindex any hash indexes + built on hstore columns, if the table might contain + data that was originally stored as far back as 8.4 and was never + dumped/reloaded since then. + + + + + + + Avoid crashes and excessive runtime with large inputs + to contrib/intarray's gist__int_ops + index support (Andrew Gierth) + + + + + + + Support new Makefile + variables PG_CFLAGS, PG_CXXFLAGS, + and PG_LDFLAGS in pgxs + builds (Christoph Berg) + + + + This simplifies customization of extension build processes. + + + + + + + Fix Perl-coded build scripts to not + assume . is in the search path, + since recent Perl versions don't include that (Andrew Dunstan) + + + + + + + Fix server command-line option parsing problems on OpenBSD (Tom Lane) + + + + + + + Relocate call of set_rel_pathlist_hook so that + extensions can use it to supply partial paths for parallel queries + (KaiGai Kohei) + + + + This is not expected to affect existing use-cases. + + + + + + + Update time zone data files to tzdata + release 2018i for DST law changes in Kazakhstan, Metlakatla, and Sao + Tome and Principe. Kazakhstan's Qyzylorda zone is split in two, + creating a new zone Asia/Qostanay, as some areas did not change UTC + offset. Historical corrections for Hong Kong and numerous Pacific + islands. + + + + + + + + + + Release 9.6.11 + + + 发布日期: + 2018-11-08 + + + + 此版本包含自 9.6.10 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.11 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + Fix corner-case failures + in has_foo_privilege() + family of functions (Tom Lane) + + + + Return NULL rather than throwing an error when an invalid object OID + is provided. Some of these functions got that right already, but not + all. has_column_privilege() was additionally + capable of crashing on some platforms. + + + + + + Avoid O(N^2) slowdown in regular expression match/split functions on + long strings (Andrew Gierth) + + + + + + Fix parsing of standard multi-character operators that are immediately + followed by a comment or + or - + (Andrew Gierth) + + + + This oversight could lead to parse errors, or to incorrect assignment + of precedence. + + + + + + Avoid O(N^3) slowdown in lexer for long strings + of + or - characters + (Andrew Gierth) + + + + + + Fix mis-execution of SubPlans when the outer query is being scanned + backwards (Andrew Gierth) + + + + + + Fix failure of UPDATE/DELETE ... WHERE CURRENT OF ... + after rewinding the referenced cursor (Tom Lane) + + + + A cursor that scans multiple relations (particularly an inheritance + tree) could produce wrong behavior if rewound to an earlier relation. + + + + + + Fix EvalPlanQual to handle conditionally-executed + InitPlans properly (Andrew Gierth, Tom Lane) + + + + This resulted in hard-to-reproduce crashes or wrong answers in + concurrent updates, if they contained code such as an uncorrelated + sub-SELECT inside a CASE + construct. + + + + + + Fix character-class checks to not fail on Windows for Unicode + characters above U+FFFF (Tom Lane, Kenji Uno) + + + + This bug affected full-text-search operations, as well + as contrib/ltree + and contrib/pg_trgm. + + + + + + Disallow pushing sub-SELECTs containing window + functions, LIMIT, or OFFSET to + parallel workers (Amit Kapila) + + + + Such cases could result in inconsistent behavior due to different + workers getting different answers, as a result of indeterminacy + due to row-ordering variations. + + + + + + Ensure that sequences owned by a foreign table are processed + by ALTER OWNER on the table (Peter Eisentraut) + + + + The ownership change should propagate to such sequences as well, but + this was missed for foreign tables. + + + + + + Ensure that the server will process + already-received NOTIFY + and SIGTERM interrupts before waiting for client + input (Jeff Janes, Tom Lane) + + + + + + Fix over-allocation of space for array_out()'s + result string (Keiichi Hirobe) + + + + + + Fix memory leak in repeated SP-GiST index scans (Tom Lane) + + + + This is only known to amount to anything significant in cases where + an exclusion constraint using SP-GiST receives many new index entries + in a single command. + + + + + + Ensure that ApplyLogicalMappingFile() closes the + mapping file when done with it (Tomas Vondra) + + + + Previously, the file descriptor was leaked, eventually resulting in + failures during logical decoding. + + + + + + Fix logical decoding to handle cases where a mapped catalog table is + repeatedly rewritten, e.g., by VACUUM FULL + (Andres Freund) + + + + + + Prevent starting the server with wal_level set + to too low a value to support an existing replication slot (Andres + Freund) + + + + + + Avoid crash if a utility command causes infinite recursion (Tom Lane) + + + + + + When initializing a hot standby, cope with duplicate XIDs caused by + two-phase transactions on the master + (Michael Paquier, Konstantin Knizhnik) + + + + + + Fix event triggers to handle nested ALTER TABLE + commands (Michael Paquier, Álvaro Herrera) + + + + + + Propagate parent process's transaction and statement start timestamps + to parallel workers (Konstantin Knizhnik) + + + + This prevents misbehavior of functions such + as transaction_timestamp() when executed in a + worker. + + + + + + Fix transfer of expanded datums to parallel workers so that alignment + is preserved, preventing crashes on alignment-picky platforms + (Tom Lane, Amit Kapila) + + + + + + Fix WAL file recycling logic to work correctly on standby servers + (Michael Paquier) + + + + Depending on the setting of archive_mode, a standby + might fail to remove some WAL files that could be removed. + + + + + + Fix handling of commit-timestamp tracking during recovery + (Masahiko Sawada, Michael Paquier) + + + + If commit timestamp tracking has been turned on or off, recovery might + fail due to trying to fetch the commit timestamp for a transaction + that did not record it. + + + + + + Randomize the random() seed in bootstrap and + standalone backends, and in initdb + (Noah Misch) + + + + The main practical effect of this change is that it avoids a scenario + where initdb might mistakenly conclude that + POSIX shared memory is not available, due to name collisions caused by + always using the same random seed. + + + + + + Allow DSM allocation to be interrupted (Chris Travers) + + + + + + Avoid failure in a parallel worker when loading an extension that + tries to access system caches within its init function (Thomas Munro) + + + + We don't consider that to be good extension coding practice, but it + mostly worked before parallel query, so continue to support it for + now. + + + + + + Properly handle turning full_page_writes on + dynamically (Kyotaro Horiguchi) + + + + + + Fix possible crash due to double free() during + SP-GiST rescan (Andrew Gierth) + + + + + + Avoid possible buffer overrun when replaying GIN page recompression + from WAL (Alexander Korotkov, Sivasubramanian Ramasubramanian) + + + + + + Fix missed fsync of a replication slot's directory (Konstantin + Knizhnik, Michael Paquier) + + + + + + Fix unexpected timeouts when + using wal_sender_timeout on a slow server + (Noah Misch) + + + + + + Ensure that hot standby processes use the correct WAL consistency + point (Alexander Kukushkin, Michael Paquier) + + + + This prevents possible misbehavior just after a standby server has + reached a consistent database state during WAL replay. + + + + + + Ensure background workers are stopped properly when the postmaster + receives a fast-shutdown request before completing database startup + (Alexander Kukushkin) + + + + + + Update the free space map during WAL replay of page all-visible/frozen + flag changes (Álvaro Herrera) + + + + Previously we were not careful about this, reasoning that the FSM is + not critical data anyway. However, if it's sufficiently out of date, + that can result in significant performance degradation after a standby + has been promoted to primary. The FSM will eventually be healed by + updates, but we'd like it to be good sooner, so work harder at + maintaining it during WAL replay. + + + + + + Avoid premature release of parallel-query resources when query end or + tuple count limit is reached (Amit Kapila) + + + + It's only okay to shut down the executor at this point if the caller + cannot demand backwards scan afterwards. + + + + + + Don't run atexit callbacks when servicing SIGQUIT + (Heikki Linnakangas) + + + + + + Don't record foreign-server user mappings as members of extensions + (Tom Lane) + + + + If CREATE USER MAPPING is executed in an extension + script, an extension dependency was created for the user mapping, + which is unexpected. Roles can't be extension members, so user + mappings shouldn't be either. + + + + + + Make syslogger more robust against failures in opening CSV log files + (Tom Lane) + + + + + + Fix psql, as well as documentation + examples, to call PQconsumeInput() before + each PQnotifies() call (Tom Lane) + + + + This fixes cases in which psql would not + report receipt of a NOTIFY message until after the + next command. + + + + + + Fix possible inconsistency in pg_dump's + sorting of dissimilar object names (Jacob Champion) + + + + + + Ensure that pg_restore will schema-qualify + the table name when + emitting DISABLE/ENABLE TRIGGER + commands (Tom Lane) + + + + This avoids failures due to the new policy of running restores with + restrictive search path. + + + + + + Fix pg_upgrade to handle event triggers in + extensions correctly (Haribabu Kommi) + + + + pg_upgrade failed to preserve an event + trigger's extension-membership status. + + + + + + Fix pg_upgrade's cluster state check to + work correctly on a standby server (Bruce Momjian) + + + + + + Enforce type cube's dimension limit in + all contrib/cube functions (Andrey Borodin) + + + + Previously, some cube-related functions could construct values that + would be rejected by cube_in(), leading to + dump/reload failures. + + + + + + In contrib/postgres_fdw, don't try to ship a + variable-free ORDER BY clause to the remote server + (Andrew Gierth) + + + + + + Fix contrib/unaccent's + unaccent() function to use + the unaccent text search dictionary that is in the + same schema as the function (Tom Lane) + + + + Previously it tried to look up the dictionary using the search path, + which could fail if the search path has a restrictive value. + + + + + + Fix build problems on macOS 10.14 (Mojave) (Tom Lane) + + + + Adjust configure to add + an switch to CPPFLAGS; + without this, PL/Perl and PL/Tcl fail to configure or build on macOS + 10.14. The specific sysroot used can be overridden at configure time + or build time by setting the PG_SYSROOT variable in + the arguments of configure + or make. + + + + It is now recommended that Perl-related extensions + write $(perl_includespec) rather + than -I$(perl_archlibexp)/CORE in their compiler + flags. The latter continues to work on most platforms, but not recent + macOS. + + + + Also, it should no longer be necessary to + specify manually to get PL/Tcl to + build on recent macOS releases. + + + + + + Fix MSVC build and regression-test scripts to work on recent Perl + versions (Andrew Dunstan) + + + + Perl no longer includes the current directory in its search path + by default; work around that. + + + + + + On Windows, allow the regression tests to be run by an Administrator + account (Andrew Dunstan) + + + + To do this safely, pg_regress now gives up + any such privileges at startup. + + + + + + Allow btree comparison functions to return INT_MIN + (Tom Lane) + + + + Up to now, we've forbidden datatype-specific comparison functions from + returning INT_MIN, which allows callers to invert + the sort order just by negating the comparison result. However, this + was never safe for comparison functions that directly return the + result of memcmp(), strcmp(), + etc, as POSIX doesn't place any such restriction on those functions. + At least some recent versions of memcmp() can + return INT_MIN, causing incorrect sort ordering. + Hence, we've removed this restriction. Callers must now use + the INVERT_COMPARE_RESULT() macro if they wish to + invert the sort order. + + + + + + Fix recursion hazard in shared-invalidation message processing + (Tom Lane) + + + + This error could, for example, result in failure to access a system + catalog or index that had just been processed by VACUUM + FULL. + + + + This change adds a new result code + for LockAcquire, which might possibly affect + external callers of that function, though only very unusual usage + patterns would have an issue with it. The API + of LockAcquireExtended is also changed. + + + + + + Save and restore SPI's global variables + during SPI_connect() + and SPI_finish() (Chapman Flack, Tom Lane) + + + + This prevents possible interference when one SPI-using function calls + another. + + + + + + Avoid using potentially-under-aligned page buffers (Tom Lane) + + + + Invent new union types PGAlignedBlock + and PGAlignedXLogBlock, and use these in place of plain + char arrays, ensuring that the compiler can't place the buffer at a + misaligned start address. This fixes potential core dumps on + alignment-picky platforms, and may improve performance even on + platforms that allow misalignment. + + + + + + Make src/port/snprintf.c follow the C99 + standard's definition of snprintf()'s result + value (Tom Lane) + + + + On platforms where this code is used (mostly Windows), its pre-C99 + behavior could lead to failure to detect buffer overrun, if the + calling code assumed C99 semantics. + + + + + + When building on i386 with the clang + compiler, require to be used (Andres Freund) + + + + This avoids problems with missed floating point overflow checks. + + + + + + Fix configure's detection of the result + type of strerror_r() (Tom Lane) + + + + The previous coding got the wrong answer when building + with icc on Linux (and perhaps in other + cases), leading to libpq not returning + useful error messages for system-reported errors. + + + + + + Update time zone data files to tzdata + release 2018g for DST law changes in Chile, Fiji, Morocco, and Russia + (Volgograd), plus historical corrections for China, Hawaii, Japan, + Macau, and North Korea. + + + + + + + + + + Release 9.6.10 + + + 发布日期: + 2018-08-09 + + + + 此版本包含自 9.6.9 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.10 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.9, + see . + + + + + 变更 + + + + + + Fix failure to reset libpq's state fully + between connection attempts (Tom Lane) + + + + An unprivileged user of dblink + or postgres_fdw could bypass the checks intended + to prevent use of server-side credentials, such as + a ~/.pgpass file owned by the operating-system + user running the server. Servers allowing peer authentication on + local connections are particularly vulnerable. Other attacks such + as SQL injection into a postgres_fdw session + are also possible. + Attacking postgres_fdw in this way requires the + ability to create a foreign server object with selected connection + parameters, but any user with access to dblink + could exploit the problem. + In general, an attacker with the ability to select the connection + parameters for a libpq-using application + could cause mischief, though other plausible attack scenarios are + harder to think of. + Our thanks to Andrew Krasichkov for reporting this issue. + (CVE-2018-10915) + + + + + + Fix INSERT ... ON CONFLICT UPDATE through a view + that isn't just SELECT * FROM ... + (Dean Rasheed, Amit Langote) + + + + Erroneous expansion of an updatable view could lead to crashes + or attribute ... has the wrong type errors, if the + view's SELECT list doesn't match one-to-one with + the underlying table's columns. + Furthermore, this bug could be leveraged to allow updates of columns + that an attacking user lacks UPDATE privilege for, + if that user has INSERT and UPDATE + privileges for some other column(s) of the table. + Any user could also use it for disclosure of server memory. + (CVE-2018-10925) + + + + + + Ensure that updates to the relfrozenxid + and relminmxid values + for nailed system catalogs are processed in a timely + fashion (Andres Freund) + + + + Overoptimistic caching rules could prevent these updates from being + seen by other sessions, leading to spurious errors and/or data + corruption. The problem was significantly worse for shared catalogs, + such as pg_authid, because the stale cache + data could persist into new sessions as well as existing ones. + + + + + + Fix case where a freshly-promoted standby crashes before having + completed its first post-recovery checkpoint (Michael Paquier, Kyotaro + Horiguchi, Pavan Deolasee, Álvaro Herrera) + + + + This led to a situation where the server did not think it had reached + a consistent database state during subsequent WAL replay, preventing + restart. + + + + + + Avoid emitting a bogus WAL record when recycling an all-zero btree + page (Amit Kapila) + + + + This mistake has been seen to cause assertion failures, and + potentially it could result in unnecessary query cancellations on hot + standby servers. + + + + + + During WAL replay, guard against corrupted record lengths exceeding + 1GB (Michael Paquier) + + + + Treat such a case as corrupt data. Previously, the code would try to + allocate space and get a hard error, making recovery impossible. + + + + + + When ending recovery, delay writing the timeline history file as long + as possible (Heikki Linnakangas) + + + + This avoids some situations where a failure during recovery cleanup + (such as a problem with a two-phase state file) led to inconsistent + timeline state on-disk. + + + + + + Improve performance of WAL replay for transactions that drop many + relations (Fujii Masao) + + + + This change reduces the number of times that shared buffers are + scanned, so that it is of most benefit when that setting is large. + + + + + + Improve performance of lock releasing in standby server WAL replay + (Thomas Munro) + + + + + + Make logical WAL senders report streaming state correctly (Simon + Riggs, Sawada Masahiko) + + + + The code previously mis-detected whether or not it had caught up with + the upstream server. + + + + + + Fix bugs in snapshot handling during logical decoding, allowing wrong + decoding results in rare cases (Arseny Sher, Álvaro Herrera) + + + + + + Ensure a table's cached index list is correctly rebuilt after an index + creation fails partway through (Peter Geoghegan) + + + + Previously, the failed index's OID could remain in the list, causing + problems later in the same session. + + + + + + Fix mishandling of empty uncompressed posting list pages in GIN + indexes (Sivasubramanian Ramasubramanian, Alexander Korotkov) + + + + This could result in an assertion failure after pg_upgrade of a + pre-9.4 GIN index (9.4 and later will not create such pages). + + + + + + Ensure that VACUUM will respond to signals + within btree page deletion loops (Andres Freund) + + + + Corrupted btree indexes could result in an infinite loop here, and + that previously wasn't interruptible without forcing a crash. + + + + + + Fix misoptimization of equivalence classes involving composite-type + columns (Tom Lane) + + + + This resulted in failure to recognize that an index on a composite + column could provide the sort order needed for a mergejoin on that + column. + + + + + + Fix planner to avoid ORDER/GROUP BY expression not found in + targetlist errors in some queries with set-returning functions + (Tom Lane) + + + + + + Fix SQL-standard FETCH FIRST syntax to allow + parameters ($n), as the + standard expects (Andrew Gierth) + + + + + + Fix EXPLAIN's accounting for resource usage, + particularly buffer accesses, in parallel workers + (Amit Kapila, Robert Haas) + + + + + + Fix failure to schema-qualify some object names + in getObjectDescription output + (Kyotaro Horiguchi, Tom Lane) + + + + Names of collations, conversions, and text search objects + were not schema-qualified when they should be. + + + + + + Fix CREATE AGGREGATE type checking so that + parallelism support functions can be attached to variadic aggregates + (Alexey Bashtanov) + + + + + + Widen COPY FROM's current-line-number counter + from 32 to 64 bits (David Rowley) + + + + This avoids two problems with input exceeding 4G lines: COPY + FROM WITH HEADER would drop a line every 4G lines, not only + the first line, and error reports could show a wrong line number. + + + + + + Add a string freeing function + to ecpg's pgtypes + library, so that cross-module memory management problems can be + avoided on Windows (Takayuki Tsunakawa) + + + + On Windows, crashes can ensue if the free call + for a given chunk of memory is not made from the same DLL + that malloc'ed the memory. + The pgtypes library sometimes returns strings + that it expects the caller to free, making it impossible to follow + this rule. Add a PGTYPESchar_free() function + that just wraps free, allowing applications + to follow this rule. + + + + + + Fix ecpg's support for long + long variables on Windows, as well as other platforms that + declare strtoll/strtoull + nonstandardly or not at all (Dang Minh Huong, Tom Lane) + + + + + + Fix misidentification of SQL statement type in PL/pgSQL, when a rule + change causes a change in the semantics of a statement intra-session + (Tom Lane) + + + + This error led to assertion failures, or in rare cases, failure to + enforce the INTO STRICT option as expected. + + + + + + Fix password prompting in client programs so that echo is properly + disabled on Windows when stdin is not the + terminal (Matthew Stickney) + + + + + + Further fix mis-quoting of values for list-valued GUC variables in + dumps (Tom Lane) + + + + The previous fix for quoting of search_path and + other list-valued variables in pg_dump + output turned out to misbehave for empty-string list elements, and it + risked truncation of long file paths. + + + + + + Fix pg_dump's failure to + dump REPLICA IDENTITY properties for constraint + indexes (Tom Lane) + + + + Manually created unique indexes were properly marked, but not those + created by declaring UNIQUE or PRIMARY + KEY constraints. + + + + + + Make pg_upgrade check that the old server + was shut down cleanly (Bruce Momjian) + + + + The previous check could be fooled by an immediate-mode shutdown. + + + + + + Fix contrib/hstore_plperl to look through Perl + scalar references, and to not crash if it doesn't find a hash + reference where it expects one (Tom Lane) + + + + + + Fix crash in contrib/ltree's + lca() function when the input array is empty + (Pierre Ducroquet) + + + + + + Fix various error-handling code paths in which an incorrect error code + might be reported (Michael Paquier, Tom Lane, Magnus Hagander) + + + + + + Rearrange makefiles to ensure that programs link to freshly-built + libraries (such as libpq.so) rather than ones + that might exist in the system library directories (Tom Lane) + + + + This avoids problems when building on platforms that supply old copies + of PostgreSQL libraries. + + + + + + Update time zone data files to tzdata + release 2018e for DST law changes in North Korea, plus historical + corrections for Czechoslovakia. + + + + This update includes a redefinition of daylight savings + in Ireland, as well as for some past years in Namibia and + Czechoslovakia. In those jurisdictions, legally standard time is + observed in summer, and daylight savings time in winter, so that the + daylight savings offset is one hour behind standard time not one hour + ahead. This does not affect either the actual UTC offset or the + timezone abbreviations in use; the only known effect is that + the is_dst column in + the pg_timezone_names view will now be true + in winter and false in summer in these cases. + + + + + + + + + + Release 9.6.9 + + + 发布日期: + 2018-05-10 + + + + 此版本包含自 9.6.8 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.9 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you use the adminpack extension, + you should update it as per the first changelog entry below. + + + + Also, if the function marking mistakes mentioned in the second and + third changelog entries below affect you, you will want to take steps + to correct your database catalogs. + + + + 另外,如果你是从 9.6.8 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + Remove public execute privilege + from contrib/adminpack's + pg_logfile_rotate() function (Stephen Frost) + + + + pg_logfile_rotate() is a deprecated wrapper + for the core function pg_rotate_logfile(). + When that function was changed to rely on SQL privileges for access + control rather than a hard-coded superuser + check, pg_logfile_rotate() should have been + updated as well, but the need for this was missed. Hence, + if adminpack is installed, any user could + request a logfile rotation, creating a minor security issue. + + + + After installing this update, administrators should + update adminpack by performing + ALTER EXTENSION adminpack UPDATE in each + database in which adminpack is installed. + (CVE-2018-1115) + + + + + + Fix incorrect volatility markings on a few built-in functions + (Thomas Munro, Tom Lane) + + + + The functions + query_to_xml, + cursor_to_xml, + cursor_to_xmlschema, + query_to_xmlschema, and + query_to_xml_and_xmlschema + should be marked volatile because they execute user-supplied queries + that might contain volatile operations. They were not, leading to a + risk of incorrect query optimization. This has been repaired for new + installations by correcting the initial catalog data, but existing + installations will continue to contain the incorrect markings. + Practical use of these functions seems to pose little hazard, but in + case of trouble, it can be fixed by manually updating these + functions' pg_proc entries, for example + ALTER FUNCTION pg_catalog.query_to_xml(text, boolean, + boolean, text) VOLATILE. (Note that that will need to be + done in each database of the installation.) Another option is + to pg_upgrade the database to a version + containing the corrected initial data. + + + + + + Fix incorrect parallel-safety markings on a few built-in functions + (Thomas Munro, Tom Lane) + + + + The functions + brin_summarize_new_values, + gin_clean_pending_list, + cursor_to_xml, + cursor_to_xmlschema, + ts_rewrite, + ts_stat, and + binary_upgrade_create_empty_extension + should be marked parallel-unsafe; some because they perform database + modifications directly, and others because they execute user-supplied + queries that might do so. They were marked parallel-restricted + instead, leading to a risk of unexpected query errors. This has been + repaired for new installations by correcting the initial catalog + data, but existing installations will continue to contain the + incorrect markings. Practical use of these functions seems to pose + little hazard unless force_parallel_mode is turned + on. In case of trouble, it can be fixed by manually updating these + functions' pg_proc entries, for example + ALTER FUNCTION pg_catalog.brin_summarize_new_values(regclass) + PARALLEL UNSAFE. (Note that that will need to be done in + each database of the installation.) Another option is + to pg_upgrade the database to a version + containing the corrected initial data. + + + + + + Avoid re-using TOAST value OIDs that match dead-but-not-yet-vacuumed + TOAST entries (Pavan Deolasee) + + + + Once the OID counter has wrapped around, it's possible to assign a + TOAST value whose OID matches a previously deleted entry in the same + TOAST table. If that entry were not yet vacuumed away, this resulted + in unexpected chunk number 0 (expected 1) for toast + value nnnnn errors, which would + persist until the dead entry was removed + by VACUUM. Fix by not selecting such OIDs when + creating a new TOAST entry. + + + + + + Change ANALYZE's algorithm for updating + pg_class.reltuples + (David Gould) + + + + Previously, pages not actually scanned by ANALYZE + were assumed to retain their old tuple density. In a large table + where ANALYZE samples only a small fraction of the + pages, this meant that the overall tuple density estimate could not + change very much, so that reltuples would + change nearly proportionally to changes in the table's physical size + (relpages) regardless of what was actually + happening in the table. This has been observed to result + in reltuples becoming so much larger than + reality as to effectively shut off autovacuuming. To fix, assume + that ANALYZE's sample is a statistically unbiased + sample of the table (as it should be), and just extrapolate the + density observed within those pages to the whole table. + + + + + + Avoid deadlocks in concurrent CREATE INDEX + CONCURRENTLY commands that are run + under SERIALIZABLE or REPEATABLE + READ transaction isolation (Tom Lane) + + + + + + Fix possible slow execution of REFRESH MATERIALIZED VIEW + CONCURRENTLY (Thomas Munro) + + + + + + Fix UPDATE/DELETE ... WHERE CURRENT OF to not fail + when the referenced cursor uses an index-only-scan plan (Yugo Nagata, + Tom Lane) + + + + + + Fix incorrect planning of join clauses pushed into parameterized + paths (Andrew Gierth, Tom Lane) + + + + This error could result in misclassifying a condition as + a join filter for an outer join when it should be a + plain filter condition, leading to incorrect join + output. + + + + + + Fix possibly incorrect generation of an index-only-scan plan when the + same table column appears in multiple index columns, and only some of + those index columns use operator classes that can return the column + value (Kyotaro Horiguchi) + + + + + + Fix misoptimization of CHECK constraints having + provably-NULL subclauses of + top-level AND/OR conditions + (Tom Lane, Dean Rasheed) + + + + This could, for example, allow constraint exclusion to exclude a + child table that should not be excluded from a query. + + + + + + Fix executor crash due to double free in some GROUPING + SET usages (Peter Geoghegan) + + + + + + Avoid crash if a table rewrite event trigger is added concurrently + with a command that could call such a trigger (Álvaro Herrera, + Andrew Gierth, Tom Lane) + + + + + + Avoid failure if a query-cancel or session-termination interrupt + occurs while committing a prepared transaction (Stas Kelvich) + + + + + + Fix query-lifespan memory leakage in repeatedly executed hash joins + (Tom Lane) + + + + + + Fix possible leak or double free of visibility map buffer pins + (Amit Kapila) + + + + + + Avoid spuriously marking pages as all-visible (Dan Wood, + Pavan Deolasee, Álvaro Herrera) + + + + This could happen if some tuples were locked (but not deleted). While + queries would still function correctly, vacuum would normally ignore + such pages, with the long-term effect that the tuples were never + frozen. In recent releases this would eventually result in errors + such as found multixact nnnnn from + before relminmxid nnnnn. + + + + + + Fix overly strict sanity check + in heap_prepare_freeze_tuple + (Álvaro Herrera) + + + + This could result in incorrect cannot freeze committed + xmax failures in databases that have + been pg_upgrade'd from 9.2 or earlier. + + + + + + Prevent dangling-pointer dereference when a C-coded before-update row + trigger returns the old tuple (Rushabh Lathia) + + + + + + Reduce locking during autovacuum worker scheduling (Jeff Janes) + + + + The previous behavior caused drastic loss of potential worker + concurrency in databases with many tables. + + + + + + Ensure client hostname is copied while copying + pg_stat_activity data to local memory + (Edmund Horner) + + + + Previously the supposedly-local snapshot contained a pointer into + shared memory, allowing the client hostname column to change + unexpectedly if any existing session disconnected. + + + + + + Fix incorrect processing of multiple compound affixes + in ispell dictionaries (Arthur Zakirov) + + + + + + Fix collation-aware searches (that is, indexscans using inequality + operators) in SP-GiST indexes on text columns (Tom Lane) + + + + Such searches would return the wrong set of rows in most non-C + locales. + + + + + + Prevent query-lifespan memory leakage with SP-GiST operator classes + that use traversal values (Anton Dignös) + + + + + + Count the number of index tuples correctly during initial build of an + SP-GiST index (Tomas Vondra) + + + + Previously, the tuple count was reported to be the same as that of + the underlying table, which is wrong if the index is partial. + + + + + + Count the number of index tuples correctly during vacuuming of a + GiST index (Andrey Borodin) + + + + Previously it reported the estimated number of heap tuples, + which might be inaccurate, and is certainly wrong if the + index is partial. + + + + + + Fix a corner case where a streaming standby gets stuck at a WAL + continuation record (Kyotaro Horiguchi) + + + + + + In logical decoding, avoid possible double processing of WAL data + when a walsender restarts (Craig Ringer) + + + + + + Allow scalarltsel + and scalargtsel to be used on non-core datatypes + (Tomas Vondra) + + + + + + Reduce libpq's memory consumption when a + server error is reported after a large amount of query output has + been collected (Tom Lane) + + + + Discard the previous output before, not after, processing the error + message. On some platforms, notably Linux, this can make a + difference in the application's subsequent memory footprint. + + + + + + Fix double-free crashes in ecpg + (Patrick Krecker, Jeevan Ladhe) + + + + + + Fix ecpg to handle long long + int variables correctly in MSVC builds (Michael Meskes, + Andrew Gierth) + + + + + + Fix mis-quoting of values for list-valued GUC variables in dumps + (Michael Paquier, Tom Lane) + + + + The local_preload_libraries, + session_preload_libraries, + shared_preload_libraries, + and temp_tablespaces variables were not correctly + quoted in pg_dump output. This would + cause problems if settings for these variables appeared in + CREATE FUNCTION ... SET or ALTER + DATABASE/ROLE ... SET clauses. + + + + + + Fix pg_recvlogical to not fail against + pre-v10 PostgreSQL servers + (Michael Paquier) + + + + A previous fix caused pg_recvlogical to + issue a command regardless of server version, but it should only be + issued to v10 and later servers. + + + + + + Ensure that pg_rewind deletes files on the + target server if they are deleted from the source server during the + run (Takayuki Tsunakawa) + + + + Failure to do this could result in data inconsistency on the target, + particularly if the file in question is a WAL segment. + + + + + + Fix pg_rewind to handle tables in + non-default tablespaces correctly (Takayuki Tsunakawa) + + + + + + Fix overflow handling in PL/pgSQL + integer FOR loops (Tom Lane) + + + + The previous coding failed to detect overflow of the loop variable + on some non-gcc compilers, leading to an infinite loop. + + + + + + Adjust PL/Python regression tests to pass + under Python 3.7 (Peter Eisentraut) + + + + + + Support testing PL/Python and related + modules when building with Python 3 and MSVC (Andrew Dunstan) + + + + + + Fix errors in initial build of contrib/bloom + indexes (Tomas Vondra, Tom Lane) + + + + Fix possible omission of the table's last tuple from the index. + Count the number of index tuples correctly, in case it is a partial + index. + + + + + + Rename internal b64_encode + and b64_decode functions to avoid conflict with + Solaris 11.4 built-in functions (Rainer Orth) + + + + + + Sync our copy of the timezone library with IANA tzcode release 2018e + (Tom Lane) + + + + This fixes the zic timezone data compiler + to cope with negative daylight-savings offsets. While + the PostgreSQL project will not + immediately ship such timezone data, zic + might be used with timezone data obtained directly from IANA, so it + seems prudent to update zic now. + + + + + + Update time zone data files to tzdata + release 2018d for DST law changes in Palestine and Antarctica (Casey + Station), plus historical corrections for Portugal and its colonies, + as well as Enderbury, Jamaica, Turks & Caicos Islands, and + Uruguay. + + + + + + + + + + Release 9.6.8 + + + 发布日期: + 2018-03-01 + + + + 此版本包含自 9.6.7 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.8 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you run an installation in which not all users are mutually + trusting, or if you maintain an application or extension that is + intended for use in arbitrary situations, it is strongly recommended + that you read the documentation changes described in the first changelog + entry below, and take suitable steps to ensure that your installation or + code is secure. + + + + Also, the changes described in the second changelog entry below may + cause functions used in index expressions or materialized views to fail + during auto-analyze, or when reloading from a dump. After upgrading, + monitor the server logs for such problems, and fix affected functions. + + + + 另外,如果你是从 9.6.7 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + Document how to configure installations and applications to guard + against search-path-dependent trojan-horse attacks from other users + (Noah Misch) + + + + Using a search_path setting that includes any + schemas writable by a hostile user enables that user to capture + control of queries and then run arbitrary SQL code with the + permissions of the attacked user. While it is possible to write + queries that are proof against such hijacking, it is notationally + tedious, and it's very easy to overlook holes. Therefore, we now + recommend configurations in which no untrusted schemas appear in + one's search path. Relevant documentation appears in + (for database administrators and users), + (for application authors), + (for extension authors), and + (for authors + of SECURITY DEFINER functions). + (CVE-2018-1058) + + + + + + Avoid use of insecure search_path settings + in pg_dump and other client programs + (Noah Misch, Tom Lane) + + + + pg_dump, + pg_upgrade, + vacuumdb and + other PostgreSQL-provided applications were + themselves vulnerable to the type of hijacking described in the previous + changelog entry; since these applications are commonly run by + superusers, they present particularly attractive targets. To make them + secure whether or not the installation as a whole has been secured, + modify them to include only the pg_catalog + schema in their search_path settings. + Autovacuum worker processes now do the same, as well. + + + + In cases where user-provided functions are indirectly executed by + these programs — for example, user-provided functions in index + expressions — the tighter search_path may + result in errors, which will need to be corrected by adjusting those + user-provided functions to not assume anything about what search path + they are invoked under. That has always been good practice, but now + it will be necessary for correct behavior. + (CVE-2018-1058) + + + + + + Fix misbehavior of concurrent-update rechecks with CTE references + appearing in subplans (Tom Lane) + + + + If a CTE (WITH clause reference) is used in an + InitPlan or SubPlan, and the query requires a recheck due to trying + to update or lock a concurrently-updated row, incorrect results could + be obtained. + + + + + + Fix planner failures with overlapping mergejoin clauses in an outer + join (Tom Lane) + + + + These mistakes led to left and right pathkeys do not match in + mergejoin or outer pathkeys do not match + mergeclauses planner errors in corner cases. + + + + + + Repair pg_upgrade's failure to + preserve relfrozenxid for materialized + views (Tom Lane, Andres Freund) + + + + This oversight could lead to data corruption in materialized views + after an upgrade, manifesting as could not access status of + transaction or found xmin from before + relfrozenxid errors. The problem would be more likely to + occur in seldom-refreshed materialized views, or ones that were + maintained only with REFRESH MATERIALIZED VIEW + CONCURRENTLY. + + + + If such corruption is observed, it can be repaired by refreshing the + materialized view (without CONCURRENTLY). + + + + + + Fix incorrect reporting of PL/Python function names in + error CONTEXT stacks (Tom Lane) + + + + An error occurring within a nested PL/Python function call (that is, + one reached via a SPI query from another PL/Python function) would + result in a stack trace showing the inner function's name twice, + rather than the expected results. Also, an error in a nested + PL/Python DO block could result in a null pointer + dereference crash on some platforms. + + + + + + Allow contrib/auto_explain's + log_min_duration setting to range up + to INT_MAX, or about 24 days instead of 35 minutes + (Tom Lane) + + + + + + Mark assorted GUC variables as PGDLLIMPORT, to + ease porting extension modules to Windows (Metin Doslu) + + + + + + + + + + Release 9.6.7 + + + 发布日期: + 2018-02-08 + + + + 此版本包含自 9.6.6 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.7 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, + if you use contrib/cube's ~> + operator, see the entry below about that. + + + + 另外,如果你是从 9.6.6 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + Ensure that all temporary files made + by pg_upgrade are non-world-readable + (Tom Lane, Noah Misch) + + + + pg_upgrade normally restricts its + temporary files to be readable and writable only by the calling user. + But the temporary file containing pg_dumpall -g + output would be group- or world-readable, or even writable, if the + user's umask setting allows. In typical usage on + multi-user machines, the umask and/or the working + directory's permissions would be tight enough to prevent problems; + but there may be people using pg_upgrade + in scenarios where this oversight would permit disclosure of database + passwords to unfriendly eyes. + (CVE-2018-1053) + + + + + + Fix vacuuming of tuples that were updated while key-share locked + (Andres Freund, Álvaro Herrera) + + + + In some cases VACUUM would fail to remove such + tuples even though they are now dead, leading to assorted data + corruption scenarios. + + + + + + Ensure that vacuum will always clean up the pending-insertions list of + a GIN index (Masahiko Sawada) + + + + This is necessary to ensure that dead index entries get removed. + The old code got it backwards, allowing vacuum to skip the cleanup if + some other process were running cleanup concurrently, thus risking + invalid entries being left behind in the index. + + + + + + Fix inadequate buffer locking in some LSN fetches (Jacob Champion, + Asim Praveen, Ashwin Agrawal) + + + + These errors could result in misbehavior under concurrent load. + The potential consequences have not been characterized fully. + + + + + + Fix incorrect query results from cases involving flattening of + subqueries whose outputs are used in GROUPING SETS + (Heikki Linnakangas) + + + + + + Avoid unnecessary failure in a query on an inheritance tree that + occurs concurrently with some child table being removed from the tree + by ALTER TABLE NO INHERIT (Tom Lane) + + + + + + Fix spurious deadlock failures when multiple sessions are + running CREATE INDEX CONCURRENTLY (Jeff Janes) + + + + + + Fix failures when an inheritance tree contains foreign child tables + (Etsuro Fujita) + + + + A mix of regular and foreign tables in an inheritance tree resulted in + creation of incorrect plans for UPDATE + and DELETE queries. This led to visible failures in + some cases, notably when there are row-level triggers on a foreign + child table. + + + + + + Repair failure with correlated sub-SELECT + inside VALUES inside a LATERAL + subquery (Tom Lane) + + + + + + Fix could not devise a query plan for the given query + planner failure for some cases involving nested UNION + ALL inside a lateral subquery (Tom Lane) + + + + + + Fix logical decoding to correctly clean up disk files for crashed + transactions (Atsushi Torikoshi) + + + + Logical decoding may spill WAL records to disk for transactions + generating many WAL records. Normally these files are cleaned up + after the transaction's commit or abort record arrives; but if + no such record is ever seen, the removal code misbehaved. + + + + + + Fix walsender timeout failure and failure to respond to interrupts + when processing a large transaction (Petr Jelinek) + + + + + + Fix has_sequence_privilege() to + support WITH GRANT OPTION tests, + as other privilege-testing functions do (Joe Conway) + + + + + + In databases using UTF8 encoding, ignore any XML declaration that + asserts a different encoding (Pavel Stehule, Noah Misch) + + + + We always store XML strings in the database encoding, so allowing + libxml to act on a declaration of another encoding gave wrong results. + In encodings other than UTF8, we don't promise to support non-ASCII + XML data anyway, so retain the previous behavior for bug compatibility. + This change affects only xpath() and related + functions; other XML code paths already acted this way. + + + + + + Provide for forward compatibility with future minor protocol versions + (Robert Haas, Badrul Chowdhury) + + + + Up to now, PostgreSQL servers simply + rejected requests to use protocol versions newer than 3.0, so that + there was no functional difference between the major and minor parts + of the protocol version number. Allow clients to request versions 3.x + without failing, sending back a message showing that the server only + understands 3.0. This makes no difference at the moment, but + back-patching this change should allow speedier introduction of future + minor protocol upgrades. + + + + + + Cope with failure to start a parallel worker process + (Amit Kapila, Robert Haas) + + + + Parallel query previously tended to hang indefinitely if a worker + could not be started, as the result of fork() + failure or other low-probability problems. + + + + + + Fix collection of EXPLAIN statistics from parallel + workers (Amit Kapila, Thomas Munro) + + + + + + Avoid unsafe alignment assumptions when working + with __int128 (Tom Lane) + + + + Typically, compilers assume that __int128 variables are + aligned on 16-byte boundaries, but our memory allocation + infrastructure isn't prepared to guarantee that, and increasing the + setting of MAXALIGN seems infeasible for multiple reasons. Adjust the + code to allow use of __int128 only when we can tell the + compiler to assume lesser alignment. The only known symptom of this + problem so far is crashes in some parallel aggregation queries. + + + + + + Prevent stack-overflow crashes when planning extremely deeply + nested set operations + (UNION/INTERSECT/EXCEPT) + (Tom Lane) + + + + + + Fix null-pointer crashes for some types of LDAP URLs appearing + in pg_hba.conf (Thomas Munro) + + + + + + Fix sample INSTR() functions in the PL/pgSQL + documentation (Yugo Nagata, Tom Lane) + + + + These functions are stated to + be Oracle compatible, but + they weren't exactly. In particular, there was a discrepancy in the + interpretation of a negative third parameter: Oracle thinks that a + negative value indicates the last place where the target substring can + begin, whereas our functions took it as the last place where the + target can end. Also, Oracle throws an error for a zero or negative + fourth parameter, whereas our functions returned zero. + + + + The sample code has been adjusted to match Oracle's behavior more + precisely. Users who have copied this code into their applications + may wish to update their copies. + + + + + + Fix pg_dump to make ACL (permissions), + comment, and security label entries reliably identifiable in archive + output formats (Tom Lane) + + + + The tag portion of an ACL archive entry was usually + just the name of the associated object. Make it start with the object + type instead, bringing ACLs into line with the convention already used + for comment and security label archive entries. Also, fix the + comment and security label entries for the whole database, if present, + to make their tags start with DATABASE so that they + also follow this convention. This prevents false matches in code that + tries to identify large-object-related entries by seeing if the tag + starts with LARGE OBJECT. That could have resulted + in misclassifying entries as data rather than schema, with undesirable + results in a schema-only or data-only dump. + + + + Note that this change has user-visible results in the output + of pg_restore --list. + + + + + + Rename pg_rewind's + copy_file_range function to avoid conflict + with new Linux system call of that name (Andres Freund) + + + + This change prevents build failures with newer glibc versions. + + + + + + In ecpg, detect indicator arrays that do + not have the correct length and report an error (David Rader) + + + + + + Change the behavior of contrib/cube's + cube ~> int + operator to make it compatible with KNN search (Alexander Korotkov) + + + + The meaning of the second argument (the dimension selector) has been + changed to make it predictable which value is selected even when + dealing with cubes of varying dimensionalities. + + + + This is an incompatible change, but since the point of the operator + was to be used in KNN searches, it seems rather useless as-is. + After installing this update, any expression indexes or materialized + views using this operator will need to be reindexed/refreshed. + + + + + + Avoid triggering a libc assertion + in contrib/hstore, due to use + of memcpy() with equal source and destination + pointers (Tomas Vondra) + + + + + + Fix incorrect display of tuples' null bitmaps + in contrib/pageinspect (Maksim Milyutin) + + + + + + In contrib/postgres_fdw, avoid + outer pathkeys do not match mergeclauses + planner error when constructing a plan involving a remote join + (Robert Haas) + + + + + + Provide modern examples of how to auto-start Postgres on macOS + (Tom Lane) + + + + The scripts in contrib/start-scripts/osx use + infrastructure that's been deprecated for over a decade, and which no + longer works at all in macOS releases of the last couple of years. + Add a new subdirectory contrib/start-scripts/macos + containing scripts that use the newer launchd + infrastructure. + + + + + + Fix incorrect selection of configuration-specific libraries for + OpenSSL on Windows (Andrew Dunstan) + + + + + + Support linking to MinGW-built versions of libperl (Noah Misch) + + + + This allows building PL/Perl with some common Perl distributions for + Windows. + + + + + + Fix MSVC build to test whether 32-bit libperl + needs -D_USE_32BIT_TIME_T (Noah Misch) + + + + Available Perl distributions are inconsistent about what they expect, + and lack any reliable means of reporting it, so resort to a build-time + test on what the library being used actually does. + + + + + + On Windows, install the crash dump handler earlier in postmaster + startup (Takayuki Tsunakawa) + + + + This may allow collection of a core dump for some early-startup + failures that did not produce a dump before. + + + + + + On Windows, avoid encoding-conversion-related crashes when emitting + messages very early in postmaster startup (Takayuki Tsunakawa) + + + + + + Use our existing Motorola 68K spinlock code on OpenBSD as + well as NetBSD (David Carlier) + + + + + + Add support for spinlocks on Motorola 88K (David Carlier) + + + + + + Update time zone data files to tzdata + release 2018c for DST law changes in Brazil, Sao Tome and Principe, + plus historical corrections for Bolivia, Japan, and South Sudan. + The US/Pacific-New zone has been removed (it was + only an alias for America/Los_Angeles anyway). + + + + + + + + + + Release 9.6.6 + + + 发布日期: + 2017-11-09 + + + + 此版本包含自 9.6.5 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.6 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you use BRIN indexes, see the fourth changelog entry below. + + + + 另外,如果你是从 9.6.4 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + Ensure that INSERT ... ON CONFLICT DO UPDATE checks + table permissions and RLS policies in all cases (Dean Rasheed) + + + + The update path of INSERT ... ON CONFLICT DO UPDATE + requires SELECT permission on the columns of the + arbiter index, but it failed to check for that in the case of an + arbiter specified by constraint name. + In addition, for a table with row level security enabled, it failed to + check updated rows against the table's SELECT + policies (regardless of how the arbiter index was specified). + (CVE-2017-15099) + + + + + + Fix crash due to rowtype mismatch + in json{b}_populate_recordset() + (Michael Paquier, Tom Lane) + + + + These functions used the result rowtype specified in the FROM + ... AS clause without checking that it matched the actual + rowtype of the supplied tuple value. If it didn't, that would usually + result in a crash, though disclosure of server memory contents seems + possible as well. + (CVE-2017-15098) + + + + + + Fix sample server-start scripts to become $PGUSER + before opening $PGLOG (Noah Misch) + + + + Previously, the postmaster log file was opened while still running as + root. The database owner could therefore mount an attack against + another system user by making $PGLOG be a symbolic + link to some other file, which would then become corrupted by appending + log messages. + + + + By default, these scripts are not installed anywhere. Users who have + made use of them will need to manually recopy them, or apply the same + changes to their modified versions. If the + existing $PGLOG file is root-owned, it will need to + be removed or renamed out of the way before restarting the server with + the corrected script. + (CVE-2017-12172) + + + + + + Fix BRIN index summarization to handle concurrent table extension + correctly (Álvaro Herrera) + + + + Previously, a race condition allowed some table rows to be omitted from + the index. It may be necessary to reindex existing BRIN indexes to + recover from past occurrences of this problem. + + + + + + Fix possible failures during concurrent updates of a BRIN index + (Tom Lane) + + + + These race conditions could result in errors like invalid index + offnum or inconsistent range map. + + + + + + Fix crash when logical decoding is invoked from a SPI-using function, + in particular any function written in a PL language + (Tom Lane) + + + + + + Fix incorrect query results when multiple GROUPING + SETS columns contain the same simple variable (Tom Lane) + + + + + + Fix incorrect parallelization decisions for nested queries + (Amit Kapila, Kuntal Ghosh) + + + + + + Fix parallel query handling to not fail when a recently-used role is + dropped (Amit Kapila) + + + + + + Fix json_build_array(), + json_build_object(), and their jsonb + equivalents to handle explicit VARIADIC arguments + correctly (Michael Paquier) + + + + + + + Properly reject attempts to convert infinite float values to + type numeric (Tom Lane, KaiGai Kohei) + + + + Previously the behavior was platform-dependent. + + + + + + Fix corner-case crashes when columns have been added to the end of a + view (Tom Lane) + + + + + + Record proper dependencies when a view or rule + contains FieldSelect + or FieldStore expression nodes (Tom Lane) + + + + Lack of these dependencies could allow a column or data + type DROP to go through when it ought to fail, + thereby causing later uses of the view or rule to get errors. + This patch does not do anything to protect existing views/rules, + only ones created in the future. + + + + + + Correctly detect hashability of range data types (Tom Lane) + + + + The planner mistakenly assumed that any range type could be hashed + for use in hash joins or hash aggregation, but actually it must check + whether the range's subtype has hash support. This does not affect any + of the built-in range types, since they're all hashable anyway. + + + + + + + Correctly ignore RelabelType expression nodes + when determining relation distinctness (David Rowley) + + + + This allows the intended optimization to occur when a subquery has + a result column of type varchar. + + + + + + Prevent sharing transition states between ordered-set aggregates + (David Rowley) + + + + This causes a crash with the built-in ordered-set aggregates, and + probably with user-written ones as well. v11 and later will include + provisions for dealing with such cases safely, but in released + branches, just disable the optimization. + + + + + + Prevent idle_in_transaction_session_timeout from + being ignored when a statement_timeout occurred + earlier (Lukas Fittl) + + + + + + Fix low-probability loss of NOTIFY messages due to + XID wraparound (Marko Tiikkaja, Tom Lane) + + + + If a session executed no queries, but merely listened for + notifications, for more than 2 billion transactions, it started to miss + some notifications from concurrently-committing transactions. + + + + + + + Avoid SIGBUS crash on Linux when a DSM memory + request exceeds the space available in tmpfs + (Thomas Munro) + + + + + + Reduce the frequency of data flush requests during bulk file copies to + avoid performance problems on macOS, particularly with its new APFS + file system (Tom Lane) + + + + + + + Prevent low-probability crash in processing of nested trigger firings + (Tom Lane) + + + + + + Allow COPY's FREEZE option to + work when the transaction isolation level is REPEATABLE + READ or higher (Noah Misch) + + + + This case was unintentionally broken by a previous bug fix. + + + + + + + Correctly restore the umask setting when file creation fails + in COPY or lo_export() + (Peter Eisentraut) + + + + + + + Give a better error message for duplicate column names + in ANALYZE (Nathan Bossart) + + + + + + + Add missing cases in GetCommandLogLevel(), + preventing errors when certain SQL commands are used while + log_statement is set to ddl + (Michael Paquier) + + + + + + + Fix mis-parsing of the last line in a + non-newline-terminated pg_hba.conf file + (Tom Lane) + + + + + + Fix AggGetAggref() to return the + correct Aggref nodes to aggregate final + functions whose transition calculations have been merged (Tom Lane) + + + + + + + Fix pg_dump to ensure that it + emits GRANT commands in a valid order + (Stephen Frost) + + + + + + Fix pg_basebackup's matching of tablespace + paths to canonicalize both paths before comparing (Michael Paquier) + + + + This is particularly helpful on Windows. + + + + + + Fix libpq to not require user's home + directory to exist (Tom Lane) + + + + In v10, failure to find the home directory while trying to + read ~/.pgpass was treated as a hard error, + but it should just cause that file to not be found. Both v10 and + previous release branches made the same mistake when + reading ~/.pg_service.conf, though this was less + obvious since that file is not sought unless a service name is + specified. + + + + + + + Fix libpq to guard against integer + overflow in the row count of a PGresult + (Michael Paquier) + + + + + + + Fix ecpg's handling of out-of-scope cursor + declarations with pointer or array variables (Michael Meskes) + + + + + + In ecpglib, correctly handle backslashes in string literals depending + on whether standard_conforming_strings is set + (Tsunakawa Takayuki) + + + + + + Make ecpglib's Informix-compatibility mode ignore fractional digits in + integer input strings, as expected (Gao Zengqi, Michael Meskes) + + + + + + + Fix ecpg's regression tests to work reliably + on Windows (Christian Ullrich, Michael Meskes) + + + + + + Fix missing temp-install prerequisites + for check-like Make targets (Noah Misch) + + + + Some non-default test procedures that are meant to work + like make check failed to ensure that the temporary + installation was up to date. + + + + + + + Sync our copy of the timezone library with IANA release tzcode2017c + (Tom Lane) + + + + This fixes various issues; the only one likely to be user-visible + is that the default DST rules for a POSIX-style zone name, if + no posixrules file exists in the timezone data + directory, now match current US law rather than what it was a dozen + years ago. + + + + + + Update time zone data files to tzdata + release 2017c for DST law changes in Fiji, Namibia, Northern Cyprus, + Sudan, Tonga, and Turks & Caicos Islands, plus historical + corrections for Alaska, Apia, Burma, Calcutta, Detroit, Ireland, + Namibia, and Pago Pago. + + + + + + + + + + Release 9.6.5 + + + 发布日期: + 2017-08-31 + + + + This release contains a small number of fixes from 9.6.4. + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.5 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you are upgrading from a version earlier than 9.6.4, + see . + + + + + 变更 + + + + + + + Show foreign tables + in information_schema.table_privileges + view (Peter Eisentraut) + + + + All other relevant information_schema views include + foreign tables, but this one ignored them. + + + + Since this view definition is installed by initdb, + merely upgrading will not fix the problem. If you need to fix this + in an existing installation, you can, as a superuser, do this + in psql: + +SET search_path TO information_schema; +CREATE OR REPLACE VIEW table_privileges AS + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(grantee.rolname AS sql_identifier) AS grantee, + CAST(current_database() AS sql_identifier) AS table_catalog, + CAST(nc.nspname AS sql_identifier) AS table_schema, + CAST(c.relname AS sql_identifier) AS table_name, + CAST(c.prtype AS character_data) AS privilege_type, + CAST( + CASE WHEN + -- object owner always has grant options + pg_has_role(grantee.oid, c.relowner, 'USAGE') + OR c.grantable + THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable, + CAST(CASE WHEN c.prtype = 'SELECT' THEN 'YES' ELSE 'NO' END AS yes_or_no) AS with_hierarchy + + FROM ( + SELECT oid, relname, relnamespace, relkind, relowner, (aclexplode(coalesce(relacl, acldefault('r', relowner)))).* FROM pg_class + ) AS c (oid, relname, relnamespace, relkind, relowner, grantor, grantee, prtype, grantable), + pg_namespace nc, + pg_authid u_grantor, + ( + SELECT oid, rolname FROM pg_authid + UNION ALL + SELECT 0::oid, 'PUBLIC' + ) AS grantee (oid, rolname) + + WHERE c.relnamespace = nc.oid + AND c.relkind IN ('r', 'v', 'f') + AND c.grantee = grantee.oid + AND c.grantor = u_grantor.oid + AND c.prtype IN ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER') + AND (pg_has_role(u_grantor.oid, 'USAGE') + OR pg_has_role(grantee.oid, 'USAGE') + OR grantee.rolname = 'PUBLIC'); + + This must be repeated in each database to be fixed, + including template0. + + + + + + + Clean up handling of a fatal exit (e.g., due to receipt + of SIGTERM) that occurs while trying to execute + a ROLLBACK of a failed transaction (Tom Lane) + + + + This situation could result in an assertion failure. In production + builds, the exit would still occur, but it would log an unexpected + message about cannot drop active portal. + + + + + + + Remove assertion that could trigger during a fatal exit (Tom Lane) + + + + + + + Correctly identify columns that are of a range type or domain type over + a composite type or domain type being searched for (Tom Lane) + + + + Certain ALTER commands that change the definition of a + composite type or domain type are supposed to fail if there are any + stored values of that type in the database, because they lack the + infrastructure needed to update or check such values. Previously, + these checks could miss relevant values that are wrapped inside range + types or sub-domains, possibly allowing the database to become + inconsistent. + + + + + + + Prevent crash when passing fixed-length pass-by-reference data types + to parallel worker processes (Tom Lane) + + + + + + + Fix crash in pg_restore when using parallel mode and + using a list file to select a subset of items to restore + (Fabrízio de Royes Mello) + + + + + + + Change ecpg's parser to allow RETURNING + clauses without attached C variables (Michael Meskes) + + + + This allows ecpg programs to contain SQL constructs + that use RETURNING internally (for example, inside a CTE) + rather than using it to define values to be returned to the client. + + + + + + + Change ecpg's parser to recognize backslash + continuation of C preprocessor command lines (Michael Meskes) + + + + + + + Improve selection of compiler flags for PL/Perl on Windows (Tom Lane) + + + + This fix avoids possible crashes of PL/Perl due to inconsistent + assumptions about the width of time_t values. + A side-effect that may be visible to extension developers is + that _USE_32BIT_TIME_T is no longer defined globally + in PostgreSQL Windows builds. This is not expected + to cause problems, because type time_t is not used + in any PostgreSQL API definitions. + + + + + + + Fix make check to behave correctly when invoked via a + non-GNU make program (Thomas Munro) + + + + + + + + + + Release 9.6.4 + + + 发布日期: + 2017-08-10 + + + + 此版本包含自 9.6.3 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.4 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you use foreign data servers that make use of user + passwords for authentication, see the first changelog entry below. + + + + 另外,如果你是从 9.6.3 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + Further restrict visibility + of pg_user_mappings.umoptions, to + protect passwords stored as user mapping options + (Noah Misch) + + + + The fix for CVE-2017-7486 was incorrect: it allowed a user + to see the options in her own user mapping, even if she did not + have USAGE permission on the associated foreign server. + Such options might include a password that had been provided by the + server owner rather than the user herself. + Since information_schema.user_mapping_options does not + show the options in such cases, pg_user_mappings + should not either. + (CVE-2017-7547) + + + + By itself, this patch will only fix the behavior in newly initdb'd + databases. If you wish to apply this change in an existing database, + you will need to do the following: + + + + + + Restart the postmaster after adding allow_system_table_mods + = true to postgresql.conf. (In versions + supporting ALTER SYSTEM, you can use that to make the + configuration change, but you'll still need a restart.) + + + + + + In each database of the cluster, + run the following commands as superuser: + +SET search_path = pg_catalog; +CREATE OR REPLACE VIEW pg_user_mappings AS + SELECT + U.oid AS umid, + S.oid AS srvid, + S.srvname AS srvname, + U.umuser AS umuser, + CASE WHEN U.umuser = 0 THEN + 'public' + ELSE + A.rolname + END AS usename, + CASE WHEN (U.umuser <> 0 AND A.rolname = current_user + AND (pg_has_role(S.srvowner, 'USAGE') + OR has_server_privilege(S.oid, 'USAGE'))) + OR (U.umuser = 0 AND pg_has_role(S.srvowner, 'USAGE')) + OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) + THEN U.umoptions + ELSE NULL END AS umoptions + FROM pg_user_mapping U + LEFT JOIN pg_authid A ON (A.oid = U.umuser) JOIN + pg_foreign_server S ON (U.umserver = S.oid); + + + + + + + Do not forget to include the template0 + and template1 databases, or the vulnerability will still + exist in databases you create later. To fix template0, + you'll need to temporarily make it accept connections. + In PostgreSQL 9.5 and later, you can use + +ALTER DATABASE template0 WITH ALLOW_CONNECTIONS true; + + and then after fixing template0, undo that with + +ALTER DATABASE template0 WITH ALLOW_CONNECTIONS false; + + In prior versions, instead use + +UPDATE pg_database SET datallowconn = true WHERE datname = 'template0'; +UPDATE pg_database SET datallowconn = false WHERE datname = 'template0'; + + + + + + + Finally, remove the allow_system_table_mods configuration + setting, and again restart the postmaster. + + + + + + + + + Disallow empty passwords in all password-based authentication methods + (Heikki Linnakangas) + + + + libpq ignores empty password specifications, and does + not transmit them to the server. So, if a user's password has been + set to the empty string, it's impossible to log in with that password + via psql or other libpq-based + clients. An administrator might therefore believe that setting the + password to empty is equivalent to disabling password login. + However, with a modified or non-libpq-based client, + logging in could be possible, depending on which authentication + method is configured. In particular the most common + method, md5, accepted empty passwords. + Change the server to reject empty passwords in all cases. + (CVE-2017-7546) + + + + + + + Make lo_put() check for UPDATE privilege on + the target large object (Tom Lane, Michael Paquier) + + + + lo_put() should surely require the same permissions + as lowrite(), but the check was missing, allowing any + user to change the data in a large object. + (CVE-2017-7548) + + + + + + + Correct the documentation about the process for upgrading standby + servers with pg_upgrade (Bruce Momjian) + + + + The previous documentation instructed users to start/stop the primary + server after running pg_upgrade but before syncing + the standby servers. This sequence is unsafe. + + + + + + + Fix concurrent locking of tuple update chains (Álvaro Herrera) + + + + If several sessions concurrently lock a tuple update chain with + nonconflicting lock modes using an old snapshot, and they all + succeed, it was possible for some of them to nonetheless fail (and + conclude there is no live tuple version) due to a race condition. + This had consequences such as foreign-key checks failing to see a + tuple that definitely exists but is being updated concurrently. + + + + + + + Fix potential data corruption when freezing a tuple whose XMAX is a + multixact with exactly one still-interesting member (Teodor Sigaev) + + + + + + + Avoid integer overflow and ensuing crash when sorting more than one + billion tuples in-memory (Sergey Koposov) + + + + + + + On Windows, retry process creation if we fail to reserve the address + range for our shared memory in the new process (Tom Lane, Amit + Kapila) + + + + This is expected to fix infrequent child-process-launch failures that + are probably due to interference from antivirus products. + + + + + + + Fix low-probability corruption of shared predicate-lock hash table + in Windows builds (Thomas Munro, Tom Lane) + + + + + + + Avoid logging clean closure of an SSL connection as though + it were a connection reset (Michael Paquier) + + + + + + + Prevent sending SSL session tickets to clients (Tom Lane) + + + + This fix prevents reconnection failures with ticket-aware client-side + SSL code. + + + + + + + Fix code for setting on + Solaris (Tom Lane) + + + + + + + Fix statistics collector to honor inquiry messages issued just after + a postmaster shutdown and immediate restart (Tom Lane) + + + + Statistics inquiries issued within half a second of the previous + postmaster shutdown were effectively ignored. + + + + + + + Ensure that the statistics collector's receive buffer size is at + least 100KB (Tom Lane) + + + + This reduces the risk of dropped statistics data on older platforms + whose default receive buffer size is less than that. + + + + + + + Fix possible creation of an invalid WAL segment when a standby is + promoted just after it processes an XLOG_SWITCH WAL + record (Andres Freund) + + + + + + + Fix walsender to exit promptly when client requests + shutdown (Tom Lane) + + + + + + + Fix SIGHUP and SIGUSR1 handling in + walsender processes (Petr Jelinek, Andres Freund) + + + + + + + Prevent walsender-triggered panics during shutdown checkpoints + (Andres Freund, Michael Paquier) + + + + + + + Fix unnecessarily slow restarts of walreceiver + processes due to race condition in postmaster (Tom Lane) + + + + + + + Fix leakage of small subtransactions spilled to disk during logical + decoding (Andres Freund) + + + + This resulted in temporary files consuming excessive disk space. + + + + + + + Reduce the work needed to build snapshots during creation of + logical-decoding slots (Andres Freund, Petr Jelinek) + + + + The previous algorithm was infeasibly expensive on a server with a + lot of open transactions. + + + + + + + Fix race condition that could indefinitely delay creation of + logical-decoding slots (Andres Freund, Petr Jelinek) + + + + + + + Reduce overhead in processing syscache invalidation events (Tom Lane) + + + + This is particularly helpful for logical decoding, which triggers + frequent cache invalidation. + + + + + + + Remove incorrect heuristic used in some cases to estimate join + selectivity based on the presence of foreign-key constraints + (David Rowley) + + + + In some cases where a multi-column foreign key constraint existed but + did not exactly match a query's join structure, the planner used an + estimation heuristic that turns out not to work well at all. Revert + such cases to the way they were estimated before 9.6. + + + + + + + Fix cases where an INSERT or UPDATE assigns + to more than one element of a column that is of domain-over-array + type (Tom Lane) + + + + + + + Allow window functions to be used in sub-SELECTs that + are within the arguments of an aggregate function (Tom Lane) + + + + + + + Ensure that a view's CHECK OPTIONS clause is enforced + properly when the underlying table is a foreign table (Etsuro Fujita) + + + + Previously, the update might get pushed entirely to the foreign + server, but the need to verify the view conditions was missed if so. + + + + + + + Move autogenerated array types out of the way during + ALTER ... RENAME (Vik Fearing) + + + + Previously, we would rename a conflicting autogenerated array type + out of the way during CREATE; this fix extends that + behavior to renaming operations. + + + + + + + Fix dangling pointer in ALTER TABLE when there is a + comment on a constraint belonging to the table (David Rowley) + + + + Re-applying the comment to the reconstructed constraint could fail + with a weird error message, or even crash. + + + + + + + Ensure that ALTER USER ... SET accepts all the syntax + variants that ALTER ROLE ... SET does (Peter Eisentraut) + + + + + + + Allow a foreign table's CHECK constraints to be + initially NOT VALID (Amit Langote) + + + + CREATE TABLE silently drops NOT VALID + specifiers for CHECK constraints, reasoning that the + table must be empty so the constraint can be validated immediately. + But this is wrong for CREATE FOREIGN TABLE, where there's + no reason to suppose that the underlying table is empty, and even if + it is it's no business of ours to decide that the constraint can be + treated as valid going forward. Skip this optimization for + foreign tables. + + + + + + + Properly update dependency info when changing a datatype I/O + function's argument or return type from opaque to the + correct type (Heikki Linnakangas) + + + + CREATE TYPE updates I/O functions declared in this + long-obsolete style, but it forgot to record a dependency on the + type, allowing a subsequent DROP TYPE to leave broken + function definitions behind. + + + + + + + Allow parallelism in the query plan when COPY copies from + a query's result (Andres Freund) + + + + + + + Reduce memory usage when ANALYZE processes + a tsvector column (Heikki Linnakangas) + + + + + + + Fix unnecessary precision loss and sloppy rounding when multiplying + or dividing money values by integers or floats (Tom Lane) + + + + + + + Tighten checks for whitespace in functions that parse identifiers, + such as regprocedurein() (Tom Lane) + + + + Depending on the prevailing locale, these functions could + misinterpret fragments of multibyte characters as whitespace. + + + + + + + Use relevant #define symbols from Perl while + compiling PL/Perl (Ashutosh Sharma, Tom Lane) + + + + This avoids portability problems, typically manifesting as + a handshake mismatch during library load, when working with + recent Perl versions. + + + + + + + In libpq, reset GSS/SASL and SSPI authentication + state properly after a failed connection attempt (Michael Paquier) + + + + Failure to do this meant that when falling back from SSL to non-SSL + connections, a GSS/SASL failure in the SSL attempt would always cause + the non-SSL attempt to fail. SSPI did not fail, but it leaked memory. + + + + + + + In psql, fix failure when COPY FROM STDIN + is ended with a keyboard EOF signal and then another COPY + FROM STDIN is attempted (Thomas Munro) + + + + This misbehavior was observed on BSD-derived platforms (including + macOS), but not on most others. + + + + + + + Fix pg_dump and pg_restore to + emit REFRESH MATERIALIZED VIEW commands last (Tom Lane) + + + + This prevents errors during dump/restore when a materialized view + refers to tables owned by a different user. + + + + + + + Improve pg_dump/pg_restore's + reporting of error conditions originating in zlib + (Vladimir Kunschikov, Álvaro Herrera) + + + + + + + Fix pg_dump with the + + + It also now correctly assigns ownership of event triggers; before, + they were restored as being owned by the superuser running the + restore script. + + + + + + + Fix pg_dump with the + + + + + + Fix pg_dump to not emit invalid SQL for an empty + operator class (Daniel Gustafsson) + + + + + + + Fix pg_dump output to stdout on Windows (Kuntal Ghosh) + + + + A compressed plain-text dump written to stdout would contain corrupt + data due to failure to put the file descriptor into binary mode. + + + + + + + Fix pg_get_ruledef() to print correct output for + the ON SELECT rule of a view whose columns have been + renamed (Tom Lane) + + + + In some corner cases, pg_dump relies + on pg_get_ruledef() to dump views, so that this error + could result in dump/reload failures. + + + + + + + Fix dumping of outer joins with empty constraints, such as the result + of a NATURAL LEFT JOIN with no common columns (Tom Lane) + + + + + + + Fix dumping of function expressions in the FROM clause in + cases where the expression does not deparse into something that looks + like a function call (Tom Lane) + + + + + + + Fix pg_basebackup output to stdout on Windows + (Haribabu Kommi) + + + + A backup written to stdout would contain corrupt data due to failure + to put the file descriptor into binary mode. + + + + + + + Fix pg_rewind to correctly handle files exceeding 2GB + (Kuntal Ghosh, Michael Paquier) + + + + Ordinarily such files won't appear in PostgreSQL data + directories, but they could be present in some cases. + + + + + + + Fix pg_upgrade to ensure that the ending WAL record + does not have = minimum + (Bruce Momjian) + + + + This condition could prevent upgraded standby servers from + reconnecting. + + + + + + + Fix pg_xlogdump's computation of WAL record length + (Andres Freund) + + + + + + + In postgres_fdw, re-establish connections to remote + servers after ALTER SERVER or ALTER USER + MAPPING commands (Kyotaro Horiguchi) + + + + This ensures that option changes affecting connection parameters will + be applied promptly. + + + + + + + In postgres_fdw, allow cancellation of remote + transaction control commands (Robert Haas, Rafia Sabih) + + + + This change allows us to quickly escape a wait for an unresponsive + remote server in many more cases than previously. + + + + + + + Increase MAX_SYSCACHE_CALLBACKS to provide more room for + extensions (Tom Lane) + + + + + + + Always use + + + This supports larger extension libraries on platforms where it makes + a difference. + + + + + + + In MSVC builds, handle the case where the openssl + library is not within a VC subdirectory (Andrew Dunstan) + + + + + + + In MSVC builds, add proper include path for libxml2 + header files (Andrew Dunstan) + + + + This fixes a former need to move things around in standard Windows + installations of libxml2. + + + + + + + In MSVC builds, recognize a Tcl library that is + named tcl86.lib (Noah Misch) + + + + + + + In MSVC builds, honor PROVE_FLAGS settings + on vcregress.pl's command line (Andrew Dunstan) + + + + + + + + + + Release 9.6.3 + + + 发布日期: + 2017-05-11 + + + + 此版本包含自 9.6.2 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.3 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if you use foreign data servers that make use of user + passwords for authentication, see the first changelog entry below. + + + + Also, if you are using third-party replication tools that depend + on logical decoding, see the fourth changelog entry below. + + + + 另外,如果你是从 9.6.2 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + Restrict visibility + of pg_user_mappings.umoptions, to + protect passwords stored as user mapping options + (Michael Paquier, Feike Steenbergen) + + + + The previous coding allowed the owner of a foreign server object, + or anyone he has granted server USAGE permission to, + to see the options for all user mappings associated with that server. + This might well include passwords for other users. + Adjust the view definition to match the behavior of + information_schema.user_mapping_options, namely that + these options are visible to the user being mapped, or if the mapping + is for PUBLIC and the current user is the server + owner, or if the current user is a superuser. + (CVE-2017-7486) + + + + By itself, this patch will only fix the behavior in newly initdb'd + databases. If you wish to apply this change in an existing database, + follow the corrected procedure shown in the changelog entry for + CVE-2017-7547, in . + + + + + + + Prevent exposure of statistical information via leaky operators + (Peter Eisentraut) + + + + Some selectivity estimation functions in the planner will apply + user-defined operators to values obtained + from pg_statistic, such as most common values and + histogram entries. This occurs before table permissions are checked, + so a nefarious user could exploit the behavior to obtain these values + for table columns he does not have permission to read. To fix, + fall back to a default estimate if the operator's implementation + function is not certified leak-proof and the calling user does not have + permission to read the table column whose statistics are needed. + At least one of these criteria is satisfied in most cases in practice. + (CVE-2017-7484) + + + + + + + Restore libpq's recognition of + the PGREQUIRESSL environment variable (Daniel Gustafsson) + + + + Processing of this environment variable was unintentionally dropped + in PostgreSQL 9.3, but its documentation remained. + This creates a security hazard, since users might be relying on the + environment variable to force SSL-encrypted connections, but that + would no longer be guaranteed. Restore handling of the variable, + but give it lower priority than PGSSLMODE, to avoid + breaking configurations that work correctly with post-9.3 code. + (CVE-2017-7485) + + + + + + + Fix possibly-invalid initial snapshot during logical decoding + (Petr Jelinek, Andres Freund) + + + + The initial snapshot created for a logical decoding replication slot + was potentially incorrect. This could cause third-party tools that + use logical decoding to copy incomplete/inconsistent initial data. + This was more likely to happen if the source server was busy at the + time of slot creation, or if another logical slot already existed. + + + + If you are using a replication tool that depends on logical decoding, + and it should have copied a nonempty data set at the start of + replication, it is advisable to recreate the replica after + installing this update, or to verify its contents against the source + server. + + + + + + + Fix possible corruption of init forks of unlogged indexes + (Robert Haas, Michael Paquier) + + + + This could result in an unlogged index being set to an invalid state + after a crash and restart. Such a problem would persist until the + index was dropped and rebuilt. + + + + + + + Fix incorrect reconstruction of pg_subtrans entries + when a standby server replays a prepared but uncommitted two-phase + transaction (Tom Lane) + + + + In most cases this turned out to have no visible ill effects, but in + corner cases it could result in circular references + in pg_subtrans, potentially causing infinite loops + in queries that examine rows modified by the two-phase transaction. + + + + + + + Avoid possible crash in walsender due to failure + to initialize a string buffer (Stas Kelvich, Fujii Masao) + + + + + + + Fix possible crash when rescanning a nearest-neighbor index-only scan + on a GiST index (Tom Lane) + + + + + + + Prevent delays in postmaster's launching of multiple parallel worker + processes (Tom Lane) + + + + There could be a significant delay (up to tens of seconds) before + satisfying a query's request for more than one worker process, or when + multiple queries requested workers simultaneously. On most platforms + this required unlucky timing, but on some it was the typical case. + + + + + + + Fix postmaster's handling of fork() failure for a + background worker process (Tom Lane) + + + + Previously, the postmaster updated portions of its state as though + the process had been launched successfully, resulting in subsequent + confusion. + + + + + + + Fix possible no relation entry for relid 0 error when + planning nested set operations (Tom Lane) + + + + + + + Fix assorted minor issues in planning of parallel queries (Robert Haas) + + + + + + + Avoid applying physical targetlist optimization to custom + scans (Dmitry Ivanov, Tom Lane) + + + + This optimization supposed that retrieving all columns of a tuple + is inexpensive, which is true for ordinary Postgres tuples; but it + might not be the case for a custom scan provider. + + + + + + + Use the correct sub-expression when applying a FOR ALL + row-level-security policy (Stephen Frost) + + + + In some cases the WITH CHECK restriction would be applied + when the USING restriction is more appropriate. + + + + + + + Ensure parsing of queries in extension scripts sees the results of + immediately-preceding DDL (Julien Rouhaud, Tom Lane) + + + + Due to lack of a cache flush step between commands in an extension + script file, non-utility queries might not see the effects of an + immediately preceding catalog change, such as ALTER TABLE + ... RENAME. + + + + + + + Skip tablespace privilege checks when ALTER TABLE ... ALTER + COLUMN TYPE rebuilds an existing index (Noah Misch) + + + + The command failed if the calling user did not currently have + CREATE privilege for the tablespace containing the index. + That behavior seems unhelpful, so skip the check, allowing the + index to be rebuilt where it is. + + + + + + + Fix ALTER TABLE ... VALIDATE CONSTRAINT to not recurse + to child tables when the constraint is marked NO INHERIT + (Amit Langote) + + + + This fix prevents unwanted constraint does not exist failures + when no matching constraint is present in the child tables. + + + + + + + Avoid dangling pointer in COPY ... TO when row-level + security is active for the source table (Tom Lane) + + + + Usually this had no ill effects, but sometimes it would cause + unexpected errors or crashes. + + + + + + + Avoid accessing an already-closed relcache entry in CLUSTER + and VACUUM FULL (Tom Lane) + + + + With some bad luck, this could lead to indexes on the target + relation getting rebuilt with the wrong persistence setting. + + + + + + + Fix VACUUM to account properly for pages that could not + be scanned due to conflicting page pins (Andrew Gierth) + + + + This tended to lead to underestimation of the number of tuples in + the table. In the worst case of a small heavily-contended + table, VACUUM could incorrectly report that the table + contained no tuples, leading to very bad planning choices. + + + + + + + Ensure that bulk-tuple-transfer loops within a hash join are + interruptible by query cancel requests (Tom Lane, Thomas Munro) + + + + + + + Fix incorrect support for certain box operators in SP-GiST + (Nikita Glukhov) + + + + SP-GiST index scans using the operators &< + &> &<| and |&> + would yield incorrect answers. + + + + + + + Fix integer-overflow problems in interval comparison (Kyotaro + Horiguchi, Tom Lane) + + + + The comparison operators for type interval could yield wrong + answers for intervals larger than about 296000 years. Indexes on + columns containing such large values should be reindexed, since they + may be corrupt. + + + + + + + Fix cursor_to_xml() to produce valid output + with tableforest = false + (Thomas Munro, Peter Eisentraut) + + + + Previously it failed to produce a wrapping <table> + element. + + + + + + + Fix roundoff problems in float8_timestamptz() + and make_interval() (Tom Lane) + + + + These functions truncated, rather than rounded, when converting a + floating-point value to integer microseconds; that could cause + unexpectedly off-by-one results. + + + + + + + Fix pg_get_object_address() to handle members of operator + families correctly (Álvaro Herrera) + + + + + + + Fix cancelling of pg_stop_backup() when attempting to stop + a non-exclusive backup (Michael Paquier, David Steele) + + + + If pg_stop_backup() was cancelled while waiting for a + non-exclusive backup to end, related state was left inconsistent; + a new exclusive backup could not be started, and there were other minor + problems. + + + + + + + Improve performance of pg_timezone_names view + (Tom Lane, David Rowley) + + + + + + + Reduce memory management overhead for contexts containing many large + blocks (Tom Lane) + + + + + + + Fix sloppy handling of corner-case errors from lseek() + and close() (Tom Lane) + + + + Neither of these system calls are likely to fail in typical situations, + but if they did, fd.c could get quite confused. + + + + + + + Fix incorrect check for whether postmaster is running as a Windows + service (Michael Paquier) + + + + This could result in attempting to write to the event log when that + isn't accessible, so that no logging happens at all. + + + + + + + Fix ecpg to support COMMIT PREPARED + and ROLLBACK PREPARED (Masahiko Sawada) + + + + + + + Fix a double-free error when processing dollar-quoted string literals + in ecpg (Michael Meskes) + + + + + + + Fix pgbench to handle the combination + of + + + + + + Fix pgbench to honor the long-form option + spelling + + + + + + Fix pg_dump/pg_restore to correctly + handle privileges for the public schema when + using + + + Other schemas start out with no privileges granted, + but public does not; this requires special-case treatment + when it is dropped and restored due to the + + + + + + In pg_dump, fix incorrect schema and owner marking for + comments and security labels of some types of database objects + (Giuseppe Broccolo, Tom Lane) + + + + In simple cases this caused no ill effects; but for example, a + schema-selective restore might omit comments it should include, because + they were not marked as belonging to the schema of their associated + object. + + + + + + + Fix typo in pg_dump's query for initial privileges + of a procedural language (Peter Eisentraut) + + + + This resulted in pg_dump always believing that the + language had no initial privileges. Since that's true for most + procedural languages, ill effects from this bug are probably rare. + + + + + + + Avoid emitting an invalid list file in pg_restore -l + when SQL object names contain newlines (Tom Lane) + + + + Replace newlines by spaces, which is sufficient to make the output + valid for pg_restore -L's purposes. + + + + + + + Fix pg_upgrade to transfer comments and security labels + attached to large objects (blobs) (Stephen Frost) + + + + Previously, blobs were correctly transferred to the new database, but + any comments or security labels attached to them were lost. + + + + + + + Improve error handling + in contrib/adminpack's pg_file_write() + function (Noah Misch) + + + + Notably, it failed to detect errors reported + by fclose(). + + + + + + + In contrib/dblink, avoid leaking the previous unnamed + connection when establishing a new unnamed connection (Joe Conway) + + + + + + + Fix contrib/pg_trgm's extraction of trigrams from regular + expressions (Tom Lane) + + + + In some cases it would produce a broken data structure that could never + match anything, leading to GIN or GiST indexscans that use a trigram + index not finding any matches to the regular expression. + + + + + + + In contrib/postgres_fdw, allow join conditions that + contain shippable extension-provided functions to be pushed to the + remote server (David Rowley, Ashutosh Bapat) + + + + + + + Support Tcl 8.6 in MSVC builds (Álvaro Herrera) + + + + + + + Sync our copy of the timezone library with IANA release tzcode2017b + (Tom Lane) + + + + This fixes a bug affecting some DST transitions in January 2038. + + + + + + + Update time zone data files to tzdata release 2017b + for DST law changes in Chile, Haiti, and Mongolia, plus historical + corrections for Ecuador, Kazakhstan, Liberia, and Spain. + Switch to numeric abbreviations for numerous time zones in South + America, the Pacific and Indian oceans, and some Asian and Middle + Eastern countries. + + + + The IANA time zone database previously provided textual abbreviations + for all time zones, sometimes making up abbreviations that have little + or no currency among the local population. They are in process of + reversing that policy in favor of using numeric UTC offsets in zones + where there is no evidence of real-world use of an English + abbreviation. At least for the time being, PostgreSQL + will continue to accept such removed abbreviations for timestamp input. + But they will not be shown in the pg_timezone_names + view nor used for output. + + + + + + + Use correct daylight-savings rules for POSIX-style time zone names + in MSVC builds (David Rowley) + + + + The Microsoft MSVC build scripts neglected to install + the posixrules file in the timezone directory tree. + This resulted in the timezone code falling back to its built-in + rule about what DST behavior to assume for a POSIX-style time zone + name. For historical reasons that still corresponds to the DST rules + the USA was using before 2007 (i.e., change on first Sunday in April + and last Sunday in October). With this fix, a POSIX-style zone name + will use the current and historical DST transition dates of + the US/Eastern zone. If you don't want that, remove + the posixrules file, or replace it with a copy of some + other zone file (see ). Note that + due to caching, you may need to restart the server to get such changes + to take effect. + + + + + + + + + + Release 9.6.2 + + + 发布日期: + 2017-02-09 + + + + 此版本包含自 9.6.1 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.2 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if your installation has been affected by the bug described in + the first changelog entry below, then after updating you may need + to take action to repair corrupted indexes. + + + + 另外,如果你是从 9.6.1 之前的版本升级,参见 + 。 + + + + + 变更 + + + + + + + Fix a race condition that could cause indexes built + with CREATE INDEX CONCURRENTLY to be corrupt + (Pavan Deolasee, Tom Lane) + + + + If CREATE INDEX CONCURRENTLY was used to build an index + that depends on a column not previously indexed, then rows + updated by transactions that ran concurrently with + the CREATE INDEX command could have received incorrect + index entries. If you suspect this may have happened, the most + reliable solution is to rebuild affected indexes after installing + this update. + + + + + + + Ensure that the special snapshot used for catalog scans is not + invalidated by premature data pruning (Tom Lane) + + + + Backends failed to account for this snapshot when advertising their + oldest xmin, potentially allowing concurrent vacuuming operations to + remove data that was still needed. This led to transient failures + along the lines of cache lookup failed for relation 1255. + + + + + + + Fix incorrect WAL logging for BRIN indexes (Kuntal Ghosh) + + + + The WAL record emitted for a BRIN revmap page when moving an + index tuple to a different page was incorrect. Replay would make the + related portion of the index useless, forcing it to be recomputed. + + + + + + + Unconditionally WAL-log creation of the init fork for an + unlogged table (Michael Paquier) + + + + Previously, this was skipped when + = minimal, but actually it's necessary even in that case + to ensure that the unlogged table is properly reset to empty after a + crash. + + + + + + + If the stats collector dies during hot standby, restart it (Takayuki + Tsunakawa) + + + + + + + Ensure that hot standby feedback works correctly when it's enabled at + standby server start (Ants Aasma, Craig Ringer) + + + + + + + Check for interrupts while hot standby is waiting for a conflicting + query (Simon Riggs) + + + + + + + Avoid constantly respawning the autovacuum launcher in a corner case + (Amit Khandekar) + + + + This fix avoids problems when autovacuum is nominally off and there + are some tables that require freezing, but all such tables are + already being processed by autovacuum workers. + + + + + + + Disallow setting the num_sync field to zero in + (Fujii Masao) + + + + The correct way to disable synchronous standby is to set the whole + value to an empty string. + + + + + + + Don't count background worker processes against a user's connection + limit (David Rowley) + + + + + + + Fix check for when an extension member object can be dropped (Tom Lane) + + + + Extension upgrade scripts should be able to drop member objects, + but this was disallowed for serial-column sequences, and possibly + other cases. + + + + + + + Fix tracking of initial privileges for extension member objects so + that it works correctly with ALTER EXTENSION ... ADD/DROP + (Stephen Frost) + + + + An object's current privileges at the time it is added to the + extension will now be considered its default privileges; only + later changes in its privileges will be dumped by + subsequent pg_dump runs. + + + + + + + Make sure ALTER TABLE preserves index tablespace + assignments when rebuilding indexes (Tom Lane, Michael Paquier) + + + + Previously, non-default settings + of could result in broken + indexes. + + + + + + + Fix incorrect updating of trigger function properties when changing a + foreign-key constraint's deferrability properties with ALTER + TABLE ... ALTER CONSTRAINT (Tom Lane) + + + + This led to odd failures during subsequent exercise of the foreign + key, as the triggers were fired at the wrong times. + + + + + + + Prevent dropping a foreign-key constraint if there are pending + trigger events for the referenced relation (Tom Lane) + + + + This avoids could not find trigger NNN + or relation NNN has no triggers errors. + + + + + + + Fix ALTER TABLE ... SET DATA TYPE ... USING when child + table has different column ordering than the parent + (Álvaro Herrera) + + + + Failure to adjust the column numbering in the USING + expression led to errors, + typically attribute N has wrong type. + + + + + + + Fix processing of OID column when a table with OIDs is associated to + a parent with OIDs via ALTER TABLE ... INHERIT (Amit + Langote) + + + + The OID column should be treated the same as regular user columns in + this case, but it wasn't, leading to odd behavior in later + inheritance changes. + + + + + + + Ensure that CREATE TABLE ... LIKE ... WITH OIDS creates + a table with OIDs, whether or not the LIKE-referenced + table(s) have OIDs (Tom Lane) + + + + + + + Fix CREATE OR REPLACE VIEW to update the view query + before attempting to apply the new view options (Dean Rasheed) + + + + Previously the command would fail if the new options were + inconsistent with the old view definition. + + + + + + + Report correct object identity during ALTER TEXT SEARCH + CONFIGURATION (Artur Zakirov) + + + + The wrong catalog OID was reported to extensions such as logical + decoding. + + + + + + + Fix commit timestamp mechanism to not fail when queried about + the special XIDs FrozenTransactionId + and BootstrapTransactionId (Craig Ringer) + + + + + + + Fix incorrect use of view reloptions as regular table reloptions (Tom + Lane) + + + + The symptom was spurious ON CONFLICT is not supported on table + ... used as a catalog table errors when the target + of INSERT ... ON CONFLICT is a view with cascade option. + + + + + + + Fix incorrect target lists can have at most N + entries complaint when using ON CONFLICT with + wide tables (Tom Lane) + + + + + + + Fix spurious query provides a value for a dropped column + errors during INSERT or UPDATE on a table + with a dropped column (Tom Lane) + + + + + + + Prevent multicolumn expansion of foo.* in + an UPDATE source expression (Tom Lane) + + + + This led to UPDATE target count mismatch --- internal + error. Now the syntax is understood as a whole-row variable, + as it would be in other contexts. + + + + + + + Ensure that column typmods are determined accurately for + multi-row VALUES constructs (Tom Lane) + + + + This fixes problems occurring when the first value in a column has a + determinable typmod (e.g., length for a varchar value) but + later values don't share the same limit. + + + + + + + Throw error for an unfinished Unicode surrogate pair at the end of a + Unicode string (Tom Lane) + + + + Normally, a Unicode surrogate leading character must be followed by a + Unicode surrogate trailing character, but the check for this was + missed if the leading character was the last character in a Unicode + string literal (U&'...') or Unicode identifier + (U&"..."). + + + + + + + Fix execution of DISTINCT and ordered aggregates when + multiple such aggregates are able to share the same transition state + (Heikki Linnakangas) + + + + + + + Fix implementation of phrase search operators in tsquery + (Tom Lane) + + + + Remove incorrect, and inconsistently-applied, rewrite rules that + tried to transform away AND/OR/NOT operators appearing below a PHRASE + operator; instead upgrade the execution engine to handle such cases + correctly. This fixes assorted strange behavior and possible crashes + for text search queries containing such combinations. Also fix + nested PHRASE operators to work sanely in combinations other than + simple left-deep trees, correct the behavior when removing stopwords + from a phrase search clause, and make sure that index searches behave + consistently with simple sequential-scan application of such queries. + + + + + + + Ensure that a purely negative text search query, such + as !foo, matches empty tsvectors (Tom Dunstan) + + + + Such matches were found by GIN index searches, but not by sequential + scans or GiST index searches. + + + + + + + Prevent crash when ts_rewrite() replaces a non-top-level + subtree with an empty query (Artur Zakirov) + + + + + + + Fix performance problems in ts_rewrite() (Tom Lane) + + + + + + + Fix ts_rewrite()'s handling of nested NOT operators + (Tom Lane) + + + + + + + Improve speed of user-defined aggregates that + use array_append() as transition function (Tom Lane) + + + + + + + Fix array_fill() to handle empty arrays properly (Tom Lane) + + + + + + + Fix possible crash in array_position() + or array_positions() when processing arrays of records + (Junseok Yang) + + + + + + + Fix one-byte buffer overrun in quote_literal_cstr() + (Heikki Linnakangas) + + + + The overrun occurred only if the input consisted entirely of single + quotes and/or backslashes. + + + + + + + Prevent multiple calls of pg_start_backup() + and pg_stop_backup() from running concurrently (Michael + Paquier) + + + + This avoids an assertion failure, and possibly worse things, if + someone tries to run these functions in parallel. + + + + + + + Disable transform that attempted to remove no-op AT TIME + ZONE conversions (Tom Lane) + + + + This resulted in wrong answers when the simplified expression was + used in an index condition. + + + + + + + Avoid discarding interval-to-interval casts + that aren't really no-ops (Tom Lane) + + + + In some cases, a cast that should result in zeroing out + low-order interval fields was mistakenly deemed to be a + no-op and discarded. An example is that casting from INTERVAL + MONTH to INTERVAL YEAR failed to clear the months field. + + + + + + + Fix crash if the number of workers available to a parallel query + decreases during a rescan (Andreas Seltenreich) + + + + + + + Fix bugs in transmitting GUC parameter values to parallel workers + (Michael Paquier, Tom Lane) + + + + + + + Allow statements prepared with PREPARE to be given + parallel plans (Amit Kapila, Tobias Bussmann) + + + + + + + Fix incorrect generation of parallel plans for semi-joins (Tom Lane) + + + + + + + Fix planner's cardinality estimates for parallel joins (Robert Haas) + + + + Ensure that these estimates reflect the number of rows predicted to + be seen by each worker, rather than the total. + + + + + + + Fix planner to avoid trying to parallelize plan nodes containing + initplans or subplans (Tom Lane, Amit Kapila) + + + + + + + Ensure that cached plans are invalidated by changes in foreign-table + options (Amit Langote, Etsuro Fujita, Ashutosh Bapat) + + + + + + + Fix the plan generated for sorted partial aggregation with a constant + GROUP BY clause (Tom Lane) + + + + + + + Fix could not find plan for CTE planner error when dealing + with a UNION ALL containing CTE references (Tom Lane) + + + + + + + Fix mishandling of initplans when forcibly adding a Material node to + a subplan (Tom Lane) + + + + The typical consequence of this mistake was a plan should not + reference subplan's variable error. + + + + + + + Fix foreign-key-based join selectivity estimation for semi-joins and + anti-joins, as well as inheritance cases (Tom Lane) + + + + The new code for taking the existence of a foreign key relationship + into account did the wrong thing in these cases, making the estimates + worse not better than the pre-9.6 code. + + + + + + + Fix pg_dump to emit the data of a sequence that is + marked as an extension configuration table (Michael Paquier) + + + + + + + Fix mishandling of ALTER DEFAULT PRIVILEGES ... REVOKE + in pg_dump (Stephen Frost) + + + + pg_dump missed issuing the + required REVOKE commands in cases where ALTER + DEFAULT PRIVILEGES had been used to reduce privileges to less than + they would normally be. + + + + + + + Fix pg_dump to dump user-defined casts and transforms + that use built-in functions (Stephen Frost) + + + + + + + Fix pg_restore with + + + This doesn't fix any live bug, but it may improve the behavior in + future if pg_restore is used with an archive + generated by a later pg_dump version. + + + + + + + Fix pg_basebackup's rate limiting in the presence of + slow I/O (Antonin Houska) + + + + If disk I/O was transiently much slower than the specified rate + limit, the calculation overflowed, effectively disabling the rate + limit for the rest of the run. + + + + + + + Fix pg_basebackup's handling of + symlinked pg_stat_tmp and pg_replslot + subdirectories (Magnus Hagander, Michael Paquier) + + + + + + + Fix possible pg_basebackup failure on standby + server when including WAL files (Amit Kapila, Robert Haas) + + + + + + + Improve initdb to insert the correct + platform-specific default values for + the xxx_flush_after parameters + into postgresql.conf (Fabien Coelho, Tom Lane) + + + + This is a cleaner way of documenting the default values than was used + previously. + + + + + + + Fix possible mishandling of expanded arrays in domain check + constraints and CASE execution (Tom Lane) + + + + It was possible for a PL/pgSQL function invoked in these contexts to + modify or even delete an array value that needs to be preserved for + additional operations. + + + + + + + Fix nested uses of PL/pgSQL functions in contexts such as domain + check constraints evaluated during assignment to a PL/pgSQL variable + (Tom Lane) + + + + + + + Ensure that the Python exception objects we create for PL/Python are + properly reference-counted (Rafa de la Torre, Tom Lane) + + + + This avoids failures if the objects are used after a Python garbage + collection cycle has occurred. + + + + + + + Fix PL/Tcl to support triggers on tables that have .tupno + as a column name (Tom Lane) + + + + This matches the (previously undocumented) behavior of + PL/Tcl's spi_exec and spi_execp commands, + namely that a magic .tupno column is inserted only if + there isn't a real column named that. + + + + + + + Allow DOS-style line endings in ~/.pgpass files, + even on Unix (Vik Fearing) + + + + This change simplifies use of the same password file across Unix and + Windows machines. + + + + + + + Fix one-byte buffer overrun if ecpg is given a file + name that ends with a dot (Takayuki Tsunakawa) + + + + + + + Fix incorrect error reporting for duplicate data + in psql's \crosstabview (Tom Lane) + + + + psql sometimes quoted the wrong row and/or column + values when complaining about multiple entries for the same crosstab + cell. + + + + + + + Fix psql's tab completion for ALTER DEFAULT + PRIVILEGES (Gilles Darold, Stephen Frost) + + + + + + + Fix psql's tab completion for ALTER TABLE t + ALTER c DROP ... (Kyotaro Horiguchi) + + + + + + + In psql, treat an empty or all-blank setting of + the PAGER environment variable as meaning no + pager (Tom Lane) + + + + Previously, such a setting caused output intended for the pager to + vanish entirely. + + + + + + + Improve contrib/dblink's reporting of + low-level libpq errors, such as out-of-memory + (Joe Conway) + + + + + + + Teach contrib/dblink to ignore irrelevant server options + when it uses a contrib/postgres_fdw foreign server as + the source of connection options (Corey Huinker) + + + + Previously, if the foreign server object had options that were not + also libpq connection options, an error occurred. + + + + + + + Fix portability problems in contrib/pageinspect's + functions for GIN indexes (Peter Eisentraut, Tom Lane) + + + + + + + Fix possible miss of socket read events while waiting on Windows + (Amit Kapila) + + + + This error was harmless for most uses, but it is known to cause hangs + when trying to use the pldebugger extension. + + + + + + + On Windows, ensure that environment variable changes are propagated + to DLLs built with debug options (Christian Ullrich) + + + + + + + Sync our copy of the timezone library with IANA release tzcode2016j + (Tom Lane) + + + + This fixes various issues, most notably that timezone data + installation failed if the target directory didn't support hard + links. + + + + + + + Update time zone data files to tzdata release 2016j + for DST law changes in northern Cyprus (adding a new zone + Asia/Famagusta), Russia (adding a new zone Europe/Saratov), Tonga, + and Antarctica/Casey. + Historical corrections for Italy, Kazakhstan, Malta, and Palestine. + Switch to preferring numeric zone abbreviations for Tonga. + + + + + + + + + + Release 9.6.1 + + + 发布日期: + 2016-10-27 + + + + 此版本包含自 9.6.0 以来的多种修复。 + 有关 9.6 大版本中新特性的信息,参见 + 。 + + + + 迁移到版本 9.6.1 + + + 对于运行 9.6.X 的用户,不需要转储/恢复。 + + + + However, if your installation has been affected by the bugs described in + the first two changelog entries below, then after updating you may need + to take action to repair corrupted free space maps and/or visibility + maps. + + + + + 变更 + + + + + + + Fix WAL-logging of truncation of relation free space maps and + visibility maps (Pavan Deolasee, Heikki Linnakangas) + + + + It was possible for these files to not be correctly restored during + crash recovery, or to be written incorrectly on a standby server. + Bogus entries in a free space map could lead to attempts to access + pages that have been truncated away from the relation itself, typically + producing errors like could not read block XXX: + read only 0 of 8192 bytes. Checksum failures in the + visibility map are also possible, if checksumming is enabled. + + + + Procedures for determining whether there is a problem and repairing it + if so are discussed at + . + + + + + + + Fix possible data corruption when pg_upgrade rewrites + a relation visibility map into 9.6 format (Tom Lane) + + + + On big-endian machines, bytes of the new visibility map were written + in the wrong order, leading to a completely incorrect map. On + Windows, the old map was read using text mode, leading to incorrect + results if the map happened to contain consecutive bytes that matched + a carriage return/line feed sequence. The latter error would almost + always lead to a pg_upgrade failure due to the map + file appearing to be the wrong length. + + + + If you are using a big-endian machine (many non-Intel architectures + are big-endian) and have used pg_upgrade to upgrade + from a pre-9.6 release, you should assume that all visibility maps are + incorrect and need to be regenerated. It is sufficient to truncate + each relation's visibility map + with contrib/pg_visibility's + pg_truncate_visibility_map() function. + For more information see + . + + + + + + + Don't throw serialization errors for self-conflicting insertions + in INSERT ... ON CONFLICT (Thomas Munro, Peter Geoghegan) + + + + + + + Fix use-after-free hazard in execution of aggregate functions + using DISTINCT (Peter Geoghegan) + + + + This could lead to a crash or incorrect query results. + + + + + + + Fix incorrect handling of polymorphic aggregates used as window + functions (Tom Lane) + + + + The aggregate's transition function was told that its first argument + and result were of the aggregate's output type, rather than the + state type. This led to errors or crashes with + polymorphic transition functions. + + + + + + + Fix COPY with a column name list from a table that has + row-level security enabled (Adam Brightwell) + + + + + + + Fix EXPLAIN to emit valid XML when + is on (Markus Winand) + + + + Previously the XML output-format option produced syntactically invalid + tags such as <I/O-Read-Time>. That is now + rendered as <I-O-Read-Time>. + + + + + + + Fix statistics update for TRUNCATE in a prepared + transaction (Stas Kelvich) + + + + + + + Fix bugs in merging inherited CHECK constraints while + creating or altering a table (Tom Lane, Amit Langote) + + + + Allow identical CHECK constraints to be added to a parent + and child table in either order. Prevent merging of a valid + constraint from the parent table with a NOT VALID + constraint on the child. Likewise, prevent merging of a NO + INHERIT child constraint with an inherited constraint. + + + + + + + Show a sensible value + in pg_settings.unit + for min_wal_size and max_wal_size (Tom Lane) + + + + + + + Fix replacement of array elements in jsonb_set() + (Tom Lane) + + + + If the target is an existing JSON array element, it got deleted + instead of being replaced with a new value. + + + + + + + Avoid very-low-probability data corruption due to testing tuple + visibility without holding buffer lock (Thomas Munro, Peter Geoghegan, + Tom Lane) + + + + + + + Preserve commit timestamps across server restart + (Julien Rouhaud, Craig Ringer) + + + + With turned on, old + commit timestamps became inaccessible after a clean server restart. + + + + + + + Fix logical WAL decoding to work properly when a subtransaction's WAL + output is large enough to spill to disk (Andres Freund) + + + + + + + Fix dangling-pointer problem in logical WAL decoding (Stas Kelvich) + + + + + + + Round shared-memory allocation request to a multiple of the actual + huge page size when attempting to use huge pages on Linux (Tom Lane) + + + + This avoids possible failures during munmap() on systems + with atypical default huge page sizes. Except in crash-recovery + cases, there were no ill effects other than a log message. + + + + + + + Don't try to share SSL contexts across multiple connections + in libpq (Heikki Linnakangas) + + + + This led to assorted corner-case bugs, particularly when trying to use + different SSL parameters for different connections. + + + + + + + Avoid corner-case memory leak in libpq (Tom Lane) + + + + The reported problem involved leaking an error report + during PQreset(), but there might be related cases. + + + + + + + In pg_upgrade, check library loadability in name order + (Tom Lane) + + + + This is a workaround to deal with cross-extension dependencies from + language transform modules to their base language and data type + modules. + + + + + + + Fix pg_upgrade to work correctly for extensions + containing index access methods (Tom Lane) + + + + To allow this, the server has been extended to support ALTER + EXTENSION ADD/DROP ACCESS METHOD. That functionality should have + been included in the original patch to support dynamic creation of + access methods, but it was overlooked. + + + + + + + Improve error reporting in pg_upgrade's file + copying/linking/rewriting steps (Tom Lane, Álvaro Herrera) + + + + + + + Fix pg_dump to work against pre-7.4 servers + (Amit Langote, Tom Lane) + + + + + + + Disallow specifying both + + + + + + Make pg_rewind turn off synchronous_commit + in its session on the source server (Michael Banck, Michael Paquier) + + + + This allows pg_rewind to work even when the source + server is using synchronous replication that is not working for some + reason. + + + + + + + In pg_xlogdump, retry opening new WAL segments when + using + + + This allows for a possible delay in the server's creation of the next + segment. + + + + + + + Fix contrib/pg_visibility to report the correct TID for + a corrupt tuple that has been the subject of a rolled-back update + (Tom Lane) + + + + + + + Fix makefile dependencies so that parallel make + of PL/Python by itself will succeed reliably + (Pavel Raiskup) + + + + + + + Update time zone data files to tzdata release 2016h + for DST law changes in Palestine and Turkey, plus historical + corrections for Turkey and some regions of Russia. + Switch to numeric abbreviations for some time zones in Antarctica, + the former Soviet Union, and Sri Lanka. + + + + The IANA time zone database previously provided textual abbreviations + for all time zones, sometimes making up abbreviations that have little + or no currency among the local population. They are in process of + reversing that policy in favor of using numeric UTC offsets in zones + where there is no evidence of real-world use of an English + abbreviation. At least for the time being, PostgreSQL + will continue to accept such removed abbreviations for timestamp input. + But they will not be shown in the pg_timezone_names + view nor used for output. + + + + In this update, AMT is no longer shown as being in use to + mean Armenia Time. Therefore, we have changed the Default + abbreviation set to interpret it as Amazon Time, thus UTC-4 not UTC+4. + + + + + + + + + + Release 9.6 + + + 发布日期: + 2016-09-29 + + + + Overview + + + Major enhancements in PostgreSQL 9.6 include: + + + + + + + + + Parallel execution of sequential scans, joins and aggregates + + + + + + Avoid scanning pages unnecessarily during vacuum freeze operations + + + + + + Synchronous replication now allows multiple standby servers for + increased reliability + + + + + + Full-text search can now search for phrases (multiple adjacent words) + + + + + + postgres_fdw now supports remote joins, sorts, + UPDATEs, and DELETEs + + + + + + Substantial performance improvements, especially in the area of + scalability on multi-CPU-socket servers + + + + + + + The above items are explained in more detail in the sections below. + + + + + + + 迁移到版本 9.6 + + + 希望从任何之前的版本迁移数据的用户,需要使用 + 进行转储/恢复,或使用。 + + + + Version 9.6 contains a number of changes that may affect compatibility + with previous releases. Observe the following incompatibilities: + + + + + + + + Improve the pg_stat_activity + view's information about what a process is waiting for (Amit + Kapila, Ildus Kurbangaliev) + + + + Historically a process has only been shown as waiting if it was + waiting for a heavyweight lock. Now waits for lightweight locks + and buffer pins are also shown in pg_stat_activity. + Also, the type of lock being waited for is now visible. + These changes replace the waiting column with + wait_event_type and wait_event. + + + + + + + In to_char(), + do not count a minus sign (when needed) as part of the field + width for time-related fields (Bruce Momjian) + + + + For example, to_char('-4 years'::interval, 'YY') + now returns -04, rather than -4. + + + + + + + Make extract() behave + more reasonably with infinite inputs (Vitaly Burovoy) + + + + Historically the extract() function just returned + zero given an infinite timestamp, regardless of the given + field name. Make it return infinity + or -infinity as appropriate when the + requested field is one that is monotonically increasing (e.g, + year, epoch), or NULL when + it is not (e.g., day, hour). Also, + throw the expected error for bad field names. + + + + + + + Remove PL/pgSQL's feature that suppressed the + innermost line of CONTEXT for messages emitted by + RAISE commands (Pavel Stehule) + + + + This ancient backwards-compatibility hack was agreed to have + outlived its usefulness. + + + + + + + Fix the default text search parser to allow leading digits + in email and host tokens (Artur Zakirov) + + + + In most cases this will result in few changes in the parsing of + text. But if you have data where such addresses occur frequently, + it may be worth rebuilding dependent tsvector columns + and indexes so that addresses of this form will be found properly + by text searches. + + + + + + + Extend contrib/unaccent's + standard unaccent.rules file to handle all diacritics + known to Unicode, and to expand ligatures correctly (Thomas Munro, + Léonard Benedetti) + + + + The previous version neglected to convert some less-common letters + with diacritic marks. Also, ligatures are now expanded into + separate letters. Installations that use this rules file may wish + to rebuild tsvector columns and indexes that depend on the + result. + + + + + + + Remove the long-deprecated + CREATEUSER/NOCREATEUSER options from + CREATE ROLE and allied commands (Tom Lane) + + + + CREATEUSER actually meant SUPERUSER, + for ancient backwards-compatibility reasons. This has been a + constant source of confusion for people who (reasonably) expect + it to mean CREATEROLE. It has been deprecated for + ten years now, so fix the problem by removing it. + + + + + + + Treat role names beginning with pg_ as reserved + (Stephen Frost) + + + + User creation of such role names is now disallowed. This prevents + conflicts with built-in roles created by initdb. + + + + + + + Change a column name in the + information_schema.routines + view from result_cast_character_set_name + to result_cast_char_set_name (Clément + Prévost) + + + + The SQL:2011 standard specifies the longer name, but that appears + to be a mistake, because adjacent column names use the shorter + style, as do other information_schema views. + + + + + + + psql's option no longer implies + + (Pavel Stehule, Catalin Iacob) + + + + Write (or its + abbreviation ) explicitly to obtain the old + behavior. Scripts so modified will still work with old + versions of psql. + + + + + + + Improve pg_restore's option to + match all types of relations, not only plain tables (Craig Ringer) + + + + + + + Change the display format used for NextXID in + pg_controldata and related places (Joe Conway, + Bruce Momjian) + + + + Display epoch-and-transaction-ID values in the format + number:number. + The previous format + number/number was + confusingly similar to that used for LSNs. + + + + + + + Update extension functions to be marked parallel-safe where + appropriate (Andreas Karlsson) + + + + Many of the standard extensions have been updated to allow their + functions to be executed within parallel query worker processes. + These changes will not take effect in + databases pg_upgrade'd from prior versions unless + you apply ALTER EXTENSION UPDATE to each such extension + (in each database of a cluster). + + + + + + + + + 变更 + + + Below you will find a detailed account of the changes between + PostgreSQL 9.6 and the previous major + release. + + + + Server + + + Parallel Queries + + + + + + + Parallel queries (Robert Haas, Amit Kapila, David Rowley, + many others) + + + + With 9.6, PostgreSQL introduces initial support + for parallel execution of large queries. Only strictly read-only + queries where the driving table is accessed via a sequential scan + can be parallelized. Hash joins and nested loops can be performed + in parallel, as can aggregation (for supported aggregates). + Much remains to be done, but this is already a useful set of + features. + + + + Parallel query execution is not (yet) enabled by default. + To allow it, set the new configuration + parameter to a + value larger than zero. Additional control over use of parallelism + is available through other new configuration parameters + , + , , and . + + + + + + + Provide infrastructure for marking the parallel-safety status of + functions (Robert Haas, Amit Kapila) + + + + + + + + + Indexes + + + + + + + Allow GIN index builds to + make effective use of + settings larger than 1 GB (Robert Abraham, Teodor Sigaev) + + + + + + + Add pages deleted from a GIN index's pending list to the free space + map immediately + (Jeff Janes, Teodor Sigaev) + + + + This reduces bloat if the table is not vacuumed often. + + + + + + + Add gin_clean_pending_list() + function to allow manual invocation of pending-list cleanup for a + GIN index (Jeff Janes) + + + + Formerly, such cleanup happened only as a byproduct of vacuuming or + analyzing the parent table. + + + + + + + Improve handling of dead index tuples in GiST indexes (Anastasia Lubennikova) + + + + Dead index tuples are now marked as such when an index scan notices + that the corresponding heap tuple is dead. When inserting tuples, + marked-dead tuples will be removed if needed to make space on + the page. + + + + + + + Add an SP-GiST operator class for + type box (Alexander Lebedev) + + + + + + + + + Sorting + + + + + + + Improve sorting performance by using quicksort, not replacement + selection sort, when performing external sort steps (Peter + Geoghegan) + + + + The new approach makes better use of the CPU cache + for typical cache sizes and data volumes. Where necessary, + the behavior can be adjusted via the new configuration parameter + . + + + + + + + Speed up text sorts where the same string occurs multiple times + (Peter Geoghegan) + + + + + + + Speed up sorting of uuid, bytea, and + char(n) fields by using abbreviated keys + (Peter Geoghegan) + + + + Support for abbreviated keys has also been + added to the non-default operator classes text_pattern_ops, + varchar_pattern_ops, and + bpchar_pattern_ops. Processing of ordered-set + aggregates can also now exploit abbreviated keys. + + + + + + + Speed up CREATE INDEX CONCURRENTLY by treating + TIDs as 64-bit integers during sorting (Peter + Geoghegan) + + + + + + + + + Locking + + + + + + + Reduce contention for the ProcArrayLock (Amit Kapila, + Robert Haas) + + + + + + + Improve performance by moving buffer content locks into the buffer + descriptors (Andres Freund, Simon Riggs) + + + + + + + Replace shared-buffer header spinlocks with atomic operations to + improve scalability (Alexander Korotkov, Andres Freund) + + + + + + + Use atomic operations, rather than a spinlock, to protect an + LWLock's wait queue (Andres Freund) + + + + + + + Partition the shared hash table freelist to reduce contention on + multi-CPU-socket servers (Aleksander Alekseev) + + + + + + + Reduce interlocking on standby servers during the replay of btree + index vacuuming operations (Simon Riggs) + + + + This change avoids substantial replication delays that sometimes + occurred while replaying such operations. + + + + + + + + + Optimizer Statistics + + + + + + + Improve ANALYZE's estimates for columns with many nulls + (Tomas Vondra, Alex Shulgin) + + + + Previously ANALYZE tended to underestimate the number + of non-NULL distinct values in a column with many + NULLs, and was also inaccurate in computing the + most-common values. + + + + + + + Improve planner's estimate of the number of distinct values in + a query result (Tomas Vondra) + + + + + + + Use foreign key relationships to infer selectivity for join + predicates (Tomas Vondra, David Rowley) + + + + If a table t has a foreign key restriction, say + (a,b) REFERENCES r (x,y), then a WHERE + condition such as t.a = r.x AND t.b = r.y cannot + select more than one r row per t row. + The planner formerly considered these AND conditions + to be independent and would often drastically misestimate + selectivity as a result. Now it compares the WHERE + conditions to applicable foreign key constraints and produces + better estimates. + + + + + + + + + <command>VACUUM</> + + + + + + + Avoid re-vacuuming pages containing only frozen tuples (Masahiko + Sawada, Robert Haas, Andres Freund) + + + + Formerly, anti-wraparound vacuum had to visit every page of + a table, even pages where there was nothing to do. Now, pages + containing only already-frozen tuples are identified in the table's + visibility map, and can be skipped by vacuum even when doing + transaction wraparound prevention. This should greatly reduce the + cost of maintaining large tables containing mostly-unchanging data. + + + + If necessary, vacuum can be forced to process all-frozen + pages using the new DISABLE_PAGE_SKIPPING option. + Normally this should never be needed, but it might help in + recovering from visibility-map corruption. + + + + + + + Avoid useless heap-truncation attempts during VACUUM + (Jeff Janes, Tom Lane) + + + + This change avoids taking an exclusive table lock in some cases + where no truncation is possible. The main benefit comes from + avoiding unnecessary query cancellations on standby servers. + + + + + + + + + General Performance + + + + + + + Allow old MVCC snapshots to be invalidated after a + configurable timeout (Kevin Grittner) + + + + Normally, deleted tuples cannot be physically removed by + vacuuming until the last transaction that could see + them is gone. A transaction that stays open for a long + time can thus cause considerable table bloat because + space cannot be recycled. This feature allows setting + a time-based limit, via the new configuration parameter + , on how long an + MVCC snapshot is guaranteed to be valid. After that, + dead tuples are candidates for removal. A transaction using an + outdated snapshot will get an error if it attempts to read a page + that potentially could have contained such data. + + + + + + + Ignore GROUP BY columns that are + functionally dependent on other columns (David Rowley) + + + + If a GROUP BY clause includes all columns of a + non-deferred primary key, as well as other columns of the same + table, those other columns are redundant and can be dropped + from the grouping. This saves computation in many common cases. + + + + + + + Allow use of an index-only + scan on a partial index when the index's WHERE + clause references columns that are not indexed (Tomas Vondra, + Kyotaro Horiguchi) + + + + For example, an index defined by CREATE INDEX tidx_partial + ON t(b) WHERE a > 0 can now be used for an index-only scan by + a query that specifies WHERE a > 0 and does not + otherwise use a. Previously this was disallowed + because a is not listed as an index column. + + + + + + + + Perform checkpoint writes in sorted order (Fabien Coelho, + Andres Freund) + + + + Previously, checkpoints wrote out dirty pages in whatever order + they happen to appear in shared buffers, which usually is nearly + random. That performs poorly, especially on rotating media. + This change causes checkpoint-driven writes to be done in order + by file and block number, and to be balanced across tablespaces. + + + + + + + Where feasible, trigger kernel writeback after a configurable + number of writes, to prevent accumulation of dirty data in kernel + disk buffers (Fabien Coelho, Andres Freund) + + + + PostgreSQL writes data to the kernel's disk cache, + from where it will be flushed to physical storage in due time. + Many operating systems are not smart about managing this and allow + large amounts of dirty data to accumulate before deciding to flush + it all at once, causing long delays for new I/O requests until the + flushing finishes. + This change attempts to alleviate this problem by explicitly + requesting data flushes after a configurable interval. + + + + On Linux, sync_file_range() is used for this purpose, + and the feature is on by default on Linux because that function has + few downsides. This flushing capability is also available on other + platforms if they have msync() + or posix_fadvise(), but those interfaces have some + undesirable side-effects so the feature is disabled by default on + non-Linux platforms. + + + + The new configuration parameters , , , and control this behavior. + + + + + + + Improve aggregate-function performance by sharing calculations + across multiple aggregates if they have the same arguments and + transition functions (David Rowley) + + + + For example, SELECT AVG(x), VARIANCE(x) FROM tab can use + a single per-row computation for both aggregates. + + + + + + + Speed up visibility tests for recently-created tuples by checking + the current transaction's snapshot, not pg_clog, to + decide if the source transaction should be considered committed + (Jeff Janes, Tom Lane) + + + + + + + Allow tuple hint bits to be set sooner than before (Andres Freund) + + + + + + + Improve performance of short-lived prepared transactions (Stas + Kelvich, Simon Riggs, Pavan Deolasee) + + + + Two-phase commit information is now written only to WAL + during PREPARE TRANSACTION, and will be read back from + WAL during COMMIT PREPARED if that happens + soon thereafter. A separate state file is created only if the + pending transaction does not get committed or aborted by the time + of the next checkpoint. + + + + + + + Improve performance of memory context destruction (Jan Wieck) + + + + + + + Improve performance of resource owners with many tracked objects + (Aleksander Alekseev) + + + + + + + Improve speed of the output functions for timestamp, + time, and date data types (David Rowley, + Andres Freund) + + + + + + + Avoid some unnecessary cancellations of hot-standby queries + during replay of actions that take AccessExclusive + locks (Jeff Janes) + + + + + + + Extend relations multiple blocks at a time when there is contention + for the relation's extension lock (Dilip Kumar) + + + + This improves scalability by decreasing contention. + + + + + + + Increase the number of clog buffers for better scalability (Amit + Kapila, Andres Freund) + + + + + + + Speed up expression evaluation in PL/pgSQL by + keeping ParamListInfo entries for simple variables + valid at all times (Tom Lane) + + + + + + + Avoid reducing the SO_SNDBUF setting below its default + on recent Windows versions (Chen Huajun) + + + + + + + Disable by default on + Windows (Takayuki Tsunakawa) + + + + The overhead of updating the process title is much larger on Windows + than most other platforms, and it is also less useful to do it since + most Windows users do not have tools that can display process titles. + + + + + + + + + Monitoring + + + + + + + Add pg_stat_progress_vacuum + system view to provide progress reporting for VACUUM + operations (Amit Langote, Robert Haas, Vinayak Pokale, Rahila Syed) + + + + + + + Add pg_control_system(), + pg_control_checkpoint(), + pg_control_recovery(), and + pg_control_init() functions to expose fields of + pg_control to SQL (Joe Conway, Michael + Paquier) + + + + + + + Add pg_config + system view (Joe Conway) + + + + This view exposes the same information available from + the pg_config command-line utility, + namely assorted compile-time configuration information for + PostgreSQL. + + + + + + + Add a confirmed_flush_lsn column to the pg_replication_slots + system view (Marko Tiikkaja) + + + + + + + Add pg_stat_wal_receiver + system view to provide information about the state of a hot-standby + server's WAL receiver process (Michael Paquier) + + + + + + + Add pg_blocking_pids() + function to reliably identify which sessions block which others + (Tom Lane) + + + + This function returns an array of the process IDs of any + sessions that are blocking the session with the given process ID. + Historically users have obtained such information using a self-join + on the pg_locks view. However, it is unreasonably + tedious to do it that way with any modicum of correctness, and + the addition of parallel queries has made the old approach entirely + impractical, since locks might be held or awaited by child worker + processes rather than the session's main process. + + + + + + + Add function pg_current_xlog_flush_location() + to expose the current transaction log flush location (Tomas Vondra) + + + + + + + Add function pg_notification_queue_usage() + to report how full the NOTIFY queue is (Brendan Jurd) + + + + + + + Limit the verbosity of memory context statistics dumps (Tom Lane) + + + + The memory usage dump that is output to the postmaster log during an + out-of-memory failure now summarizes statistics when there are a + large number of memory contexts, rather than possibly generating + a very large report. There is also a grand total + summary line now. + + + + + + + + + <acronym>Authentication</> + + + + + + + Add a BSD authentication + method to allow use of + the BSD Authentication service for + PostgreSQL client authentication (Marisa Emerson) + + + + BSD Authentication is currently only available on OpenBSD. + + + + + + + When using PAM + authentication, provide the client IP address or host name + to PAM modules via the PAM_RHOST item + (Grzegorz Sampolski) + + + + + + + Provide detail in the postmaster log for more types of password + authentication failure (Tom Lane) + + + + All ordinarily-reachable password authentication failure cases + should now provide specific DETAIL fields in the log. + + + + + + + Support RADIUS passwords + up to 128 characters long (Marko Tiikkaja) + + + + + + + Add new SSPI + authentication parameters + compat_realm and upn_username to control + whether NetBIOS or Kerberos + realm names and user names are used during SSPI + authentication (Christian Ullrich) + + + + + + + + + Server Configuration + + + + + + + Allow sessions to be terminated automatically if they are in + idle-in-transaction state for too long (Vik Fearing) + + + + This behavior is controlled by the new configuration parameter + . It can + be useful to prevent forgotten transactions from holding locks + or preventing vacuum cleanup for too long. + + + + + + + Raise the maximum allowed value + of to 24 hours (Simon Riggs) + + + + + + + Allow effective_io_concurrency to be set per-tablespace + to support cases where different tablespaces have different I/O + characteristics (Julien Rouhaud) + + + + + + + Add option %n to + print the current time in Unix epoch form, with milliseconds (Tomas + Vondra, Jeff Davis) + + + + + + + Add and configuration parameters + to provide more control over the message format when logging to + syslog (Peter Eisentraut) + + + + + + + Merge the archive and hot_standby values + of the configuration parameter + into a single new value replica (Peter Eisentraut) + + + + Making a distinction between these settings is no longer useful, + and merging them is a step towards a planned future simplification + of replication setup. The old names are still accepted but are + converted to replica internally. + + + + + + + Add configure option + + + This allows the use of systemd service units of + type notify, which greatly simplifies the management + of PostgreSQL under systemd. + + + + + + + Allow the server's SSL key file to have group read + access if it is owned by root (Christoph Berg) + + + + Formerly, we insisted the key file be owned by the + user running the PostgreSQL server, but + that is inconvenient on some systems (such as Debian) that are configured to manage + certificates centrally. Therefore, allow the case where the key + file is owned by root and has group read access. + It is up to the operating system administrator to ensure that + the group does not include any untrusted users. + + + + + + + + + Reliability + + + + + + + Force backends to exit if the postmaster dies (Rajeev Rastogi, + Robert Haas) + + + + Under normal circumstances the postmaster should always outlive + its child processes. If for some reason the postmaster dies, + force backend sessions to exit with an error. Formerly, existing + backends would continue to run until their clients disconnect, + but that is unsafe and inefficient. It also prevents a new + postmaster from being started until the last old backend has + exited. Backends will detect postmaster death when waiting for + client I/O, so the exit will not be instantaneous, but it should + happen no later than the end of the current query. + + + + + + + Check for serializability conflicts before reporting + constraint-violation failures (Thomas Munro) + + + + When using serializable transaction isolation, it is desirable + that any error due to concurrent transactions should manifest + as a serialization failure, thereby cueing the application that + a retry might succeed. Unfortunately, this does not reliably + happen for duplicate-key failures caused by concurrent insertions. + This change ensures that such an error will be reported as a + serialization error if the application explicitly checked for + the presence of a conflicting key (and did not find it) earlier + in the transaction. + + + + + + + Ensure that invalidation messages are recorded in WAL + even when issued by a transaction that has no XID + assigned (Andres Freund) + + + + This fixes some corner cases in which transactions on standby + servers failed to notice changes, such as new indexes. + + + + + + + Prevent multiple processes from trying to clean a GIN + index's pending list concurrently (Teodor Sigaev, Jeff Janes) + + + + This had been intentionally allowed, but it causes race conditions + that can result in vacuum missing index entries it needs to delete. + + + + + + + + + + + Replication and Recovery + + + + + + + Allow synchronous replication to support multiple simultaneous + synchronous standby servers, not just one (Masahiko Sawada, + Beena Emerson, Michael Paquier, Fujii Masao, Kyotaro Horiguchi) + + + + The number of standby servers that must acknowledge a commit + before it is considered complete is now configurable as part of + the parameter. + + + + + + + Add new setting remote_apply for configuration + parameter (Thomas Munro) + + + + In this mode, the master waits for the transaction to be + applied on the standby server, not just written + to disk. That means that you can count on a transaction started + on the standby to see all commits previously acknowledged by + the master. + + + + + + + Add a feature to the replication + protocol, and a corresponding option to pg_create_physical_replication_slot(), + to allow reserving WAL immediately when creating a + replication slot (Gurjeet Singh, Michael Paquier) + + + + This allows the creation of a replication slot to guarantee + that all the WAL needed for a base backup will be + available. + + + + + + + Add a option to + pg_basebackup + (Peter Eisentraut) + + + + This lets pg_basebackup use a replication + slot defined for WAL streaming. After the base + backup completes, selecting the same slot for regular streaming + replication allows seamless startup of the new standby server. + + + + + + + Extend pg_start_backup() + and pg_stop_backup() to support non-exclusive backups + (Magnus Hagander) + + + + + + + + + Queries + + + + + + + Allow functions that return sets of tuples to return simple + NULLs (Andrew Gierth, Tom Lane) + + + + In the context of SELECT FROM function(...), a function + that returned a set of composite values was previously not allowed + to return a plain NULL value as part of the set. + Now that is allowed and interpreted as a row of NULLs. + This avoids corner-case errors with, for example, unnesting an + array of composite values. + + + + + + + Fully support array subscripts and field selections in the + target column list of an INSERT with multiple + VALUES rows (Tom Lane) + + + + Previously, such cases failed if the same target column was + mentioned more than once, e.g., INSERT INTO tab (x[1], + x[2]) VALUES (...). + + + + + + + When appropriate, postpone evaluation of SELECT + output expressions until after an ORDER BY sort + (Konstantin Knizhnik) + + + + This change ensures that volatile or expensive functions in the + output list are executed in the order suggested by ORDER + BY, and that they are not evaluated more times than required + when there is a LIMIT clause. Previously, these + properties held if the ordering was performed by an index scan or + pre-merge-join sort, but not if it was performed by a top-level + sort. + + + + + + + Widen counters recording the number of tuples processed to 64 bits + (Andreas Scherbaum) + + + + This change allows command tags, e.g., SELECT, to + correctly report tuple counts larger than 4 billion. This also + applies to PL/pgSQL's GET DIAGNOSTICS ... ROW_COUNT + command. + + + + + + + Avoid doing encoding conversions by converting through the + MULE_INTERNAL encoding (Tom Lane) + + + + Previously, many conversions for Cyrillic and Central + European single-byte encodings were done by converting to a + related MULE_INTERNAL coding scheme and then to the + destination encoding. Aside from being inefficient, this meant + that when the conversion encountered an untranslatable character, + the error message would confusingly complain about failure to + convert to or from MULE_INTERNAL, rather than the + user-visible encoding. + + + + + + + Consider performing joins of foreign tables remotely only when the + tables will be accessed under the same role ID (Shigeru Hanada, + Ashutosh Bapat, Etsuro Fujita) + + + + Previously, the foreign join pushdown infrastructure left the + question of security entirely up to individual foreign data + wrappers, but that made it too easy for an FDW to + inadvertently create subtle security holes. So, make it the core + code's job to determine which role ID will access each table, + and do not attempt join pushdown unless the role is the same for + all relevant relations. + + + + + + + + + Utility Commands + + + + + + + Allow COPY to copy the output of an + INSERT/UPDATE/DELETE + ... RETURNING query (Marko Tiikkaja) + + + + Previously, an intermediate CTE had to be written to + get this result. + + + + + + + Introduce ALTER object DEPENDS ON + EXTENSION (Abhijit Menon-Sen) + + + + This command allows a database object to be marked as depending + on an extension, so that it will be dropped automatically if + the extension is dropped (without needing CASCADE). + However, the object is not part of the extension, and thus will + be dumped separately by pg_dump. + + + + + + + Make ALTER object SET SCHEMA do nothing + when the object is already in the requested schema, rather than + throwing an error as it historically has for most object types + (Marti Raudsepp) + + + + + + + Add options to ALTER OPERATOR to allow changing + the selectivity functions associated with an existing operator + (Yury Zhuravlev) + + + + + + + Add an + + + + + + Reduce the lock strength needed by ALTER TABLE + when setting fillfactor and autovacuum-related relation options + (Fabrízio de Royes Mello, Simon Riggs) + + + + + + + Introduce CREATE + ACCESS METHOD to allow extensions to create index access + methods (Alexander Korotkov, Petr Jelínek) + + + + + + + Add a CASCADE option to CREATE + EXTENSION to automatically create any extensions the + requested one depends on (Petr Jelínek) + + + + + + + Make CREATE TABLE ... LIKE include an OID + column if any source table has one (Bruce Momjian) + + + + + + + If a CHECK constraint is declared NOT VALID + in a table creation command, automatically mark it as valid + (Amit Langote, Amul Sul) + + + + This is safe because the table has no existing rows. This matches + the longstanding behavior of FOREIGN KEY constraints. + + + + + + + Fix DROP OPERATOR to clear + pg_operator.oprcom and + pg_operator.oprnegate links to + the dropped operator (Roma Sokolov) + + + + Formerly such links were left as-is, which could pose a problem + in the somewhat unlikely event that the dropped operator's + OID was reused for another operator. + + + + + + + Do not show the same subplan twice in EXPLAIN output + (Tom Lane) + + + + In certain cases, typically involving SubPlan nodes in index + conditions, EXPLAIN would print data for the same + subplan twice. + + + + + + + Disallow creation of indexes on system columns, except for + OID columns (David Rowley) + + + + Such indexes were never considered supported, and would very + possibly misbehave since the system might change the system-column + fields of a tuple without updating indexes. However, previously + there were no error checks to prevent them from being created. + + + + + + + + + Permissions Management + + + + + + + Use the privilege system to manage access to sensitive functions + (Stephen Frost) + + + + Formerly, many security-sensitive functions contained hard-wired + checks that would throw an error if they were called by a + non-superuser. This forced the use of superuser roles for + some relatively pedestrian tasks. The hard-wired error checks + are now gone in favor of making initdb revoke the + default public EXECUTE privilege on these functions. + This allows installations to choose to grant usage of such + functions to trusted roles that do not need all superuser + privileges. + + + + + + + Create some built-in roles + that can be used to grant access to what were previously + superuser-only functions (Stephen Frost) + + + + Currently the only such role is pg_signal_backend, + but more are expected to be added in future. + + + + + + + + + Data Types + + + + + + + Improve full-text search to support + searching for phrases, that is, lexemes appearing adjacent to each + other in a specific order, or with a specified distance between + them (Teodor Sigaev, Oleg Bartunov, Dmitry Ivanov) + + + + A phrase-search query can be specified in tsquery + input using the new operators <-> and + <N>. The former means + that the lexemes before and after it must appear adjacent to + each other in that order. The latter means they must be exactly + N lexemes apart. + + + + + + + Allow omitting one or both boundaries in an array slice specifier, + e.g., array_col[3:] (Yury Zhuravlev) + + + + Omitted boundaries are taken as the upper or lower limit of the + corresponding array subscript. This allows simpler specification + for many common use-cases. + + + + + + + Be more careful about out-of-range dates and timestamps (Vitaly + Burovoy) + + + + This change prevents unexpected out-of-range errors for + timestamp with time zone values very close to the + implementation limits. Previously, the same value might + be accepted or not depending on the timezone setting, + meaning that a dump and reload could fail on a value that had been + accepted when presented. Now the limits are enforced according + to the equivalent UTC time, not local time, so as to + be independent of timezone. + + + + Also, PostgreSQL is now more careful to detect + overflow in operations that compute new date or timestamp values, + such as date + integer. + + + + + + + For geometric data types, make sure infinity and + NaN component values are treated consistently during + input and output (Tom Lane) + + + + Such values will now always print the same as they would in + a simple float8 column, and be accepted the same way + on input. Previously the behavior was platform-dependent. + + + + + + + Upgrade + the ispell + dictionary type to handle modern Hunspell files and + support more languages (Artur Zakirov) + + + + + + + Implement look-behind constraints + in regular expressions + (Tom Lane) + + + + A look-behind constraint is like a lookahead constraint in that it + consumes no text; but it checks for existence (or nonexistence) + of a match ending at the current point in the string, rather + than one starting at the current point. Similar features exist + in many other regular-expression engines. + + + + + + + In regular expressions, if an apparent three-digit octal escape + \nnn would exceed 377 (255 decimal), + assume it is a two-digit octal escape instead (Tom Lane) + + + + This makes the behavior match current Tcl releases. + + + + + + + Add transaction ID operators xid <> + xid and xid <> int4, + for consistency with the corresponding equality operators + (Michael Paquier) + + + + + + + + + Functions + + + + + + + 修复 numeric power() 在边界情况下精度丢失的问题 + (Dean Rasheed) + + + + + + + Add a scale(numeric) + function to extract the display scale of a numeric value + (Marko Tiikkaja) + + + + + + + Add trigonometric functions that work in degrees (Dean Rasheed) + + + + For example, sind() + measures its argument in degrees, whereas sin() + measures in radians. These functions go to some lengths to + deliver exact results for values where an exact result can be + expected, for instance sind(30) = 0.5. + + + + + + + Ensure that trigonometric functions handle infinity + and NaN inputs per the POSIX standard + (Dean Rasheed) + + + + The POSIX standard says that these functions should + return NaN for NaN input, and should throw + an error for out-of-range inputs including infinity. + Previously our behavior varied across platforms. + + + + + + + Make to_timestamp(float8) + convert float infinity to + timestamp infinity (Vitaly Burovoy) + + + + Formerly it just failed on an infinite input. + + + + + + + Add new functions for tsvector data (Stas Kelvich) + + + + The new functions are ts_delete(), + ts_filter(), unnest(), + tsvector_to_array(), array_to_tsvector(), + and a variant of setweight() that sets the weight + only for specified lexeme(s). + + + + + + + Allow ts_stat() + and tsvector_update_trigger() + to operate on values that are of types binary-compatible with the + expected argument type, not just exactly that type; for example + allow citext where text is expected (Teodor + Sigaev) + + + + + + + Add variadic functions num_nulls() + and num_nonnulls() that count the number of their + arguments that are null or non-null (Marko Tiikkaja) + + + + An example usage is CHECK(num_nonnulls(a,b,c) = 1) + which asserts that exactly one of a,b,c is not NULL. + These functions can also be used to count the number of null or + nonnull elements in an array. + + + + + + + Add function parse_ident() + to split a qualified, possibly quoted SQL identifier + into its parts (Pavel Stehule) + + + + + + + In to_number(), + interpret a V format code as dividing by 10 to the + power of the number of digits following V (Bruce + Momjian) + + + + This makes it operate in an inverse fashion to + to_char(). + + + + + + + Make the to_reg*() + functions accept type text not cstring + (Petr Korobeinikov) + + + + This avoids the need to write an explicit cast in most cases + where the argument is not a simple literal constant. + + + + + + + Add pg_size_bytes() + function to convert human-readable size strings to numbers (Pavel + Stehule, Vitaly Burovoy, Dean Rasheed) + + + + This function converts strings like those produced by + pg_size_pretty() into bytes. An example + usage is SELECT oid::regclass FROM pg_class WHERE + pg_total_relation_size(oid) > pg_size_bytes('10 GB'). + + + + + + + In pg_size_pretty(), + format negative numbers similarly to positive ones (Adrian + Vondendriesch) + + + + Previously, negative numbers were never abbreviated, just printed + in bytes. + + + + + + + Add an optional missing_ok argument to the current_setting() + function (David Christensen) + + + + This allows avoiding an error for an unrecognized parameter + name, instead returning a NULL. + + + + + + + Change various catalog-inspection functions to return + NULL for invalid input (Michael Paquier) + + + + pg_get_viewdef() + now returns NULL if given an invalid view OID, + and several similar functions likewise return NULL for + bad input. Previously, such cases usually led to cache + lookup failed errors, which are not meant to occur in + user-facing cases. + + + + + + + Fix pg_replication_origin_xact_reset() + to not have any arguments (Fujii Masao) + + + + The documentation said that it has no arguments, and the C code did + not expect any arguments, but the entry in pg_proc + mistakenly specified two arguments. + + + + + + + + + Server-Side Languages + + + + + + + In PL/pgSQL, detect mismatched + CONTINUE and EXIT statements while + compiling a function, rather than at execution time + (Jim Nasby) + + + + + + + Extend PL/Python's error-reporting and + message-reporting functions to allow specifying additional message + fields besides the primary error message (Pavel Stehule) + + + + + + + Allow PL/Python functions to call themselves recursively + via SPI, and fix the behavior when multiple + set-returning PL/Python functions are called within one query + (Alexey Grishchenko, Tom Lane) + + + + + + + Fix session-lifespan memory leaks in PL/Python (Heikki Linnakangas, + Haribabu Kommi, Tom Lane) + + + + + + + Modernize PL/Tcl to use Tcl's object + APIs instead of simple strings (Jim Nasby, Karl + Lehenbauer) + + + + This can improve performance substantially in some cases. + Note that PL/Tcl now requires Tcl 8.4 or later. + + + + + + + In PL/Tcl, make database-reported errors return + additional information in Tcl's errorCode global + variable (Jim Nasby, Tom Lane) + + + + This feature follows the Tcl convention for returning auxiliary + data about an error. + + + + + + + Fix PL/Tcl to perform encoding conversion between + the database encoding and UTF-8, which is what Tcl + expects (Tom Lane) + + + + Previously, strings were passed through without conversion, + leading to misbehavior with non-ASCII characters when + the database encoding was not UTF-8. + + + + + + + + + Client Interfaces + + + + + + + Add a nonlocalized version of + the severity field in + error and notice messages (Tom Lane) + + + + This change allows client code to determine severity of an error or + notice without having to worry about localized variants of the + severity strings. + + + + + + + Introduce a feature in libpq whereby the + CONTEXT field of messages can be suppressed, either + always or only for non-error messages (Pavel Stehule) + + + + The default behavior of PQerrorMessage() + is now to print CONTEXT + only for errors. The new function PQsetErrorContextVisibility() + can be used to adjust this. + + + + + + + Add support in libpq for regenerating an error + message with a different verbosity level (Alex Shulgin) + + + + This is done with the new function PQresultVerboseErrorMessage(). + This supports psql's new \errverbose + feature, and may be useful for other clients as well. + + + + + + + Improve libpq's PQhost() function to return + useful data for default Unix-socket connections (Tom Lane) + + + + Previously it would return NULL if no explicit host + specification had been given; now it returns the default socket + directory path. + + + + + + + Fix ecpg's lexer to handle line breaks within + comments starting on preprocessor directive lines (Michael Meskes) + + + + + + + + + Client Applications + + + + + + + Add a + + + This option causes the program to complain if there is no match + for a or option, rather + than silently doing nothing. + + + + + + + In pg_dump, dump locally-made changes of privilege + assignments for system objects (Stephen Frost) + + + + While it has always been possible for a superuser to change + the privilege assignments for built-in or extension-created + objects, such changes were formerly lost in a dump and reload. + Now, pg_dump recognizes and dumps such changes. + (This works only when dumping from a 9.6 or later server, however.) + + + + + + + Allow pg_dump to dump non-extension-owned objects + that are within an extension-owned schema + (Martín Marqués) + + + + Previously such objects were ignored because they were mistakenly + assumed to belong to the extension owning their schema. + + + + + + + In pg_dump output, include the table name in object + tags for object types that are only uniquely named per-table + (for example, triggers) (Peter Eisentraut) + + + + + + + <xref linkend="APP-PSQL"> + + + + + + + Support multiple and + command-line options (Pavel Stehule, Catalin Iacob) + + + + The specified operations are carried out in the order in which the + options are given, and then psql terminates. + + + + + + + Add a \crosstabview command that prints the results of + a query in a cross-tabulated display (Daniel Vérité) + + + + In the crosstab display, data values from one query result column + are placed in a grid whose column and row headers come from other + query result columns. + + + + + + + Add an \errverbose command that shows the last server + error at full verbosity (Alex Shulgin) + + + + This is useful after getting an unexpected error — you + no longer need to adjust the VERBOSITY variable and + recreate the failure in order to see error fields that are not + shown by default. + + + + + + + Add \ev and \sv commands for editing and + showing view definitions (Petr Korobeinikov) + + + + These are parallel to the existing \ef and + \sf commands for functions. + + + + + + + Add a \gexec command that executes a query and + re-submits the result(s) as new queries (Corey Huinker) + + + + + + + Allow \pset C string + to set the table title, for consistency with \C + string (Bruce Momjian) + + + + + + + In \pset expanded auto mode, do not use expanded + format for query results with only one column (Andreas Karlsson, + Robert Haas) + + + + + + + Improve the headers output by the \watch command + (Michael Paquier, Tom Lane) + + + + Include the \pset title string if one has + been set, and shorten the prefabricated part of the + header to be timestamp (every + Ns). Also, the timestamp format now + obeys psql's locale environment. + + + + + + + Improve tab-completion logic to consider the entire input query, + not only the current line (Tom Lane) + + + + Previously, breaking a command into multiple lines defeated any + tab completion rules that needed to see words on earlier lines. + + + + + + + Numerous minor improvements in tab-completion behavior (Peter + Eisentraut, Vik Fearing, Kevin Grittner, Kyotaro Horiguchi, Jeff + Janes, Andreas Karlsson, Fujii Masao, Thomas Munro, Masahiko + Sawada, Pavel Stehule) + + + + + + + Add a PROMPT option %p to insert the + process ID of the connected backend (Julien Rouhaud) + + + + + + + Introduce a feature whereby the CONTEXT field of + messages can be suppressed, either always or only for non-error + messages (Pavel Stehule) + + + + Printing CONTEXT only for errors is now the default + behavior. This can be changed by setting the special variable + SHOW_CONTEXT. + + + + + + + Make \df+ show function access privileges and + parallel-safety attributes (Michael Paquier) + + + + + + + + + <xref linkend="pgbench"> + + + + + + + SQL commands in pgbench scripts are now ended by + semicolons, not newlines (Kyotaro Horiguchi, Tom Lane) + + + + This change allows SQL commands in scripts to span multiple lines. + Existing custom scripts will need to be modified to add a semicolon + at the end of each line that does not have one already. (Doing so + does not break the script for use with older versions + of pgbench.) + + + + + + + Support floating-point arithmetic, as well as some built-in functions, in + expressions in backslash commands (Fabien Coelho) + + + + + + + Replace \setrandom with built-in functions (Fabien + Coelho) + + + + The new built-in functions include random(), + random_exponential(), and + random_gaussian(), which perform the same work as + \setrandom, but are easier to use since they can be + embedded in larger expressions. Since these additions have made + \setrandom obsolete, remove it. + + + + + + + Allow invocation of multiple copies of the built-in scripts, + not only custom scripts (Fabien Coelho) + + + + This is done with the new + + + + + + Allow changing the selection probabilities (weights) for scripts + (Fabien Coelho) + + + + When multiple scripts are specified, each pgbench + transaction randomly chooses one to execute. Formerly this was + always done with uniform probability, but now different selection + probabilities can be specified for different scripts. + + + + + + + Collect statistics for each script in a multi-script run (Fabien + Coelho) + + + + This feature adds an intermediate level of detail to existing + global and per-command statistics printouts. + + + + + + + Add a + + + + + + Allow the number of client connections ( + + + + + + When the + + + Previously, specifying a low transaction rate could cause + pgbench to wait significantly longer than + specified. + + + + + + + + + + + Server Applications + + + + + + + Improve error reporting during initdb's + post-bootstrap phase (Tom Lane) + + + + Previously, an error here led to reporting the entire input + file as the failing query; now just the current + query is reported. To get the desired behavior, queries in + initdb's input files must be separated by blank + lines. + + + + + + + Speed up initdb by using just one + standalone-backend session for all the post-bootstrap steps + (Tom Lane) + + + + + + + Improve pg_rewind + so that it can work when the target timeline changes (Alexander + Korotkov) + + + + This allows, for example, rewinding a promoted standby back to + some state of the old master's timeline. + + + + + + + + + Source Code + + + + + + + Remove obsolete + heap_formtuple/heap_modifytuple/heap_deformtuple + functions (Peter Geoghegan) + + + + + + + Add macros to make AllocSetContextCreate() calls simpler + and safer (Tom Lane) + + + + Writing out the individual sizing parameters for a memory context + is now deprecated in favor of using one of the new + macros ALLOCSET_DEFAULT_SIZES, + ALLOCSET_SMALL_SIZES, + or ALLOCSET_START_SMALL_SIZES. + Existing code continues to work, however. + + + + + + + Unconditionally use static inline functions in header + files (Andres Freund) + + + + This may result in warnings and/or wasted code space with very + old compilers, but the notational improvement seems worth it. + + + + + + + Improve TAP testing infrastructure (Michael + Paquier, Craig Ringer, Álvaro Herrera, Stephen Frost) + + + + Notably, it is now possible to test recovery scenarios using + this infrastructure. + + + + + + + Make trace_lwlocks identify individual locks by name + (Robert Haas) + + + + + + + Improve psql's tab-completion code infrastructure + (Thomas Munro, Michael Paquier) + + + + Tab-completion rules are now considerably easier to write, and + more compact. + + + + + + + Nail the pg_shseclabel system catalog into cache, + so that it is available for access during connection authentication + (Adam Brightwell) + + + + The core code does not use this catalog for authentication, + but extensions might wish to consult it. + + + + + + + Restructure index access + method API to hide most of it at + the C level (Alexander Korotkov, Andrew Gierth) + + + + This change modernizes the index AM API to look more + like the designs we have adopted for foreign data wrappers and + tablesample handlers. This simplifies the C code + and makes it much more practical to define index access methods in + installable extensions. A consequence is that most of the columns + of the pg_am system catalog have disappeared. + New inspection + functions have been added to allow SQL queries to determine + index AM properties that used to be discoverable + from pg_am. + + + + + + + Add pg_init_privs + system catalog to hold original privileges + of initdb-created and extension-created objects + (Stephen Frost) + + + + This infrastructure allows pg_dump to dump changes + that an installation may have made in privileges attached to + system objects. Formerly, such changes would be lost in a dump + and reload, but now they are preserved. + + + + + + + Change the way that extensions allocate custom LWLocks + (Amit Kapila, Robert Haas) + + + + The RequestAddinLWLocks() function is removed, + and replaced by RequestNamedLWLockTranche(). + This allows better identification of custom LWLocks, + and is less error-prone. + + + + + + + Improve the isolation tester to allow multiple sessions to wait + concurrently, allowing testing of deadlock scenarios (Robert Haas) + + + + + + + Introduce extensible node types (KaiGai Kohei) + + + + This change allows FDWs or custom scan providers + to store data in a plan tree in a more convenient format than + was previously possible. + + + + + + + Make the planner deal with post-scan/join query steps by generating + and comparing Paths, replacing a lot of ad-hoc logic + (Tom Lane) + + + + This change provides only marginal user-visible improvements today, + but it enables future work on a lot of upper-planner improvements + that were impractical to tackle using the old code structure. + + + + + + + Support partial aggregation (David Rowley, Simon Riggs) + + + + This change allows the computation of an aggregate function to be + split into separate parts, for example so that parallel worker + processes can cooperate on computing an aggregate. In future + it might allow aggregation across local and remote data to occur + partially on the remote end. + + + + + + + Add a generic command progress reporting facility (Vinayak Pokale, + Rahila Syed, Amit Langote, Robert Haas) + + + + + + + Separate out psql's flex lexer to + make it usable by other client programs (Tom Lane, Kyotaro + Horiguchi) + + + + This eliminates code duplication for programs that need to be able + to parse SQL commands well enough to identify command boundaries. + Doing that in full generality is more painful than one could + wish, and up to now only psql has really gotten + it right among our supported client programs. + + + + A new source-code subdirectory src/fe_utils/ has + been created to hold this and other code that is shared across + our client programs. Formerly such sharing was accomplished by + symbolic linking or copying source files at build time, which + was ugly and required duplicate compilation. + + + + + + + Introduce WaitEventSet API to allow + efficient waiting for event sets that usually do not change from + one wait to the next (Andres Freund, Amit Kapila) + + + + + + + Add a generic interface for writing WAL records + (Alexander Korotkov, Petr Jelínek, Markus Nullmeier) + + + + This change allows extensions to write WAL records for + changes to pages using a standard layout. The problem of needing to + replay WAL without access to the extension is solved by + having generic replay code. This allows extensions to implement, + for example, index access methods and have WAL + support for them. + + + + + + + Support generic WAL messages for logical decoding + (Petr Jelínek, Andres Freund) + + + + This feature allows extensions to insert data into the + WAL stream that can be read by logical-decoding + plugins, but is not connected to physical data restoration. + + + + + + + Allow SP-GiST operator classes to store an arbitrary + traversal value while descending the index (Alexander + Lebedev, Teodor Sigaev) + + + + This is somewhat like the reconstructed value, but it + could be any arbitrary chunk of data, not necessarily of the same + data type as the indexed column. + + + + + + + Introduce a LOG_SERVER_ONLY message level for + ereport() (David Steele) + + + + This level acts like LOG except that the message is + never sent to the client. It is meant for use in auditing and + similar applications. + + + + + + + Provide a Makefile target to build all generated + headers (Michael Paquier, Tom Lane) + + + + submake-generated-headers can now be invoked to ensure + that generated backend header files are up-to-date. This is + useful in subdirectories that might be built standalone. + + + + + + + Support OpenSSL 1.1.0 (Andreas Karlsson, Heikki Linnakangas) + + + + + + + + + Additional Modules + + + + + + + Add configuration parameter auto_explain.sample_rate to + allow contrib/auto_explain + to capture just a configurable fraction of all queries (Craig + Ringer, Julien Rouhaud) + + + + This allows reduction of overhead for heavy query traffic, while + still getting useful information on average. + + + + + + + Add contrib/bloom module that + implements an index access method based on Bloom filtering (Teodor + Sigaev, Alexander Korotkov) + + + + This is primarily a proof-of-concept for non-core index access + methods, but it could be useful in its own right for queries that + search many columns. + + + + + + + In contrib/cube, introduce + distance operators for cubes, and support kNN-style searches in + GiST indexes on cube columns (Stas Kelvich) + + + + + + + Make contrib/hstore's hstore_to_jsonb_loose() + and hstore_to_json_loose() functions agree on what + is a number (Tom Lane) + + + + Previously, hstore_to_jsonb_loose() would convert + numeric-looking strings to JSON numbers, rather than + strings, even if they did not exactly match the JSON + syntax specification for numbers. This was inconsistent with + hstore_to_json_loose(), so tighten the test to match + the JSON syntax. + + + + + + + Add selectivity estimation functions for + contrib/intarray operators + to improve plans for queries using those operators (Yury Zhuravlev, + Alexander Korotkov) + + + + + + + Make contrib/pageinspect's + heap_page_items() function show the raw data in each + tuple, and add new functions tuple_data_split() and + heap_page_item_attrs() for inspection of individual + tuple fields (Nikolay Shaplov) + + + + + + + Add an optional S2K iteration count parameter to + contrib/pgcrypto's + pgp_sym_encrypt() function (Jeff Janes) + + + + + + + Add support for word similarity to + contrib/pg_trgm + (Alexander Korotkov, Artur Zakirov) + + + + These functions and operators measure the similarity between one + string and the most similar single word of another string. + + + + + + + Add configuration parameter + pg_trgm.similarity_threshold for + contrib/pg_trgm's similarity threshold (Artur Zakirov) + + + + This threshold has always been configurable, but formerly it was + controlled by special-purpose functions set_limit() + and show_limit(). Those are now deprecated. + + + + + + + Improve contrib/pg_trgm's GIN operator class to + speed up index searches in which both common and rare keys appear + (Jeff Janes) + + + + + + + Improve performance of similarity searches in + contrib/pg_trgm GIN indexes (Christophe Fornaroli) + + + + + + + Add contrib/pg_visibility module + to allow examining table visibility maps (Robert Haas) + + + + + + + Add ssl_extension_info() + function to contrib/sslinfo, to print information + about SSL extensions present in the X509 + certificate used for the current connection (Dmitry Voronin) + + + + + + + <link linkend="postgres-fdw"><filename>postgres_fdw</></> + + + + + + + Allow extension-provided operators and functions to be sent for + remote execution, if the extension is whitelisted in the foreign + server's options (Paul Ramsey) + + + + Users can enable this feature when the extension is known to exist + in a compatible version in the remote database. It allows more + efficient execution of queries involving extension operators. + + + + + + + Consider performing sorts on the remote server (Ashutosh Bapat) + + + + + + + Consider performing joins on the remote server (Shigeru Hanada, + Ashutosh Bapat) + + + + + + + When feasible, perform UPDATE or DELETE + entirely on the remote server (Etsuro Fujita) + + + + Formerly, remote updates involved sending a SELECT FOR UPDATE + command and then updating or deleting the selected rows one-by-one. + While that is still necessary if the operation requires any local + processing, it can now be done remotely if all elements of the + query are safe to send to the remote server. + + + + + + + Allow the fetch size to be set as a server or table option + (Corey Huinker) + + + + Formerly, postgres_fdw always fetched 100 rows at + a time from remote queries; now that behavior is configurable. + + + + + + + Use a single foreign-server connection for local user IDs that + all map to the same remote user (Ashutosh Bapat) + + + + + + + Transmit query cancellation requests to the remote server + (Michael Paquier, Etsuro Fujita) + + + + Previously, a local query cancellation request did not cause an + already-sent remote query to terminate early. + + + + + + + + + + + + diff --git a/zh/9.6/release.sgml b/zh/9.6/release.sgml new file mode 100644 index 00000000..f7ce3a03 --- /dev/null +++ b/zh/9.6/release.sgml @@ -0,0 +1,88 @@ + + + + + 发布说明 + + + 发布说明包含每个PostgreSQL发布中的重要变更, + 并将主要特性和迁移问题列在最前面。发布说明不包含只影响少数用户的变更, + 也不包含仅限内部实现、因而对用户不可见的变更。例如,优化器几乎在每次 + 发布中都会得到改进,但用户通常只会把这些改进感知为查询速度更快。 + + + + 每个发布的完整变更列表都可以通过查看对应版本的Git + 日志获得。pgsql-committers + 邮件列表也记录了所有源代码变更。另有一个Web 界面 + 可显示特定文件的变更。 + + + + 每个条目旁边标出的姓名表示该条目的主要开发者。当然,所有变更都经历了 + 社区讨论和补丁审查,因此每个条目实际上都是社区共同努力的成果。 + + + + +&release-9.6; + + + 先前版本 + + + 先前各发布分支的发布说明可在 + + https://www.postgresql.org/docs/release/找到。 + + + + diff --git a/zh/9.6/replication-origins.sgml b/zh/9.6/replication-origins.sgml new file mode 100644 index 00000000..0bf4ab1b --- /dev/null +++ b/zh/9.6/replication-origins.sgml @@ -0,0 +1,77 @@ + + + 复制进度跟踪 + + + 复制进度跟踪 + + + 复制源 + + + + 复制源旨在让在逻辑解码之上实现逻辑复制方案变得更容易。 + 它们为两个常见问题提供了解决方案: + + + 如何安全地跟踪复制进度 + + + 如何根据一行数据的来源改变复制行为;例如,在双向复制拓扑中防止形成环路 + + + + + + 复制源只有两个属性:名称和 OID。名称是在系统之间引用该复制源时应使用的标识, + 它是自由形式的 text 值。使用它时,应尽量降低不同复制方案创建的复制源之间发生冲突的概率; + 例如,可以在前面加上复制方案的名称作为前缀。OID 仅用于在空间效率很重要的场合避免存储较长的名称形式。 + 它绝不应在不同系统之间共享。 + + + + 可以使用函数 + pg_replication_origin_create() 创建复制源; + 使用 + pg_replication_origin_drop() 删除复制源; + 并可在 + pg_replication_origin + 系统目录中查看它们。 + + + + 构建复制方案时,一个并不简单的部分是要以安全的方式跟踪重放进度。 + 当应用进程或整个集簇崩溃时,必须能够找出数据已经成功复制到了哪里。 + 对此的一些朴素解决方案,例如每重放一个事务就更新表中的某一行,会带来运行时开销和数据库膨胀等问题。 + + + + 借助复制源基础设施,可以将一个会话标记为正在重放来自远程节点的数据 + (使用函数 + pg_replication_origin_session_setup())。 + 此外,还可以使用 + pg_replication_origin_xact_setup() + 按事务为每个源事务配置其 LSN 和提交时间戳。这样做之后,复制进度就会以崩溃安全的方式持久保存。 + 所有复制源的重放进度都可以在 + + pg_replication_origin_status + 视图中查看。 + 某个单独复制源的进度,例如在恢复复制时,可以通过 + pg_replication_origin_progress() + (适用于任意复制源)或 + pg_replication_origin_session_progress() + (适用于当前会话中配置的复制源)来获取。 + + + + 在比恰好从一个系统复制到另一个系统更复杂的复制拓扑中,另一个问题在于很难避免再次复制已经重放过的行。 + 这既会导致复制出现环路,也会带来低效。复制源提供了一种可选机制来识别并防止这种情况。 + 当使用前一段提到的函数进行配置后,传递给输出插件回调 + (见 )的每一项变更和事务, + 都会带上其生成会话的复制源标记。这使得输出插件可以区别对待它们, + 例如忽略所有并非源自本地的行。此外, + + filter_by_origin_cb 回调还可用于根据来源过滤逻辑解码变更流。 + 虽然灵活性较低,但通过该回调进行过滤要比在输出插件中自行过滤高效得多。 + + diff --git a/zh/9.6/rowtypes.sgml b/zh/9.6/rowtypes.sgml new file mode 100644 index 00000000..00293d60 --- /dev/null +++ b/zh/9.6/rowtypes.sgml @@ -0,0 +1,344 @@ + + + + 复合类型 + + + 复合类型 + + + + 行类型 + + + + 复合类型表示一行或一条记录的结构;本质上它就是字段名及其数据类型的列表。PostgreSQL允许像使用简单类型那样,在许多相同的场合使用复合类型。例如,表中的一列可以声明为复合类型。 + + + + 复合类型的声明 + + + 下面是两个定义复合类型的简单示例: + +CREATE TYPE complex AS ( + r double precision, + i double precision +); + +CREATE TYPE inventory_item AS ( + name text, + supplier_id integer, + price numeric +); + + 这种语法与CREATE TABLE类似,但只能指定字段名和类型;目前还不能包含约束(例如NOT NULL)。注意,AS关键字必不可少;如果没有它,系统会认为你想使用另一种CREATE TYPE命令,并产生令人费解的语法错误。 + + + + 定义了这些类型之后,我们可以用它们来创建表: + + +CREATE TABLE on_hand ( + item inventory_item, + count integer +); + +INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000); + + + 或者创建函数: + + +CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric +AS 'SELECT $1.price * $2' LANGUAGE SQL; + +SELECT price_extension(item, 10) FROM on_hand; + + + + + 每当创建一张表时,也会自动创建一个与该表同名的复合类型,用来表示表的行类型。例如,假如我们执行了: +CREATE TABLE inventory_item ( + name text, + supplier_id integer REFERENCES suppliers, + price numeric CHECK (price > 0) +); +那么,与上文相同的inventory_item复合类型就会随之产生,而且可以像上文那样使用。不过,请注意当前实现的一个重要限制:由于复合类型本身不关联任何约束,表定义中的约束并不适用于表之外的复合类型值。(一种部分解决办法是使用域类型作为复合类型的成员。) + + + + 构造组合值 + + + 复合类型 + 常量 + + + + 要把组合值写成字面常量,请将各字段值放在圆括号内,并用逗号分隔。你可以给任意字段值加双引号;如果它包含逗号或圆括号,则必须这样做。(更多细节见下文。)因此,组合常量的一般格式如下: + +'( val1 , val2 , ... )' + + 一个示例是: + +'("fuzzy dice",42,1.99)' + + 这就是上文定义的inventory_item类型的一个合法值。要让某个字段为 NULL,就在列表中对应的位置什么也不写。例如,这个常量指定第三个字段为 NULL: + +'("fuzzy dice",42,)' + + 如果想写空字符串而不是 NULL,请写双引号: + +'("",42,)' + + 这里第一个字段是非 NULL 的空字符串,第三个字段是 NULL。 + + + + (这些常量实际上只是中讨论的通用类型常量的一种特例。该常量最初会被当作字符串处理,然后传递给复合类型输入转换例程。必要时可能需要显式指定类型。) + + + 这种ROW表达式语法也可以用于构造复合值。在大多数情况下,它比字符串字面量语法简单得多,因为你不必担心多层引号。我们在上文已经用过这种方法: +ROW('fuzzy dice', 42, 1.99) +ROW('', 42, NULL) +只要表达式中有多个字段,ROW 关键字实际上是可选的,因此这些可以简写为: +('fuzzy dice', 42, 1.99) +('', 42, NULL) +这种ROW表达式语法的更多细节见。 + + + + + + 访问复合类型 + + + 要访问组合列中的某个字段,可以写一个点号再加字段名,这很像通过表名选取字段。实际上,它与通过表名选取字段太像了,以至于你通常必须使用圆括号,以免让解析器混淆。例如,你可能尝试从示例表on_hand中选取一些子字段: + + +SELECT item.name FROM on_hand WHERE item.price > 9.99; + + + 这不会起作用,因为根据 SQL 语法规则,名称item会被当成表名,而不是on_hand的列名。你必须写成这样: + + +SELECT (item).name FROM on_hand WHERE (item).price > 9.99; + + + 或者,如果你还需要使用表名(例如在多表查询中),可以这样写: + + +SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99; + + + 现在,加上括号的对象就会被正确解释为对item列的引用,然后就可以从中选出子字段。 + + + + 无论何时从组合值中选择字段,都会遇到类似的语法问题。例如,要从一个返回组合值的函数结果中只选取一个字段,你需要这样写: + + +SELECT (my_func(...)).field FROM ... + + + 如果没有额外的圆括号,这将生成一个语法错误。 + + + + 特殊字段名*表示所有字段,其进一步解释见。 + + + + + 修改组合值 + + + 下面是一些插入和更新组合列时正确语法的示例。先看插入或更新整个列值的情况: + + +INSERT INTO mytab (complex_col) VALUES((1.1,2.2)); + +UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...; + + + 第一个示例省略了ROW,第二个示例使用了它;两种写法都可以。 + + + + 我们也可以更新组合列中的单个子字段: + + +UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...; + + + 注意,这里不需要(事实上也不能)给紧跟在SET后面的列名加圆括号,但在等号右侧的表达式中引用同一列时,则需要加圆括号。 + + + + 我们也可以把子字段指定为INSERT的目标: + + +INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2); + + + 如果我们没有为该列的所有子字段提供值,其余子字段就会填充为 NULL 值。 + + + + + 在查询中使用复合类型 + + + 在查询中,复合类型有多种特殊的语法规则和行为。这些规则提供了有用的简写形式,但如果不了解背后的逻辑,也可能让人困惑。 + + + + 在PostgreSQL中,查询中对表名(或别名)的引用,实际上就是对该表当前行的组合值的引用。例如,如果我们有一个如上文所示的表inventory_item,就可以写: + +SELECT c FROM inventory_item c; + + 这个查询会产生一个单独的组合值列,因此我们可能得到如下输出: + + c +------------------------ + ("fuzzy dice",42,1.99) +(1 row) + + 不过要注意,简单名称会先与列名匹配,再与表名匹配,因此这个示例之所以可行,只是因为该查询涉及的表中没有名为c的列。 + + + + 普通的限定列名语法table_name.column_name可以理解为对该表当前行的组合值进行字段选择。(出于效率原因,实际上并不是这样实现的。) + + + + 当我们写 + +SELECT c.* FROM inventory_item c; + + 时,根据 SQL 标准,应该得到把该表内容展开为独立列后的结果: + + name | supplier_id | price +------------+-------------+------- + fuzzy dice | 42 | 1.99 +(1 row) + + 就好像查询写成了 + +SELECT c.name, c.supplier_id, c.price FROM inventory_item c; + + PostgreSQL会对任何结果为复合类型的表达式应用这种展开行为,不过正如上文所示,只要.*所作用的值不是简单表名,就需要给该值加圆括号。例如,如果myfunc()是一个返回复合类型的函数,该复合类型有abc三列,那么下面两个查询的结果相同: + +SELECT (myfunc(x)).* FROM some_table; +SELECT (myfunc(x)).a, (myfunc(x)).b, (myfunc(x)).c FROM some_table; + + + + + + PostgreSQL处理列展开时,实际上会把第一种形式转换成第二种形式。因此,在这个示例中,myfunc()每行都会被调用三次,无论采用哪种语法。如果它是一个开销较大的函数,你可能希望避免这种情况,可以使用如下查询: +SELECT (m).* FROM (SELECT myfunc(x) AS m FROM some_table OFFSET 0) ss; +这里的OFFSET 0子句可以防止优化器展平子查询,从而避免形成会多次调用以下函数的形式:myfunc()。 + + + + 这里的composite_value.*语法在以下结构的顶层出现时会产生这类列展开:(SELECT输出列表)、RETURNING列表(位于INSERT/UPDATE/DELETE)、VALUES子句,或行构造器。在所有其他上下文中(包括嵌套在上述结构之内时),将.*附加到复合值上不会改变该值,因为它表示所有列,因此结果仍然是同一个复合值。例如,如果somefunc()接受一个复合值参数,这些查询就是等价的: +SELECT somefunc(c.*) FROM inventory_item c; +SELECT somefunc(c) FROM inventory_item c; +在这两种情况下,inventory_item的当前行都会作为单个复合值参数传递给该函数。即使.*在这种情况下不起作用,使用它仍是良好的风格,因为它明确表示这里需要的是复合值。特别是,解析器会将c(位于c.*)解释为表名或别名,而不是列名,因此不存在歧义;但如果没有.*,就不能明确判断c表示表名还是列名,而且会优先采用列名解释,只要存在一列名为c。 + + + + 另一个说明这些概念的例子是,下面这些查询的含义都相同: + +SELECT * FROM inventory_item c ORDER BY c; +SELECT * FROM inventory_item c ORDER BY c.*; +SELECT * FROM inventory_item c ORDER BY ROW(c.*); + + 所有这些ORDER BY子句都指定了该行的组合值,因此会按照中描述的规则对行进行排序。不过,如果inventory_item包含一个名为c的列,第一种情况就会不同于其他情况,因为它表示只按那一列排序。按照前面展示的列名,下面这些查询也与上述查询等效: + +SELECT * FROM inventory_item c ORDER BY ROW(c.name, c.supplier_id, c.price); +SELECT * FROM inventory_item c ORDER BY (c.name, c.supplier_id, c.price); + + (最后一种情况使用的是省略了关键字ROW的行构造器。) + + + + 另一种与组合值有关的特殊语法行为是,我们可以使用函数记法来提取组合值中的字段。简单来说,记法field(table)table.field可以互换。例如,这些查询是等价的: + + +SELECT c.name FROM inventory_item c WHERE c.price > 1000; +SELECT name(c) FROM inventory_item c WHERE price(c) > 1000; + + + 此外,如果我们有一个接受单个复合类型参数的函数,也可以用这两种记法来调用它。这些查询都等价: + + +SELECT somefunc(c) FROM inventory_item c; +SELECT somefunc(c.*) FROM inventory_item c; +SELECT c.somefunc FROM inventory_item c; + + + + + 函数记法与字段记法之间的这种等价性,使得我们可以通过在复合类型上使用函数来实现计算字段。 + + computed field + + + field + computed + + 使用上面最后一种查询形式的应用程序,无需直接知道somefunc并不是该表中的真实列。 + + + + 由于这种行为,不宜让接受单个复合类型参数的函数与该复合类型中的任何字段同名。如果存在歧义,会优先选择字段名解释,因此不采用特殊办法就无法调用这样的函数。强制按函数解释的一种方法是为函数名加模式限定,也就是写成 schema.func(compositevalue) + + + + + + 复合类型的输入和输出语法 + + + 组合值的外部文本表示由两部分组成:一部分是按照各字段类型的 I/O 转换规则解释的项,另一部分是表明组合结构的附加符号。这些附加符号包括包围整个值的圆括号(()),以及相邻项之间的逗号(,)。圆括号外部的空白会被忽略;但在圆括号内部,空白会被视为字段值的一部分,其是否有意义取决于该字段数据类型的输入转换规则。例如,在 + +'( 42)' + + 中,如果字段类型是 integer,则空白会被忽略;如果是 text,则不会被忽略。 + + + + 如前所示,在写组合值时,你可以给任意单个字段值加双引号。如果字段值本身可能让组合值解析器混淆,则必须这样做。特别是,包含圆括号、逗号、双引号或反斜杠的字段必须用双引号括起来。要在带引号的组合字段值中写入双引号或反斜杠,需要在其前面加一个反斜杠。(另外,带双引号的字段值内部成对出现的双引号会被视为一个双引号字符,这与 SQL 字面字符串中单引号的规则类似。)或者,你也可以完全不使用引号,而改用反斜杠转义,保护所有原本会被当作组合语法的数据字符。 + + + + 完全空的字段值(即逗号或圆括号之间一个字符也没有)表示 NULL。要写一个空字符串值而不是 NULL,可以写成""。 + + + + 如果字段值是空字符串,或者包含圆括号、逗号、双引号、反斜杠或空白字符,复合类型输出例程就会在其周围加上双引号。(对空白字符这样做并非必需,但有助于提高可读性。)嵌入字段值中的双引号和反斜杠会被加倍。 + + + + + + 记住,你在 SQL 命令中写的内容会先被解释为字符串字面量,然后才会被解释为组合值。这会使所需的反斜杠数量翻倍(假定使用的是转义字符串语法)。例如,要在组合值中插入一个包含双引号和反斜杠的text字段,需要写成: + +INSERT ... VALUES ('("\"\\")'); + + 字符串字面量处理器会去掉一层反斜杠,因此传到组合值解析器时看起来是("\"\\")。随后,送入text数据类型输入例程的字符串就变成了"\。(如果我们使用的数据类型的输入例程也会把反斜杠当作特殊字符处理,例如bytea,那么为了在存储的组合字段中得到一个反斜杠,命令里可能需要多达八个反斜杠。)美元引用(见)可用于避免反斜杠加倍的需要。 + + + + + + + 在 SQL 命令中编写组合值时,ROW构造器语法通常比组合字面量语法更容易使用。在ROW中,各个字段值的写法与它们不是组合成员时完全相同。 + + + + + diff --git a/zh/9.6/rules.sgml b/zh/9.6/rules.sgml new file mode 100644 index 00000000..e2584b95 --- /dev/null +++ b/zh/9.6/rules.sgml @@ -0,0 +1,1748 @@ + + + +规则系统 + + + 规则 + + + + 本章讨论PostgreSQL中的规则系统。产生式规则系统在概念上很简单,但在实际使用时涉及许多微妙之处。 + + + + 有些其他数据库系统定义了主动型数据库规则,它们通常表现为存储过程和触发器。在PostgreSQL中,这类功能同样可以用函数和触发器实现。 + + + + 规则系统(更准确地说,查询重写规则系统)与存储过程和触发器完全不同。它会修改查询,使其将规则纳入考虑,然后把修改后的查询交给查询规划器进行规划和执行。它非常强大,可用于查询语言过程、视图和版本等许多用途。关于这一规则系统的理论基础及其能力,另见。 + + + +查询树 + + + 查询树 + + + + 要理解规则系统如何工作,必须先知道它在何时被调用,以及它的输入和输出是什么。 + + + + 规则系统位于解析器和规划器之间。它接收解析器的输出,即一棵查询树,以及用户定义的重写规则;这些规则本身也是带有一些附加信息的查询树。它会生成零棵或多棵查询树作为结果。因此,它的输入和输出始终都是解析器本身也可能产生的东西,所以它看到的任何内容基本上都可以表示为一个SQL语句。 + + + + 那么什么是查询树?它是SQL语句的一种内部表示,其中构成语句的各个部分被分别存储。如果你设置了配置参数debug_print_parsedebug_print_rewrittendebug_print_plan,这些查询树就可以显示在服务器日志中。规则动作也以查询树的形式存储在系统目录pg_rewrite中。它们的格式不像日志输出,但包含的却是完全相同的信息。 + + + + 阅读原始查询树需要一些经验。但由于查询树的SQL表示已足以理解规则系统,本章不会讲解如何阅读它们。 + + + + 阅读本章查询树的 SQL 表示时,必须能够识别语句以查询树结构表示时被拆分成的各个部分。查询树的组成部分如下: + + + + + 命令类型 + + + + 这是一个简单的值,用来说明是哪一种命令(SELECTINSERTUPDATEDELETE)产生了该查询树。 + + + + + + + 范围表 + 范围表 + + + + 范围表是查询中使用的关系列表。在SELECT语句中,它们就是关键字FROM后面给出的关系。 + + + + 每个范围表项标识一个表或视图,并说明在查询的其他部分以哪个名称来称呼它。在查询树中,范围表项是按编号而不是按名称引用的,因此即使像SQL语句中那样出现重名,这里也无关紧要。这种情况可能出现在规则的范围表被合并之后。本章中的示例不会涉及这种情形。 + + + + + + + 结果关系 + + + + 这是范围表中的一个索引,用来标识查询结果应写入哪个关系。 + + + + SELECT查询没有结果关系。(SELECT INTO这一特殊情况与CREATE TABLE后接INSERT ... SELECT几乎相同,这里不再单独讨论。) + + + + 对于INSERTUPDATEDELETE命令,结果关系就是要让修改生效的表(或视图!)。 + + + + + + + 目标列表 + 目标列表 + + + + 目标列表是定义查询结果的表达式列表。对于SELECT,这些表达式构成查询的最终输出。它们对应于关键字SELECTFROM之间的表达式。(*只是某个关系全部列名的缩写。解析器会把它展开为各个独立列,因此规则系统永远不会看到它。) + + + + DELETE命令不需要普通的目标列表,因为它们不产生任何结果。相反,规划器会向空目标列表加入一个特殊的CTID项,以便执行器找到要删除的行。(当结果关系是普通表时,会加入CTID;如果结果关系是视图,则规则系统会改为加入一个整行变量,如所述。) + + + + 对于INSERT命令,目标列表描述的是将要进入结果关系的新行。它由VALUES子句中的表达式,或INSERT ... SELECTSELECT子句中的表达式组成。重写过程的第一步会为原始命令未赋值但带有默认值的列补上目标列表项。其余列(既没有给定值也没有默认值)将由规划器填入常量空值表达式。 + + + + 对于UPDATE命令,目标列表描述要替换旧行的新行。在规则系统中,它只包含命令中SET column = expression部分的表达式。规划器会通过插入把旧行值复制到新行的表达式来处理缺失列。与DELETE一样,还会加入一个CTID或整行变量,以便执行器标识要更新的旧行。 + + + + 目标列表中的每一项都包含一个表达式,它可以是常量值、指向范围表中某个关系列的变量、一个参数,或者由函数调用、常量、变量、操作符等构成的表达式树。 + + + + + + + 条件 + + + + 查询的条件是一个表达式,与目标列表项中的表达式很相似。该表达式的结果值是布尔值,用来说明是否应对最终结果行执行相应操作(INSERTUPDATEDELETESELECT)。它对应于SQL语句的WHERE子句。 + + + + + + + 连接树 + + + + 查询的连接树显示了FROM子句的结构。对于SELECT ... FROM a, b, c这样的简单查询,连接树只是FROM项的一个列表,因为允许按任意顺序连接它们。但当使用JOIN表达式,特别是外连接时,就必须按连接所显示的顺序进行连接。在这种情况下,连接树展示的是JOIN表达式的结构。与特定JOIN子句相关的限制(来自ONUSING表达式)会作为条件表达式附着在相应的连接树节点上。把顶层WHERE表达式也存储为附着在顶层连接树项上的一个条件,会很方便。因此,连接树实际上同时表示SELECTFROMWHERE子句。 + + + + + + + 其他 + + + + 查询树的其他部分,如ORDER BY子句,这里不作关注。规则系统在应用规则时会替换其中某些项,但这与规则系统的基本原理关系不大。 + + + + + + + + + +视图和规则系统 + + + 规则 + 和视图 + + + + 视图 + 通过规则实现 + + + + 在 PostgreSQL 中,视图通过规则系统实现。实际上,以下命令: + + +CREATE VIEW myview AS SELECT * FROM mytab; + + + 与下面这两条命令基本没有区别: + + +CREATE TABLE myview (same column list as mytab); +CREATE RULE "_RETURN" AS ON SELECT TO myview DO INSTEAD + SELECT * FROM mytab; + + + 因为这正是 CREATE VIEW 命令在内部所做的事情。这会带来一些副作用。其中之一是,在 PostgreSQL 系统目录中,视图的信息与表的信息完全相同。因此,对解析器而言,表和视图完全没有区别。它们是同一种东西:关系。 + + + +<command>SELECT</command> 规则如何工作 + + + 规则 + 用于 SELECT + + + + ON SELECT规则会在所有查询上作为最后一步应用,即使给出的命令是INSERTUPDATEDELETE也一样。它们与其他命令类型上的规则在语义上不同,因为它们是就地修改查询树,而不是创建新的查询树。因此我们先讨论SELECT规则。 + + + + 目前,一个ON SELECT规则中只能有一个动作,而且它必须是一个无条件、带有INSTEADSELECT动作。之所以有这个限制,是为了让规则足够安全,从而能够向普通用户开放;它也把ON SELECT规则限制为像视图那样工作。 + + + + 本章的示例是两个进行一些计算的连接视图,以及另外一些依次使用它们的视图。最初两个视图中的一个会在后面通过为INSERTUPDATEDELETE操作添加规则来定制,从而最终得到一个在行为上像真正的表、但又带有某些特殊功能的视图。作为入门示例,这并不算简单,因此会让理解变得更难一些。但与其使用许多可能令人混淆的不同示例,不如用一个示例逐步覆盖这里讨论的全部要点。 + + + + 在前两节对规则系统的描述中,我们需要用到如下真实表: + + +CREATE TABLE shoe_data ( + shoename text, -- primary key + sh_avail integer, -- available number of pairs + slcolor text, -- preferred shoelace color + slminlen real, -- minimum shoelace length + slmaxlen real, -- maximum shoelace length + slunit text -- length unit +); + +CREATE TABLE shoelace_data ( + sl_name text, -- primary key + sl_avail integer, -- available number of pairs + sl_color text, -- shoelace color + sl_len real, -- shoelace length + sl_unit text -- length unit +); + +CREATE TABLE unit ( + un_name text, -- primary key + un_fact real -- factor to transform to cm +); + + + 如你所见,它们表示的是鞋店数据。 + + + + 这些视图是这样创建的: + + +CREATE VIEW shoe AS + SELECT sh.shoename, + sh.sh_avail, + sh.slcolor, + sh.slminlen, + sh.slminlen * un.un_fact AS slminlen_cm, + sh.slmaxlen, + sh.slmaxlen * un.un_fact AS slmaxlen_cm, + sh.slunit + FROM shoe_data sh, unit un + WHERE sh.slunit = un.un_name; + +CREATE VIEW shoelace AS + SELECT s.sl_name, + s.sl_avail, + s.sl_color, + s.sl_len, + s.sl_unit, + s.sl_len * u.un_fact AS sl_len_cm + FROM shoelace_data s, unit u + WHERE s.sl_unit = u.un_name; + +CREATE VIEW shoe_ready AS + SELECT rsh.shoename, + rsh.sh_avail, + rsl.sl_name, + rsl.sl_avail, + least(rsh.sh_avail, rsl.sl_avail) AS total_avail + FROM shoe rsh, shoelace rsl + WHERE rsl.sl_color = rsh.slcolor + AND rsl.sl_len_cm >= rsh.slminlen_cm + AND rsl.sl_len_cm <= rsh.slmaxlen_cm; + + + 创建shoelace视图的CREATE VIEW命令(这是我们这里最简单的一个例子)会创建一个关系shoelace,并在pg_rewrite中创建一项,说明只要查询的范围表中引用了关系shoelace,就必须应用一条重写规则。该规则没有规则条件(稍后在讨论非SELECT规则时再谈,因为目前SELECT规则不能有规则条件),并且它是INSTEAD规则。注意,规则条件与查询条件不是一回事。这里我们的规则动作带有一个查询条件。规则动作本身是一棵查询树,它是视图创建命令中SELECT语句的一个副本。 + + + + + + 你在pg_rewrite项中看到的、用于NEWOLD的两个额外范围表项,与SELECT规则无关。 + + + +现在我们填充 unitshoe_data 和 + shoelace_data,然后对视图运行一个简单查询: +INSERT INTO unit VALUES ('cm', 1.0); +INSERT INTO unit VALUES ('m', 100.0); +INSERT INTO unit VALUES ('inch', 2.54); + +INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm'); +INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch'); +INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm'); +INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch'); + +INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm'); +INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm'); +INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0 , 'inch'); +INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0 , 'inch'); +INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0 , 'm'); +INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9 , 'm'); +INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60 , 'cm'); +INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40 , 'inch'); + +SELECT * FROM shoelace; + + sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm +-----------+----------+----------+--------+---------+----------- + sl1 | 5 | black | 80 | cm | 80 + sl2 | 6 | black | 100 | cm | 100 + sl7 | 7 | brown | 60 | cm | 60 + sl3 | 0 | black | 35 | inch | 88.9 + sl4 | 8 | black | 40 | inch | 101.6 + sl8 | 1 | brown | 40 | inch | 101.6 + sl5 | 4 | brown | 1 | m | 100 + sl6 | 0 | brown | 0.9 | m | 90 +(8 rows) + + + + + 这是你能在这些视图上执行的最简单的SELECT,因此我们借此机会说明视图规则的基础。SELECT * FROM shoelace由解析器解释后,会生成如下查询树: + + +SELECT shoelace.sl_name, shoelace.sl_avail, + shoelace.sl_color, shoelace.sl_len, + shoelace.sl_unit, shoelace.sl_len_cm + FROM shoelace shoelace; + + + 然后它会被交给规则系统。规则系统遍历范围表,检查其中的关系是否有相应规则。在处理shoelace的范围表项时(到目前为止只有这一个),它会找到带有如下查询树的_RETURN规则: + + +SELECT s.sl_name, s.sl_avail, + s.sl_color, s.sl_len, s.sl_unit, + s.sl_len * u.un_fact AS sl_len_cm + FROM shoelace old, shoelace new, + shoelace_data s, unit u + WHERE s.sl_unit = u.un_name; + + + +为了展开视图,重写器只需创建一个子查询范围表条目,其中包含规则的动作查询树,然后用这个范围表条目替换原先引用视图的条目。所得的重写后查询树几乎等同于输入以下语句的结果: +SELECT shoelace.sl_name, shoelace.sl_avail, + shoelace.sl_color, shoelace.sl_len, + shoelace.sl_unit, shoelace.sl_len_cm + FROM (SELECT s.sl_name, + s.sl_avail, + s.sl_color, + s.sl_len, + s.sl_unit, + s.sl_len * u.un_fact AS sl_len_cm + FROM shoelace_data s, unit u + WHERE s.sl_unit = u.un_name) shoelace; +不过有一点不同:子查询的范围表中有两个额外条目,shoelace old 和 + shoelace new。这些条目不直接参与查询,因为子查询的连接树或目标列表并未引用它们。重写器用它们保存原先引用视图的范围表条目中的访问权限检查信息。这样,即使重写后的查询没有直接使用视图,执行器仍会检查用户是否具有访问该视图所需的权限。 + + + 这就是应用的第一条规则。规则系统接着会检查顶层查询中剩余的范围表项(本例中已经没有了),并递归检查新增子查询中的范围表项,看它们是否引用了视图。(但它不会展开oldnew,否则就会出现无限递归!)在这个例子中,shoelace_dataunit都没有重写规则,因此重写到此结束,上面的结果就是交给规划器的最终结果。 + + + + 现在我们想写一个查询,找出商店里目前哪些鞋子有颜色和长度都匹配的鞋带,并且完全匹配的总双数大于等于二。 + + +SELECT * FROM shoe_ready WHERE total_avail >= 2; + + shoename | sh_avail | sl_name | sl_avail | total_avail +----------+----------+---------+----------+------------- + sh1 | 2 | sl1 | 5 | 2 + sh3 | 4 | sl7 | 7 | 4 +(2 rows) + + + + + 这一次解析器的输出是如下查询树: + + +SELECT shoe_ready.shoename, shoe_ready.sh_avail, + shoe_ready.sl_name, shoe_ready.sl_avail, + shoe_ready.total_avail + FROM shoe_ready shoe_ready + WHERE shoe_ready.total_avail >= 2; + + + 首先应用的是用于 shoe_ready 视图的规则,它得到如下查询树: + + +SELECT shoe_ready.shoename, shoe_ready.sh_avail, + shoe_ready.sl_name, shoe_ready.sl_avail, + shoe_ready.total_avail + FROM (SELECT rsh.shoename, + rsh.sh_avail, + rsl.sl_name, + rsl.sl_avail, + least(rsh.sh_avail, rsl.sl_avail) AS total_avail + FROM shoe rsh, shoelace rsl + WHERE rsl.sl_color = rsh.slcolor + AND rsl.sl_len_cm >= rsh.slminlen_cm + AND rsl.sl_len_cm <= rsh.slmaxlen_cm) shoe_ready + WHERE shoe_ready.total_avail >= 2; + + + 类似地,shoe 和 + shoelace 的规则会被替换进子查询的范围表中,最终得到一棵三层查询树: + + +SELECT shoe_ready.shoename, shoe_ready.sh_avail, + shoe_ready.sl_name, shoe_ready.sl_avail, + shoe_ready.total_avail + FROM (SELECT rsh.shoename, + rsh.sh_avail, + rsl.sl_name, + rsl.sl_avail, + least(rsh.sh_avail, rsl.sl_avail) AS total_avail + FROM (SELECT sh.shoename, + sh.sh_avail, + sh.slcolor, + sh.slminlen, + sh.slminlen * un.un_fact AS slminlen_cm, + sh.slmaxlen, + sh.slmaxlen * un.un_fact AS slmaxlen_cm, + sh.slunit + FROM shoe_data sh, unit un + WHERE sh.slunit = un.un_name) rsh, + (SELECT s.sl_name, + s.sl_avail, + s.sl_color, + s.sl_len, + s.sl_unit, + s.sl_len * u.un_fact AS sl_len_cm + FROM shoelace_data s, unit u + WHERE s.sl_unit = u.un_name) rsl + WHERE rsl.sl_color = rsh.slcolor + AND rsl.sl_len_cm >= rsh.slminlen_cm + AND rsl.sl_len_cm <= rsh.slmaxlen_cm) shoe_ready + WHERE shoe_ready.total_avail > 2; + + + + + 这看起来可能效率不高,但规划器会通过上拉子查询把它折叠成单层查询树,然后像我们手工写出这些连接一样来规划它们。因此,折叠查询树属于一种优化,重写系统本身无须关心。 + + + + +非 <command>SELECT</command> 语句中的视图规则 + + + 上文对视图规则的说明没有涉及查询树中的两个细节:命令类型和结果关系。实际上,视图规则并不需要命令类型,但结果关系可能会影响查询重写器的工作方式,因为当结果关系是视图时,必须进行特殊处理。 + + + + SELECT的查询树与其他任何命令的查询树之间只有少数差别。显然,它们的命令类型不同;对于SELECT之外的命令,结果关系会指向结果应写入的那个范围表项。除此之外,其余部分完全相同。因此,假设有两个表t1t2,都具有列ab,那么下面两条语句的查询树: + + +SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a; + +UPDATE t1 SET b = t2.b FROM t2 WHERE t1.a = t2.a; + + + 几乎是一样的。特别是: + + + + + 范围表中包含表t1t2的项。 + + + + + + 目标列表中都包含一个变量,该变量指向表t2的范围表项中的列b。 + + + + + + 条件表达式会比较两个范围表项中的列a是否相等。 + + + + + + 连接树都表示t1t2之间的一次简单连接。 + + + + + + + 结果是,这两个查询树都会产生相似的执行计划:它们都是对这两个表的连接。对于UPDATE,规划器会把t1中缺失的列补入目标列表,最终查询树会变成: + + +UPDATE t1 SET a = t1.a, b = t2.b FROM t2 WHERE t1.a = t2.a; + + + 因而,执行器在这个连接上运行时会产生与下面语句完全相同的结果集: + + +SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a; + + + 但在UPDATE中有个小问题:执行器计划中负责连接的那一部分并不关心连接结果将被用于什么。它只是生成一个行结果集。一个是SELECT命令,另一个是UPDATE命令,这一差别是在执行器更高层处理的;在那里,系统知道这是一个UPDATE,也知道结果应写入表t1。但问题在于:其中哪一行应当被新行替换? + + + + 为了解决这个问题,UPDATE(以及DELETE)语句的目标列表中会额外加入一项:当前元组 ID(CTID)。CTID这是一个系统列,包含该行所在的文件块号以及在块中的位置。已知表之后,就可以利用CTID取回要更新的t1原始行。把CTID加入目标列表后,查询实际上会变成: + + +SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a; + + + 现在还要引入PostgreSQL的另一个细节。旧表行不会被覆盖,这也是ROLLBACK之所以很快的原因。在UPDATE中,新结果行会被插入表中(去掉CTID之后),而CTID所指向的旧行,其行头中的cmaxxmax项会被设置为当前命令计数器和当前事务 ID。于是旧行被隐藏起来,事务提交后,清理器最终就可以移除这条死行。 + + + + 知道了这些以后,我们就可以用完全相同的方式把视图规则应用到任何命令上。没有区别。 + + + + +<productname>PostgreSQL</productname> 中视图的威力 + + + 上文演示了规则系统如何把视图定义整合进原始查询树。在第二个示例中,从一个视图发出的简单SELECT最终生成了一棵四表连接的查询树(unit以不同名称使用了两次)。 + + + + 用规则系统实现视图的好处在于:规划器能够在单棵查询树中同时看到哪些表必须被扫描、这些表之间的关系、来自视图的限制条件,以及原始查询自身的条件。即使原始查询本身已经是对若干视图的连接,情况也仍然如此。规划器必须决定执行查询的最佳路径,而它掌握的信息越多,这个决定通常就越好。PostgreSQL实现的规则系统能够保证,到那个阶段为止,关于该查询的全部可用信息都已经集中在这里。 + + + + +更新视图 + + + 如果某个视图被指定为INSERTUPDATEDELETE的目标关系,会发生什么?如果按上文描述的方式进行替换,就会得到一棵结果关系指向子查询范围表项的查询树,而这是行不通的。不过,PostgreSQL仍有几种方法能够支持“更新视图”这种表象。 + + + + 如果子查询从单个基础关系取数并且足够简单,重写器就可以自动用底层基础关系替换该子查询,使INSERTUPDATEDELETE以适当方式作用于基础关系。对这种足够简单的视图,称为自动可更新视图。关于哪些视图可以自动更新的详细信息,请参见。 + + + + 另一种办法是由视图上的用户自定义INSTEAD OF触发器来处理该操作。在这种情况下,重写的工作方式会略有不同。对于INSERT,重写器完全不处理该视图,而是让它继续作为查询的结果关系。对于UPDATEDELETE,仍然需要展开视图查询,以产生命令打算更新或删除的行。因此,视图会照常展开,但查询中还会再添加一个未展开的范围表项,用来表示该视图作为结果关系时的角色。 + + + + 此时出现的问题是,如何标识视图中需要更新的行。回想一下,当结果关系是表时,目标列表中会加入一个特殊的CTID项,用来标识待更新行的物理位置。如果结果关系是视图,这就行不通了,因为视图没有CTID,它的行并没有实际物理位置。取而代之的是,对于UPDATEDELETE操作,目标列表中会加入一个特殊的wholerow项,它会展开为该视图的全部列。执行器利用这个值把行传给INSTEAD OF触发器。之后由触发器根据旧行值和新行值决定应当更新什么。 + + + + 还有一种可能,是由用户定义INSTEAD规则,为视图上的INSERTUPDATEDELETE命令指定替代动作。这些规则会重写命令,通常是把它改写成更新一个或多个表而不是视图的命令。这正是的主题。 + + + + 注意,规则总是先被处理,也就是在原始查询被规划和执行之前先完成重写。因此,如果一个视图上同时有INSTEAD OF触发器以及INSERTUPDATEDELETE规则,那么规则会先执行;根据其结果,触发器甚至可能完全不会被用到。 + + + + 对简单视图上的INSERTUPDATEDELETE查询,总是最后才尝试自动重写。因此,如果某个视图定义了规则或触发器,它们会覆盖自动可更新视图的默认行为。 + + + + 如果该视图既没有INSTEAD规则,也没有INSTEAD OF触发器,并且重写器又无法把查询自动改写成对底层基础关系的更新,那么就会抛出错误,因为执行器本身并不能直接更新视图。 + + + + + + + +物化视图 + + + 规则 + 和物化视图 + + + + 物化视图 + 通过规则实现 + + + + 视图 + 物化 + + + + PostgreSQL中的物化视图和视图一样使用规则系统,但会以类似表的形式持久保存结果。下面两者之间的主要区别是: + + +CREATE MATERIALIZED VIEW mymatview AS SELECT * FROM mytab; + + + 以及: + + +CREATE TABLE mymatview AS SELECT * FROM mytab; + + + 物化视图之后不能被直接更新,并且用于创建物化视图的查询,其存储方式与视图查询的存储方式完全相同,因此可以通过下面的命令为物化视图生成新数据: + + +REFRESH MATERIALIZED VIEW mymatview; + + + 在PostgreSQL系统目录中,物化视图的信息与表或视图的信息完全相同。因此,对于解析器来说,物化视图也是一种关系,就像表或视图一样。当查询引用物化视图时,数据会像从表中那样直接从物化视图返回;规则只用于填充物化视图。 + + + + 访问物化视图中存储的数据,往往比直接或通过视图访问底层表快得多,但这些数据并不总是最新的;不过,有时并不需要最新数据。考虑以下记录销售情况的表: + + +CREATE TABLE invoice ( + invoice_no integer PRIMARY KEY, + seller_no integer, -- ID of salesperson + invoice_date date, -- date of sale + invoice_amt numeric(13,2) -- amount of sale +); + + + 如果希望快速绘制历史销售数据的图表,就可能需要汇总数据,而不必关心当天尚不完整的数据: + + +CREATE MATERIALIZED VIEW sales_summary AS + SELECT + seller_no, + invoice_date, + sum(invoice_amt)::numeric(13,2) as sales_amt + FROM invoice + WHERE invoice_date < CURRENT_DATE + GROUP BY + seller_no, + invoice_date + ORDER BY + seller_no, + invoice_date; + +CREATE UNIQUE INDEX sales_summary_seller + ON sales_summary (seller_no, invoice_date); + + + 这个物化视图可以用于在为销售人员创建的仪表板中显示图表。可以调度一个作业,每晚使用以下 SQL 语句更新统计数据: + + +REFRESH MATERIALIZED VIEW sales_summary; + + + + + 物化视图的另一种用途,是让通过外部数据包装器从远程系统获取的数据能够被更快地访问。下面给出一个使用file_fdw的简单示例,并附带计时结果;不过由于这里使用的是本地系统缓存,因此与真正访问远程系统相比,性能差异通常会比这里展示的更大。还要注意,我们同时利用了可以在物化视图上建立索引这一能力,而file_fdw本身并不支持索引;对其他类型的外部数据访问,这一优势未必适用。 + + + + 准备工作: + + +CREATE EXTENSION file_fdw; +CREATE SERVER local_file FOREIGN DATA WRAPPER file_fdw; +CREATE FOREIGN TABLE words (word text NOT NULL) + SERVER local_file + OPTIONS (filename '/usr/share/dict/words'); +CREATE MATERIALIZED VIEW wrd AS SELECT * FROM words; +CREATE UNIQUE INDEX wrd_word ON wrd (word); +CREATE EXTENSION pg_trgm; +CREATE INDEX wrd_trgm ON wrd USING gist (word gist_trgm_ops); +VACUUM ANALYZE wrd; + + + 现在来检查一个单词的拼写。直接使用 file_fdw: + + +SELECT count(*) FROM words WHERE word = 'caterpiler'; + + count +------- + 0 +(1 row) + + + 使用 EXPLAIN ANALYZE 可以看到: + + + Aggregate (cost=21763.99..21764.00 rows=1 width=0) (actual time=188.180..188.181 rows=1 loops=1) + -> Foreign Scan on words (cost=0.00..21761.41 rows=1032 width=0) (actual time=188.177..188.177 rows=0 loops=1) + Filter: (word = 'caterpiler'::text) + Rows Removed by Filter: 479829 + Foreign File: /usr/share/dict/words + Foreign File Size: 4953699 + Planning time: 0.118 ms + Execution time: 188.273 ms + + + 如果改用物化视图,查询会快得多: + + + Aggregate (cost=4.44..4.45 rows=1 width=0) (actual time=0.042..0.042 rows=1 loops=1) + -> Index Only Scan using wrd_word on wrd (cost=0.42..4.44 rows=1 width=0) (actual time=0.039..0.039 rows=0 loops=1) + Index Cond: (word = 'caterpiler'::text) + Heap Fetches: 0 + Planning time: 0.164 ms + Execution time: 0.117 ms + + + 无论采用哪种方式,这个单词的拼写都是错误的,因此来找找我们可能想要的单词。再次使用 file_fdw: + + +SELECT word FROM words ORDER BY word <-> 'caterpiler' LIMIT 10; + + word +--------------- + cater + caterpillar + Caterpillar + caterpillars + caterpillar's + Caterpillar's + caterer + caterer's + caters + catered +(10 rows) + + + + Limit (cost=11583.61..11583.64 rows=10 width=32) (actual time=1431.591..1431.594 rows=10 loops=1) + -> Sort (cost=11583.61..11804.76 rows=88459 width=32) (actual time=1431.589..1431.591 rows=10 loops=1) + Sort Key: ((word <-> 'caterpiler'::text)) + Sort Method: top-N heapsort Memory: 25kB + -> Foreign Scan on words (cost=0.00..9672.05 rows=88459 width=32) (actual time=0.057..1286.455 rows=479829 loops=1) + Foreign File: /usr/share/dict/words + Foreign File Size: 4953699 + Planning time: 0.128 ms + Execution time: 1431.679 ms + + + 使用物化视图: + + + Limit (cost=0.29..1.06 rows=10 width=10) (actual time=187.222..188.257 rows=10 loops=1) + -> Index Scan using wrd_trgm on wrd (cost=0.29..37020.87 rows=479829 width=10) (actual time=187.219..188.252 rows=10 loops=1) + Order By: (word <-> 'caterpiler'::text) + Planning time: 0.196 ms + Execution time: 198.640 ms + + + 如果能够接受将远程数据定期更新到本地数据库,性能收益可能相当可观。 + + + + + +<command>INSERT</command>、<command>UPDATE</command>和<command>DELETE</command>规则 + + + 规则 + 用于 INSERT + + + + 规则 + 用于 UPDATE + + + + 规则 + 用于 DELETE + + + + 定义在INSERTUPDATEDELETE上的规则与前几节描述的视图规则有明显的不同。首先,它们的CREATE RULE命令允许更多: + + + + + 它们可以没有动作。 + + + + + + 它们可以有多个动作。 + + + + + + 它们可以是INSTEADALSO(缺省)。 + + + + + + 伪关系NEWOLD可以派上用场。 + + + + + + 它们可以有规则条件。 + + + + + 第二,它们不是就地修改查询树,而是创建零棵或多棵新查询树,并且可能丢弃原始查询树。 + + + + + + 在很多情况下,使用INSERT/UPDATE/DELETE规则完成的任务,用触发器会做得更好。触发器在写法上稍微复杂一些,但它们的语义要简单得多。当原始查询包含易变函数时,规则往往会产生出人意料的结果:在执行规则的过程中,易变函数的执行次数可能比预期更多。 + + + + 此外,还有一些情况根本无法由这些类型的规则支持,尤其是原始查询中包含WITH子句,或者UPDATE查询的SET列表中存在多重赋值的子SELECT。这是因为把这些结构复制到规则查询中会导致子查询被多次求值,这与查询作者的明确意图相违背。 + + + + +更新规则如何工作 + + + 记住以下语法: + + +CREATE [ OR REPLACE ] RULE name AS ON event + TO table [ WHERE condition ] + DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) } + + + 在后文中,更新规则是指定义在INSERTUPDATEDELETE上的规则。 + + + + 当查询树的结果关系和命令类型分别等于CREATE RULE命令中给出的对象和事件时,规则系统就会应用更新规则。对于更新规则,规则系统会创建一个查询树列表,初始时该列表为空。动作可以有零个(关键字NOTHING)、一个或多个。为简化说明,我们先看只有一个动作的规则。这个规则可以有条件,也可以没有条件;它还可以是INSTEADALSO(默认值)。 + + + + 什么是规则条件?它是一种限制,用来说明何时执行规则动作、何时不执行。这个条件只能引用伪关系NEW和/或OLD,它们基本上表示作为对象给出的那个关系,只是带有特殊含义。 + + + + 因此,对于只有一个动作的规则,有以下三种情况会产生相应的查询树。 + + + + 没有条件,有ALSOINSTEAD + + + 规则动作的查询树,再附加原始查询树的条件 + + + + + + 给出了条件,有ALSO + + + 规则动作的查询树,再附加规则条件和原始查询树的条件 + + + + + + 给出了条件,有INSTEAD + + + 规则动作的查询树,再附加规则条件和原始查询树的条件;以及附加了规则条件取反后的原始查询树 + + + + + + 最后,如果规则是 ALSO,就把未经更改的原始查询树加入列表。由于只有带条件的 INSTEAD 规则已经加入了原始查询树,因此,对于只有一个动作的规则,最终会得到一棵或两棵输出查询树。 + + + + 对于ON INSERT规则,原始查询(如果未被INSTEAD抑制)会先于规则添加的任何动作执行。这样一来,这些动作就能看到被插入的行。但对ON UPDATEON DELETE规则,原始查询会在规则添加的动作之后执行。这就保证了这些动作能够看到将被更新或删除的行;否则,动作可能什么也做不了,因为它们找不到符合条件的行。 + + + + 由规则动作生成的查询树会再次送入重写系统,随后可能还会有更多规则被应用,从而产生更多或更少的查询树。因此,一个规则的动作必须具有不同的命令类型,或者具有与该规则自身不同的结果关系。否则,这种递归过程就会陷入无限循环。(规则的递归展开会被检测出来,并作为错误报告。) + + + + pg_rewrite系统目录中动作里的查询树只是模板。由于它们可以引用NEWOLD的范围表项,因此在使用前必须进行一些替换。对任何NEW的引用,都会先在原始查询的目标列表中查找相应项。如果找到了,就用该项的表达式替换该引用。否则,NEW就与OLD含义相同(对于UPDATE),或者被替换为一个空值(对于INSERT)。任何对OLD的引用,都会被替换为对结果关系对应范围表项的引用。 + + + + 在系统完成应用更新规则后,它再应用视图规则到生成的查询树上。视图无法插入新的更新动作,所以没有必要向视图重写的输出应用更新规则。 + + + +第一个规则:逐步分析 + + + 假设我们想跟踪shoelace_data关系中sl_avail列的变化。因此,我们建立一个日志表和一条规则,使其在shoelace_data上执行UPDATE时,有条件地写入一条日志记录。 + + +CREATE TABLE shoelace_log ( + sl_name text, -- shoelace changed + sl_avail integer, -- new available value + log_who text, -- who did it + log_when timestamp -- when +); + +CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data + WHERE NEW.sl_avail <> OLD.sl_avail + DO INSERT INTO shoelace_log VALUES ( + NEW.sl_name, + NEW.sl_avail, + current_user, + current_timestamp + ); + + + + + 现在有人执行了: + + +UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7'; + + + 然后我们查看日志表: + + +SELECT * FROM shoelace_log; + + sl_name | sl_avail | log_who | log_when +---------+----------+---------+---------------------------------- + sl7 | 6 | Al | Tue Oct 20 16:14:45 1998 MET DST +(1 row) + + + + + 这正是我们预期的结果。后台发生的事情如下。解析器创建了查询树: + + +UPDATE shoelace_data SET sl_avail = 6 + FROM shoelace_data shoelace_data + WHERE shoelace_data.sl_name = 'sl7'; + + + 此时存在一条ON UPDATE规则log_shoelace,它带有如下规则条件表达式: + + +NEW.sl_avail <> OLD.sl_avail + + + 其动作是: + + +INSERT INTO shoelace_log VALUES ( + new.sl_name, new.sl_avail, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old; + + + (这看起来有点奇怪,因为通常你不能写INSERT ... VALUES ... FROM。这里的FROM子句只是为了表明查询树中存在用于newold的范围表项。之所以需要这些项,是为了让INSERT命令查询树中的变量能够引用它们。) + + + + 该规则是一条带条件的ALSO规则,因此规则系统必须返回两棵查询树:修改后的规则动作,以及原始查询树。第 1 步中,原始查询的范围表会并入规则动作的查询树,得到: + + +INSERT INTO shoelace_log VALUES ( + new.sl_name, new.sl_avail, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old, + shoelace_data shoelace_data; + + + 第 2 步将规则条件加进去,因此结果集被限制为sl_avail发生变化的行: + + +INSERT INTO shoelace_log VALUES ( + new.sl_name, new.sl_avail, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old, + shoelace_data shoelace_data + WHERE new.sl_avail <> old.sl_avail; + + + (这看起来更奇怪,因为INSERT ... VALUES同样没有WHERE子句,但规划器和执行器处理它并无困难。反正它们本来也需要为INSERT ... SELECT支持相同功能。) + + + + 第 3 步把原始查询树的条件加进去,把结果集进一步限制为只有那些原始查询会触及的行: + + +INSERT INTO shoelace_log VALUES ( + new.sl_name, new.sl_avail, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old, + shoelace_data shoelace_data + WHERE new.sl_avail <> old.sl_avail + AND shoelace_data.sl_name = 'sl7'; + + + + + 第 4 步把NEW引用替换为原始查询树中的目标列表项,或者替换为结果关系中相应的变量引用: + + +INSERT INTO shoelace_log VALUES ( + shoelace_data.sl_name, 6, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old, + shoelace_data shoelace_data + WHERE 6 <> old.sl_avail + AND shoelace_data.sl_name = 'sl7'; + + + + + + 第 5 步把OLD引用替换为结果关系引用: + + +INSERT INTO shoelace_log VALUES ( + shoelace_data.sl_name, 6, + current_user, current_timestamp ) + FROM shoelace_data new, shoelace_data old, + shoelace_data shoelace_data + WHERE 6 <> shoelace_data.sl_avail + AND shoelace_data.sl_name = 'sl7'; + + + + + 至此就完成了。由于规则是ALSO,我们还要输出原始查询树。简而言之,规则系统输出的是一个包含两棵查询树的列表,它们对应于以下语句: + + +INSERT INTO shoelace_log VALUES ( + shoelace_data.sl_name, 6, + current_user, current_timestamp ) + FROM shoelace_data + WHERE 6 <> shoelace_data.sl_avail + AND shoelace_data.sl_name = 'sl7'; + +UPDATE shoelace_data SET sl_avail = 6 + WHERE sl_name = 'sl7'; + + + 它们会按这个顺序执行,而这正是该规则想要达到的效果。 + + + + 上述替换以及附加的条件能够保证:如果原始查询是下面这样,就不会写入任何日志记录: + + +UPDATE shoelace_data SET sl_color = 'green' + WHERE sl_name = 'sl7'; + + + 在这种情况下,原始查询树不包含sl_avail的目标列表项,因此NEW.sl_avail会被shoelace_data.sl_avail替换。于是,规则生成的额外命令是: + + +INSERT INTO shoelace_log VALUES ( + shoelace_data.sl_name, shoelace_data.sl_avail, + current_user, current_timestamp ) + FROM shoelace_data + WHERE shoelace_data.sl_avail <> shoelace_data.sl_avail + AND shoelace_data.sl_name = 'sl7'; + + + 而该条件永远不可能为真。 + + + + 如果原始查询修改多行,这种机制同样能够正常工作。例如,假设有人发出如下命令: + + +UPDATE shoelace_data SET sl_avail = 0 + WHERE sl_color = 'black'; + + + 实际上会更新四行(sl1sl2sl3sl4)。但sl3本来就已经是sl_avail = 0。在这种情况下,原始查询树的条件不同,因此规则会产生额外的查询树: + + +INSERT INTO shoelace_log +SELECT shoelace_data.sl_name, 0, + current_user, current_timestamp + FROM shoelace_data + WHERE 0 <> shoelace_data.sl_avail + AND shoelace_data.sl_color = 'black'; + + + 这棵查询树必然会插入三条新的日志记录。这完全正确。 + + + + 到这里就能看出,为什么原始查询树最后执行至关重要。如果先执行UPDATE,那么所有行都已经被设为零,记日志的INSERT就找不到任何满足0 <> shoelace_data.sl_avail的行了。 + + + + + + +与视图的协作 + +viewupdating + + + 为了防止有人像前面提到的那样对视图关系执行INSERTUPDATEDELETE,一种简单的办法是让那些查询树直接被丢弃。因此我们可以创建如下规则: + + +CREATE RULE shoe_ins_protect AS ON INSERT TO shoe + DO INSTEAD NOTHING; +CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe + DO INSTEAD NOTHING; +CREATE RULE shoe_del_protect AS ON DELETE TO shoe + DO INSTEAD NOTHING; + + + 如果现在某人尝试对视图关系shoe执行这些操作,规则系统就会应用这些规则。由于这些规则没有动作而且是INSTEAD,生成的查询树列表将为空;整个查询也就什么都不会做,因为规则系统处理完后已经没有任何内容可供优化或执行。 + + + + 另一种更完善的做法,是创建一些规则,把查询树重写成在真实表上执行正确操作的查询树。要在视图shoelace上做到这一点,我们创建下列规则: + + +CREATE RULE shoelace_ins AS ON INSERT TO shoelace + DO INSTEAD + INSERT INTO shoelace_data VALUES ( + NEW.sl_name, + NEW.sl_avail, + NEW.sl_color, + NEW.sl_len, + NEW.sl_unit + ); + +CREATE RULE shoelace_upd AS ON UPDATE TO shoelace + DO INSTEAD + UPDATE shoelace_data + SET sl_name = NEW.sl_name, + sl_avail = NEW.sl_avail, + sl_color = NEW.sl_color, + sl_len = NEW.sl_len, + sl_unit = NEW.sl_unit + WHERE sl_name = OLD.sl_name; + +CREATE RULE shoelace_del AS ON DELETE TO shoelace + DO INSTEAD + DELETE FROM shoelace_data + WHERE sl_name = OLD.sl_name; + + + + + 如果你想在视图上支持RETURNING查询,就需要让规则包含计算视图行的RETURNING子句。对于单表视图,这通常很简单;但对于像shoelace这样的连接视图,就会有点烦琐。插入场景的一个示例如下: + + +CREATE RULE shoelace_ins AS ON INSERT TO shoelace + DO INSTEAD + INSERT INTO shoelace_data VALUES ( + NEW.sl_name, + NEW.sl_avail, + NEW.sl_color, + NEW.sl_len, + NEW.sl_unit + ) + RETURNING + shoelace_data.*, + (SELECT shoelace_data.sl_len * u.un_fact + FROM unit u WHERE shoelace_data.sl_unit = u.un_name); + + + 请注意,这一条规则同时支持该视图上的INSERTINSERT RETURNING查询;对于普通INSERTRETURNING子句会被简单忽略。 + + + + 现在假定商店每隔一段时间就会收到一包鞋带,随附一份很长的部件清单。但你不想每次都手工更新 shoelace 视图。因此,我们建立两个小表:一个用于插入部件清单中的项目,另一个则采用一个特殊技巧。创建命令如下: + + +CREATE TABLE shoelace_arrive ( + arr_name text, + arr_quant integer +); + +CREATE TABLE shoelace_ok ( + ok_name text, + ok_quant integer +); + +CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok + DO INSTEAD + UPDATE shoelace + SET sl_avail = sl_avail + NEW.ok_quant + WHERE sl_name = NEW.ok_name; + + + 现在可以将部件清单中的数据填入 shoelace_arrive 表: + + +SELECT * FROM shoelace_arrive; + + arr_name | arr_quant +----------+----------- + sl3 | 10 + sl6 | 20 + sl8 | 20 +(3 rows) + + + 快速查看一下当前数据: + + +SELECT * FROM shoelace; + + sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm +----------+----------+----------+--------+---------+----------- + sl1 | 5 | black | 80 | cm | 80 + sl2 | 6 | black | 100 | cm | 100 + sl7 | 6 | brown | 60 | cm | 60 + sl3 | 0 | black | 35 | inch | 88.9 + sl4 | 8 | black | 40 | inch | 101.6 + sl8 | 1 | brown | 40 | inch | 101.6 + sl5 | 4 | brown | 1 | m | 100 + sl6 | 0 | brown | 0.9 | m | 90 +(8 rows) + + + 现在把到货的鞋带入库: + + +INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive; + + + 然后检查结果: + + +SELECT * FROM shoelace ORDER BY sl_name; + + sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm +----------+----------+----------+--------+---------+----------- + sl1 | 5 | black | 80 | cm | 80 + sl2 | 6 | black | 100 | cm | 100 + sl7 | 6 | brown | 60 | cm | 60 + sl4 | 8 | black | 40 | inch | 101.6 + sl3 | 10 | black | 35 | inch | 88.9 + sl8 | 21 | brown | 40 | inch | 101.6 + sl5 | 4 | brown | 1 | m | 100 + sl6 | 20 | brown | 0.9 | m | 90 +(8 rows) + +SELECT * FROM shoelace_log; + + sl_name | sl_avail | log_who| log_when +---------+----------+--------+---------------------------------- + sl7 | 6 | Al | Tue Oct 20 19:14:45 1998 MET DST + sl3 | 10 | Al | Tue Oct 20 19:25:16 1998 MET DST + sl6 | 20 | Al | Tue Oct 20 19:25:16 1998 MET DST + sl8 | 21 | Al | Tue Oct 20 19:25:16 1998 MET DST +(4 rows) + + + + + 从一个INSERT ... SELECT到这些结果,中间要经历相当长的一段过程。本章最后再来描述这一查询树转换的细节。首先,看一下解析器的输出: + + +INSERT INTO shoelace_ok +SELECT shoelace_arrive.arr_name, shoelace_arrive.arr_quant + FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok; + + + 现在应用第一条规则shoelace_ok_ins,它会把这一输出转换成: + + +UPDATE shoelace + SET sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant + FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, + shoelace_ok old, shoelace_ok new, + shoelace shoelace + WHERE shoelace.sl_name = shoelace_arrive.arr_name; + + + 同时会丢弃针对shoelace_ok的原始INSERT。这个重写后的查询会再次交给规则系统,而接下来应用的规则shoelace_upd会生成: + + +UPDATE shoelace_data + SET sl_name = shoelace.sl_name, + sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant, + sl_color = shoelace.sl_color, + sl_len = shoelace.sl_len, + sl_unit = shoelace.sl_unit + FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, + shoelace_ok old, shoelace_ok new, + shoelace shoelace, shoelace old, + shoelace new, shoelace_data shoelace_data + WHERE shoelace.sl_name = shoelace_arrive.arr_name + AND shoelace_data.sl_name = shoelace.sl_name; + + + 这同样是一条INSTEAD规则,因此前一个查询树会被丢弃。注意,这个查询仍然使用视图shoelace。但规则系统尚未完成,因此会继续在其上应用_RETURN规则,于是得到: + + +UPDATE shoelace_data + SET sl_name = s.sl_name, + sl_avail = s.sl_avail + shoelace_arrive.arr_quant, + sl_color = s.sl_color, + sl_len = s.sl_len, + sl_unit = s.sl_unit + FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, + shoelace_ok old, shoelace_ok new, + shoelace shoelace, shoelace old, + shoelace new, shoelace_data shoelace_data, + shoelace old, shoelace new, + shoelace_data s, unit u + WHERE s.sl_name = shoelace_arrive.arr_name + AND shoelace_data.sl_name = s.sl_name; + + + 最后,规则log_shoelace被应用,生成额外的查询树: + + +INSERT INTO shoelace_log +SELECT s.sl_name, + s.sl_avail + shoelace_arrive.arr_quant, + current_user, + current_timestamp + FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, + shoelace_ok old, shoelace_ok new, + shoelace shoelace, shoelace old, + shoelace new, shoelace_data shoelace_data, + shoelace old, shoelace new, + shoelace_data s, unit u, + shoelace_data old, shoelace_data new + shoelace_log shoelace_log + WHERE s.sl_name = shoelace_arrive.arr_name + AND shoelace_data.sl_name = s.sl_name + AND (s.sl_avail + shoelace_arrive.arr_quant) <> s.sl_avail; + + + 到这里,规则系统已经没有更多规则可用,并返回生成的查询树。 + + + + 因此,我们最终得到两棵查询树,它们等效于以下SQL语句: + + +INSERT INTO shoelace_log +SELECT s.sl_name, + s.sl_avail + shoelace_arrive.arr_quant, + current_user, + current_timestamp + FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data, + shoelace_data s + WHERE s.sl_name = shoelace_arrive.arr_name + AND shoelace_data.sl_name = s.sl_name + AND s.sl_avail + shoelace_arrive.arr_quant <> s.sl_avail; + +UPDATE shoelace_data + SET sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant + FROM shoelace_arrive shoelace_arrive, + shoelace_data shoelace_data, + shoelace_data s + WHERE s.sl_name = shoelace_arrive.sl_name + AND shoelace_data.sl_name = s.sl_name; + + + 结果是:来自一个关系的数据被插入到另一个关系中,这个插入被改写为对第三个关系的更新,再被改写为对第四个关系的更新外加在第五个关系中记录该更新,最后整个过程被化简为两个查询。 + + + + 这里有个稍显难看的小细节。观察这两个查询会发现,shoelace_data关系在范围表中出现了两次,而实际上完全可以缩减成一次。规划器不会处理这一点,因此规则系统为INSERT输出的执行计划会是 + + +Nested Loop + -> Merge Join + -> Seq Scan + -> Sort + -> Seq Scan on s + -> Seq Scan + -> Sort + -> Seq Scan on shoelace_arrive + -> Seq Scan on shoelace_data + + + 而省略那个额外的范围表项则会得到 + + +Merge Join + -> Seq Scan + -> Sort + -> Seq Scan on s + -> Seq Scan + -> Sort + -> Seq Scan on shoelace_arrive + + + 这会在日志表中生成完全相同的项。因此,规则系统导致了对shoelace_data表的一次完全不必要的额外扫描。而在UPDATE中,同样的冗余扫描还会再发生一次。不过,能让这一切总体上工作起来,已经是一项相当艰巨的工作。 + + + + 现在我们来做一个关于PostgreSQL规则系统及其威力的最后演示。假设你向数据库中加入了一些颜色特别的鞋带: + + +INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0); +INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0); + + + 我们想建立一个视图,用来检查哪些shoelace项在颜色上与任何鞋子都不匹配。这个视图是: + + +CREATE VIEW shoelace_mismatch AS + SELECT * FROM shoelace WHERE NOT EXISTS + (SELECT shoename FROM shoe WHERE slcolor = sl_color); + + + 它的输出是: + + +SELECT * FROM shoelace_mismatch; + + sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm +---------+----------+----------+--------+---------+----------- + sl9 | 0 | pink | 35 | inch | 88.9 + sl10 | 1000 | magenta | 40 | inch | 101.6 + + + + + 现在希望将没有库存的不匹配鞋带从数据库中删除。为了给 PostgreSQL 增加一点难度,我们不直接删除,而是再创建一个视图: + + +CREATE VIEW shoelace_can_delete AS + SELECT * FROM shoelace_mismatch WHERE sl_avail = 0; + + + 然后这样执行: + + +DELETE FROM shoelace WHERE EXISTS + (SELECT * FROM shoelace_can_delete + WHERE sl_name = shoelace.sl_name); + + + Voilà: + + +SELECT * FROM shoelace; + + sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm +---------+----------+----------+--------+---------+----------- + sl1 | 5 | black | 80 | cm | 80 + sl2 | 6 | black | 100 | cm | 100 + sl7 | 6 | brown | 60 | cm | 60 + sl4 | 8 | black | 40 | inch | 101.6 + sl3 | 10 | black | 35 | inch | 88.9 + sl8 | 21 | brown | 40 | inch | 101.6 + sl10 | 1000 | magenta | 40 | inch | 101.6 + sl5 | 4 | brown | 1 | m | 100 + sl6 | 20 | brown | 0.9 | m | 90 +(9 rows) + + + + + 作用于一个视图上的DELETE,其子查询条件总共使用了四个嵌套/连接的视图,其中一个视图自身又带有一个包含视图的子查询条件,并且还用到了计算得到的视图列,最终仍会被重写成单棵查询树,从真正的表中删除所请求的数据。 + + + + 在现实世界中,大概只有很少的场景会需要这样的构造。但知道它确实能工作,总归让人安心。 + + + + + + +规则和权限 + + + 权限 + 与规则 + + + + 权限 + 与视图 + + + + 由于PostgreSQL规则系统会重写查询,因此可能访问原始查询中并未使用的其他表或视图。使用更新规则时,这甚至可能包括对表的写访问。 + + + + 重写规则没有单独的所有者。关系(表或视图)的所有者会自动成为为其定义的重写规则的所有者。PostgreSQL规则系统改变了默认访问控制系统的行为。由于规则而使用的关系都会根据规则所有者的权限进行检查,而不是调用规则的用户。这意味着用户只需要对其查询中明确命名的表/视图具有所需的权限。 + + + + 例如,某个用户有一份电话号码列表,其中一部分是私人的,另一部分对办公室助理有用。该用户可以这样构造: + + +CREATE TABLE phone_data (person text, phone text, private boolean); +CREATE VIEW phone_number AS + SELECT person, CASE WHEN NOT private THEN phone END AS phone + FROM phone_data; +GRANT SELECT ON phone_number TO assistant; + + + 除了该用户本人(以及数据库超级用户)之外,没有人可以访问phone_data表。但由于GRANT的存在,助理可以对phone_number视图执行SELECT。规则系统会把对phone_numberSELECT重写成对phone_dataSELECT。由于该用户是phone_number的所有者,因此也是规则的所有者,对phone_data的读访问会按照该用户的权限进行检查,于是查询被允许。同时,对phone_number本身的访问检查仍会执行,但这是针对调用用户进行的,因此除了用户本人和助理之外,没有其他人能使用它。 + + + + 权限是逐条规则检查的。因此,目前助理是唯一能看到公开电话号码的人。但助理还可以再建立一个视图,并把该视图授予公众访问。这样,任何人都可以通过助理的视图看到phone_number中的数据。助理做不到的是创建一个直接访问phone_data的视图。(其实助理可以创建,但它不会起作用,因为每次访问都会在权限检查时被拒绝。)而且,一旦用户发现助理开放了其phone_number视图,用户就可以撤销助理的访问权限。那样一来,对助理视图的任何访问都会立即失败。 + + + + 这种逐条规则的检查看起来似乎像是一个安全漏洞,但实际上并不是。因为即使不这样工作,助理也可以建立一个与phone_number具有相同列的表,每天把数据复制进去。那样一来,这些就是助理自己的数据,助理同样可以把访问权限授予任何人。GRANT的含义本来就是我信任你。如果某个你信任的人做了上面的事,那么该重新考虑这份信任,并使用REVOKE了。 + + + + 需要注意的是,虽然视图可以用前文展示的技术来隐藏某些列的内容,但除非设置了security_barrier标志,否则它们不能被用来可靠地隐藏不可见行中的数据。例如,下面这个视图就是不安全的: + +CREATE VIEW phone_number AS + SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%'; + + 这个视图看起来似乎很安全,因为规则系统会把对phone_number的任何SELECT重写为对phone_dataSELECT,并附加条件,要求只处理phone不以 412 开头的项。但是,如果用户可以创建自己的函数,就不难让规划器在NOT LIKE表达式之前先执行用户定义函数。例如: + +CREATE FUNCTION tricky(text, text) RETURNS bool AS $$ +BEGIN + RAISE NOTICE '% => %', $1, $2; + RETURN true; +END; +$$ LANGUAGE plpgsql COST 0.0000000000000000000001; + +SELECT * FROM phone_number WHERE tricky(person, phone); + + phone_data表中的每个人和电话号码都会被打印为一条NOTICE,因为规划器会选择先执行廉价的tricky函数,再执行更昂贵的NOT LIKE。即使禁止用户定义新函数,内置函数也可以用于类似攻击。(例如,大多数类型转换函数都会在其错误消息中包含输入值。) + + + + 类似的考虑也适用于更新规则。在上一节的示例中,示例数据库中那些表的所有者可以把shoelace视图上的SELECTINSERTUPDATEDELETE权限授予其他用户,但对shoelace_log只授予SELECT权限。写日志记录的规则动作仍会成功执行,因此其他用户可以看到日志记录。但他们不能伪造记录,也不能操纵或删除已有记录。在这个例子里,不存在通过说服规划器改变操作顺序来破坏规则的可能性,因为唯一引用shoelace_log的规则是一条无条件的INSERT。在更复杂的场景中,情况未必如此。 + + + + 当视图需要提供行级安全时,应为该视图应用 security_barrier 属性。这可以防止在视图完成其工作之前,将行中的值传给恶意选择的函数和操作符。例如,如果上面展示的视图按以下方式创建,就会是安全的: + +CREATE VIEW phone_number WITH (security_barrier) AS + SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%'; + + 使用 security_barrier 创建的视图,其性能可能远低于未使用此选项的视图。通常无法避免这一点:如果最快的执行计划可能危及安全,就必须拒绝它。因此,此选项默认不启用。 + + + + 在处理没有副作用的函数时,查询规划器有更大的灵活性。这类函数称为LEAKPROOF,其中包括许多简单且常用的操作符,例如大量等值操作符。规划器可以安全地允许这类函数在查询执行过程中的任意位置求值,因为即便把它们应用到用户不可见的行上,也不会泄露这些不可见行的信息。此外,不带参数的函数,或者没有从安全屏障视图接收任何参数的函数,即使未被标记为LEAKPROOF,也可以下推,因为它们根本不会接收到来自视图的数据。相反,那些可能根据参数值抛出错误的函数(例如在发生溢出或除零时抛错的函数)就不具备防泄漏性;如果在安全视图的行过滤之前对其求值,就可能泄露关于不可见行的重要信息。 + + + + 需要理解的一点是,即使一个视图是用security_barrier选项创建的,它的安全性也只限于这样一种意义:不可见元组的内容不会被传递给可能不安全的函数。用户仍然可能通过其他方式推断不可见数据;例如,他们可以使用EXPLAIN查看查询计划,或者测量针对该视图执行查询所需的时间。恶意攻击者或许能够推断出不可见数据的大致数量,甚至获得有关数据分布或高频值的一些信息(因为这些因素可能影响计划运行时间,甚至影响计划的选择,因为它们同样反映在优化器统计信息中)。如果这类“隐通道”攻击值得担心,那么向任何人授予这类数据的访问权限本身就可能是不明智的。 + + + + + +规则和命令状态 + + + PostgreSQL服务器会为收到的每条命令返回一个命令状态字符串,例如INSERT 149592 1。在没有规则参与时,这很简单;但当查询被规则重写时会发生什么呢? + + + + 规则对命令状态的影响如下: + + + + + 如果该查询没有无条件的INSTEAD规则,那么原始给出的查询就会照常执行,并返回它自身的命令状态。(但请注意,如果存在任何带条件的INSTEAD规则,那么这些规则条件的取反会被加到原始查询上。这可能会减少它处理的行数,从而影响报告的状态。) + + + + + + 如果该查询存在任意无条件的INSTEAD规则,那么原始查询将完全不会执行。在这种情况下,服务器会返回由INSTEAD规则(带条件或不带条件)插入的、最后一条且与原始查询具有相同命令类型(INSERTUPDATEDELETE)的查询的命令状态。如果没有任何规则添加出满足这些条件的查询,那么返回的命令状态会显示原始查询类型,并且把行计数和 OID 字段都置零。 + + + + + + + 在第二种情况下,程序员可以通过为所需的INSTEAD规则指定一个在活动规则中按字母顺序排在最后的规则名,来确保由它设置命令状态,因为它会最后被应用。 + + + + +规则与触发器 + + + 规则 + 与触发器比较 + + + + 触发器 + 与规则比较 + + + + 许多能够用触发器完成的事情,同样也可以用PostgreSQL规则系统实现。不能用规则实现的内容之一是某些约束,尤其是外键。你可以放置一条带条件的规则:当某列的值没有出现在另一张表中时,把命令重写成NOTHING。但那样做会悄无声息地丢弃数据,这并不是好主意。如果需要检查值的有效性,并在值无效时生成错误消息,就必须使用触发器。 + + + + 在本章中,我们重点讨论了用规则来更新视图。本章中所有更新规则的示例,也都可以用视图上的INSTEAD OF触发器来实现。编写这类触发器通常比编写规则更容易,尤其是在需要用复杂逻辑执行更新时。 + + + + 对于两者都能实现的事情,哪一种更好取决于数据库的使用方式。触发器会对每个受影响的行触发一次,而规则会修改查询,或生成额外的查询。因此,如果一条语句会影响很多行,那么发出一条额外命令的规则往往会比触发器更快,因为触发器必须为每一行调用一次,并反复判断该做什么。不过,触发器方法在概念上要比规则方法简单得多,也更容易让新手写对。 + + + + 下面通过一个例子说明,在一种具体情形中选择规则或触发器会有什么不同。这里有两个表: + + +CREATE TABLE computer ( + hostname text, -- indexed + manufacturer text -- indexed +); + +CREATE TABLE software ( + software text, -- indexed + hostname text -- indexed +); + + + 两个表都有数千行,并且 hostname 上的索引都是唯一索引。规则或触发器应实现这样一个约束:从 software 中删除引用了已删除计算机的行。触发器会使用以下命令: + + +DELETE FROM software WHERE hostname = $1; + + + 由于从 computer 中删除的每一行都会调用触发器,因此触发器可以为这条命令准备并保存执行计划,并通过参数传入 hostname 值。规则则会写成: + + +CREATE RULE computer_del AS ON DELETE TO computer + DO DELETE FROM software WHERE hostname = OLD.hostname; + + + + + 现在我们来看不同类型的删除。对于下面这种情况: + + +DELETE FROM computer WHERE hostname = 'mypc.local.net'; + + + computer表会通过索引扫描(很快),由触发器发出的命令也会使用索引扫描(同样很快)。规则产生的额外查询是: + + +DELETE FROM software WHERE computer.hostname = 'mypc.local.net' + AND software.hostname = computer.hostname; + + + 由于已经建立了合适的索引,规划器将创建一个规划 + + +Nestloop + -> Index Scan using comp_hostidx on computer + -> Index Scan using soft_hostidx on software + + + 因此,触发器实现和规则实现之间的速度差异不会太大。 + + + + 在下一个删除场景中,我们想去掉全部 2000 台hostnameold开头的计算机。有两种命令可以做到这件事。其中一种是: + + +DELETE FROM computer WHERE hostname >= 'old' + AND hostname < 'ole' + + + 规则添加出来的命令将是: + + +DELETE FROM software WHERE computer.hostname >= 'old' AND computer.hostname < 'ole' + AND software.hostname = computer.hostname; + + + 其计划为: + + +Hash Join + -> Seq Scan on software + -> Hash + -> Index Scan using comp_hostidx on computer + + + 另一个可能的命令是: + + +DELETE FROM computer WHERE hostname ~ '^old'; + + + 它会让规则所添加的命令得到下面这个执行计划: + + +Nestloop + -> Index Scan using comp_hostidx on computer + -> Index Scan using soft_hostidx on software + + + 这说明,当多个条件表达式通过AND组合在一起时,规划器并没有意识到,对computerhostname的限制同样也可以用于software上的索引扫描,而在该命令的正则表达式版本里它却能做到。触发器会对需要删除的 2000 台旧计算机中的每一台各调用一次,这意味着对computer做一次索引扫描,并对software做 2000 次索引扫描。规则实现则用两条使用索引的命令完成这件事。不过,在顺序扫描这种情况下,规则是否仍然更快,还取决于software表的总体大小。即使所有索引块很快都会进入缓存,通过 SPI 管理器执行来自触发器的 2000 条命令仍然要耗费一些时间。 + + + + 我们要看的最后一个命令是: + + +DELETE FROM computer WHERE manufacturer = 'bim'; + + + 同样,这也可能导致从computer中删除很多行。因此,触发器同样会通过执行器运行很多条命令。规则生成的命令则是: + + +DELETE FROM software WHERE computer.manufacturer = 'bim' + AND software.hostname = computer.hostname; + + + 这个命令的计划又将是在两个索引扫描上的嵌套循环,只不过使用了computer上的另一个索引: + + +Nestloop + -> Index Scan using comp_manufidx on computer + -> Index Scan using soft_hostidx on software + + + 在上述任何一种情况下,规则系统产生的额外命令都或多或少与该命令影响的行数无关。 + + + + 总结来说,只有当规则动作导致了规模很大且条件很差的连接,而规划器又无能为力时,规则才会明显慢于触发器。 + + + + diff --git a/zh/9.6/runtime.sgml b/zh/9.6/runtime.sgml new file mode 100644 index 00000000..624ce23c --- /dev/null +++ b/zh/9.6/runtime.sgml @@ -0,0 +1,1568 @@ + + + + 服务器设置和操作 + + 本章讨论如何设置和运行数据库服务器,以及它与操作系统的交互。 + + + <productname>PostgreSQL</productname>用户账户 + + + postgres 用户 + + + 与任何可从外部访问的服务器守护进程一样,建议使用单独的用户账户运行 PostgreSQL。该用户账户应该只拥有服务器管理的数据,不应与其他守护进程共用。(例如,使用 nobody 用户就不是好主意。)不建议将可执行文件安装为由该用户所有,因为系统一旦被攻破,就可能修改自己的二进制文件。 + + + 要在系统中添加一个 Unix 用户账户,请查找useraddadduser命令。通常使用的用户名是postgres,本书也都按此假定,不过你也可以使用其他名称。 + + + + + 创建一个数据库集簇 + + + 数据库集簇 + + + + 数据区域 + 数据库集簇 + + + + 在做任何事情之前,必须先在磁盘上初始化一个数据库存储区域。我们把它称为数据库集簇。(SQL 标准使用术语目录集簇(catalog cluster)。)数据库集簇是由一个正在运行的数据库服务器实例管理的一组数据库。初始化之后,数据库集簇中会包含一个名为postgres的数据库,作为供工具、用户和第三方应用使用的默认数据库。数据库服务器本身并不要求该postgres数据库存在,但许多外部工具程序都假定它存在。初始化期间,集簇中还会创建另一个数据库,名为template1。顾名思义,它会作为后续新建数据库的模板;不应将它用于实际工作。(关于在集簇中创建新数据库的信息,见。) + + + 从文件系统角度看,数据库集簇就是一个目录,所有数据都存储在其下。我们将它称为数据目录数据区域。数据存放位置完全由你决定,没有默认值;常用位置包括 /usr/local/pgsql/data/var/lib/pgsql/data。要初始化数据库集簇,请使用命令 initdb该命令随以下产品安装:PostgreSQL。数据库集簇在文件系统中的目标位置通过 选项指定,例如: +$ initdb -D /usr/local/pgsql/data +注意,必须先登录 PostgreSQL 用户账户,再执行此命令。该账户已在上一节介绍。 + + + + + 除了使用 选项,也可以设置环境变量 PGDATA。 + PGDATA + + + + + 或者,也可以通过pg_ctl程序运行initdb,如下所示: + +$ pg_ctl -D /usr/local/pgsql/data initdb + + 如果你打算用pg_ctl来启动和停止服务器(见),这种方式可能更直观,因为这样pg_ctl就会成为你管理数据库服务器实例时使用的唯一命令。 + + + + 如果指定的目录尚不存在,initdb会尝试创建它。当然,如果initdb对其父目录没有写权限,这会失败。通常建议让PostgreSQL用户不仅拥有数据目录,也拥有其父目录,这样就不会有这个问题。如果目标父目录也不存在,则需要先创建它;如果祖父目录不可写,就需要使用 root 权限。因此,过程可能如下所示: + +root# mkdir /usr/local/pgsql +root# chown postgres /usr/local/pgsql +root# su postgres +postgres$ initdb -D /usr/local/pgsql/data + + + + + 如果数据目录已存在且其中已有文件,initdb会拒绝运行;这是为了防止意外覆盖现有安装。 + + + 由于数据目录包含存储在数据库中的全部数据,必须保护它免遭未经授权的访问。因此,initdb 会撤销除 PostgreSQL 用户以外所有人的访问权限。 + + 不过,尽管目录内容是安全的,默认的客户端认证设置却允许任何本地用户连接到数据库,甚至成为数据库超级用户。如果你不信任其他本地用户,我们建议在运行 initdb 时使用 之一,为数据库超级用户指定密码。 密码 超级用户的 另外,指定 ,以避免使用默认的 trust 认证方式;或者在运行 initdb 之后、首次启动服务器之前修改生成的 pg_hba.conf 文件。(其他合理方法包括使用 peer 认证,或使用文件系统权限限制连接。更多信息见 。) + + initdb 还会初始化数据库集簇的默认区域设置区域设置。通常,它会直接采用环境中的区域设置,并将其应用于初始化的数据库。可以为数据库指定不同的区域设置;更多信息见 。特定数据库集簇中使用的默认排序顺序由 initdb 设置,虽然你可以使用不同的排序顺序创建新数据库,但 initdb 创建的模板数据库所使用的顺序,只有删除并重建这些数据库才能更改。使用 CPOSIX 以外的区域设置还会影响性能。因此,一开始就作出正确选择很重要。 + + + initdb还会为数据库集簇设置默认字符集编码。通常应选择与区域设置相匹配的字符集编码。详见。 + + + + 非C和非POSIX的区域设置依赖操作系统的排序规则库来决定字符集排序。这会控制索引中存储键值的顺序。因此,无论是通过快照恢复、二进制流复制、切换到不同操作系统,还是升级操作系统,都不能让一个集簇切换到不兼容版本的排序规则库。 + + + + + 二级文件系统的使用 + + + 文件系统挂载点 + + + + 很多安装会把数据库集簇建在机器卷以外的文件系统(卷)上。如果这样做,不建议把二级卷的最顶层目录(挂载点)直接用作数据目录。最佳实践是在挂载点目录下创建一个由PostgreSQL用户拥有的目录,再在其下创建数据目录。这样可以避免权限问题,尤其是在执行pg_upgrade之类的操作时;同时也能确保二级卷脱机时以干净的方式失败。 + + + + + + 使用网络文件系统 + + + 网络文件系统 + + NFS网络文件系统 + 网络附加存储(NAS网络文件系统 + + 许多安装会在网络文件系统上创建数据库集簇。有时通过 NFS 完成,有时则使用内部采用 NFS 的网络附加存储(NAS)设备。PostgreSQL 不会对 NFS 文件系统做特殊处理,也就是说,它假定 NFS 的行为与本地连接的驱动器完全一致。如果客户端或服务器端的 NFS 实现不提供标准文件系统语义,就可能导致可靠性问题(参见 )。具体而言,对 NFS 服务器的延迟(异步)写入可能导致数据损坏。如果可能,应以同步方式(不使用缓存)挂载 NFS 文件系统,以避免这一风险。此外,不建议对 NFS 文件系统使用软挂载。 + + 存储区域网络(SAN)通常使用 NFS 以外的通信协议,可能存在也可能不存在此类风险。建议查阅供应商文档中有关数据一致性保证的说明。PostgreSQL 的可靠性不可能高于它所使用的文件系统。 + + + + + + + 启动数据库服务器 + + 在任何人访问数据库之前,必须先启动数据库服务器。数据库服务器程序名为 postgrespostgres其中,postgres 程序必须知道应到哪里查找它要使用的数据。这通过 选项指定。因此,启动服务器最简单的方法是: +$ postgres -D /usr/local/pgsql/data +这会让服务器在前台运行。必须先登录 PostgreSQL 用户账户再执行此操作。如果不使用 ,服务器会尝试使用环境变量所指定的数据目录,该变量为 PGDATA。如果也未提供该变量,则会失败。 + + + 通常最好在后台启动postgres。做法是使用常见的 Unix shell 语法: + +$ postgres -D /usr/local/pgsql/data >logfile 2>&1 & + + 如上所示,把服务器的stdoutstderr输出保存到某个地方非常重要。这有助于审计,也有助于诊断问题。(关于日志文件处理的更深入讨论,见。) + + + + postgres还接受许多其他命令行选项。更多信息请见参考页以及下面的。 + + + + 这些 shell 语法很快就会让人觉得繁琐。因此提供了包装器程序pg_ctl来简化一些任务。例如: + +pg_ctl start -l logfile + + 这会在后台启动服务器,并把输出写入指定的日志文件。这里的 选项含义与在 postgres 中相同。pg_ctl 还可用于停止服务器。 + + + 通常,你会希望在计算机启动时就启动数据库服务器。 引导 期间启动服务器 自动启动脚本依赖于操作系统。PostgreSQLcontrib/start-scripts 目录中附带了一些示例脚本。安装这些脚本需要 root 权限。 + + + 不同系统在引导时启动守护进程的惯例各不相同。许多系统有/etc/rc.local/etc/rc.d/rc.local文件,其他系统则使用init.drc.d目录。无论采用哪种方式,服务器都必须由PostgreSQL用户账户而不是 root或其他用户来启动。因此,你大概应该在命令中使用su postgres -c '...'这种形式。例如: + +su postgres -c 'pg_ctl start -D /usr/local/pgsql/data -l serverlog' + + + + + 下面是一些与操作系统相关的补充建议。(在每种情况下,请确保使用正确的安装目录和用户名;这里展示的是通用值。) + + + + + 对于FreeBSD,请查看PostgreSQL源码发布包中的contrib/start-scripts/freebsd文件。 + FreeBSD启动脚本 + + + + + + 在OpenBSD上,把以下内容加入/etc/rc.local: + OpenBSD启动脚本 + +if [ -x /usr/local/pgsql/bin/pg_ctl -a -x /usr/local/pgsql/bin/postgres ]; then + su -l postgres -c '/usr/local/pgsql/bin/pg_ctl start -s -l /var/postgresql/log -D /usr/local/pgsql/data' + echo -n ' postgresql' +fi + + + + + + + 在Linux系统上,可以把 + Linux启动脚本 + +/usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data + + 加入/etc/rc.d/rc.local/etc/rc.local,或者查看PostgreSQL源码发布包中的contrib/start-scripts/linux文件。 + + + + 使用systemd时,可以采用下面的服务单元文件(例如放在/etc/systemd/system/postgresql.service):systemd + +[Unit] +Description=PostgreSQL database server +Documentation=man:postgres(1) +[Service] +Type=notify +User=postgres +ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data +ExecReload=/bin/kill -HUP $MAINPID +KillMode=mixed +KillSignal=SIGINT +TimeoutSec=0 + +[Install] +WantedBy=multi-user.target + + 使用Type=notify要求服务器二进制文件在构建时启用了configure --with-systemd。 + + + + 请仔细考虑超时设置。撰写本文时,systemd 的默认超时为 90 秒,如果进程在这段时间内未通知就绪,它就会将其杀死。但一个在启动时可能需要执行崩溃恢复的PostgreSQL服务器,准备就绪可能远不止这么久。建议值 0 可以禁用该超时逻辑。 + + + + + + 在NetBSD上,可根据偏好使用FreeBSDLinux的启动脚本。 + NetBSD启动脚本 + + + + + + 在Solaris上,创建一个名为/etc/init.d/postgresql的文件,其中包含以下内容: + Solaris启动脚本 + +su - postgres -c "/usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data" + + 然后,在/etc/rc3.d中为它创建一个名为S99postgresql的符号链接。 + + + + + + + + 当服务器运行时,其PID会保存在数据目录中的postmaster.pid文件里。这可以防止多个服务器实例在同一个数据目录上运行,也可以用来关闭服务器。 + + + + 服务器启动失败 + + + 服务器启动失败有几种常见原因。请检查服务器日志文件,或者手工启动服务器(不要重定向标准输出和标准错误),看看出现了什么错误消息。下面将更详细地解释一些最常见的错误消息。 + + + + +LOG: could not bind IPv4 socket: Address already in use +HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry. +FATAL: could not create TCP/IP listen socket + + 这通常就是字面上的意思:你试图在一个已有服务器运行的端口上再启动另一个服务器。不过,如果内核错误消息不是Address already in use或类似变体,也可能是别的问题。例如,试图在一个保留端口上启动服务器,可能会得到类似下面的消息: + +$ postgres -p 666 +LOG: could not bind IPv4 socket: Permission denied +HINT: Is another postmaster already running on port 666? If not, wait a few seconds and retry. +FATAL: could not create TCP/IP listen socket + + + + 像下面这样的消息: +FATAL: could not create shared memory segment: Invalid argument +DETAIL: Failed system call was shmget(key=5440001, size=4011376640, 03600). +可能表示内核对共享内存大小的限制小于 PostgreSQL 尝试创建的工作区(本例中为 4011376640 字节)。也可能表示内核中根本没有配置 System V 风格的共享内存支持。作为临时解决办法,可以尝试以少于通常数量的缓冲区启动服务器()。最终还是需要重新配置内核,增大允许的共享内存大小。如果尝试在同一台机器上启动多个服务器,而它们请求的总空间超出内核限制,也可能看到这条消息。 + + + 像下面这样的错误: + +FATAL: could not create semaphores: No space left on device +DETAIL: Failed system call was semget(5440126, 17, 03600). + + 并意味着你已经用光了磁盘空间。它表示你的内核对System V信号量数量的限制,小于PostgreSQL想要创建的数量。和上面一样,你也许可以通过减少允许的连接数()来暂时绕过这个问题,但最终还是应该提高内核限制。 + + + 如果收到 illegal system call 错误,很可能是内核根本不支持共享内存或信号量。在这种情况下,唯一的办法就是重新配置内核以启用这些功能。 + + + 关于配置System V IPC功能的细节请见。 + + + + + 客户端连接问题 + + + 客户端一侧可能出现的错误种类很多,并且依赖于具体应用,但其中有一些可能直接与服务器的启动方式有关。除下面列出的几种情况外,其他问题应查阅相应客户端应用的文档。 + + + + +psql: could not connect to server: Connection refused + Is the server running on host "server.joe.com" and accepting + TCP/IP connections on port 5432? +这种常见的失败表示我找不到可通信的服务器。尝试 TCP/IP 通信时,它就会显示为上述形式。一个常见错误是忘记配置服务器以允许 TCP/IP 连接。 + + 另一种情况是,尝试通过 Unix 域套接字与本地服务器通信时,会得到以下消息: +psql: could not connect to server: No such file or directory + Is the server running locally and accepting + connections on Unix domain socket "/tmp/.s.PGSQL.5432"? + + + + 最后一行有助于确认客户端是否正尝试连接到正确的位置。如果那里实际上没有运行服务器,内核错误消息通常会像示例那样是 Connection refusedNo such file or directory。(要注意,在这里 Connection refused 并不表示服务器收到了你的连接请求并拒绝了它;那种情况会产生另一条消息,如 所示。)其他错误消息,例如 Connection timed out,可能表示更底层的问题,例如网络不通。 + + + + + 管理内核资源 + + + PostgreSQL有时会达到操作系统的各种资源上限,尤其是在同一系统上运行多个服务器实例,或在非常大型的安装环境中时更是如此。本节解释PostgreSQL使用的内核资源,以及你可以采取哪些步骤来解决与内核资源消耗相关的问题。 + + + + 共享内存和信号量 + + + 共享内存 + + + + 信号量 + + + + 共享内存和信号量统称为System V + IPC(连同消息队列,但消息队列与PostgreSQL无关)。除了在Windows上——PostgreSQL在那里为这些设施提供了自己的替代实现——之外,运行PostgreSQL需要这些设施。 + + + 完全缺少这些功能时,通常会在服务器启动时出现 Illegal system call 错误。在这种情况下,除了重新配置内核别无选择。没有它们,PostgreSQL 就无法工作。不过,在现代操作系统中,这种情况很少见。 + + + 当PostgreSQL超出各种硬性IPC限制之一时,服务器会拒绝启动,并应留下带有指导性的错误消息,说明问题所在以及应如何处理(另见)。相关内核参数在不同系统上的命名基本一致,给出了概览;不过,设置它们的方法却各不相同。下面给出一些平台上的建议。 + + + + + 在 PostgreSQL 9.3 之前,启动服务器所需的 System V 共享内存量大得多。如果你运行的是较旧版本的服务器,请查阅相应服务器版本的文档。 + + + + + + <systemitem class="osname">System V</systemitem> <acronym>IPC</acronym>参数 + + + + + + 名称 + 描述 + 合理的值 + + + + + + SHMMAX + 共享内存段的最大尺寸(字节) + 至少 1kB(如果运行多个服务器副本则需更多) + + + + SHMMIN + 共享内存段的最小尺寸(字节) + 1 + + + + SHMALL + 可用共享内存的总量(字节或页面) + 如果是字节,同SHMMAX;如果是页面,为ceil(SHMMAX/PAGE_SIZE) + + + + SHMSEG + 每个进程的最大共享内存段数目 + 只需要 1 段,但是默认值高很多 + + + + SHMMNI + 系统范围内的最大共享内存段数目 + SHMSEG外加其他应用的空间 + + + + SEMMNI + 信号量标识符(即,集合)的最大数目 + 至少 ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16) + + + + SEMMNS + 系统范围内的最大信号量数目 + ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16) * 17,并为其他应用保留空间 + + + + SEMMSL + 每个集合中信号量的最大数目 + 至少 17 + + + + SEMMAP + 信号量映射中的项数 + 见文本 + + + + SEMVMX + 信号量的最大值 + 至少 1000 (默认值常常是 32767,如非必要不要更改) + + + + +
+ + PostgreSQL 的每个服务器实例都需要少量 System V 共享内存(在 64 位平台上通常为 48 字节)。大多数现代操作系统都能轻松分配这个数量。但是,如果运行许多服务器实例,或者其他应用程序也在使用 System V 共享内存,可能需要增大 SHMMAX(共享内存段的最大字节数)或 SHMALL(系统范围内 System V 共享内存的总量)。注意,在许多系统上,SHMALL 以页而非字节为单位。 + + + 不太可能出问题的是共享内存段的最小尺寸(SHMMIN),对PostgreSQL来说应该最多大约是 32 字节(通常只是1)。而系统范围(SHMMNI)或每个进程(SHMSEG)的最大共享内存段数目不太可能会导致问题,除非你的系统把它们设成零。 + + + PostgreSQL 为每个允许的连接()、自动清理工作进程()和后台进程()各使用一个信号量,每 16 个组成一组。每组还包含第 17 个信号量,其中存放一个魔数,用来检测与其他应用程序使用的信号量集的冲突。系统中的信号量最大数量由 SEMMNS 设置,因此它必须至少等于 max_connectionsautovacuum_max_workersmax_worker_processes 之和,再为每 16 个允许的连接和工作进程额外增加一个信号量(参见 中的公式)。参数 SEMMNI 限制系统中同时存在的信号量集的数量。因此,此参数必须至少为 ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16)。降低允许的连接数,可以临时规避 semget 函数的失败;此类失败通常会报告令人困惑的 No space left on device + + + 在某些情况下,可能还需要增大SEMMAP,使其至少与SEMMNS处于同一数量级。如果系统提供这个参数(很多系统没有),它定义的是信号量资源映射的大小;映射中每个连续的可用信号量块都需要占用一项。每当一个信号量集合被释放时,它要么会并入与该释放块相邻的现有项,要么会登记为一个新的映射项。如果映射已满,被释放的信号量就会丢失(直到重启)。因此,随着时间推移,信号量空间碎片化可能会导致可用信号量少于应有数量。 + + + SEMMSL 参数决定一个信号量集中可以包含多少个信号量,对于PostgreSQL,它必须至少为 17。 + + + 与信号量撤销有关的其他各种设置,如SEMMNUSEMUME,不会影响PostgreSQL。 + + + + + + AIX + AIXIPC 配置 + + + 至少从 5.1 版起,应该不需要对 SHMMAX 等参数进行任何特殊配置,因为它似乎已配置为允许将全部内存用作共享内存。这也是 DB/2 等其他数据库常用的配置。 + + 不过,可能需要修改 /etc/security/limits 中的全局 ulimit 信息,因为文件大小(fsize)和文件数量(nofiles)的默认硬限制可能过低。 + + + + + + FreeBSD + FreeBSDIPC 配置 + + + + 可以使用sysctlloader接口更改默认 IPC 配置。下列参数可用sysctl设置: + +# sysctl kern.ipc.shmall=32768 +# sysctl kern.ipc.shmmax=134217728 + + 要让这些设置在重启之后也保持,请修改/etc/sysctl.conf。 + + + 这些信号量相关设置对于 sysctl 是只读的;设置它们时,可修改 /boot/loader.conf: + +kern.ipc.semmni=256 +kern.ipc.semmns=512 +修改该文件后,需要重启才能使新设置生效。 + + 你可能还希望配置内核,把共享内存锁定在 RAM 中,防止其被换出到交换区。这可以通过 sysctl 设置 kern.ipc.shm_use_phys 来实现。 + + 如果通过启用 sysctlsecurity.jail.sysvipc_allowed 在 FreeBSD jail 中运行,应让不同 jail 中的 postmaster 使用不同的操作系统用户。这能提高安全性,因为它可以防止非 root 用户干扰不同 jail 中的共享内存或信号量,也能让 PostgreSQL 的 IPC 清理代码正常工作。(在 FreeBSD 6.0 及更高版本中,IPC 清理代码无法正确检测其他 jail 中的进程,导致无法在不同 jail 中使用同一端口运行 postmaster。) + + 4.0 之前的 FreeBSD 版本与旧版 OpenBSD 的行为相同(见下文)。 + + + + + NetBSD + NetBSDIPC 配置 + + + NetBSD 5.0 及更高版本中,可以通过以下命令调整 IPC 参数:sysctl。例如: +# sysctl -w kern.ipc.semmni=100 +要让这些设置在重启后仍然生效,请修改 /etc/sysctl.conf。 + + + 通常需要增大 kern.ipc.semmnikern.ipc.semmns,因为 NetBSD 对这两项的默认设置小得令人担忧。 + + 你可能还希望配置内核,把共享内存锁定在 RAM 中,防止其被换出到交换区。这可以通过 sysctl 设置 kern.ipc.shm_use_phys 来实现。 + + 5.0 之前的 NetBSD 版本与旧版 OpenBSD 的行为相同(见下文),但设置内核参数时应使用关键字 options,而非 option + + + + + OpenBSD + OpenBSDIPC 配置 + + + OpenBSD 3.3 及更高版本中,可以通过以下命令调整 IPC 参数:sysctl。例如: +# sysctl kern.seminfo.semmni=100 +要让这些设置在重启后仍然生效,请修改 /etc/sysctl.conf。 + + + 通常需要增大 kern.seminfo.semmnikern.seminfo.semmns,因为 OpenBSD 对这两项的默认设置小得令人担忧。 + + 在较旧的 OpenBSD 版本中,需要构建自定义内核才能更改 IPC 参数。请确保以下选项:SYSVSHMSYSVSEM 也已启用(它们默认启用)。以下示例展示了如何在内核配置文件中设置各项参数: +option SYSVSHM +option SHMMAXPGS=4096 +option SHMSEG=256 + +option SYSVSEM +option SEMMNI=256 +option SEMMNS=512 +option SEMMNU=256 + + + + + + + + HP-UX + HP-UXIPC 配置 + + + 默认设置通常足以满足普通安装的需求。在 HP-UX 10 上,SEMMNS 的出厂默认值为 128,对较大的数据库站点来说可能过低。 + 可以在 System Administration ManagerSAM)的 Kernel ConfigurationConfigurable Parameters 中设置 IPC 参数。完成后选择 Create A New Kernel + + + + + + Linux + LinuxIPC 配置 + + + 默认最大段大小为 32 MB,默认最大总大小为 2097152 页。除使用大页的特殊内核配置外,页大小几乎总是 4096 字节(使用 getconf PAGE_SIZE 验证)。 + + 可以通过以下接口更改共享内存大小设置:sysctl。例如,要允许 16 GB: +$ sysctl -w kernel.shmmax=17179869184 +$ sysctl -w kernel.shmall=4194304 +此外,可以将这些设置保存在以下文件中,使其在重启后仍然生效:/etc/sysctl.conf。强烈建议这样做。 + + 很旧的发行版可能没有 sysctl 程序,但可以通过操作 /proc 文件系统进行等效更改: +$ echo 17179869184 >/proc/sys/kernel/shmmax +$ echo 4194304 >/proc/sys/kernel/shmall + + + + 其他默认值都相当充裕,通常不需要更改。 + + + + + + OS X + OS XIPC 配置 + + + + 在 OS X 中配置共享内存的推荐方法是创建一个名为/etc/sysctl.conf的文件,其中包含这样的变量赋值: + +kern.sysv.shmmax=4194304 +kern.sysv.shmmin=1 +kern.sysv.shmmni=32 +kern.sysv.shmseg=8 +kern.sysv.shmall=1024 + + 注意,在某些 OS X 版本中,全部五个共享内存参数都必须在/etc/sysctl.conf中设置,否则这些值会被忽略。 + + + 请注意,近期 OS X 版本会忽略将 SHMMAX 设为非 4096 整数倍的值的尝试。 + + 在这个平台上,SHMALL 以 4 kB 的页为单位。 + + 在较旧的 OS X 版本中,必须重启才能使共享内存参数的变更生效。从 10.5 起,除了 SHMMNI,都可以使用 sysctl 在线更改。但最好仍通过 /etc/sysctl.conf 设置所需的值,以便重启后保留这些值。 + + 文件 /etc/sysctl.conf 只在 OS X 10.3.9 及更高版本中生效。如果运行的是更早的 10.3.x 版本,必须编辑文件 /etc/rc 并更改以下命令中的值: +sysctl -w kern.sysv.shmmax +sysctl -w kern.sysv.shmmin +sysctl -w kern.sysv.shmmni +sysctl -w kern.sysv.shmseg +sysctl -w kern.sysv.shmall +注意,/etc/rc 通常会被 OS X 系统更新覆盖,因此每次更新后可能都需要重新进行这些编辑。 + + 在 OS X 10.2 及更早版本中,请改为编辑 /System/Library/StartupItems/SystemTuning/SystemTuning 文件中的这些命令。 + + + + + + SCO OpenServer + SCO OpenServerIPC 配置 + + + + 在默认配置中,每个共享内存段只允许 512 kB。要提高此设置,首先切换到目录/etc/conf/cf.d。要显示SHMMAX的当前值,运行: + +./configure -y SHMMAX + + 要为SHMMAX设置新值,运行: + +./configure SHMMAX=value + + 其中value是你要使用的新值(以字节为单位)。设置SHMMAX后,重新构建内核: + +./link_unix + + 然后重启。 + + + + + + + Solaris 2.6 至 2.9(Solaris 6 至 Solaris 9)SolarisIPC 配置 + + 可以在以下文件中更改相关设置:/etc/system。例如: +set shmsys:shminfo_shmmax=0x2000000 +set shmsys:shminfo_shmmin=1 +set shmsys:shminfo_shmmni=256 +set shmsys:shminfo_shmseg=256 + +set semsys:seminfo_semmap=256 +set semsys:seminfo_semmni=512 +set semsys:seminfo_semmns=512 +set semsys:seminfo_semmsl=32 +需要重启才能使变更生效。另请参见 ,了解旧版 Solaris 中共享内存的信息。 + + + + + Solaris 2.10(Solaris 10)及更高版本 + OpenSolaris + + 在 Solaris 10 及更高版本和 OpenSolaris 中,默认共享内存和信号量设置足以满足大多数 PostgreSQL 应用的需求。Solaris 现在默认将 SHMMAX 设为系统 RAM 的四分之一。要进一步调整此设置,请使用与以下用户关联的项目设置:postgres。例如,执行以下命令时使用的用户为 root: + +projadd -c "PostgreSQL DB User" -K "project.max-shm-memory=(privileged,8GB,deny)" -U postgres -G postgres user.postgres + + + + + 该命令会新增user.postgres项目,并把postgres用户的共享内存上限设为 8GB;它会在该用户下次登录时或重启PostgreSQL时(不是重新加载)生效。上述示例假定PostgreSQLpostgres组中的postgres用户运行。不需要重启操作系统。 + + + + 对于会承载大量连接的数据库服务器,我们还建议修改以下内核设置: + +project.max-shm-ids=(priv,32768,deny) +project.max-sem-ids=(priv,4096,deny) +project.max-msg-ids=(priv,4096,deny) + + + + + 此外,如果你正在某个区(zone)中运行PostgreSQL,可能也需要提高该区的资源使用限制。关于projectsprctl的更多信息,请参见System Administrator's Guide中的 "Chapter 2: Projects and Tasks"。 + + + + + + UnixWare + UnixWareIPC 配置 + + + + 在 UnixWare 7 上,默认配置中共享内存段的最大尺寸为 512 kB。 + 要显示SHMMAX的当前值,运行: + +/etc/conf/bin/idtune -g SHMMAX + + 这会显示当前值、默认值、最小值和最大值。要为SHMMAX设置新值,运行: + +/etc/conf/bin/idtune SHMMAX value + + 其中value是你要使用的新值(以字节为单位)。设置SHMMAX后,重新构建内核: + +/etc/conf/bin/idbuild -B + + 然后重启。 + + + + + + +
+ + + + systemd RemoveIPC + + + systemd + RemoveIPC + + + + 如果使用systemd,必须注意不要让操作系统过早移除 IPC 资源(共享内存和信号量)。这在从源码安装 PostgreSQL 时尤其值得关注。使用发行版软件包的用户较不容易受到影响,因为此时postgres用户通常会被创建为系统用户。 + + + + logind.conf 中的 RemoveIPC 设置控制当用户完全注销时是否移除 IPC 对象。系统用户不受此限制。原版 systemd 默认开启该设置,但某些操作系统发行版默认将其关闭。 + + + + 当此设置开启时,一个常见现象是,PostgreSQL 服务器使用的信号量对象会在看似随机的时间被删除,导致服务器崩溃并在日志中留下类似下面的消息: + +LOG: semctl(1234567890, 0, IPC_RMID, ...) failed: Invalid argument + + 不同类型的 IPC 对象(共享内存与信号量、System V 与 POSIX)在 systemd 中的处理略有不同,因此你可能会发现某些 IPC 资源不会像其他资源那样被删除。但依赖这些细微差别并不可取。 + + + + 用户注销可能会作为维护作业的一部分发生,也可能在管理员以postgres用户登录或执行类似操作时手工触发,因此通常很难彻底防止。 + + + + 什么算作系统用户,是在编译 systemd 时依据 /etc/login.defs 中的 SYS_UID_MAX 设置确定的。 + + + + 打包和部署脚本应当谨慎地使用useradd -radduser --system或其等价方式,把postgres用户创建为系统用户。 + + + + 或者,如果该用户账户创建得不正确,或无法更改,建议在/etc/systemd/logind.conf或其他适当的配置文件中设置 + +RemoveIPC=no + + + + + + + 上述两件事至少要确保做到其中之一,否则 PostgreSQL 服务器会变得非常不可靠。 + + + + + + 资源限制 + + + 类 Unix 操作系统会施加多种资源限制,这些限制可能干扰 PostgreSQL 服务器的运行。其中尤其重要的是:每个用户可用的进程数限制、每个进程可打开文件数限制,以及每个进程可用内存量限制。每一种限制都有限制和限制。实际生效的是软限制,但用户可以自行把它调高到不超过硬限制;硬限制则只能由 root 用户修改。系统调用setrlimit负责设置这些参数。shell 的内置命令ulimit(Bourne shell)或limitcsh)可用于在命令行控制资源限制。在 BSD 派生系统上,/etc/login.conf文件控制登录时设置的各种资源限制。详细信息请参阅操作系统文档。相关参数有maxprocopenfilesdatasize。例如: + +default:\ +... + :datasize-cur=256M:\ + :maxproc-cur=256:\ + :openfiles-cur=256:\ +... + + (-cur表示软限制;把它改为-max则表示设置硬限制。) + + + + 内核还可能对某些资源施加系统范围的限制。 + + + + 在Linux上,内核参数 + /proc/sys/fs/file-max确定内核支持的最大打开文件数。 + 可以向该文件写入一个不同的数字来修改它,也可以在/etc/sysctl.conf中添加相应赋值。 + 每个进程的文件数上限是在内核编译时固定的;请参阅 + /usr/src/linux/Documentation/proc.txt获取更多信息。 + + + + + + + PostgreSQL服务器为每个连接使用一个进程,因此你至少应提供与允许连接数相同数量的进程,再加上系统其他部分所需的进程数。通常这不是问题,但如果你在一台机器上运行多个服务器,资源可能就会变得紧张。 + + + + 打开文件数的出厂默认限制通常被设为对共享环境友好的值,也就是允许许多用户在一台机器上共存,而不会占用不成比例的系统资源。如果你在一台机器上运行很多服务器,这也许正合适;但在专用服务器上,你可能会希望提高这个限制。 + + + + 另一方面,有些系统允许单个进程打开非常多的文件;如果不止少数几个进程都这么做,就很容易超过系统范围的限制。如果你遇到这种情况,又不想修改系统范围的限制,可以设置PostgreSQL配置参数来限制其打开文件的消耗。 + + + + + + Linux 内存过量分配 + + + 内存过量分配 + + + + OOM + + + + 过量分配 + + + 在 Linux 2.4 及更高版本中,默认的虚拟内存行为对 PostgreSQL 并非最佳。由于内核实现内存过量分配的方式,如果 PostgreSQL 或其他进程的内存需求导致系统耗尽虚拟内存,内核可能会终止 PostgreSQL 的 postmaster(主管服务器进程)。 + + + 如果发生这种情况,你会看到类似下面的内核消息(至于应到哪里查看此类消息,请参阅你的系统文档和配置): + +Out of Memory: Killed process 12345 (postgres). + + 这表示 postgres 进程因内存压力而被终止。尽管现有数据库连接仍会继续正常运行,但新连接将不再被接受。要恢复服务,必须重启PostgreSQL。 + + + + 避免该问题的一种办法,是让PostgreSQL运行在一台你能确定不会被其他进程耗尽内存的机器上。如果内存紧张,增加操作系统交换空间也有助于避免这个问题,因为内存不足(OOM)杀手只有在物理内存和交换空间都耗尽时才会被触发。 + + + 如果导致系统耗尽内存的正是 PostgreSQL 自身,那么你可以通过调整配置来避免该问题。在某些情况下,调低与内存相关的配置参数会有帮助,尤其是 shared_bufferswork_mem。在其他情况下,允许数据库服务器接受过多连接也可能导致这一问题。很多时候,更好的做法可能是减小 max_connections,并转而使用外部连接池软件。 + + 在 Linux 2.6 及更高版本中,可以修改内核的行为,使其不会过量分配内存。虽然此设置无法完全阻止 OOM 杀手 被调用,但会显著降低发生概率,从而使系统行为更健壮。方法是选择严格的过量分配模式,使用以下命令:sysctl: + +sysctl -w vm.overcommit_memory=2 +或者在以下文件中加入等效条目:/etc/sysctl.conf。你可能还希望修改相关设置 vm.overcommit_ratio。详细信息参见内核文档文件 。 + + + + 另一种方法可在修改或不修改 vm.overcommit_memory 的情况下使用: + 把 postmaster 进程专属的OOM 评分调整值设为 -1000, + 从而保证它不会成为 OOM 杀手的目标。最简单的做法是在 postmaster + 启动脚本中、调用 postmaster 之前执行: + +echo -1000 > /proc/self/oom_score_adj + + 请注意,这个操作必须以 root 身份完成,否则不会生效;因此,由 root 拥有的启动脚本是最容易执行该操作的位置。 + 如果这样做,还应在调用 postmaster 之前,在启动脚本中设置以下环境变量: + +export PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj +export PG_OOM_ADJUST_VALUE=0 + + 这些设置会使 postmaster 子进程以常规的 OOM 评分调整值零运行,以便 OOM 杀手在需要时仍可将它们作为目标。 + 如果希望子进程以其他 OOM 评分调整值运行,也可以为 PG_OOM_ADJUST_VALUE 指定其他值。 + (也可以省略 PG_OOM_ADJUST_VALUE,此时默认为零。) + 如果不设置 PG_OOM_ADJUST_FILE,子进程就会和 postmaster 使用相同的 OOM 评分调整值, + 这并不明智,因为这样做的目的正是确保 postmaster 获得优先保护。 + + + 较旧的 Linux 内核不提供 /proc/self/oom_score_adj,但可能提供名为 /proc/self/oom_adj 的同类功能的早期版本。其工作方式相同,只是禁用值为 -17 而非 -1000 + + + 据报告,某些厂商的 Linux 2.4 内核包含 2.6 版过量分配 sysctl 参数的早期实现。但是,在没有相关代码的 2.4 内核上将 vm.overcommit_memory 设为 2,会使情况更糟,而非更好。建议在 2.4 安装上尝试之前,检查实际内核源码(参见文件 mm/mmap.c 中的函数 vm_enough_memory),确认内核支持的功能。存在 overcommit-accounting 文档文件,不能作为该功能已存在的证据。如有任何疑问,请咨询内核专家或内核供应商。 + + + + + Linux 大页 + + 使用大块连续内存时,大页可以降低开销;PostgreSQL 就采用这种内存使用方式,尤其是将 设为较大值时。要让 PostgreSQL 使用此功能,内核需要配置为 CONFIG_HUGETLBFS=yCONFIG_HUGETLB_PAGE=y。还必须调整内核设置 vm.nr_hugepages。要估算所需的大页数量,请启动 PostgreSQL 时先禁用大页,然后检查 postmaster 的 VmPeak 值以及系统的大页大小,使用的接口是 /proc 文件系统。操作可能如下所示: +$ head -1 $PGDATA/postmaster.pid +4170 +$ grep ^VmPeak /proc/4170/status +VmPeak: 6490428 kB +$ grep ^Hugepagesize /proc/meminfo +Hugepagesize: 2048 kB + + 6490428 / 2048 约等于 3169.154,因此本例至少需要 3170 个大页,可以这样设置: +$ sysctl -w vm.nr_hugepages=3170 +如果机器上的其他程序也需要大页,应使用更大的设置值。不要忘记将此设置添加到 /etc/sysctl.conf,使其在重启后重新生效。 + + 有时内核无法立即分配所需数量的大页,因此可能需要重复执行该命令或重启。(刚重启后,机器的大部分内存应该都可以转换为大页。)要确认大页分配情况,请使用: +$ grep Huge /proc/meminfo + + + + + 可能还需要通过 sysctl 设置 vm.hugetlb_shm_group, + 授予数据库服务器的操作系统用户使用大页的权限,以及/或者通过 ulimit -l 授予其锁定内存的权限。 + + + PostgreSQL 对大页的默认行为是:只要可能就使用它们;如果失败,则回退到普通页。要强制使用大页,可以在 postgresql.conf 中将 设为 on。请注意,在这种设置下,如果没有足够的大页可用,PostgreSQL 将无法启动。 + + + 关于Linux大页特性的详细说明,请参见。 + + + +
+ + + + 关闭服务器 + + + 关闭 + + + 有几种方法可以关闭数据库服务器。通过向主管进程发送不同的信号,可以控制关闭类型,该进程为 postgres + + SIGTERMSIGTERM + + + 这是智能关闭模式。 + 收到SIGTERM后,服务器禁止新连接,但允许现有会话正常结束工作。 + 仅在所有会话终止后才会关闭。如果服务器处于在线备份模式,还会等待该模式结束。在线备份模式生效期间,仍允许新的超级用户连接(这一例外允许超级用户连接以结束在线备份模式),但不允许其他新连接。如果服务器在请求智能关闭时处于恢复状态, + 则只有在所有常规会话终止后,恢复和流复制才会停止。 + + + + + + SIGINTSIGINT + + + 这是快速关闭模式。 + 服务器禁止新连接并向所有现有服务器进程发送SIGTERM, + 这将导致它们中止当前事务并迅速退出。然后等待所有服务器进程退出,最后关闭。 + 如果服务器处于在线备份模式,该模式将被终止,导致备份不可用。 + + + + + SIGQUITSIGQUIT + + 这是立即关闭模式。服务器将向所有子进程发送 SIGQUIT 并等待它们终止。如果有任何进程在 5 秒内未终止,它们将被发送 SIGKILL。一旦所有子进程退出,主管服务器进程将立即退出,而不进行正常的数据库关闭处理。这会导致下次启动时通过重放 WAL 日志执行恢复。仅建议在紧急情况下使用。 + + + + + + + 程序提供了一个便于发送这些信号以关闭服务器的接口。另外,在非 Windows 系统上,你也可以用kill直接发送这些信号。可以用ps程序,或者从数据目录中的postmaster.pid文件找到postgres进程的PID。例如,要执行一次快速关闭: + +$ kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid` + + + + + 最好不要使用 SIGKILL 关闭服务器。这样做会阻止服务器释放共享内存和信号量,因此在启动新服务器之前,可能必须手动释放它们。此外,SIGKILL 会终止 postgres 进程,使其没有机会将信号转发给子进程,因此还必须手动逐个终止子进程。 + + + + 要终止单个会话并允许其他会话继续运行,可使用pg_terminate_backend()(参阅),或者向与该会话相关的子进程发送SIGTERM信号。 + + + + + 升级 <productname>PostgreSQL</productname> 集簇 + + + 升级 + + + + 版本 + 兼容性 + + + + 本节讨论如何把数据库数据从一个PostgreSQL发行版升级到较新的发行版。 + + + + PostgreSQL的主版本由版本号的前两组数字表示,例如 8.4。PostgreSQL的次版本由版本号的第三组数字表示,例如 8.4.2 就是 8.4 的第二个次版本。次版本从不改变内部存储格式,并且始终与同一主版本号中的更早和更晚次版本兼容,例如 8.4.2 与 8.4、8.4.1 和 8.4.6 兼容。要在兼容版本之间更新,只需在服务器关闭时替换可执行文件,然后重启服务器即可。数据目录保持不变 — 次版本升级就是这么简单。 + + + + 对于PostgreSQL版本,内部数据存储格式可能会发生变化,因此升级会复杂得多。将数据迁移到新的主版本的传统方法是转储并重新载入数据库,不过这可能比较慢。更快的方法是。此外,也可以使用复制方法,如下所述。 + + + 新的主版本通常还会引入一些用户可见的不兼容变化,因此应用程序可能也需要做出修改。所有用户可见的变化都列在发行说明()中;请特别留意标为迁移(Migration)的小节。虽然你可以从一个主版本直接升级到另一个主版本,而不必逐个升级中间版本,但仍应阅读所有中间版本的主版本发行说明。 + + + + 谨慎的用户会希望在完全切换之前,先在新版本上测试客户端应用。因此,同时安装旧版本和新版本通常是个好主意。测试PostgreSQL主版本升级时,可以考虑以下几类可能的变化: + + + + + + + 管理 + + + + 管理员可用于监控和控制服务器的功能,在每个主版本中常常都会变化并有所增强。 + + + + + + + SQL + + + + 通常这意味着 SQL 命令能力会增加,而行为不会变化,除非发行说明中特别提到。 + + + + + + + 库 API + + + + 通常像libpq这样的库只会增加新功能,除非发行说明中特别说明。 + + + + + + + 系统目录 + + + + 系统目录的变化通常只影响数据库管理工具。 + + + + + + + 服务器端 C 语言 API + + + + 这涉及使用 C 语言编写的后端函数 API 的变更。这类变更会影响那些深入引用服务器内部后端函数的代码。 + + + + + + + + 通过<application>pg_dumpall</application>升级数据 + + 一种升级方法是从某个主版本的 PostgreSQL 转储数据,再在另一个版本中重新载入。要这样做,必须使用逻辑备份工具,例如 pg_dumpall;文件系统级备份方法不起作用。(系统中有相应检查,会阻止你在不兼容版本的 PostgreSQL 上使用数据目录,因此尝试用错误版本的服务器启动某个数据目录,不会造成严重损害。) + + 建议使用来自较新版本 PostgreSQLpg_dumppg_dumpall 程序,以利用这些程序可能包含的改进。当前版本的转储程序可以读取从 7.0 起任意服务器版本的数据。 + + + 以下说明假定你现有的安装位于/usr/local/pgsql目录下,数据区域位于/usr/local/pgsql/data。请根据实际情况替换成你的路径。 + + + + + + + 如果是在制作备份,请确认数据库此时没有正在进行更新。更新操作不会影响备份的完整性,但那些变更当然不会包含在备份中。必要时,可修改/usr/local/pgsql/data/pg_hba.conf(或等效文件)中的访问权限,禁止除你之外的其他人访问数据库。有关访问控制的更多信息见。 + + + + + pg_dumpall + 在升级期间使用 + + + 要备份整个数据库安装,请输入: + +pg_dumpall > outputfile + + + + + 制作备份时,你可以使用当前正在运行版本中的pg_dumpall命令,详见。不过,为了获得最佳结果,尽量使用PostgreSQL &version; 自带的pg_dumpall命令,因为这一版本相较旧版本包含了缺陷修复和改进。这个建议看起来可能有些反常,因为你这时还没有安装新版本;但如果你计划让新旧版本并行安装,遵循这一建议是明智的。在那种情况下,你可以先正常完成新版本安装,稍后再迁移数据,这样也能减少停机时间。 + + + + + + + 关闭旧服务器: + +pg_ctl stop + + 在那些会在开机时自动启动PostgreSQL的系统上,可能已经有某个启动脚本能完成同样的工作。例如,在Red Hat Linux系统上,也许下面这条命令就可以: + +/etc/rc.d/init.d/postgresql stop + + 有关服务器启动和停止的细节见。 + + + + + + + 如果是从备份恢复,请重命名或删除旧的安装目录,前提是它不是按版本区分的目录。与其删除,不如重命名,这样如果你遇到问题需要回退,它仍然还在。请记住,该目录可能会占用相当可观的磁盘空间。要重命名该目录,可以使用类似下面的命令: + +mv /usr/local/pgsql /usr/local/pgsql.old + + (请确保把该目录作为一个整体移动,这样相对路径才会保持不变。) + + + + + 安装新版本的 PostgreSQL,步骤见 。]]> + + + + + + 如有需要,创建一个新的数据库集簇。请记住,执行这些命令时必须登录到专用数据库用户账户(如果你正在升级,就已经拥有这个账户)。 + +/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data + + + + + + + + 恢复先前的pg_hba.conf,以及你对postgresql.conf所做的任何修改。 + + + + + + + 启动数据库服务器,同样要使用该专门的数据库用户账户: + +/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data + + + + + + 最后,使用以下命令从备份恢复数据: +/usr/local/pgsql/bin/psql -d postgres -f outputfile +这里应使用新版 psql。 + + + + + + 若要把停机时间降到最低,可以把新服务器安装到不同目录中,并让新旧两个服务器在不同端口上并行运行。这样就可以使用类似下面的命令: + + +pg_dumpall -p 5432 | psql -d postgres -p 5433 + + 来传输数据。 + + + + + + + 通过<application>pg_upgrade</application>升级数据 + + + 模块允许把一次安装从一个PostgreSQL主版本就地迁移到另一个主版本。升级可在几分钟内完成,特别是在使用模式时更是如此。它需要执行与上面pg_dumpall方法类似的步骤,例如启动/停止服务器、运行initdb等。pg_upgrade文档概述了所需步骤。 + + + + + + 通过复制升级数据 + + 也可以使用某些复制方法,例如 Slony,创建一个运行较新版本 PostgreSQL 的备库。之所以可行,是因为 Slony 支持不同主版本 PostgreSQL 之间进行复制。该备库可以与旧服务器部署在同一台机器上,也可以部署在不同机器上。一旦它与主库(运行旧版本 PostgreSQL)同步,就可以切换主备角色,让备库成为主库,并关闭旧的数据库实例。这种计划内切换只会为升级带来几秒钟的停机时间。 + + + + + + 防止服务器欺骗 + + + 服务器欺骗 + + + + 当服务器正在运行时,恶意用户不可能取代正常的数据库服务器。然而,当服务器关闭时,本地用户却可能通过启动自己的服务器来冒充正常服务器。这个伪造服务器可以读取客户端发出的密码和查询语句,但由于PGDATA目录仍然受目录权限保护,它无法返回任何真实数据。之所以会出现这种冒充,是因为任何用户都可以启动一个数据库服务器;而客户端除非经过专门配置,否则无法识别服务器是否为伪造。 + + + + 防止local连接被欺骗的一种办法,是使用一个 Unix 域套接字目录(),并且只允许可信的本地用户对该目录拥有写权限。这样可以防止恶意用户在其中创建自己的套接字文件。如果你担心某些应用程序仍会引用/tmp中的套接字文件,因此仍然容易受到欺骗,那么可以在操作系统启动时创建一个符号链接/tmp/.s.PGSQL.5432,使其指向迁移后的套接字文件。你可能还需要修改/tmp清理脚本,防止该符号链接被删除。 + + + + 对于local连接,另一个选项是让客户端使用requirepeer,指定连接到该套接字的服务器进程必须由哪个用户拥有。 + + + 要防止 TCP 连接上的服务器欺骗,最佳办法是使用 SSL 证书,并确保客户端检查服务器证书。为此,服务器必须配置为仅接受 hostssl 连接(),并且具备 SSL 密钥和证书文件()。TCP 客户端必须使用 sslmode=verify-caverify-full 连接,并安装适当的根证书文件()。 + + + + 加密选项 + + + 加密 + + + + PostgreSQL提供了多个层次的加密,并在防止数据因数据库服务器被盗、不诚实的管理员或不安全网络等原因而泄露方面提供了很高的灵活性。为了保护医疗记录、金融交易等敏感数据,也可能需要使用加密。 + + + + + + 密码存储加密 + + + + 默认情况下,数据库用户密码以 MD5 哈希形式存储,因此管理员无法得知分配给用户的实际密码。如果客户端认证使用 MD5 加密,明文密码甚至不会暂时出现在服务器端,因为客户端会在通过网络发送前先对其进行 MD5 加密。 + + + + + + 跨网络安全密码 + + + + MD5认证方法会在把密码发送到服务器之前,在客户端对它进行双重加密。它首先基于用户名对密码做 MD5 加密,然后再基于建立数据库连接时服务器发送的一个随机盐进行加密。通过网络发送给服务器的正是这个双重加密后的值。双重加密不仅可防止密码被破解,还能防止其他连接在之后利用同一个加密密码连接到数据库服务器。 + + + + + + + 指定列加密 + + + + + 模块允许以加密形式存储特定字段。这在只有部分数据敏感时很有用。客户端提供解密密钥,数据在服务器端解密后再发送给客户端。 + + + + 当数据被解密并在服务器与客户端之间传输时,解密后的数据和解密密钥会在服务器端短暂存在。这就给那些可以完全访问数据库服务器的人(例如系统管理员)提供了一个短暂的机会来截获密钥和数据。 + + + + + + + 数据分区加密 + + + + + 存储加密可以在文件系统层或块设备层实现。Linux 的文件系统加密方案包括 eCryptfs 和 EncFS,而 FreeBSD 使用 PEFS。块级或整盘加密方案则包括 Linux 上的 dm-crypt + LUKS,以及 FreeBSD 上的 GEOM 模块 geli 和 gbde。许多其他操作系统也支持这类功能,包括 Windows。 + + + + 这种机制可以防止在整台计算机或磁盘被盗时,从驱动器中直接读取未加密数据。但它无法防御文件系统已挂载时的攻击,因为在挂载之后,操作系统会提供数据的解密视图。不过,要挂载该文件系统,就必须以某种方式把加密密钥提供给操作系统,而有时这个密钥就存放在挂载该磁盘的主机上的某处。 + + + + + + 跨网络加密数据 + + + SSL 连接会加密通过网络发送的全部数据:密码、查询和返回的数据。pg_hba.conf 文件允许管理员指定哪些主机可以使用非加密连接(host),哪些主机必须使用 SSL 加密连接(hostssl)。此外,客户端可以指定仅通过 SSL 连接到服务器。也可以使用 StunnelSSH 加密传输。 + + + + + + SSL 主机认证 + + + + + 客户端和服务器都可以向对方提供 SSL 证书。这需要双方做一些额外配置,但它提供的认证强度高于单纯依赖密码。它可以防止某台计算机短暂冒充服务器,只为读取客户端发送的密码;同时也有助于防御中间人攻击,即某台位于客户端与服务器之间的计算机冒充服务器,读取并转发双方之间的全部数据。 + + + + + + + 客户端加密 + + + + + 如果服务器所在机器的系统管理员不可信,那么就有必要由客户端自行加密数据。这样一来,未加密的数据就永远不会出现在数据库服务器上。数据在发送给服务器之前就在客户端加密,而查询结果也必须在客户端解密后才能使用。 + + + + + + + + + + 使用 SSL 的安全 TCP/IP 连接 + + + SSL + + + + PostgreSQL原生支持使用SSL连接来加密客户端与服务器之间的通信,以提高安全性。这要求客户端和服务器系统上都安装了OpenSSL,并且在构建PostgreSQL时启用了该支持(见)。 + + + 如果构建时启用了 SSL 支持,那么在 postgresql.conf 中将参数 设为 on,就可以在启动 PostgreSQL 服务器时启用 SSL。服务器会在同一个 TCP 端口上同时监听普通连接和 SSL 连接,并与每个连接进来的客户端协商是否使用 SSL。默认情况下,这由客户端决定;关于如何把服务器配置为要求某些或全部连接必须使用 SSL,请参阅 + + + PostgreSQL会读取系统范围的OpenSSL配置文件。默认情况下,该文件名为openssl.cnf,位于openssl version -d报告的目录中。可以通过把环境变量OPENSSL_CONF设为所需配置文件的名称,覆盖这一默认值。 + + + + OpenSSL支持种类繁多、强度不一的密码套件和认证算法。虽然可以在OpenSSL配置文件中指定密码套件列表,但你也可以通过修改postgresql.conf中的,专门为数据库服务器指定要使用的密码套件。 + + + + + + 使用NULL-SHANULL-MD5密码套件,可以在没有加密开销的情况下完成认证。不过,中间人仍然能够读取并转发客户端与服务器之间的通信。此外,与认证的开销相比,加密开销本身很小。基于这些原因,不建议使用 NULL 密码套件。 + + + + + 要以SSL模式启动服务器,必须存在包含服务器证书和私钥的文件。默认情况下,这些文件应分别命名为server.crtserver.key,并放在服务器的数据目录中;不过也可以通过配置参数指定其他名称和位置。 + + + + 在 Unix 系统上,server.key的权限必须禁止组和其他用户访问;可以通过chmod 0600 server.key实现。或者,该文件也可以由 root 拥有并授予组读权限(即0640)。这种设置适用于证书和密钥文件由操作系统统一管理的安装方式。运行PostgreSQL服务器的用户此时应属于有权访问这些证书和密钥文件的组。 + + + 如果私钥受口令保护,服务器会提示输入口令,并在输入之前一直等待,不会启动。 + + + server.crt中的第一个证书必须是服务器证书,因为它必须与服务器私钥相匹配。中间证书颁发机构的证书也可以追加到该文件中。假设根证书和中间证书是使用v3_ca 扩展创建的(这会将证书的 CA 基本约束设为 true),这样做就可以避免在客户端上存储中间证书。同时,这也使中间证书更容易单独过期。 + + + + 无需把根证书加入server.crt。相反,客户端必须持有服务器证书链对应的根证书。 + + + + 使用客户端证书 + + 要要求客户端提供受信任的证书,请把你信任的根证书颁发机构(CA)的证书放入数据目录中的某个文件,在 postgresql.conf 中将参数 设为该文件名,并在 pg_hba.conf 中相应的 hostssl 行上添加认证选项 clientcert=1。这样,SSL 连接启动时就会向客户端请求证书。(关于如何在客户端设置证书,请参见 。)服务器会验证客户端证书是否由某个受信任的证书颁发机构签名。 + + 如果希望避免在客户端上存储中间证书,那么与现有根证书构成链的中间证书也可以出现在 root.crt 文件中(前提是根证书和中间证书使用 v3_ca 扩展创建)。如果设置了参数 ,还会检查证书吊销列表(CRL)条目。 + + clientcert 认证选项适用于所有认证方法,但只适用于 pg_hba.conf 中指定为 hostssl 的行。未指定 clientcert 或将其设为 0 时,如果配置了 CA 文件,服务器仍会依据该文件验证客户端提供的证书,但不会强制要求客户端提供证书。 + + 如果正在设置客户端证书,可以考虑使用 cert 认证方法,让证书在提供连接安全性的同时也控制用户认证。详情参见 。(使用 cert 认证方法时,不必显式指定 clientcert=1。) + + + + SSL 服务器文件用法 + + 概括了服务器端 SSL 设置相关的文件。(表中显示的是默认或典型文件名;本地实际配置的名称可能不同。) + + + SSL 服务器文件用法 + + + + + 文件 + 内容 + 效果 + + + + + + + ($PGDATA/server.crt) + 服务器证书 + 发送给客户端,用于表明服务器身份 + + + + ($PGDATA/server.key) + 服务器私钥 + 证明服务器证书是其所有者发送的,并不说明证书所有者是值得信任的 + + + + ($PGDATA/root.crt) + 可信的证书颁发机构 + 检查客户端证书是由一个可信的证书颁发机构签名的 + + + + ($PGDATA/root.crl) + 被证书颁发机构吊销的证书 + 客户端证书不能出现在这个列表上 + + + + +
+ + + 文件server.keyserver.crtroot.crtroot.crl(或它们配置的替代名称)只在服务器启动期间检查;因此要让这些文件中的变更生效,必须重启服务器。 + +
+ + + + 创建证书 + + + 要为服务器创建一个有效期为 365 天的简单自签名证书,可以使用下面的OpenSSL命令,并将dbhost.yourdomain.com替换为服务器主机名: + +openssl req -new -x509 -days 365 -nodes -text -out server.crt \ + -keyout server.key -subj "/CN=dbhost.yourdomain.com" + + 然后执行: + +chmod og-rwx server.key + + 如果该文件权限比这更宽松,服务器会拒绝使用它。关于如何创建服务器私钥和证书的更多细节,请参阅OpenSSL文档。 + + + + 虽然自签名证书可用于测试,但在生产环境中应使用由证书颁发机构(CA,通常是企业范围内的根 CA)签名的证书。 + + + + 要创建一个可由客户端验证其身份的服务器证书,首先要创建证书签名请求(CSR)以及公钥/私钥文件: + +openssl req -new -nodes -text -out root.csr \ + -keyout root.key -subj "/CN=root.yourdomain.com" +chmod og-rwx root.key + + 然后,使用该密钥对请求进行签名,以创建一个根证书颁发机构(这里使用的是LinuxOpenSSL配置文件的默认位置): + +openssl x509 -req -in root.csr -text -days 3650 \ + -extfile /etc/ssl/openssl.cnf -extensions v3_ca \ + -signkey root.key -out root.crt + + 最后,创建一个由新根证书颁发机构签名的服务器证书: + +openssl req -new -nodes -text -out server.csr \ + -keyout server.key -subj "/CN=dbhost.yourdomain.com" +chmod og-rwx server.key + +openssl x509 -req -in server.csr -text -days 365 \ + -CA root.crt -CAkey root.key -CAcreateserial \ + -out server.crt + + server.crtserver.key应存放在服务器上,而root.crt应存放在客户端上,以便客户端能够验证服务器的叶子证书是否由其受信任的根证书签名。root.key应离线保存,以供将来签发证书时使用。 + + + + 也可以创建包含中间证书的信任链: + +# root +openssl req -new -nodes -text -out root.csr \ + -keyout root.key -subj "/CN=root.yourdomain.com" +chmod og-rwx root.key +openssl x509 -req -in root.csr -text -days 3650 \ + -extfile /etc/ssl/openssl.cnf -extensions v3_ca \ + -signkey root.key -out root.crt + +# intermediate +openssl req -new -nodes -text -out intermediate.csr \ + -keyout intermediate.key -subj "/CN=intermediate.yourdomain.com" +chmod og-rwx intermediate.key +openssl x509 -req -in intermediate.csr -text -days 1825 \ + -extfile /etc/ssl/openssl.cnf -extensions v3_ca \ + -CA root.crt -CAkey root.key -CAcreateserial \ + -out intermediate.crt + +# leaf +openssl req -new -nodes -text -out server.csr \ + -keyout server.key -subj "/CN=dbhost.yourdomain.com" +chmod og-rwx server.key +openssl x509 -req -in server.csr -text -days 365 \ + -CA intermediate.crt -CAkey intermediate.key -CAcreateserial \ + -out server.crt + + server.crtintermediate.crt应拼接为一个证书文件包并存放在服务器上。server.key也应存放在服务器上。root.crt应存放在客户端上,以便客户端验证服务器的叶子证书是否由一条可追溯到其受信任根证书的证书链签名。root.keyintermediate.key应离线保存,以供将来签发证书时使用。 + + + +
+ + + 通过 <application>SSH</application> 隧道建立安全 TCP/IP 连接 + + + ssh + + + + 可以使用SSH来加密客户端与PostgreSQL服务器之间的网络连接。如果配置得当,即使客户端本身不支持 SSL,这种方法也能提供足够安全的网络连接。 + + + + 首先确认在PostgreSQL服务器所在的同一台机器上有一个正常运行的SSH服务器,并且你可以使用ssh以某个用户身份登录;然后就可以建立通向远程服务器的安全隧道。安全隧道会监听本地端口,并把所有流量转发到远程机器上的某个端口。发送到该远程端口的流量可以到达其localhost地址,或者在需要时到达其他绑定地址;对远端来说,这些流量看起来并不是来自你的本地机器。下面这个命令会创建一条从客户端机器到远程机器foo.com的安全隧道: + +ssh -L 63333:localhost:5432 joe@foo.com + + 参数中的第一个数字 63333 是隧道在本地的端口号;它可以是任何未使用的端口。(IANA 保留 49152 到 65535 端口供私用。)其后的名称或 IP 地址是你要连接的远程绑定地址,这里是默认值localhost。第二个数字 5432 是隧道远端的端口,例如数据库服务器所使用的端口。要通过这条隧道连接数据库服务器,只需连接本地机器上的 63333 端口: + +psql -h localhost -p 63333 postgres + + 对数据库服务器来说,它看到的是来自主机foo.com上用户joelocalhost绑定地址的连接,并将应用为该用户到该绑定地址所配置的认证方式。请注意,服务器不会认为这是 SSL 加密连接,因为实际上SSH服务器与PostgreSQL服务器之间并没有加密。不过这通常不会带来额外安全风险,因为两者位于同一台机器上。 + + + 要让这条隧道建立成功,你必须有权通过 sshjoe@foo.com 身份连接,就像尝试使用 ssh 创建终端会话一样。 + + + 你也可以把端口转发设成这样: + +ssh -L 63333:foo.com:5432 joe@foo.com + + 但这样一来,数据库服务器会把该连接看作是到达其foo.com绑定地址的连接,而默认设置listen_addresses = 'localhost'并不会监听该地址。这通常不是你想要的结果。 + + + + 如果你必须经由某个登录主机跳转到数据库服务器,那么一种可能的设置如下: + +ssh -L 63333:db.foo.com:5432 joe@shell.foo.com + + 请注意,这种从shell.foo.comdb.foo.com的连接不会受到 SSH 隧道的加密保护。当网络受到各种限制时,SSH 还提供了许多其他配置方式。详情请参阅 SSH 文档。 + + + + + + 还有一些其他应用也能提供安全隧道,其思路与刚才描述的 SSH 方法类似。 + + + + + + + 在<systemitem class="osname">Windows</systemitem>上注册<application>事件日志</application> + + + 事件日志 + 事件日志 + + + + 要向操作系统注册一个Windows事件日志库,请执行以下命令: + +regsvr32 pgsql_library_directory/pgevent.dll + + 这会创建被事件查看器使用的注册表项,默认事件源命名为PostgreSQL。 + + + + 若要指定不同的事件源名称(见),请使用/n/i选项: + +regsvr32 /n /i:event_source_name pgsql_library_directory/pgevent.dll + + + + + 要从操作系统中注销该事件日志库,请执行以下命令: + +regsvr32 /u [/i:event_source_name] pgsql_library_directory/pgevent.dll + + + + + + + 要在数据库服务器中启用事件日志,请在postgresql.conf中修改,使其包含eventlog。 + + + + +
diff --git a/zh/9.6/seg.sgml b/zh/9.6/seg.sgml new file mode 100644 index 00000000..aebe6096 --- /dev/null +++ b/zh/9.6/seg.sgml @@ -0,0 +1,337 @@ + + + + seg — 用于线段或浮点区间的数据类型 + + + seg + + + + 该模块实现了一个用于表示线段或浮点区间的seg数据类型。 + seg可以表示区间端点中的不确定性,因此特别适合表示实验室测量结果。 + + + + 原理 + + + 测量值的几何形态通常比数值连续体中的一个点更复杂。一次测量通常是该 + 连续体上的一段,其边界多少有些模糊。测量结果之所以会呈现为区间, + 一方面是由于不确定性和随机性,另一方面也是因为被测值本身可能天然就是 + 表示某种状态的区间,例如蛋白质的稳定温度范围。 + + + + 凭常识也能看出,把这类数据存储为区间比存储为一对数字更方便。实际上, + 在大多数应用中,这样做甚至更高效。 + + + + 再顺着这一常识往下想,边界的模糊性说明,使用传统数值数据类型会造成 + 一定的信息损失。设想一下:你的仪器读数是 6.50,而你把这个读数输入到 + 数据库。取出来时会得到什么?请看: + + +test=> select 6.50 :: float8 as "pH"; + pH +--- +6.5 +(1 row) + + + 在测量领域,6.50 与 6.5 并不相同。有时这种差别至关重要。实验人员通常会 + 记下(并发表)他们认为可靠的那些数位。6.50 实际上是一个模糊区间,它包含在 + 更大、也更模糊的区间 6.5 之中;它们共享的特征(大概)只有中心点。 + 我们当然不希望这类不同的数据项看起来却一样。 + + + + 结论是什么?最好能有一种专门的数据类型,能够以任意可变的精度记录区间边界。 + 这里所谓“可变”,是指每个数据元素都记录其自身的精度。 + + + + 看看这个: + + +test=> select '6.25 .. 6.50'::seg as "pH"; + pH +------------ +6.25 .. 6.50 +(1 row) + + + + + + 语法 + + + 区间的外部表示由一个或两个浮点数通过范围操作符(.. + 或 ...)连接而成。另一种写法是指定中心点再加减一个 + 偏差值。还可以存储可选的确定性指示符(<、 + >~)。(不过,所有内置操作符 + 都会忽略这些确定性指示符。)概述了允许的 + 表示形式;给出了一些示例。 + + + + 在中,x、 + ydelta表示浮点数。 + xy前面可以带确定性 + 指示符,而delta不可以。 + + + + + <type>seg</type> 外部表示 + + + + + x + 单个值(零长度区间) + + + + + x .. y + xy的区间 + + + + + x (+-) delta + x - delta到 + x + delta的区间 + + + + + x .. + 下界为x、上界开放的区间 + + + + + .. x + 上界为x、下界开放的区间 + + + + +
+ + + 合法 <type>seg</type> 输入示例 + + + + + 5.0 + + 创建一个零长度线段(如果你愿意,也可以把它看成一个点) + + + + + ~5.0 + + 创建一个零长度线段,并在数据中记录~。 + ~会被seg操作忽略,但会作为注释保留下来。 + + + + + <5.0 + + 在 5.0 处创建一个点。<会被忽略,但会作为注释保留下来。 + + + + + >5.0 + + 在 5.0 处创建一个点。>会被忽略,但会作为注释保留下来。 + + + + + 5(+-)0.3 + + 创建区间4.7 .. 5.3。注意,(+-)记法不会被保留。 + + + + + 50 .. + 所有大于或等于 50 的值 + + + + .. 0 + 所有小于或等于 0 的值 + + + + 1.5e-2 .. 2E-2 + 创建区间0.015 .. 0.02 + + + + 1 ... 2 + + 与1...21 .. 21..2相同 + (范围操作符两侧的空格会被忽略) + + + + +
+ + + 由于...操作符在数据源中被广泛使用,因此允许把它作为 + ..操作符的另一种写法。不幸的是,这会带来解析歧义: + 无法确定0...23中的上界是23还是 + 0.23。解决办法是要求seg输入中的所有 + 数字在小数点前至少有一位数字。 + + + + 作为合理性检查,seg会拒绝下界大于上界的区间,例如 + 5 .. 2。 + + +
+ + + 精度 + + + seg值在内部存储为一对 32 位浮点数。这意味着有效位数超过 7 位的 + 数字会被截断。 + + + + 有效位数不超过 7 位的数字会保留其原始精度。也就是说,如果你的查询返回 + 0.00,你可以确信末尾的零不是格式化造成的假象,而是反映了原始数据的精度。 + 前导零的个数不影响精度:值 0.0067 被认为只有 2 位有效数字。 + + + + + 用法 + + + seg模块包含适用于seg值的一个 GiST 索引 + 操作符类。该 GiST 操作符类支持的操作符见。 + + + + seg GiST 操作符 + + + + + 操作符 + 描述 + + + + + + [a, b] << [c, d] + [a, b] 完全位于 [c, d] 的左侧。也就是说,当 b < c 时,[a, b] << [c, d] 为真,否则为假。 + + + + [a, b] >> [c, d] + [a, b] 完全位于 [c, d] 的右侧。也就是说,当 a > d 时,[a, b] >> [c, d] 为真,否则为假。 + + + + [a, b] &< [c, d] + 重叠或位于左侧 — 也可以更好地理解为不会延伸到右侧。当 b <= d 时为真。 + + + + [a, b] &> [c, d] + 重叠或位于右侧 — 也可以更好地理解为不会延伸到左侧。当 a >= c 时为真。 + + + + [a, b] = [c, d] + 相同 — 线段 [a, b] 和 [c, d] 相同,即 a = c 且 b = d。 + + + + [a, b] && [c, d] + 线段 [a, b] 和 [c, d] 重叠。 + + + + [a, b] @> [c, d] + 线段 [a, b] 包含线段 [c, d],即 a <= c 且 b >= d。 + + + + [a, b] <@ [c, d] + 线段 [a, b] 包含于 [c, d] 中,即 a >= c 且 b <= d。 + + + +
+ + (在 PostgreSQL 8.2 之前,包含操作符 @><@ 分别称为 @~。这些名称仍然可用,但已弃用,最终将被删除。请注意,旧名称与核心几何数据类型以前采用的约定正好相反!) + + 还提供标准的 B-树操作符,例如操作符描述[a, b] < [c, d]小于[a, b] > [c, d]大于这些操作符除了排序之外,在实际用途上没有太大意义。它们首先比较 (a) 与 (c),如果相等,再比较 (b) 与 (d)。这在大多数情况下会产生相当好的排序效果;如果希望对这种类型使用 ORDER BY,这会很有用。 +
+ + + 注意 + + + 有关用法示例,请参见回归测试sql/seg.sql。 + + + + 把(+-)转换为常规范围的机制,在确定边界的有效位数时 + 并不完全准确。例如,如果结果区间包含 10 的幂,它会在下边界上多加一位: + + +postgres=> select '10(+-)1'::seg as seg; + seg +--------- +9.0 .. 11 -- should be: 9 .. 11 + + + + + R 树索引的性能很大程度上取决于输入值的初始顺序。按seg列 + 对输入表排序,可能会很有帮助;示例见脚本 + sort-segments.pl。 + + + + + 致谢 + + + 原作者:Gene Selkov, Jr. selkovjr@mcs.anl.gov, + 阿贡国家实验室数学与计算机科学部。 + + + + 我首先要感谢 Joe Hellerstein 教授 + (), + 他为我阐明了 GiST + () 的要旨。 + 我同样感谢过去和现在所有的 Postgres 开发者, + 他们使我得以创造自己的世界并在其中不受打扰地生活。 + 我还要感谢阿贡实验室以及美国能源部,多年来始终如一地支持我的数据库研究。 + + + + +
diff --git a/zh/9.6/sepgsql.sgml b/zh/9.6/sepgsql.sgml new file mode 100644 index 00000000..b7fc7510 --- /dev/null +++ b/zh/9.6/sepgsql.sgml @@ -0,0 +1,671 @@ + + + + sepgsql — + 基于 SELinux 标签的强制访问控制(MAC)安全模块 + + + sepgsql + + + + sepgsql 是一个可加载模块,支持基于 + SELinux 安全策略和安全标签的强制访问控制(MAC)。 + + + + + 当前实现存在重大限制,并不会对所有操作实施强制访问控制。详见 + 。 + + + + + 概述 + + + 该模块与 SELinux 集成,在 + PostgreSQL 通常提供的安全检查之外再增加一层 + 安全检查。从 SELinux 的角度看,该模块使 + PostgreSQL 能够充当用户空间对象管理器。由 DML + 查询发起的每一次表或函数访问,都会依据系统安全策略进行检查。这种检查是 + 对 PostgreSQL 常规 SQL 权限检查的补充。 + + + + SELinux 的访问控制决策通过安全标签作出,安全标签 + 以 system_u:object_r:sepgsql_table_t:s0 这样的字符串表示。 + 每个访问控制决策都涉及两个标签:尝试执行操作的主体标签,以及要执行该操 + 作的对象标签。由于这些标签可以应用到任意类型的对象,数据库中存储对象的 + 访问控制决策也可以与文件等其他类型对象一样,遵循同一套一般性标准;使 + 用该模块时也确实如此。这样的设计旨在让集中式安全策略能够保护信息资产, + 而不受这些资产具体存储方式的影响。 + + + 语句允许为数据库对象分配安全标签。 + + + + 安装 + + + sepgsql 只能在启用了 + SELinux 的 + Linux 2.6.28 或更高版本上使用。 + 它在其他任何平台上都不可用。还需要 + libselinux 2.1.10 或更高版本,以及 + selinux-policy 3.9.13 或更高版本(尽管某些发行版 + 可能会把所需规则回移植到较旧的策略版本中)。 + + + + 可以使用 sestatus 命令检查 + SELinux 的状态。典型显示如下: + +$ sestatus +SELinux status: enabled +SELinuxfs mount: /selinux +Current mode: enforcing +Mode from config file: enforcing +Policy version: 24 +Policy from config file: targeted + + 如果 SELinux 被禁用或尚未安装,则必须先安装并配置 + 它,之后才能安装本模块。 + + + 要构建此模块,请将选项 --with-selinux 加入 PostgreSQL 的 configure 命令中。还要确保构建时已经安装 libselinux-devel RPM。 + + + 要使用此模块,必须将 sepgsql 包含在 + 参数中,该参数位于 + postgresql.conf 内。如果以其他任何方式加载该模块,它都 + 无法正确工作。 + 模块加载后,应在每个数据库中执行 sepgsql.sql。这会安装 + 安全标签管理所需的函数,并分配初始安全标签。 + + + + 下面的示例展示了如何初始化一个全新的数据库集簇,并安装 + sepgsql 函数和安全标签。请根据实际安装情况调整其中的路径: + + + +$ export PGDATA=/path/to/data/directory +$ initdb +$ vi $PGDATA/postgresql.conf + change + #shared_preload_libraries = '' # (change requires restart) + to + shared_preload_libraries = 'sepgsql' # (change requires restart) +$ for DBNAME in template0 template1 postgres; do + postgres --single -F -c exit_on_error=true $DBNAME \ + </usr/local/pgsql/share/contrib/sepgsql.sql >/dev/null + done + + + + 请注意,具体会看到以下通知中的哪些,取决于所使用的 + libselinux 和 + selinux-policy 版本,可能是部分,也可能是全部: + +/etc/selinux/targeted/contexts/sepgsql_contexts: line 33 has invalid object type db_blobs +/etc/selinux/targeted/contexts/sepgsql_contexts: line 36 has invalid object type db_language +/etc/selinux/targeted/contexts/sepgsql_contexts: line 37 has invalid object type db_language +/etc/selinux/targeted/contexts/sepgsql_contexts: line 38 has invalid object type db_language +/etc/selinux/targeted/contexts/sepgsql_contexts: line 39 has invalid object type db_language +/etc/selinux/targeted/contexts/sepgsql_contexts: line 40 has invalid object type db_language + + 这些消息没有害处,应予忽略。 + + + + 如果安装过程无错误完成,此时就可以正常启动服务器。 + + + + + 回归测试 + + 由于 SELinux 的特性,运行 sepgsql 的回归测试需要额外执行若干配置步骤,其中部分步骤必须以 root 身份完成。普通的 make checkmake installcheck 命令不会运行这些回归测试;必须完成配置,然后手工调用测试脚本。这些测试必须在已配置的 PostgreSQL 构建树的 contrib/sepgsql 目录中运行。虽然需要构建树,但这些测试是为已安装的服务器设计的,也就是说,它们类似于 make installcheck 而不是 make check + + 首先,在一个正常工作的数据库中设置 sepgsql,具体请按照 中的说明操作。注意,当前操作系统用户必须能够不经密码认证就以超级用户身份连接数据库。 + + + 第二,构建并安装回归测试所需的策略包。sepgsql-regtest + 策略是一个专用策略包,提供了一组允许在回归测试期间使用的规则。它应从策 + 略源文件 sepgsql-regtest.te 构建,构建过程通过使用 + make 和由 SELinux 提供的 Makefile 完成。需要在本机上找 + 到合适的 Makefile;下面展示的路径仅为示例。构建完成后,使用 + semodule 命令安装该策略包,该命令会将提供的策略包装载到 + 内核中。如果安装正确,semodule -l 应该把 + sepgsql-regtest 列为可用策略包之一: + + + +$ cd .../contrib/sepgsql +$ make -f /usr/share/selinux/devel/Makefile +$ sudo semodule -u sepgsql-regtest.pp +$ sudo semodule -l | grep sepgsql +sepgsql-regtest 1.07 + + + + 第三,打开 sepgsql_regression_test_mode。出于安全原因, + sepgsql-regtest 中的规则默认并未启用; + sepgsql_regression_test_mode 参数会启用启动回归测试所需 + 的规则。可以使用 setsebool 命令将其打开: + + + +$ sudo setsebool sepgsql_regression_test_mode on +$ getsebool sepgsql_regression_test_mode +sepgsql_regression_test_mode --> on + + + + 第四,确认 shell 正在 unconfined_t 域中运行: + + +$ id -Z +unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 + + + + 如有需要,请参阅 ,了解如何调整工作域。 + + + 最后,运行回归测试脚本: + +$ ./test_sepgsql + + + + 该脚本会尝试验证是否已正确完成全部配置步骤,然后运行 + sepgsql 模块的回归测试。 + + + + 完成测试后,建议关闭 + sepgsql_regression_test_mode 参数: + + + +$ sudo setsebool sepgsql_regression_test_mode off + + + + 也可以选择彻底移除 sepgsql-regtest 策略: + + + +$ sudo semodule -r sepgsql-regtest + + + + + GUC 参数 + + + + + sepgsql.permissive (boolean) + + sepgsql.permissive 配置参数 + + + + + 该参数使 sepgsql 无论系统设置如何都以宽容模式运行。 + 默认值为关闭。该参数只能在 postgresql.conf 文件中 + 或服务器命令行上设置。 + + + + 当该参数打开时,sepgsql 会以宽容模式运行,即使 + SELinux 整体处于强制模式也是如此。该参数主要用于测试。 + + + + + + + sepgsql.debug_audit (boolean) + + sepgsql.debug_audit 配置参数 + + + + + 该参数会在不考虑系统策略设置的情况下启用审计消息输出。 + 默认值为关闭,这意味着消息将按系统设置输出。 + + + + SELinux 的安全策略本身也有规则来控制是否记录特 + 定的访问。默认情况下,访问违例会被记录,而被允许的访问不会被记录。 + + + + 该参数会在不考虑系统策略的情况下,强制打开所有可能的日志记录。 + + + + + + + + 特性 + + 受控对象类 + + SELinux 的安全模型把全部访问控制规则描述为主体 + 实体(通常是数据库客户端)与对象实体(例如数据库对象)之间的关系,这两 + 者都由安全标签标识。如果尝试访问一个未带标签的对象,则会把它视为被赋予 + 了 unlabeled_t 标签。 + + + + 当前,sepgsql 允许为模式、表、列、序列、视图和函数分 + 配安全标签。使用 sepgsql 时,安全标签会在受支持的数据 + 库对象创建时自动分配。这个标签称为默认安全标签,其取值由系统安全策略决 + 定;策略的输入包括创建者的标签、新对象父对象的标签,以及待创建对象的名 + 称(如果需要)。 + + + + 新数据库对象基本上会继承父对象的安全标签,但如果安全策略中存在称为类型 + 转换规则的特殊规则,则可能会应用不同的标签。对于模式,父对象是当前数据 + 库;对于表、序列、视图和函数,父对象是其所在模式;对于列,父对象是其所 + 在表。 + + + + + DML 权限 + + + 对于表,会根据语句类型,对所有被引用的目标表检查 + db_table:selectdb_table:insert、 + db_table:updatedb_table:delete; + 此外,对于其列在 WHERERETURNING + 子句中被引用的所有表,以及作为 UPDATE 数据源的所有表等, + 还会检查 db_table:select。 + + + + 每个被引用的列也都会检查列级权限。db_column:select + 不仅会对通过 SELECT 读取的列进行检查,也会对其他 DML + 语句中被引用的列进行检查;对于被 UPDATE 或 + INSERT 修改的列,还会检查 + db_column:update 或 + db_column:insert。 + + + 例如,请看: +UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100; +这里,db_column:update会针对以下列进行检查:t1.x,因为它正在被更新;db_column:{select update}会针对以下列进行检查:t1.y,因为它既被更新又被引用;而db_column:select会针对以下列进行检查:t1.z,因为它仅被引用。db_table:{select update}也会在表级进行检查。 + + + 对于序列,当使用 SELECT 引用序列对象时,会检查 + db_sequence:get_value;但请注意,当前不会检查执行相 + 应函数(如 lastval())的权限。 + + + + 对于视图,会先检查 db_view:expand,然后对由该视图展 + 开得到的各个对象分别检查所需的其他权限。 + + + + 对于函数,当用户试图在查询中执行某个函数,或通过快速路径调用执行某个函 + 数时,会检查 db_procedure:{execute}。如果该函数是受信 + 任过程,还会检查 db_procedure:{entrypoint} 权限,以判定 + 它是否可以充当受信任过程的入口点。 + + + + 要访问任何模式对象,都需要在其所在模式上具有 + db_schema:search 权限。当对象在引用时未带模式限定时, + 不具备该权限的模式不会被搜索(就像用户在该模式上没有 + USAGE 权限一样)。如果使用了显式模式限定,而用户在所指 + 定模式上又没有所需权限,则会报错。 + + + + 客户端必须被允许访问所有被引用的表和列,即使这些表和列最初来自随后被展 + 开的视图也是如此,这样我们才能在不受表内容引用方式影响的情况下,应用一 + 致的访问控制规则。 + + + + 默认数据库权限系统允许数据库超级用户使用 DML 命令修改系统目录,也允许引 + 用或修改 TOAST 表。启用 sepgsql 后,这些操作都会被禁 + 止。 + + + + + DDL 权限 + + SELinux 为每种对象类型定义了若干权限,用于控制 + 常见操作,例如创建、修改、删除以及重设安全标签。此外,若干对象类 + 型还具有特殊权限,用于控制它们特有的操作,例如在特定模式中添加或删除名 + 称项。 + + + 创建新的数据库对象需要 create 权限。 + SELinux 会根据客户端的安全标签以及为新对象拟定 + 的安全标签授予或拒绝该权限。在某些情况下,还需要额外权限: + + + + + + 还需要对源数据库或模板数据库具 + 有 getattr 权限。 + + + + + 创建模式对象还需要在父模式上具有 add_name 权限。 + + + + + 创建表还需要有权限创建每个单独的表列,就好像每个表列都是独立的顶层对 + 象一样。 + + + + + 创建被标记为 LEAKPROOF 的函数还需要 + install 权限。(对现有函数设置 + LEAKPROOF 时同样会检查该权限。) + + + + + + 执行 DROP 命令时,会对将被移除的对象检查 + drop 权限。通过 CASCADE 间接删除的对 + 象同样会检查权限。删除位于特定模式中的对象(表、视图、序列和过程)还需 + 要该模式上的 remove_name 权限。 + + + + 执行 ALTER 命令时,会针对被修改的对象检查 + setattr 权限,但表的索引或触发器等附属对象除外,这类情 + 况改为在父对象上检查权限。在某些情况下,还需要额外权限: + + + + + + 将对象移动到新模式还需要在旧模式上具有 + remove_name 权限,并在新模式上具有 + add_name 权限。 + + + + + 在函数上设置 LEAKPROOF 属性需要 + install 权限。 + + + + + 在对象上使用 + 还需要同时具备与其旧安全标签组合的对象 + relabelfrom 权限,以及与其新安全标签组合的对象 + relabelto 权限。(在安装了多个标签提供者且用户尝试设 + 置一个不由 SELinux 管理的安全标签时,这里本应 + 只检查 setattr。由于实现限制,当前尚未这样做。) + + + + + + + + 受信任过程 + + 受信任过程类似于安全定义器函数或 setuid 命令。 + SELinux 提供了一项功能,允许受信任代码以不同于客 + 户端的安全标签运行,通常用于以高度受控的方式访问敏感数据(例如可以省略 + 某些行,或降低已存储值的精度)。函数是否充当受信任过程,由其安全标签和操 + 作系统安全策略控制。例如: + + + +postgres=# CREATE TABLE customer ( + cid int primary key, + cname text, + credit text + ); +CREATE TABLE +postgres=# SECURITY LABEL ON COLUMN customer.credit + IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +SECURITY LABEL +postgres=# CREATE FUNCTION show_credit(int) RETURNS text + AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'') + FROM customer WHERE cid = $1' + LANGUAGE sql; +CREATE FUNCTION +postgres=# SECURITY LABEL ON FUNCTION show_credit(int) + IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0'; +SECURITY LABEL + + + + 上述操作应由管理用户执行。 + + + +postgres=# SELECT * FROM customer; +ERROR: SELinux: security policy violation +postgres=# SELECT cid, cname, show_credit(cid) FROM customer; + cid | cname | show_credit +-----+--------+--------------------- + 1 | taro | 1111-2222-3333-xxxx + 2 | hanako | 5555-6666-7777-xxxx +(2 rows) + + + + 在这种情况下,普通用户不能直接引用 customer.credit, + 但受信任过程 show_credit 允许用户打印客户信用卡号,并对其 + 中部分数字做掩码处理。 + + + + + 动态域转换 + + 如果安全策略允许,可以利用 SELinux 的动态域转换功能,将客户端进程 + (即客户端域)的安全标签切换到新的上下文。客户端域需要 + setcurrent 权限,以及从旧域切换到新域的 + dyntransition 权限。 + + + 动态域转换需要谨慎考虑,因为它允许用户按自己的意愿切换标签,因而也能切 + 换其权限,而不是像受信任过程那样由系统强制规定。因此,只有当它被用于切换 + 到一个权限集合少于原始域的新域时,dyntransition 权限才 + 被认为是安全的。例如: + + +regression=# select sepgsql_getcon(); + sepgsql_getcon +------------------------------------------------------- + unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 +(1 row) + +regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c4'); + sepgsql_setcon +---------------- + t +(1 row) + +regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c1023'); +ERROR: SELinux: security policy violation + + + 在上面的例子中,允许从较大的 MCS 范围 c1.c1023 切换到较 + 小的范围 c1.c4,但不允许切换回去。 + + + 动态域转换与受信任过程的组合支持一种很有意思的用例,它契合连接池软件的 + 典型进程生命周期。即使连接池软件本身不允许运行大多数 SQL 命令,也可以让 + 它在受信任过程中调用 sepgsql_setcon() 函数来切换客户端的 + 安全标签;该过程应要求某种凭据,以授权切换客户端标签的请求。此后,这个 + 会话就将拥有目标用户而非连接池软件的权限。之后,连接池软件还可以再次在 + 具有适当权限检查的受信任过程中,以 NULL 参数调用 + sepgsql_setcon(),从而恢复这一安全标签变更。这里的关键 + 在于,真正有权修改生效安全标签的只有受信任过程,而且它只会在获得适当凭据 + 时这样做。当然,为了安全运行,凭据存储(表、过程定义或其他载体)必须防 + 止未授权访问。 + + + + + 杂项 + + 我们一律拒绝 命令,因为装入任意模块都可能轻易绕过安 + 全策略的强制执行。 + + + + + + + sepgsql 函数 + + 展示了可用函数。 + + + + sepgsql 函数 + + + + sepgsql_getcon() returns text + + 返回客户端域,也就是客户端当前的安全标签。 + + + + sepgsql_setcon(text) returns bool + + 如果安全策略允许,将当前会话的客户端域切换到新域。它也接受 + NULL 输入,表示请求切换到客户端的原始域。 + + + + sepgsql_mcstrans_in(text) returns text + + 如果 mcstrans 守护进程正在运行,则把给定的限定格式 MLS/MCS 范围转换为 + 原始格式。 + + + + sepgsql_mcstrans_out(text) returns text + + 如果 mcstrans 守护进程正在运行,则把给定的原始 MLS/MCS 范围转换为限定 + 格式。 + + + + sepgsql_restorecon(text) returns bool + 为当前数据库中的所有对象设置初始安全标签。参数可以为 NULL,也可以是一个 specfile 的名称,用来替代系统默认文件。 + + + +
+
+ + + 限制 + + + + 数据定义语言(DDL)权限 + + + 由于实现限制,某些 DDL 操作不会检查权限。 + + + + + + 数据控制语言(DCL)权限 + + + 由于实现限制,DCL 操作不会检查权限。 + + + + + + 行级访问控制 + + + PostgreSQL 支持行级访问,但 + sepgsql 不支持。 + + + + + + 隐蔽通道 + + + sepgsql 不会试图隐藏某个对象的存在,即使用户无权引用 + 它也是如此。例如,即便我们无法取得一个不可见对象的内容,也仍然可以从主 + 键冲突、外键违例等结果中推断出它的存在。绝密表的存在无法隐藏;我们只希 + 望隐藏其内容。 + + + + + + + + 外部资源 + + + SE-PostgreSQL Introduction + + + 该 wiki 页面提供了简要概述,并介绍了安全设计、体系结构、管理以及未来特 + 性。 + + + + + Fedora SELinux User Guide + + + 该文档提供了在系统上管理 SELinux 所需的广泛知 + 识。它主要聚焦于 Fedora,但并不限于 Fedora。 + + + + + Fedora SELinux FAQ + + + 该文档回答了关于 SELinux 的常见问题。它主要 + 聚焦于 Fedora,但并不限于 Fedora。 + + + + + + + + 作者 + + KaiGai Kohei kaigai@ak.jp.nec.com + + +
diff --git a/zh/9.6/sourcerepo.sgml b/zh/9.6/sourcerepo.sgml new file mode 100644 index 00000000..28617d6a --- /dev/null +++ b/zh/9.6/sourcerepo.sgml @@ -0,0 +1,67 @@ + + + + 源代码仓库 + + + PostgreSQL的源代码使用Git版本控制系统进行存储和管理。官方提供了主仓库的公开镜像;主仓库发生任何变更后,该镜像都会在一分钟内更新。 + + + + 我们的 wiki()上有一些关于使用 Git 的讨论。 + + + + 请注意,从源代码仓库构建PostgreSQL需要版本相当新的 + bisonflexPerl。 + 从发行版 tarball 构建则不需要这些工具,因为这些工具生成的文件已经包含在 tarball 中。 + 其他工具要求与中的说明相同。 + + + + 通过 <productname>Git</productname> 获取源代码 + + + 使用Git时,你将在本地机器上创建整个代码仓库的一个副本,因此即使离线也能访问全部历史和分支。这是开发或测试补丁最快且最灵活的方式。 + + + + Git + + + + 你需要安装Git,可从获取。许多系统默认已安装了较新版本的Git,或者可以通过其软件包分发系统获得。 + + + + + 要开始使用 Git 仓库,请克隆官方镜像: +git clone https://git.postgresql.org/git/postgresql.git +这会将完整的仓库复制到本地机器,因此可能需要一些时间才能完成,尤其是网络连接较慢时。文件将被放置在当前目录下新建的postgresql子目录中。 + + 也可以通过 Git 协议访问 Git 镜像。只需将 URL 前缀改为git,如下所示: +git clone git://git.postgresql.org/git/postgresql.git + + + + + + + + 每当想要获取系统的最新更新时,cd进入该仓库目录并运行: + + +git fetch + + + + + + + Git能做的远不止获取源代码。要了解更多信息,请参阅Git的手册页,或者访问网站。 + + + + diff --git a/zh/9.6/sources.sgml b/zh/9.6/sources.sgml new file mode 100644 index 00000000..d7dd95b9 --- /dev/null +++ b/zh/9.6/sources.sgml @@ -0,0 +1,776 @@ + + + + PostgreSQL 编码约定 + + + 格式 + + + 源代码格式使用 4 列制表宽度,并保留制表符(即,不会把制表符展开为空格)。 + 每个逻辑缩进层级都对应再增加一个制表位。 + + + + 布局规则(花括号位置等)遵循 BSD 约定。特别是,if、 + whileswitch 等控制语句所对应代码块的花括号都单独占一行。 + + + + 应限制行长度,使代码在 80 列窗口中可读。(这并不是说绝不能超过 80 列。例如, + 仅仅为了让代码保持在 80 列内而任意拆开一条很长的错误消息字符串,未必会提升可读性。) + + + 不要使用 C++ 风格注释(// 注释)。严格的 ANSI C 编译器不接受这种注释。同样,也不要使用在块中途声明新变量这样的 C++ 扩展。 + + + 多行注释块的首选样式是 + +/* + * comment text begins here + * and continues here + */ + + 注意,从第 1 列开始的注释块会被 pgindent 原样保留, + 但它会把缩进的注释块当作普通文本重新排版。如果想保留缩进块中的换行, + 请像下面这样加上横线: + + /*---------- + * comment text begins here + * and continues here + *---------- + */ + + + + + 尽管提交的补丁并不绝对必须遵循这些格式规则,但这样做是个好主意。 + 你的代码会在下一个版本发布前经过 pgindent 处理, + 因此按另一套格式约定把它写得再漂亮也没有意义。补丁的一个经验法则是 + 让新代码看起来像周围现有的代码。 + + + + src/tools 目录中包含可供 emacs、 + xemacsvim 编辑器使用的示例设置文件, + 以帮助确保它们按这些约定格式化代码。 + + + + 文本浏览工具 moreless 可以这样调用: + +more -x4 +less -x4 + + 以便正确显示制表符。 + + + + + 在服务器内部报告错误 + + + ereport + + + elog + + + + 服务器代码内生成的错误、警告和日志消息应使用 ereport, + 或其更老的近亲 elog 来创建。这一函数的用法相当复杂, + 因此需要一些解释。 + + + + 每条消息都必须包含两个元素:严重性级别(范围从 + DEBUGPANIC)以及主消息文本。 + 此外还可以有可选元素,其中最常见的是遵循 SQL 规范 SQLSTATE 约定的错误标识符代码。 + ereport 本身只是一个包装函数,主要为了语法上的便利, + 使消息生成在 C 源代码中看起来像一次函数调用。 + ereport 唯一直接接受的参数是严重性级别。 + 主消息文本以及任何可选消息元素都是通过在 ereport 调用中调用辅助函数 + (例如 errmsg)来生成的。 + + + ereport的一次典型调用可能如下: +ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); +这指定了错误严重性级别ERROR(一种普通错误)。errcode调用使用定义在src/include/utils/errcodes.h中的一个宏指定 SQLSTATE 错误代码。errmsg调用提供主消息文本。注意辅助函数调用外面多了一层圆括号 — 它们虽然烦人,但在语法上是必需的。 + + 这里有一个更复杂的示例: +ereport(ERROR, + (errcode(ERRCODE_AMBIGUOUS_FUNCTION), + errmsg("function %s is not unique", + func_signature_string(funcname, nargs, + NIL, actual_arg_types)), + errhint("Unable to choose a best candidate function. " + "You might need to add explicit typecasts."))); +这展示了如何用格式代码把运行时值嵌入消息文本中。此外还提供了一条可选的提示消息。 + + + 如果严重性级别是 ERROR 或更高, + ereport 会中止用户定义函数的执行,并且不会返回给调用者。 + 如果严重性级别低于 ERRORereport 会正常返回。 + + + 适用于ereport的辅助例程有: + + + errcode(sqlerrcode) 为该条件指定 SQLSTATE 错误标识符代码。 + 如果不调用这个例程,则默认错误标识符在错误严重性级别为 ERROR + 或更高时为 ERRCODE_INTERNAL_ERROR,在错误级别为 + WARNING 时为 ERRCODE_WARNING, + 否则(对于 NOTICE 及以下)为 + ERRCODE_SUCCESSFUL_COMPLETION。虽然这些默认值常常很方便, + 但在省略 errcode() 调用之前,始终要先想想它们是否合适。 + + + + + errmsg(const char *msg, ...) 指定主错误消息文本, + 以及可能要插入其中的运行时值。插入项通过 sprintf 风格的格式代码指定。 + 除了 sprintf 接受的标准格式代码外,还可以使用格式代码 + %m 插入 strerrorerrno + 当前值返回的错误消息。 + + + 也就是说,是到达 ereport 调用点时的那个值; + 辅助报告例程内部对 errno 的更改不会影响它。 + 如果显式写出 strerror(errno) 作为 + errmsg 的参数列表内容,就不是这样了;因此不要这么做。 + + + %m 不需要在 errmsg 的参数列表中有任何对应项。 + 注意,在处理格式代码之前,消息字符串会先经过 gettext + 以便可能进行本地化。 + + + + + errmsg_internal(const char *msg, ...) 与 + errmsg 相同,只是消息字符串不会被翻译,也不会被收入国际化消息字典。 + 这应当用于那些 不可能发生、大概不值得为之投入翻译精力的情况。 + + + + + errmsg_plural(const char *fmt_singular, const char *fmt_plural, + unsigned long n, ...) 类似于 errmsg, + 但支持消息的各种复数形式。fmt_singular 是英文单数格式, + fmt_plural 是英文复数格式, + n 是决定需要哪种复数形式的整数值, + 其余参数按所选格式字符串进行格式化。更多信息见 + 。 + + + + + errdetail(const char *msg, ...) 提供一条可选的 + 详情消息;当有额外信息但似乎不适合放在主消息中时,可使用它。 + 消息字符串的处理方式与 errmsg 完全相同。 + + + + + errdetail_internal(const char *msg, ...) 与 + errdetail 相同,只是消息字符串不会被翻译,也不会被收入国际化消息字典。 + 这应当用于那些不值得投入翻译精力的详情消息,例如它们对大多数用户来说技术性太强而无甚用处。 + + + + + errdetail_plural(const char *fmt_singular, const char *fmt_plural, + unsigned long n, ...) 类似于 errdetail, + 但支持消息的各种复数形式。更多信息见 。 + + + + + errdetail_log(const char *msg, ...) 与 + errdetail 相同,只是该字符串只会写入服务器日志, + 绝不会发送给客户端。如果同时使用 errdetail(或其上面的某个等价函数) + 和 errdetail_log,那么一条字符串会发往客户端,另一条会发往日志。 + 对于那些因安全性过于敏感或内容过于庞大而不适合放入发给客户端的报告中的错误细节, + 这很有用。 + + + + + errdetail_log_plural(const char *fmt_singular, const char + *fmt_plural, unsigned long n, ...) 类似于 + errdetail_log,但支持消息的各种复数形式。 + 更多信息见 。 + + + + + errhint(const char *msg, ...) 提供一条可选的 + 提示消息;它用于给出如何修复该问题的建议,而不是说明究竟哪里出了错的事实细节。 + 消息字符串的处理方式与 errmsg 完全相同。 + + + + + errcontext(const char *msg, ...) 通常不会直接在 + ereport 消息处调用;它用于 + error_context_stack 回调函数中,提供错误发生时的上下文信息, + 例如 PL 函数中的当前位置。消息字符串的处理方式与 errmsg 完全相同。 + 与其他辅助函数不同,它在每次 ereport 调用中都可以调用多次; + 这样提供的各个字符串会以换行分隔并连接起来。 + + + + + errposition(int cursorpos) 指定错误在查询字符串中的文本位置。 + 目前,它只对在查询处理的词法和语法分析阶段检测到的错误有用。 + + + + + errtable(Relation rel) 指定一个关系,它的名称和模式名应作为辅助字段包含在错误报告中。 + + + + + errtablecol(Relation rel, int attnum) 指定一列, + 它的列名、表名和模式名应作为辅助字段包含在错误报告中。 + + + + + errtableconstraint(Relation rel, const char *conname) + 指定一个表约束,它的名称、表名和模式名应作为辅助字段包含在错误报告中。 + 为此目的,索引也应被视为约束,无论它们是否有关联的 + pg_constraint 条目。请注意,作为 rel + 传入的应是底层堆关系,而不是索引本身。 + + + + + errdatatype(Oid datatypeOid) 指定一个数据类型, + 它的名称和模式名应作为辅助字段包含在错误报告中。 + + + + + errdomainconstraint(Oid datatypeOid, const char *conname) + 指定一个域约束,它的名称、域名和模式名应作为辅助字段包含在错误报告中。 + + + + + errcode_for_file_access() 是一个便捷函数, + 用于为与文件访问相关的系统调用失败选择合适的 SQLSTATE 错误标识符。 + 它使用保存下来的 errno 来确定要生成哪种错误代码。 + 通常应将它与主错误消息文本中的 %m 结合使用。 + + + + + errcode_for_socket_access() 是一个便捷函数, + 用于为与套接字相关的系统调用失败选择合适的 SQLSTATE 错误标识符。 + + + + + 可以调用 errhidestmt(bool hide_stmt) 来指定抑制 + postmaster 日志中消息的 STATEMENT: 部分。 + 一般来说,如果消息文本本身已经包含当前语句,这么做就是合适的。 + + + + + 可以调用 errhidecontext(bool hide_ctx) 来指定抑制 + postmaster 日志中消息的 CONTEXT: 部分。 + 这只应用于详细调试消息,因为在这类消息中反复包含上下文会让日志过于臃肿。 + + + + + + + + 在一次 ereport 调用中, + errtableerrtablecol、 + errtableconstrainterrdatatype 和 + errdomainconstraint 这些函数中最多只能使用一个。 + 这些函数的存在,是为了让应用能够提取与错误条件关联的数据库对象名称, + 而无需检查可能已经本地化的错误消息文本。这些函数应当用于应用很可能希望自动处理的错误报告。 + 截至 PostgreSQL 9.3,只有 SQLSTATE 类 23 + (完整性约束违反)中的错误实现了完整覆盖,但未来很可能会扩展。 + + + + 还有一个较旧的函数elog,至今仍被大量使用。一个elog调用: +elog(level, "format string", ...); +完全等价于: +ereport(level, (errmsg_internal("format string", ...))); +注意,SQLSTATE 错误代码总会取默认值,而且消息字符串不会被翻译。因此,elog只应用于内部错误和低层调试日志。凡是普通用户可能感兴趣的消息,都应通过ereport。尽管如此,系统中仍有足够多的内部不可能发生错误检查,因此elog依然被广泛使用;对这类消息来说,由于记法更简洁,它更受青睐。 + + + 关于如何编写良好的错误消息,可参见 。 + + + + + 错误消息风格指南 + + + 提供这份风格指南,是希望在 PostgreSQL + 生成的所有消息中保持一致、对用户友好的风格。 + + + + 各部分写什么 + + + 主消息应简短、陈述事实,并避免提及具体函数名等实现细节。 + 简短 的意思是 在正常情况下应能放在一行里。 + 如果为了保持主消息简短,或者你觉得有必要提及特定失败的系统调用之类的实现细节, + 可使用详情消息。主消息和详情消息都应当陈述事实。提示消息则用于给出如何修复问题的建议, + 尤其是在该建议未必总是适用时。 + + + 例如,不要写: +IpcMemoryCreate: shmget(key=%d, size=%u, 0%o) failed: %m +(plus a long addendum that is basically a hint) +而要写: +Primary: could not create shared memory segment: %m +Detail: Failed syscall was shmget(key=%d, size=%u, 0%o). +Hint: the addendum + + + + + 原理:保持主消息简短,有助于使其切中要点,也便于客户端假定一行足以容纳错误消息, + 从而安排屏幕空间。详情消息和提示消息可以归入详细模式,或者放到弹出的错误详情窗口中。 + 此外,为节省空间,详情消息和提示消息通常会从服务器日志中省略。 + 最好避免提及实现细节,因为用户通常并不了解这些细节。 + + + + + + 格式 + + + 不要在消息文本中写入任何特定的格式假设。应预期客户端和服务器日志会按照各自需要折行。 + 对于较长的消息,可以使用换行字符(\n)来表示建议的分段。不要让消息以换行结束。 + 不要使用制表符或其他格式控制字符。(在错误上下文显示中,会自动添加换行以分隔函数调用等上下文层次。) + + + + 原理:消息不一定显示在终端类设备上。在 GUI 显示或浏览器中, + 这些格式指令充其量也只是被忽略。 + + + + + + 引号 + + + 英文文本在适合引用时应使用双引号。其他语言的文本应一致地使用一种引号, + 这种引号应符合该语言的出版习惯以及其他程序的计算机输出习惯。 + + + + 原理:选择双引号而不是单引号多少有些武断,但通常更符合首选用法。 + 有人建议按照 SQL 约定,根据对象类型来选择引号的种类 + (即字符串用单引号,标识符用双引号)。但这是语言内部的技术问题, + 很多用户甚至并不熟悉;它也无法扩展到其他类型的被引术语, + 更无法翻译到其他语言中,而且本身也没什么意义。 + + + + + + 引号的用法 + + + 对文件名、用户提供的标识符以及其他可能包含单词的变量, + 始终使用引号界定。对于不会包含单词的变量(例如操作符名),则不要加引号。 + + + + 后端中有些函数会在需要时自行给其输出加上双引号(例如 + format_type_be())。不要再对这类函数的输出额外加引号。 + + + + 原理:对象的名称在嵌入消息时可能造成歧义。对于插入的名称从哪里开始、到哪里结束,要保持一致。 + 但也不要用不必要或重复的引号把消息弄得杂乱无章。 + + + + + + 语法和标点 + + + 主错误消息与详情/提示消息的规则不同: + + + + 主错误消息:首字母不要大写。消息末尾不要加句号。更不要想着在消息末尾加感叹号。 + + + + 详情消息和提示消息:使用完整句子,并且每条都用句号结束。句子的第一个词要首字母大写。 + 如果后面还有一句,在句号后放两个空格(针对英文文本;对其他语言可能并不合适)。 + + + + 错误上下文字符串:首字母不要大写,字符串末尾不要加句号。上下文字符串通常不应是完整句子。 + + + + 原理:避免使用标点,能让客户端应用更容易把消息嵌入各种语法上下文中。 + 主消息往往本来也不是语法完整的句子。(而如果它们长到不止一句, + 就应当拆分成主消息和详情消息。)不过,详情消息和提示消息更长, + 也可能需要包含多个句子。为了保持一致,即便只有一个句子,它们也应遵循完整句子的风格。 + + + + + + 大写与小写 + + + 消息措辞应使用小写,包括主错误消息的首字母。如果消息中出现 SQL 命令或关键字,则使用大写形式。 + + + + 原理:这样更容易让所有消息看起来一致,因为有些消息是完整句子,而有些则不是。 + + + + + + 避免被动语态 + + + 使用主动语态。有施动者时,用完整句子(A could not do B)。 + 如果施动者就是程序本身,则使用无主语的电报式写法,但不要把程序写成 I。 + + + + 原理:程序不是人。不要假装它是。 + + + + + + 现在时与过去时 + + + 如果一次尝试做某事失败了,但下次仍可能成功(也许在修复某个问题之后), + 就使用过去时。如果失败显然是永久性的,就使用现在时。 + + + + 下面两种句式在语义上有明显差别: + +could not open file "%s": %m + +和: + +cannot open file "%s" + + 第一种表示尝试打开文件失败了。消息应给出原因,例如 磁盘已满 + 或 文件不存在。过去时更合适,因为下次磁盘可能就不满了, + 或者所请求的文件可能已经存在。 + + + + 第二种形式表示,程序中根本不存在打开该命名文件的功能,或者从概念上就不可能。 + 现在时更合适,因为这种情况会无限期持续下去。 + + + + 原理:诚然,普通用户未必能仅凭消息的时态得出什么重要结论, + 但既然语言提供了语法,我们就应当正确使用它。 + + + + + + 对象类型 + + + 在引用对象名称时,要说明它是什么类型的对象。 + + + + 原理:否则没人会知道 foo.bar.baz 指的是什么。 + + + + + + 括号 + + + 方括号只应用于:(1)命令概要中表示可选参数,或(2)表示数组下标。 + + + + 原理:其他任何用法都不符合广为人知的习惯用法,而且会让人困惑。 + + + + + + 组装错误消息 + + + 当消息中包含其他地方生成的文本时,应按以下方式嵌入: + +could not open file %s: %m + + + + + 原理:要把这种文本拼进一个流畅的单句中,同时照顾所有可能的错误代码会很困难, + 因此需要某种标点。也有人建议把嵌入的文本放在圆括号里,但如果嵌入的文本很可能是消息中最重要的部分 + (而这往往确实如此),那样就显得不自然了。 + + + + + + 错误原因 + + + 消息总应说明错误发生的原因。例如: + +BAD: could not open file %s +BETTER: could not open file %s (I/O failure) + + 如果不知道原因,最好去修复代码。 + + + + + + 函数名 + + 不要在错误文本中包含报告该错误的例程名。需要时,我们有其他机制可以找出它,而且对大多数用户来说,这项信息没有帮助。如果去掉函数名后错误文本就不够清楚,请重新措辞。 +BAD: pg_atoi: error in "z": cannot parse "z" +BETTER: invalid input syntax for integer: "z" + + + + + 也要避免提及被调用函数的名称;应改为说明代码想要做什么: + +BAD: open() failed: %m +BETTER: could not open file %s: %m + + 如果确实有必要,可在详情消息中提及系统调用。 + (在某些情况下,在详情消息中提供传给该系统调用的实际值也许是合适的信息。) + + + + 原理:用户并不知道那些函数都做了什么。 + + + + + + 应避免的词语 + + + Unable + + Unable 几乎就是被动语态。更好的做法是视情况使用 + cannotcould not。 + + + + + Bad + + 像 bad result 这样的错误消息,实在很难被合理理解。 + 最好写出结果为什么是 bad,例如 invalid format。 + + + + + Illegal + + Illegal 指的是违法,其余情况应使用 invalid。 + 更好的是,直接说明为什么无效。 + + + + + Unknown + + 尽量避免 unknown。考虑 error: unknown + response。如果你都不知道响应是什么,又如何知道它有错? + Unrecognized 往往是更好的选择。此外,一定要包含被抱怨的值。 + +BAD: unknown node type +BETTER: unrecognized node type: 42 + + + + + + Find vs. Exists + + 如果程序为了定位某个资源使用了非平凡算法(例如路径搜索),而该算法失败了, + 那么说程序没能 find 该资源是公平的。另一方面,如果资源的预期位置已知, + 但程序无法在那里访问它,那么就应说该资源并不 exist。 + 在这种情况下使用 find 显得含糊,也会混淆问题。 + + + + + May、Can 与 Might + + May 暗示许可(例如,"You may borrow my rake."), + 在文档或错误消息中用途不大。Can 暗示能力 + (例如,"I can lift that log."),而 might 暗示可能性 + (例如,"It might rain today.")。使用恰当的词能澄清含义,也有助于翻译。 + + + + + Contractions + + 避免使用缩略形式,例如 can't;应改用 cannot。 + + + + + Non-negative + + 避免使用 non-negative,因为它是否接受零有歧义。更好的是使用 + greater than zerogreater than or equal to zero。 + + + + + + + 完整拼写 + + + 单词应完整拼写。例如,避免使用: + + + + spec + + + + + stats + + + + + parens + + + + + auth + + + + + xact + + + + + + + 原理:这会提升一致性。 + + + + + + 本地化 + + + 请记住,错误消息文本需要被翻译成其他语言。请遵循 + 中的指导,避免给翻译者制造麻烦。 + + + + + + + 其他编码约定 + + + C 标准 + PostgreSQL 中的代码应只依赖 C89 标准提供的语言特性。这意味着,至少除去少数依赖平台的部分,符合 C89 标准的编译器必须能够编译 postgres。如果提供了回退方案,则可以使用来自 C 标准后续修订的特性或编译器特定特性。 + 例如,目前使用了 static inline_Static_assert(),尽管它们来自 C 标准的较新修订。如果这些特性不可用,我们会分别回退为定义不带 inline 的函数,以及使用一种兼容 C89 的替代方案,后者执行相同的检查,但输出的消息比较晦涩。 + + + + 类函数宏和内联函数 + 带参数的宏和static inline函数都可以使用。如果把某段代码写成宏会有多次求值风险,那么后者更可取,例如下面这种情况: +#define Max(x, y) ((x) > (y) ? (x) : (y)) +或者当宏会变得非常长时也是如此。在其他情况下,只能使用宏,或者至少使用宏更容易。例如,需要向宏传递各种不同类型的表达式时就是如此。 + + 当某个内联函数的定义引用了只在后端可用的符号(即变量、函数)时, + 该函数在前端代码中被包含时就不应可见。 + +#ifndef FRONTEND +static inline MemoryContext +MemoryContextSwitchTo(MemoryContext context) +{ + MemoryContext old = CurrentMemoryContext; + + CurrentMemoryContext = context; + return old; +} +#endif /* FRONTEND */ + + 在这个示例中,只在后端中可用的 CurrentMemoryContext 被引用了, + 因此该函数用 #ifndef FRONTEND 隐藏起来。 + 这条规则之所以存在,是因为有些编译器即使函数未被使用, + 也会为内联函数中包含的符号发出引用。 + + + + + 编写信号处理器 + + 要让代码适合在信号处理器中运行,必须非常谨慎地编写。根本问题在于, + 只要没有被阻塞,信号处理器就能在任何时刻中断代码。 + 如果信号处理器中的代码使用了与外部代码相同的状态,就可能引发混乱。 + 举例来说,想想如果信号处理器试图获取一个已经被中断代码持有的锁,会发生什么。 + + + 除非有特殊安排,信号处理器中的代码只能调用异步信号安全函数 + (按 POSIX 的定义),并且只能访问类型为 volatile sig_atomic_t + 的变量。postgres 中也有少数函数被认为是信号安全的, + 其中尤其重要的是 SetLatch()。 + + 在大多数情况下,信号处理器只应记录信号已经到达,并使用锁存器唤醒在信号处理器之外运行的代码。下面就是这样的一个处理器示例: +static void +handle_sighup(SIGNAL_ARGS) +{ + int save_errno = errno; + + got_SIGHUP = true; + SetLatch(MyLatch); + + errno = save_errno; +} + + errno被保存并恢复,是因为SetLatch()可能会改变它。如果不这样做,当前正在检查errno的被中断代码可能会看到错误的值。 + + + + diff --git a/zh/9.6/spgist.sgml b/zh/9.6/spgist.sgml new file mode 100644 index 00000000..273652fd --- /dev/null +++ b/zh/9.6/spgist.sgml @@ -0,0 +1,642 @@ + + + +SP-GiST 索引 + + + 索引 + SP-GiST + + + + 简介 + + + SP-GiST 是空间分区 GiST + 的缩写。SP-GiST 支持分区搜索树,这使得开发多种不同的 + 非平衡数据结构成为可能,例如四叉树、k-d 树以及基数树(trie)。这些结构 + 的共同特征是,它们会反复将搜索空间划分为不必等大的分区。与这种划分规则良 + 好匹配的搜索可以非常快。 + + + + 这些常见数据结构最初是为内存中使用而开发的。在主存中,它们通常被设计成一 + 组由指针链接的动态分配结点。由于这些指针链可能相当长,直接存储到磁盘上并 + 不合适,因为那会需要过多的磁盘访问。相比之下,基于磁盘的数据结构应当具有 + 较高的扇出,以尽量减少 I/O。SP-GiST 要解决的难题是, + 如何以这样的方式将搜索树结点映射到磁盘页:即使搜索遍历了许多结点,也只需 + 访问少数几个磁盘页。 + + + + 像 GiST 一样,SP-GiST 的目标是让 + 数据类型领域专家而非数据库专家,能够针对自定义数据类型开发合适的访问方法。 + + + + 这里的一些信息来自普渡大学的 SP-GiST 索引项目 + 网站。 + SP-GiSTPostgreSQL + 中的实现主要由 Teodor Sigaev 和 Oleg Bartunov 维护,他们的 + 网站 + 上还有更多信息。 + + + + + + 内置操作符类 + + + PostgreSQL 核心发行版包含了 + SP-GiST 操作符类,如 + 所示。 + + + + 内置 <acronym>SP-GiST</acronym> 操作符类 + + + + 名称 + 被索引数据类型 + 可索引操作符 + + + + + kd_point_ops + point + << <@ <^ >> >^ ~= + + + quad_point_ops + point + << <@ <^ >> >^ ~= + + + range_ops + 任意范围类型 + && &< &> -|- << <@ = >> @> + + + box_ops + box + << &< && &> >> ~= @> <@ &<| <<| |>> |&> + + + text_ops + text + < <= = > >= ~<=~ ~<~ ~>=~ ~>~ + + + + +
+ + + 对于类型 point 的两个操作符类, + quad_point_ops 是默认选项。 + kd_point_ops 支持相同的操作符,但使用不同的索引数据 + 结构,在某些应用中可能提供更好的性能。 + + +
+ + + 可扩展性 + + + SP-GiST 提供了一个高度抽象的接口,访问方法开发者只 + 需实现特定数据类型所需的方法。SP-GiST 核心负责将树 + 结构高效映射到磁盘并执行搜索,同时也处理并发与日志记录方面的问题。 + + + SP-GiST 树的叶子元组包含与被索引列相同数据类型的值。位于根层的叶子元组始终包含原始的被索引数据值,而位于较低层的叶子元组可能只包含压缩表示,例如一个后缀。在这种情况下,操作符类支持函数必须能够利用为到达叶子层而经过的内部元组中累积的信息,重建出原始值。 + + + 内部元组更为复杂,因为它们是搜索树中的分支点。每个内部元组都包含一个或 + 多个结点,代表相似叶子值的分组。一个结点包含一 + 个向下链接,它要么指向更低层级的另一个内部元组,要么指向一小组位于 + 同一索引页上的叶子元组。每个结点通常都有一个描述它的标签; + 例如在基数树中,结点标签可以是字符串值的下一个字符。(或者,如果某个 + 操作符类对所有内部元组都使用固定的一组结点,也可以省略结点标签;参见 + 。)内部元组还可以选择带有一个描 + 述其所有成员的前缀值。在基数树中,这可以是所 + 表示字符串的公共前缀。前缀值不一定真的是前缀,它也可以是操作符类需要的 + 任何数据;例如在四叉树中,它可以存储划分四个象限所依据的中心点。这样, + 四叉树的内部元组还会包含四个结点,对应于该中心点周围的四个象限。 + + + + 某些树算法需要知道当前元组所在的层级(或深度),因此 + SP-GiST 核心允许操作符类在沿树向下遍历时管理层级计 + 数。它还支持在需要时增量重建所表示的值,并支持在树下降过程中向下传递额 + 外数据(称为遍历值)。 + + + + + SP-GiST 核心代码负责处理值为 null 的索引项。虽然 + SP-GiST 索引会为被索引列中的 null 值存储索引项, + 但索引操作符类代码看不到这些项:值为 null 的索引项或搜索条件绝不会传给 + 操作符类方法。(这里假定 SP-GiST 操作符是严格的, + 因此对 null 值不可能返回真。)所以这里不再讨论 null 值。 + + + + + SP-GiST 的索引操作符类必须提供五个用户定义方法。五个必需方法都遵循这样的约定:接受两个 + internal 参数,第一个参数是指向某个 C 结构体的指针,其中包 + 含该支持方法的输入值;第二个参数也是指向某个 C 结构体的指针,方法必须将输 + 出值写入其中。四个必需方法只返回 void,因为它们的全部结果 + 都体现在输出结构体中;但 leaf_consistent 还返回一个 + boolean 结果。这些方法不得修改其输入结构体中的任何字段。在 + 所有情况下,调用用户定义方法之前,输出结构体都会先被清零。 + + + 五个用户定义方法是: + + + + config + + + 返回索引实现的静态信息,包括前缀和结点标签数据类型的 OID。 + + SQL声明必须如下所示: +CREATE FUNCTION my_config(internal, internal) RETURNS void ... +第一个参数是一个指向spgConfigInC 结构体的指针,其中包含该函数的输入数据。第二个参数是一个指向spgConfigOutC 结构体的指针,函数必须将结果数据填入其中。 +typedef struct spgConfigIn +{ + Oid attType; /* 要被索引的数据类型 */ +} spgConfigIn; + +typedef struct spgConfigOut +{ + Oid prefixType; /* 内部元组前缀的数据类型 */ + Oid labelType; /* 内部元组结点标签的数据类型 */ + bool canReturnData; /* 操作符类能重建原始数据 */ + bool longValuesOK; /* 操作符类能处理大小 > 1 页的值 */ +} spgConfigOut; + + + attType的传入是为了支持多态索引操作符类;对于普通的固定数据类型操作符类,它始终具有相同的值,因此可以忽略。 + + + 对于不使用前缀的操作符类,可以将 prefixType + 设为 VOIDOID。同样,对于不使用结点标签的操作符类, + 可以将 labelType 设为 + VOIDOID。如果操作符类能够重建最初提供的索引值, + 则应将 canReturnData 设为真。只有在 + attType 是变长类型,并且该操作符类能够通 + 过反复取后缀来切分长值时,才应将 + longValuesOK 设为真(参见 + )。 + + + + + + choose + + + 为向内部元组插入新值选择一种方法。 + + + SQL声明必须如下所示: +CREATE FUNCTION my_choose(internal, internal) RETURNS void ... +第一个参数是一个指向spgChooseInC 结构体的指针,其中包含该函数的输入数据。第二个参数是一个指向spgChooseOutC 结构体的指针,函数必须将结果数据填入其中。 +typedef struct spgChooseIn +{ + Datum datum; /* 要被索引的原始 datum */ + Datum leafDatum; /* 当前要存储在叶子中的 datum */ + int level; /* 当前层级(从零开始计) */ + + /* 来自当前内部元组的数据 */ + bool allTheSame; /* 元组被标记为全部相同? */ + bool hasPrefix; /* 元组有前缀? */ + Datum prefixDatum; /* 如果有,前缀值 */ + int nNodes; /* 内部元组中的结点数 */ + Datum *nodeLabels; /* 结点标签值(如果没有则为 NULL) */ +} spgChooseIn; + +typedef enum spgChooseResultType +{ + spgMatchNode = 1, /* 下降到现有结点 */ + spgAddNode, /* 向内部元组添加一个结点 */ + spgSplitTuple /* 拆分内部元组(修改其前缀) */ +} spgChooseResultType; + +typedef struct spgChooseOut +{ + spgChooseResultType resultType; /* 动作代码,见上文 */ + union + { + struct /* spgMatchNode 的结果 */ + { + int nodeN; /* 下降到该结点(索引从 0 开始) */ + int levelAdd; /* 层级增加这么多 */ + Datum restDatum; /* 新的叶子 datum */ + } matchNode; + struct /* spgAddNode 的结果 */ + { + Datum nodeLabel; /* 新结点的标签 */ + int nodeN; /* 在哪里插入它(索引从 0 开始) */ + } addNode; + struct /* spgSplitTuple 的结果 */ + { + /* 构造只有一个结点的新内部元组所需的信息 */ + bool prefixHasPrefix; /* 元组应有前缀? */ + Datum prefixPrefixDatum; /* 如果有,前缀值 */ + Datum nodeLabel; /* 结点的标签 */ + + /* 构造包含所有旧结点的新下层内部元组所需的信息 */ + bool postfixHasPrefix; /* 元组应有前缀? */ + Datum postfixPrefixDatum; /* 如果有,前缀值 */ + } splitTuple; + } result; +} spgChooseOut; + + + datum是将要插入索引的原始 datum。leafDatum最初与datum相同,但在树的较低层可能发生变化,如果choosepicksplit方法对它进行了修改。当插入搜索到达叶页时,leafDatum的当前值将存储到新创建的叶子元组中。level是当前内部元组的层级,根层为零。allTheSame为真,表示当前内部元组被标记为包含多个等价结点(参见)。 + hasPrefix为真时,表示当前内部元组包含前缀;若是如此,prefixDatum就是该前缀值。nNodes是内部元组中包含的子结点数量,而nodeLabels是它们的标签值数组;如果没有标签,则为 NULL。 + + + choose 函数可以判定:新值要么匹配某个现有子结 + 点,要么必须添加一个新子结点,要么与该元组的前缀不一致,因此必须拆分 + 该内部元组以创建限制性更弱的前缀。 + + + + 如果新值匹配某个现有子结点,则将 + resultType 设为 + spgMatchNode。将 nodeN + 设为该结点在结点数组中的索引(从零开始)。将 + levelAdd 设为通过该结点向下下降所导致的 + level 增量;如果操作符类不使用层级,则保 + 持为零。若操作符类不会在层级之间修改 datum,则将 + restDatum 设为与 + datum 相等;否则,将它设为下一层要用 + 作 leafDatum 的修改后值。 + + + + 如果必须添加一个新子结点,则将 + resultType 设为 + spgAddNode。将 nodeLabel + 设为新结点要使用的标签,并将 nodeN 设为 + 该结点应插入到结点数组中的位置索引(从零开始)。添加该结点后, + choose 函数会使用修改后的内部元组再次被调用; + 这次调用应返回 spgMatchNode。 + + + + 如果新值与该元组的前缀不一致,则将 + resultType 设为 + spgSplitTuple。这个动作会把所有现有结点移动到一 + 个新的较低层内部元组中,并用一个仅含单个结点、链接到该新下层内部元 + 组的元组替换现有内部元组。将 + prefixHasPrefix 设为指示新的上层元组是 + 否应有前缀;若应有,则将 + prefixPrefixDatum 设为该前缀值。这个新前 + 缀值必须比原来的限制性更弱,以便能够接受将要索引的新值,并且它不应 + 比原来的前缀更长。将 + nodeLabel 设为指向新的较低层内部元组的 + 那个结点所使用的标签。将 + postfixHasPrefix 设为指示新的较低层内部 + 元组是否应有前缀;若应有,则将 + postfixPrefixDatum 设为该前缀值。这两个前 + 缀与附加标签的组合,必须与原始前缀具有相同的含义,因为没有机会修改 + 被移动到新下层元组中的结点标签,也不能更改任何子索引项。结点拆分完 + 成后,choose 函数会用替换后的内部元组再次被调 + 用。这次调用通常会得到 spgAddNode 结果,因为拆 + 分步骤中加入的结点标签多半不会匹配新值;因此之后还会有第三次调用, + 它最终返回 spgMatchNode,让插入下降到叶子层。 + + + + + + picksplit + + + 决定如何在一组叶子元组上创建一个新的内部元组。 + + + SQL声明必须如下所示: +CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ... +第一个参数是一个指向spgPickSplitInC 结构体的指针,其中包含该函数的输入数据。第二个参数是一个指向spgPickSplitOutC 结构体的指针,函数必须将结果数据填入其中。 +typedef struct spgPickSplitIn +{ + int nTuples; /* 叶子元组的数量 */ + Datum *datums; /* 它们的 datum(长度为 nTuples 的数组) */ + int level; /* 当前层级(从零开始计) */ +} spgPickSplitIn; + +typedef struct spgPickSplitOut +{ + bool hasPrefix; /* 新内部元组应有前缀? */ + Datum prefixDatum; /* 如果有,前缀值 */ + + int nNodes; /* 新内部元组的结点数 */ + Datum *nodeLabels; /* 它们的标签(或为 NULL 表示无标签) */ + + int *mapTuplesToNodes; /* 每个叶子元组对应的结点索引 */ + Datum *leafTupleDatums; /* 每个新叶子元组中存储的 datum */ +} spgPickSplitOut; + + + nTuples是所提供的叶子元组数量。datums是这些元组的 datum 值数组。level是所有这些叶子元组当前共同的层级,它将成为新内部元组的层级。 + + + 将 hasPrefix 设为指示新的内部元组是否应 + 有前缀;若应有,则将 prefixDatum 设为该 + 前缀值。将 nNodes 设为新内部元组将包含 + 的结点数,并将 nodeLabels 设为这些结点 + 的标签值数组;如果不需要结点标签,则设为 NULL。将 + mapTuplesToNodes 设为一个数组,其中给出 + 每个叶子元组应分配到的结点索引(从零开始)。将 + leafTupleDatums 设为要存储在新叶子元组 + 中的值数组(如果操作符类不会在层级之间修改 datum,这些值就与输入的 + datums 相同)。注意, + picksplit 函数负责为 + nodeLabels、 + mapTuplesToNodes 和 + leafTupleDatums 数组执行 palloc。 + + + + 如果提供了多于一个叶子元组,则期望 + picksplit 函数把它们划分到多于一个结点中;否 + 则就无法把叶子元组拆分到多个页上,而这正是此操作的最终目的。因此, + 如果 picksplit 最终把所有叶子元组都放进同一个 + 结点,SP-GiST 核心代码会覆盖这一决定,生成一个内部元组,并将叶子元 + 组随机分配到多个标签相同的结点上。这样的元组会被标记为 + allTheSame,以表明发生了这种情况。 + chooseinner_consistent + 函数必须对这种内部元组做出恰当处理。更多信息见 + 。 + + + + 只有在 config 函数将 + longValuesOK 设为真,并且提供了一个大于一 + 页的输入值时,picksplit 才会应用到单个叶子元 + 组。在这种情况下,这个操作的目的是剥离一个前缀,并产生一个新的、更短 + 的叶子 datum 值。该调用会重复进行,直到生成足够短、能够放入一页的叶 + 子 datum。更多信息见 。 + + + + + + inner_consistent + + + 在树搜索期间返回需要继续跟随的一组结点(分支)。 + + + SQL声明必须如下所示: +CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ... +第一个参数是一个指向spgInnerConsistentInC 结构体的指针,其中包含该函数的输入数据。第二个参数是一个指向spgInnerConsistentOutC 结构体的指针,函数必须将结果数据填入其中。 +typedef struct spgInnerConsistentIn +{ + ScanKey scankeys; /* 操作符和比较值的数组 */ + int nkeys; /* 数组长度 */ + + void *traversalValue; /* 操作符类特定的遍历值 */ + Datum reconstructedValue; /* 在父元组处重建的值 */ + MemoryContext traversalMemoryContext; /* 将新的遍历值放在这里 */ + int level; /* 当前层级(从零开始计) */ + bool returnData; /* 必须返回原始数据? */ + + /* 来自当前内部元组的数据 */ + bool allTheSame; /* 元组被标记为全部相同? */ + bool hasPrefix; /* 元组有前缀? */ + Datum prefixDatum; /* 如果有,前缀值 */ + int nNodes; /* 内部元组中的结点数 */ + Datum *nodeLabels; /* 结点标签值(如果没有则为 NULL) */ +} spgInnerConsistentIn; + +typedef struct spgInnerConsistentOut +{ + int nNodes; /* 需要访问的子结点数 */ + int *nodeNumbers; /* 它们在结点数组中的索引 */ + int *levelAdds; /* 对每个结点层级增加这么多 */ + Datum *reconstructedValues; /* 关联的重建值 */ + void **traversalValues; /* 操作符类特定的遍历值 */ +} spgInnerConsistentOut; +数组scankeys的长度为nkeys,它描述索引搜索条件。这些条件用 AND 组合 — 只有满足全部条件的索引项才是我们关心的。(注意,nkeys= 0 表示所有索引项都满足该查询。)通常一致性检查函数只关心每个数组元素的sk_strategysk_argument字段,它们分别给出可索引操作符和比较值。特别地,无需检查sk_flags以判断比较值是否为 NULL,因为 SP-GiST 核心代码会过滤掉此类条件。reconstructedValue是为父元组重建的值;以下情况下它为(Datum) 0:位于根层,或者inner_consistent函数没有在父层提供该值。traversalValue是指向任意遍历数据的指针,这些数据由上一次调用inner_consistent处理父索引元组时向下传递;在根层时则为 NULL。traversalMemoryContext是存放输出遍历值(见下文)的内存上下文。level是当前内部元组的层级,根层为零。returnDatatrue表示本查询需要重建数据;这要求config函数将canReturnData设为真。 + allTheSame为真,表示当前内部元组被标记为全部相同;在这种情况下,所有结点都具有相同的标签(如果有),因此要么全部匹配该查询,要么全部不匹配(参见)。 + hasPrefix为真时,表示当前内部元组包含前缀;若是如此,prefixDatum就是该前缀值。nNodes是内部元组中包含的子结点数量,而nodeLabels是它们的标签值数组;如果结点没有标签,则为 NULL。 + + + nNodes 必须设为搜索需要访问的子结点数 + 量,并且 nodeNumbers 必须设为这些结点 + 索引的数组。如果操作符类跟踪层级,则将 + levelAdds 设为一个数组,其中给出下降到 + 每个待访问结点时所需增加的层数。(这些增量常常对所有结点都相同,但并 + 非必然如此,所以这里使用数组。)如果需要值重建,则将 + reconstructedValues 设为一个数组,其中包 + 含为每个待访问子结点重建的值;否则,将 + reconstructedValues 保持为 NULL。如果希望将额外的带外信息 + (遍历值)向下传递到树搜索的更低层,则将 + traversalValues 设为适当遍历值的数组,每 + 个待访问子结点对应一个;否则,将 + traversalValues 保持为 NULL。注意, + inner_consistent 函数负责在当前内存上下文中为 + nodeNumbers、 + levelAdds、 + reconstructedValues 和 + traversalValues 数组执行 palloc。不过, + traversalValues 数组所指向的任何输出遍历 + 值都应在 traversalMemoryContext 中分配。 + 每个遍历值都必须是单独 palloc 的一个块。 + + + + + + leaf_consistent + + + 如果叶子元组满足查询,则返回 true。 + + + SQL声明必须如下所示: +CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ... +第一个参数是一个指向spgLeafConsistentInC 结构体的指针,其中包含该函数的输入数据。第二个参数是一个指向spgLeafConsistentOutC 结构体的指针,函数必须将结果数据填入其中。 +typedef struct spgLeafConsistentIn +{ + ScanKey scankeys; /* 操作符和比较值的数组 */ + int nkeys; /* 数组长度 */ + + Datum reconstructedValue; /* 在父元组处重建的值 */ + void *traversalValue; /* 操作符类特定的遍历值 */ + int level; /* 当前层级(从零开始计) */ + bool returnData; /* 必须返回原始数据? */ + + Datum leafDatum; /* 叶子元组中的 datum */ +} spgLeafConsistentIn; + +typedef struct spgLeafConsistentOut +{ + Datum leafValue; /* 重建出的原始数据(如果有) */ + bool recheck; /* 如果必须重新检查操作符则设为真 */ +} spgLeafConsistentOut; +数组scankeys的长度为nkeys,它描述索引搜索条件。这些条件用 AND 组合 — 只有满足全部条件的索引项才满足该查询。(注意,nkeys= 0 表示所有索引项都满足该查询。)通常一致性检查函数只关心每个数组元素的sk_strategysk_argument字段,它们分别给出可索引操作符和比较值。特别地,无需检查sk_flags以判断比较值是否为 NULL,因为 SP-GiST 核心代码会过滤掉此类条件。reconstructedValue是为父元组重建的值;以下情况下它为(Datum) 0:位于根层,或者inner_consistent函数没有在父层提供该值。traversalValue是指向任意遍历数据的指针,这些数据由上一次调用inner_consistent处理父索引元组时向下传递;在根层时则为 NULL。level是当前叶子元组的层级,根层为零。returnDatatrue表示本查询需要重建数据;这要求config函数将canReturnData设为真。 + leafDatum是当前叶子元组中存储的键值。 + + + 如果叶子元组匹配查询,则该函数必须返回 true, + 否则返回 false。在返回 + true 的情况下,如果 + returnDatatrue, + 则必须将 leafValue 设为最初为该叶子元组 + 提供并建立索引的值。 + 此外,如果匹配结果不确定,因而必须将操作符重新应用到实际的堆元组上以 + 验证匹配,则可以将 recheck 设为 + true。 + + + + + + + 所有 SP-GiST 支持方法通常都在一个短生命周期的内存上下文中调用;也就是 + 说,处理完每个元组后,CurrentMemoryContext 都会被 + 重置。因此,通常不必太担心是否 pfree 了你用 palloc 分配的所有内容。 + (config 方法是个例外:它应尽量避免内存泄漏。不 + 过通常 config 方法只需把常量赋入传入的参数结构体即 + 可。) + + + + 如果被索引列属于支持排序规则的数据类型,则索引排序规则会通过标准的 + PG_GET_COLLATION() 机制传递给所有支持方法。 + + + + + + 实现 + + + 本节介绍实现细节以及其他一些对 SP-GiST 操作符类实 + 现者有用的技巧。 + + + + SP-GiST 限制 + + + 单个叶子元组和内部元组都必须能放入单个索引页中(默认 8kB)。因此,在对 + 变长数据类型的值建立索引时,只有像基数树这类方法才能支持长值:树的 + 每一层都包含足够短、能放进一页的前缀,而最终叶子层也包含足够短、能放进 + 一页的后缀。只有当操作符类准备好保证这一点时,才应将 + longValuesOK 设为 TRUE。否则, + SP-GiST 核心会拒绝为过大、无法放入索引页的值建立 + 索引的请求。 + + + + 同样,确保内部元组不会增长到大得无法放入索引页,也是操作符类的责任;这 + 限制了单个内部元组中可使用的子结点数量,以及前缀值的最大大小。 + + + + 另一个限制是,当内部元组的某个结点指向一组叶子元组时,这些元组必须全部 + 位于同一个索引页上。(这是一个设计决策,目的是减少寻道,并节省把这类元 + 组链接成链时所需链接占用的空间。)如果一组叶子元组增长到单页无法容纳,就会执行拆分 + 并插入一个中间内部元组。要解决这个问题,新的内部元组必须 + 把叶子值集合划分成多个结点组。如果操作符类的 + picksplit 函数做不到这一点, + SP-GiST 核心就会诉诸 + 中描述的非常规措施。 + + + + 当 longValuesOK 为真时,预期 + SP-GiST 树的连续各层会把越来越多的信息吸收到内部 + 元组的前缀和结点标签中,从而使所需的叶子 datum 越来越小,最终能够放入 + 一页。为了防止操作符类中的缺陷导致插入陷入无限循环,如果在连续十次调 + 用 choose 方法之后叶子 datum 仍没有变小, + SP-GiST 核心就会报错。 + + + + + 无结点标签的 SP-GiST + + + 某些树算法对每个内部元组都使用固定的一组结点;例如在四叉树中,总是恰好 + 有四个结点,对应于内部元组中心点周围的四个象限。在这种情况下,代码通常 + 按编号处理结点,因此不需要显式的结点标签。为了省略结点标签(从而节省一 + 些空间),picksplit 函数可以为 + nodeLabels 数组返回 NULL。随后对 + chooseinner_consistent + 的调用中,nodeLabels 也将为 NULL。原则上, + 同一个索引中可以对某些内部元组使用结点标签,而对另一些省略。 + + + + 当处理具有无标签结点的内部元组时,choose 返回 + spgAddNode 是错误的,因为在这种情况下结点集合应被 + 视为固定不变。另外,spgSplitTuple 动作中没有生成 + 无标签结点的规定,因为预期随后还需要执行 + spgAddNode 动作。 + + + + + <quote>全部相同</quote>的内部元组 + + + 当 picksplit 无法把提供的叶子值划分为至少两个结点 + 类别时,SP-GiST 核心可以覆盖操作符类 + picksplit 函数的结果。在这种情况下,会创建一个新 + 的内部元组,其中有多个结点,而每个结点都具有相同的标签(如果有);这个 + 标签就是 picksplit 给它唯一使用的那个结点分配的 + 标签。叶子值会被随机分配到这些等价结点中。该内部元组会设置 + allTheSame 标志,用来提醒 + chooseinner_consistent + 函数:这个元组的结点集合并不是它们通常会预期的那种。 + + + + 在处理 allTheSame 元组时, + choose 返回 spgMatchNode + 表示新值可以分配给任意一个等价结点;核心代码会忽略给出的 + nodeN 值,并随机下降到其中一个结点(以保持 + 树的平衡)。choose 返回 + spgAddNode 则属于错误,因为那会使结点不再全部等 + 价;如果待插入的值与现有结点不匹配,就必须使用 + spgSplitTuple 动作。 + + + + 在处理 allTheSame 元组时, + inner_consistent 函数应当要么把全部结点返回为继 + 续索引搜索的目标,要么一个也不返回,因为它们都是等价的。这是否需要编写特殊情 + 况代码,取决于 inner_consistent 函数平常对这些结 + 点含义做了多大程度的假定。 + + + + + + + 示例 + + + PostgreSQL 源代码发行版中包含了若干 + SP-GiST 索引操作符类示例,如 + 所述。要查看代码,请参阅 + src/backend/access/spgist/ 和 + src/backend/utils/adt/。 + + + + +
diff --git a/zh/9.6/spi.sgml b/zh/9.6/spi.sgml new file mode 100644 index 00000000..2abad792 --- /dev/null +++ b/zh/9.6/spi.sgml @@ -0,0 +1,4051 @@ + + + + 服务器编程接口 + + + SPI + + + + 服务器编程接口SPI)使用户定义 + C 函数的编写者能够在其函数中运行 + SQL 命令。SPI 是一组接口函数, + 用于简化对解析器、规划器和执行器的访问。SPI + 还负责一部分内存管理工作。 + + + + + 可用的过程语言提供了多种从过程中执行 SQL 命令的方法。这些设施大多基于 + SPI,因此本文档对这些语言的用户也会有帮助。 + + + + + 为避免误解,本文用函数SPI接口函数,而用过程指使用SPI的用户定义 C 函数。 + + + + 注意,如果通过 SPI 调用的某条命令失败,控制不会返回到你的过程。相反, + 执行该过程的事务或子事务会被回滚。(考虑到 SPI 函数大多记录了错误返回 + 约定,这一点可能看起来有些意外。不过,这些约定只适用于在 SPI 函数自身内 + 部检测到的错误。)如果在可能失败的 SPI 调用外围建立自己的子事务,则可以 + 在出错后重新取得控制权。 + + + + SPI 函数在成功时返回非负结果(要么直接作为整数返回值, + 要么如后文所述存放在全局变量 SPI_result 中)。发生错 + 误时,则返回负值或 NULL。 + + + + 使用 SPI 的源代码文件必须包含头文件 + executor/spi.h。 + + + + + 接口函数 + + + SPI_connect + + + SPI_connect + 3 + + + + SPI_connect + 将一个过程连接到 SPI 管理器 + + + + +int SPI_connect(void) + + + + + 描述 + + + SPI_connect 会为某次过程调用打开到 SPI 管理器 + 的连接。如果要通过 SPI 执行命令,就必须调用此函数。不过,有些 SPI + 辅助函数可以在未连接的过程中调用。 + + + + 如果你的过程已经处于连接状态,SPI_connect将返回错误代码SPI_ERROR_CONNECT。当某个直接调用了SPI_connect的过程又直接调用另一个调用SPI_connect的过程时,就会发生这种情况。虽然当通过 SPI 调用的 SQL 命令调用另一个使用 SPI 的函数时,允许对 SPI 管理器的递归调用,但直接嵌套调用SPI_connectSPI_finish是被禁止的。(但请参见SPI_pushSPI_pop。) + + + + + 返回值 + + + + SPI_OK_CONNECT + + + 成功时 + + + + + + SPI_ERROR_CONNECT + + + 发生错误时 + + + + + + + + + + + SPI_finish + + + SPI_finish + 3 + + + + SPI_finish + 将一个过程与 SPI 管理器断开 + + + + +int SPI_finish(void) + + + + + 描述 + + + SPI_finish 关闭到 SPI 管理器的现有连接。在完成当前 + 这次过程调用所需的 SPI 操作后,必须调用此函数。不过,如果通过 + elog(ERROR) 中止了事务,就不必担心是否显式调用它; + 这种情况下 SPI 会自行清理。 + + + + 如果在没有有效连接的情况下调用SPI_finish,它将返回SPI_ERROR_UNCONNECTED。这并不会带来根本性问题;它只表示 SPI 管理器无事可做。 + + + + + 返回值 + + + + SPI_OK_FINISH + + + 如果正确地断开连接 + + + + + + SPI_ERROR_UNCONNECTED + + + 如果从未连接的过程中调用 + + + + + + + + + + + + + SPI_push + + + SPI_push + 3 + + + + SPI_push + 压入 SPI 栈以允许递归使用 SPI + + + + +void SPI_push(void) + + + + + 描述 + + + SPI_push应当在执行另一个自身可能希望使用 SPI 的过程之前调用。 + 在SPI_push之后,SPI 不再处于连接状态,除非重新执行SPI_connect,否则 SPI 函数调用将被拒绝。这确保了你的过程的 SPI 状态与你所调用的另一个过程的状态之间的清晰分离。在另一个过程返回之后,调用SPI_pop恢复对你自己 SPI 状态的访问。 + + + + 注意,SPI_execute及相关函数在把控制交还给 SQL 执行引擎之前,会自动完成相当于SPI_push的操作,因此在使用这些函数时无需为此操心。只有当你直接调用可能包含SPI_connect调用的任意代码时,才需要发出SPI_pushSPI_pop。 + + + + + + + + + SPI_pop + + + SPI_pop + 3 + + + + SPI_pop + 弹出 SPI 栈以从递归 SPI 使用中返回 + + + + +void SPI_pop(void) + + + + + 描述 + + + SPI_pop从 SPI 调用栈中弹出先前的环境。参见SPI_push。 + + + + + + + + + SPI_execute + + + SPI_execute + 3 + + + + SPI_execute + 执行一个命令 + + + + +int SPI_execute(const char * command, bool read_only, long count) + + + + + 描述 + + + SPI_execute 执行指定的 SQL 命令,并最多检索 + count 行。如果 read_only + 为 true,该命令必须是只读的,且执行开销会略有降低。 + + + + 此函数只能从已连接的过程中调用。 + + + + 如果 count 为零,则该命令会针对其适用的所有行执 + 行。如果 count 大于零,则最多检索 + count 行;达到该计数时就会停止执行,这很像给查 + 询增加了一个 LIMIT 子句。例如: + +SPI_execute("SELECT * FROM foo", true, 5); + + 最多会从该表中检索 5 行。注意,这种限制只有在命令实际返回行时才有效。 + 例如: + +SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5); + + 会忽略 count 参数,把 + bar 中的所有行都插入进去。不过: + +SPI_execute("INSERT INTO foo SELECT * FROM bar RETURNING *", false, 5); + + 最多只会插入 5 行,因为取到第 5 行 RETURNING 结果后 + 就会停止执行。 + + + + 你可以在一个字符串中传递多条命令;SPI_execute + 返回最后执行的那条命令的结果。count 限制会分别 + 作用于每条命令(尽管实际返回的只有最后一条命令的结果)。该限制不适用于 + 规则生成的任何隐藏命令。 + + + + 当 read_onlyfalse 时, + SPI_execute 会递增命令计数器,并在执行字符串中的每 + 条命令前计算新的快照。如果当前事务隔离级别是 + SERIALIZABLEREPEATABLE READ, + 这个快照实际上不会变化;但在 READ COMMITTED 模式下, + 更新快照会让每条命令都能看到其他会话中新近提交事务的结果。这对于修改数 + 据库的命令获得一致行为至关重要。 + + + + 当 read_onlytrue 时, + SPI_execute 不会更新快照和命令计数器,并且只允许命 + 令字符串中出现普通的 SELECT 命令。这些命令会使用外 + 围查询先前建立的快照来执行。由于消除了每条命令的额外开销,这种执行模式 + 比读写模式略快。它还允许构造真正稳定的函数:由 + 于连续执行都会使用同一个快照,结果也就不会发生变化。 + + + + 在同一个使用 SPI 的函数中混合只读命令和读写命令通常并不明智,因为只读 + 查询看不到读写查询所做的数据库更新,这可能导致非常令人困惑的行为。 + + + + (最后一条)命令实际执行所处理的行数,会通过全局变量 + SPI_processed 返回。如果函数返回值是 + SPI_OK_SELECT、 + SPI_OK_INSERT_RETURNING、 + SPI_OK_DELETE_RETURNING、 + SPI_OK_UPDATE_RETURNING,则可以通过全局指针 + SPITupleTable *SPI_tuptable 访问结果行。有些工具命令 + (如 EXPLAIN)也会返回结果行集,此时 + SPI_tuptable 同样会保存结果。另一些工具命令 + (COPYCREATE TABLE AS)不返 + 回行集,因此 SPI_tuptable 为 NULL,但它们依然会在 + SPI_processed 中返回处理的行数。 + + + + 结构 SPITupleTable 定义如下: + +typedef struct +{ + MemoryContext tuptabcxt; /* 结果表的内存上下文 */ + uint64 alloced; /* 已分配的 vals 数量 */ + uint64 free; /* 空闲的 vals 数量 */ + TupleDesc tupdesc; /* 行描述符 */ + HeapTuple *vals; /* 行 */ +} SPITupleTable; + + vals 是一个指向行的指针数组。(有效项数为 SPI_processed。) + tupdesc 是一个行描述符,可以传给处理行的 SPI 函数。tuptabcxt, + alloced 和 free 是内部字段,不供 SPI 调用者使用。 + + + + SPI_finish 会释放当前过程调用期间分配的全部 + SPITupleTable。如果某个结果表已经不再需要,也 + 可以提前调用 SPI_freetuptable 释放它。 + + + + + 参数 + + + + const char * command + + + 包含待执行命令的字符串 + + + + + + bool read_only + + true 表示只读执行 + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 如果命令执行成功,则返回下列(非负)值之一: + + + + SPI_OK_SELECT + + + 执行了 SELECT(但不是 + SELECT INTO) + + + + + + SPI_OK_SELINTO + + + 执行了 SELECT INTO + + + + + + SPI_OK_INSERT + + + 执行了 INSERT + + + + + + SPI_OK_DELETE + + + 执行了 DELETE + + + + + + SPI_OK_UPDATE + + + 执行了 UPDATE + + + + + + SPI_OK_INSERT_RETURNING + + + 执行了 INSERT RETURNING + + + + + + SPI_OK_DELETE_RETURNING + + + 执行了 DELETE RETURNING + + + + + + SPI_OK_UPDATE_RETURNING + + + 执行了 UPDATE RETURNING + + + + + + SPI_OK_UTILITY + + + 执行了工具命令(例如 CREATE TABLE) + + + + + + SPI_OK_REWRITTEN + + + 命令被 规则 重写成了另一类命令 + (例如 UPDATE 变成了 + INSERT) + + + + + + + + 出错时,返回以下负值之一: + + + + SPI_ERROR_ARGUMENT + + + commandNULL,或者 + count 小于 0 + + + + + + SPI_ERROR_COPY + + + 尝试执行了 COPY TO stdout 或 + COPY FROM stdin + + + + + + SPI_ERROR_TRANSACTION + + + 尝试执行了事务控制命令(BEGIN、 + COMMITROLLBACK、 + SAVEPOINT、 + PREPARE TRANSACTION、 + COMMIT PREPARED、 + ROLLBACK PREPARED 及其各种变体) + + + + + + SPI_ERROR_OPUNKNOWN + + + 命令类型未知(理论上不应发生) + + + + + + SPI_ERROR_UNCONNECTED + + + 如果从未连接的过程中调用 + + + + + + + + + 注解 + + + 所有 SPI 查询执行函数都会设置 SPI_processed 和 + SPI_tuptable(只设置指针,而不更改结构体内容)。如果 + 需要在后续调用之后继续访问 SPI_execute 或其他查询 + 执行函数的结果表,请把这两个全局变量保存到过程局部变量中。 + + + + + + + + SPI_exec + + + SPI_exec + 3 + + + + SPI_exec + 执行一个读/写命令 + + + + +int SPI_exec(const char * command, long count) + + + + + 描述 + + + SPI_exec和 + SPI_execute相同,但后者的 + read_only参数的值总是取 + false。 + + + + + 参数 + + + + const char * command + + + 包含待执行命令的字符串 + + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 见SPI_execute。 + + + + + + + + SPI_execute_with_args + + + SPI_execute_with_args + 3 + + + + SPI_execute_with_args + 执行使用外部参数的命令 + + + + +int SPI_execute_with_args(const char *command, + int nargs, Oid *argtypes, + Datum *values, const char *nulls, + bool read_only, long count) + + + + + 描述 + + + SPI_execute_with_args 执行一条可能包含外部提供参数引 + 用的命令。命令文本中的参数引用写作 + $n,而该调用会为每个这 + 类符号指定数据类型和值。read_only 和 + count 的含义与 + SPI_execute 中相同。 + + + + 相比 SPI_execute,这个例程的主要优点在于可将数据 + 值插入命令而无需繁琐的引用和转义,因此能够显著降低 SQL 注入攻击的风险。 + + + + 也可以通过先调用 SPI_prepare,再调用 + SPI_execute_plan 达到类似效果。不过,使用本函数 + 时,查询计划总会针对所提供的具体参数值进行定制。对于一次性查询执行,应 + 优先选择本函数。如果同一条命令要用许多不同参数重复执行,则两种方式孰快 + 取决于重新规划的代价与定制计划收益之间的权衡。 + + + + + 参数 + + + + const char * command + + + 命令字符串 + + + + + + int nargs + + + 输入参数的数量($1$2 等) + + + + + + Oid * argtypes + + + 一个长度为 nargs 的数组,包含参数数据类型的 + OID + + + + + + Datum * values + + + 一个长度为 nargs 的数组,包含实际参数值 + + + + + + const char * nulls + + + 一个长度为 nargs 的数组,用于描述哪些参数为 + 空值 + + + + 如果 nullsNULL,则 + SPI_execute_with_args 会假定没有参数为空值。 + 否则,如果对应参数值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应参数值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + bool read_only + + true 表示只读执行 + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 返回值与 SPI_execute 相同。 + + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute 相同。 + + + + + + + + SPI_prepare + + + SPI_prepare + 3 + + + + SPI_prepare + 准备一个语句,但暂不执行 + + + + +SPIPlanPtr SPI_prepare(const char * command, int nargs, Oid * argtypes) + + + + + 描述 + + + SPI_prepare 为指定命令创建并返回一个预备语句,但并 + 不执行该命令。该预备语句之后可以通过 + SPI_execute_plan 重复执行。 + + + + 当同一条或相似命令需要反复执行时,通常只做一次解析分析是有利的,而重用 + 该命令的执行计划也可能进一步带来收益。 + SPI_prepare 会把命令字符串转换成一个封装了解析分 + 析结果的预备语句。如果发现为每次执行都生成定制计划并无益处,该预备语句 + 还可用来缓存执行计划。 + + + + 预备命令可以通过在普通命令原本应写常量的位置写入参数 + ($1$2 等)而得到泛化。这些参数 + 的实际值会在调用 SPI_execute_plan 时指定。这样, + 预备语句就能适用于比无参形式更广泛的场景。 + + + + SPI_prepare 返回的语句只能在当前这次过程调用中使 + 用,因为 SPI_finish 会释放为此类语句分配的内存。 + 不过,也可以使用 SPI_keepplan 或 + SPI_saveplan 将该语句保存得更久。 + + + + + 参数 + + + + const char * command + + + 命令字符串 + + + + + + int nargs + + + 输入参数的数量($1$2 等) + + + + + + Oid * argtypes + + + 一个数组指针,它指向的数组包含参数的数据类型的 + OID + + + + + + + + 返回值 + + + SPI_prepare 返回一个非空指针,指向表示预备语句的不透明结构体 SPIPlan。发生错误时会返回 + NULL,并将 SPI_result 设为 + SPI_execute 所使用的那些错误码之一;但如果 + commandNULL,或者 + nargs 小于 0,或者 nargs + 大于 0 且 argtypesNULL, + 则会将其设置为 SPI_ERROR_ARGUMENT。 + + + + + 注解 + + + 如果没有定义参数,则在第一次使用 SPI_execute_plan + 时会创建一个通用计划,并在之后的所有执行中继续使用它。如果存在参数, + SPI_execute_plan 在最初几次使用时会根据提供的参数 + 值生成定制计划。当同一个预备语句被使用足够多次之后, + SPI_execute_plan 会构建一个通用计划;如果它的代价 + 没有比定制计划高出太多,就会开始改用通用计划,而不是每次都重新规划。如 + 果这种默认行为不合适,可以把 CURSOR_OPT_GENERIC_PLAN + 或 CURSOR_OPT_CUSTOM_PLAN 标志传给 + SPI_prepare_cursor,分别强制使用通用计划或定制计划。 + + + + 尽管预备语句的主要目的在于避免重复进行解析分析和规划,但只要语句中使用 + 的数据库对象自上次使用该预备语句以来发生了定义性(DDL)变更, + PostgreSQL 就会在再次使用前强制重新分析并重新规划该语句。此外,如果 的值在两次 + 使用之间发生变化,该语句也会基于新的 search_path 重 + 新解析。(后一种行为是从 PostgreSQL 9.3 开 + 始引入的。)有关预备语句行为的更多信息,请参见 + 。 + + + + 此函数只能从已连接的过程中调用。 + + + + SPIPlanPtrspi.h 中被声明为指向不 + 透明结构体类型的指针。尝试直接访问其内容并不明智,因为这会让你的代码在 + PostgreSQL 后续版本中更容易失效。 + + + + SPIPlanPtr 这个名字多少带有历史色彩,因为该数据结构已不再 + 必然包含执行计划。 + + + + + + + + SPI_prepare_cursor + + + SPI_prepare_cursor + 3 + + + + SPI_prepare_cursor + 准备一个语句,但暂不执行 + + + + +SPIPlanPtr SPI_prepare_cursor(const char * command, int nargs, + Oid * argtypes, int cursorOptions) + + + + + 描述 + + + SPI_prepare_cursor 与 + SPI_prepare 相同,但额外允许指定规划器的 + 游标选项参数。该参数是一个位掩码,其可取值对应于 + nodes/parsenodes.h 中 + DeclareCursorStmt 的 + options 字段。SPI_prepare + 总是把游标选项设为零。 + + + + + 参数 + + + + const char * command + + + 命令字符串 + + + + + + int nargs + + + 输入参数的数量($1$2 等) + + + + + + Oid * argtypes + + + 一个数组指针,它指向的数组包含参数的数据类型的 + OID + + + + + + int cursorOptions + + + 整数形式的游标选项位掩码,零会导致默认行为 + + + + + + + + 返回值 + + + SPI_prepare_cursor 的返回约定与 + SPI_prepare 相同。 + + + + + 注解 + + + 适合在 cursorOptions 中设置的位包括 + CURSOR_OPT_SCROLL、 + CURSOR_OPT_NO_SCROLL、 + CURSOR_OPT_FAST_PLAN、 + CURSOR_OPT_GENERIC_PLAN 和 + CURSOR_OPT_CUSTOM_PLAN。需要特别注意的是, + CURSOR_OPT_HOLD 会被忽略。 + + + + + + + + SPI_prepare_params + + + SPI_prepare_params + 3 + + + + SPI_prepare_params + 准备一个语句,但暂不执行 + + + + +SPIPlanPtr SPI_prepare_params(const char * command, + ParserSetupHook parserSetup, + void * parserSetupArg, + int cursorOptions) + + + + + 描述 + + + SPI_prepare_params 为指定命令创建并返回一个预备语 + 句,但不执行该命令。它相当于 SPI_prepare_cursor, + 只是额外允许调用者指定解析器钩子函数,以控制外部参数引用的解析。 + + + + + 参数 + + + + const char * command + + + 命令字符串 + + + + + + ParserSetupHook parserSetup + + + 解析器钩子设置函数 + + + + + + void * parserSetupArg + + + 传递给 parserSetup 的透传参数 + + + + + + int cursorOptions + + + 整数形式的游标选项位掩码,零会导致默认行为 + + + + + + + + 返回值 + + + SPI_prepare_params 的返回约定与 + SPI_prepare 相同。 + + + + + + + + SPI_getargcount + + + SPI_getargcount + 3 + + + + SPI_getargcount + 返回由 SPI_prepare 准备的语句所需参数个数 + + + + +int SPI_getargcount(SPIPlanPtr plan) + + + + + 描述 + + + SPI_getargcount 返回执行由 + SPI_prepare 准备的语句所需的参数个数。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + + + 返回值 + + plan 所需的参数个数。如果 + planNULL 或无效,则 + SPI_result 会被设置为 + SPI_ERROR_ARGUMENT,并返回 -1。 + + + + + + + + SPI_getargtypeid + + + SPI_getargtypeid + 3 + + + + SPI_getargtypeid + 返回由 SPI_prepare 准备的语句中某个参数的数据类型 OID + + + + +Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex) + + + + + 描述 + + + SPI_getargtypeid 返回由 + SPI_prepare 准备的语句中第 + argIndex 个参数类型的 OID。第一个参数的索引为 + 零。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + int argIndex + + + 参数的索引,从零开始 + + + + + + + + 返回值 + + 给定索引处参数的类型 OID。如果 plan 为 + NULL 或无效,或者 argIndex + 小于 0,或者不小于为 plan 声明的参数个数,则 + SPI_result 会被设置为 + SPI_ERROR_ARGUMENT,并返回 + InvalidOid。 + + + + + + + + SPI_is_cursor_plan + + + SPI_is_cursor_plan + 3 + + + + SPI_is_cursor_plan + 如果由 SPI_prepare 准备的语句可用于 SPI_cursor_open 则返回 + true + + + + +bool SPI_is_cursor_plan(SPIPlanPtr plan) + + + + + 描述 + + + 如果由 SPI_prepare 准备的语句可以作为参数传给 + SPI_cursor_open,则 + SPI_is_cursor_plan 返回 true; + 否则返回 false。判定标准是 + plan 必须表示单条命令,并且该命令会向调用者返 + 回元组。例如,不包含 INTO 子句的 + SELECT 是允许的,而 UPDATE 只 + 有包含 RETURNING 子句时才允许。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + + + 返回值 + + 返回 truefalse,表示 + plan 能否产生游标,同时将 + SPI_result 设为零。如果无法确定答案(例如 + planNULL 或无效,或者在 + 未连接到 SPI 时调用),则会将 SPI_result 设为合适 + 的错误码,并返回 false。 + + + + + + + + SPI_execute_plan + + + SPI_execute_plan + 3 + + + + SPI_execute_plan + 执行由 SPI_prepare 准备好的语句 + + + + +int SPI_execute_plan(SPIPlanPtr plan, Datum * values, const char * nulls, + bool read_only, long count) + + + + + 描述 + + + SPI_execute_plan 执行由 SPI_prepare + 或其同类函数准备好的语句。read_only 和 + count 的含义与 + SPI_execute 中相同。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + Datum * values + + + 一个实际参数值的数组。必须和语句的参数数量等长。 + + + + + + const char * nulls + + + 一个描述哪些参数为空值的数组。必须和语句的参数数量等长。 + + + + 如果 nullsNULL,则 + SPI_execute_plan 会假定没有参数为空值。 + 否则,如果对应参数值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应参数值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + bool read_only + + true 表示只读执行 + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 返回值和SPI_execute相同, + 还有下列额外可能的错误(负值)结果: + + + + SPI_ERROR_ARGUMENT + + + 如果planNULL + 或者非法,或者count小于 0 + + + + + + SPI_ERROR_PARAM + + + 如果valuesNULL,但 + plan在准备时带有参数 + + + + + + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute 相同。 + + + + + + + + SPI_execute_plan_with_paramlist + + + SPI_execute_plan_with_paramlist + 3 + + + + SPI_execute_plan_with_paramlist + 执行由 SPI_prepare 准备好的语句 + + + + +int SPI_execute_plan_with_paramlist(SPIPlanPtr plan, + ParamListInfo params, + bool read_only, + long count) + + + + + 描述 + + + SPI_execute_plan_with_paramlist 执行由 + SPI_prepare 准备好的语句。它相当于 + SPI_execute_plan,只是向查询传递参数值的方式不同。 + ParamListInfo 表示形式便于传递已经按这种格式存在的值, + 也支持通过 ParamListInfo 中指定的钩子函数使用动态参数 + 集。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + ParamListInfo params + + + 包含参数类型和值的数据结构;没有参数时为 NULL + + + + + + bool read_only + + true 表示只读执行 + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 返回值与 SPI_execute_plan 相同。 + + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute_plan 相同。 + + + + + + + + SPI_execp + + + SPI_execp + 3 + + + + SPI_execp + 以读/写模式执行一个语句 + + + + +int SPI_execp(SPIPlanPtr plan, Datum * values, const char * nulls, long count) + + + + + 描述 + + + SPI_execp 与 + SPI_execute_plan 相同,只不过后者的 + read_only 参数固定为 + false。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + Datum * values + + + 一个实际参数值的数组。必须和语句的参数数量等长。 + + + + + + const char * nulls + + + 一个描述哪些参数为空值的数组。必须和语句的参数数量等长。 + + + + 如果 nullsNULL,则 + SPI_execp 会假定没有参数为空值。 + 否则,如果对应参数值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应参数值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + long count + + + 要返回的最大行数,或者用 0 表示不限制 + + + + + + + + 返回值 + + + 见 SPI_execute_plan。 + + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute 相同。 + + + + + + + + SPI_cursor_open + + + SPI_cursor_open + 3 + + + + SPI_cursor_open + 使用由SPI_prepare创建的 + 语句建立一个游标 + + + + +Portal SPI_cursor_open(const char * name, SPIPlanPtr plan, + Datum * values, const char * nulls, + bool read_only) + + + + + 描述 + + + SPI_cursor_open建立一个游标(在内部是一个 + portal),该游标将执行由SPI_prepare准备好 + 的一个语句。参数具有和SPI_execute_plan的 + 相应参数相同的含义。 + + + + 使用游标而不是直接执行该语句有两个好处。首先,可以一次只取出少量结果 + 行,从而避免对返回大量行的查询耗尽内存。其次,一个 portal 可以比当前 + 的过程存活更久(实际上,它可以一直存活到当前事务结束)。把 portal + 的名称返回给该过程的调用者,就提供了一种把行集作为结果返回的方法。 + + + + 传入的参数数据会被复制到该游标的 portal 中,因此即使该游标仍然存在, + 也可以释放这些参数数据。 + + + + + 参数 + + + + const char * name + + + portal 的名称,或为NULL以让系统选择名称 + + + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + Datum * values + + + 一个实际参数值的数组。必须和语句的参数数量等长。 + + + + + + const char * nulls + + + 一个描述哪些参数为空值的数组。必须和语句的参数数量等长。 + + + + 如果 nullsNULL,则 + SPI_cursor_open 会假定没有参数为空值。 + 否则,如果对应参数值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应参数值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + bool read_only + + true 表示只读执行 + + + + + + + 返回值 + + + 指向包含该游标的 portal 的指针。注意这里没有错误返回约定, + 任何错误都将通过elog报告。 + + + + + + + + SPI_cursor_open_with_args + + + SPI_cursor_open_with_args + 3 + + + + SPI_cursor_open_with_args + 使用一个查询和参数建立一个游标 + + + + +Portal SPI_cursor_open_with_args(const char *name, + const char *command, + int nargs, Oid *argtypes, + Datum *values, const char *nulls, + bool read_only, int cursorOptions) + + + + + 描述 + + + SPI_cursor_open_with_args建立一个将 + 执行指定查询的游标(在内部是一个 portal)。大部分参数具有和 + SPI_prepare_cursor + 和SPI_cursor_open中相应参数相同的含 + 义。 + + + + 对于一次性查询执行,应优先使用此函数,而不是先调用 + SPI_prepare_cursor 再调用 + SPI_cursor_open。如果同一条命令要用许多不同参数执行, + 哪种方法更快取决于重新规划的代价与定制计划收益之间的权衡。 + + + + 传入的参数数据会被复制到该游标的 portal 中,因此即使该游标仍然存在, + 也可以释放这些参数数据。 + + + + + 参数 + + + + const char * name + + + portal 的名称,或为NULL以让系统选择名称 + + + + + + const char * command + + + 命令字符串 + + + + + + int nargs + + + 输入参数的数量($1$2 等) + + + + + + Oid * argtypes + + + 一个长度为 nargs 的数组,包含参数数据类型的 + OID + + + + + + Datum * values + + + 一个长度为 nargs 的数组,包含实际参数值 + + + + + + const char * nulls + + + 一个长度为 nargs 的数组,用于描述哪些参数为 + 空值 + + + + 如果 nullsNULL,则 + SPI_cursor_open_with_args 会假定没有参数为空值。 + 否则,如果对应参数值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应参数值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + bool read_only + + true 表示只读执行 + + + + + int cursorOptions + + + 整数形式的游标选项位掩码,零会导致默认行为 + + + + + + + + 返回值 + + + 指向包含该游标的 portal 的指针。注意这里没有错误返回约定, + 任何错误都将通过elog报告。 + + + + + + + + SPI_cursor_open_with_paramlist + + + SPI_cursor_open_with_paramlist + 3 + + + + SPI_cursor_open_with_paramlist + 使用参数建立一个游标 + + + + +Portal SPI_cursor_open_with_paramlist(const char *name, + SPIPlanPtr plan, + ParamListInfo params, + bool read_only) + + + + + 描述 + + + SPI_cursor_open_with_paramlist 建立一个游标 + (内部即一个 portal),用来执行由 SPI_prepare + 准备好的语句。它相当于 SPI_cursor_open,只是向查 + 询传递参数值的方式不同。ParamListInfo 表示形式便于传 + 递已经按这种格式存在的值,也支持通过 + ParamListInfo 中指定的钩子函数使用动态参数集。 + + + + 传入的参数数据会被复制到该游标的 portal 中,因此即使该游标仍然存在, + 也可以释放这些参数数据。 + + + + + 参数 + + + + const char * name + + + portal 的名称,或为NULL以让系统选择名称 + + + + + + SPIPlanPtr plan + + + 预备语句(由SPI_prepare返回) + + + + + + ParamListInfo params + + + 包含参数类型和值的数据结构;没有参数时为 NULL + + + + + + bool read_only + + true 表示只读执行 + + + + + + + 返回值 + + + 指向包含该游标的 portal 的指针。注意这里没有错误返回约定, + 任何错误都将通过elog报告。 + + + + + + + + SPI_cursor_find + + + SPI_cursor_find + 3 + + + + SPI_cursor_find + 用名称查找一个现有的游标 + + + + +Portal SPI_cursor_find(const char * name) + + + + + 描述 + + + SPI_cursor_find 按名称查找现有的 portal。这主要用 + 于解析由其他函数以文本形式返回的游标名。 + + + + + 参数 + + + + const char * name + + + 该 portal 的名称 + + + + + + + + 返回值 + + + 带有指定名称的 portal 的指针,如果没有找到就是 + NULL + + + + + + + + SPI_cursor_fetch + + + SPI_cursor_fetch + 3 + + + + SPI_cursor_fetch + 从一个游标取出一些行 + + + + +void SPI_cursor_fetch(Portal portal, bool forward, long count) + + + + + 描述 + + + SPI_cursor_fetch从一个游标取得一些行。 + 这等效于 SQL 命令FETCH的一个子集(更多功能 + 见SPI_scroll_cursor_fetch)。 + + + + + 参数 + + + + Portal portal + + + 包含该游标的 portal + + + + + + bool forward + + + 为真表示向前获取,为假表示向后获取 + + + + + + long count + + + 要取得的最大行数 + + + + + + + + 返回值 + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute 相同。 + + + + + 注解 + + + 如果该游标的计划不是用CURSOR_OPT_SCROLL + 选项创建的,向后获取可能失败。 + + + + + + + + SPI_cursor_move + + + SPI_cursor_move + 3 + + + + SPI_cursor_move + 移动一个游标 + + + + +void SPI_cursor_move(Portal portal, bool forward, long count) + + + + + 描述 + + + SPI_cursor_move跳过一个游标中的一些行。 + 这等效于 SQL 命令MOVE的一个子集(更多的功能 + 请见SPI_scroll_cursor_move)。 + + + + + 参数 + + + + Portal portal + + + 包含该游标的 portal + + + + + + bool forward + + + 为真表示前移,为假表示后移 + + + + + + long count + + + 要移动的最大行数 + + + + + + + + 注解 + + + 如果该游标的计划不是用CURSOR_OPT_SCROLL + 选项创建的,向后移动可能失败。 + + + + + + + + SPI_scroll_cursor_fetch + + + SPI_scroll_cursor_fetch + 3 + + + + SPI_scroll_cursor_fetch + 从一个游标取出一些行 + + + + +void SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, + long count) + + + + + 描述 + + + SPI_scroll_cursor_fetch从一个游标中取出一些行。 + 这等效于 SQL 命令FETCH。 + + + + + 参数 + + + + Portal portal + + + 包含该游标的 portal + + + + + + FetchDirection direction + + + FETCH_FORWARD、 + FETCH_BACKWARD、 + FETCH_ABSOLUTE或者 + FETCH_RELATIVE之一 + + + + + + long count + + + FETCH_FORWARD或 + FETCH_BACKWARD方式中要取出的行数; + FETCH_ABSOLUTE方式中要取出的绝对行号; + FETCH_RELATIVE方式中要取出的相对行号 + + + + + + + + 返回值 + + + 成功时,SPI_processed 和 + SPI_tuptable 的设置方式与 + SPI_execute 相同。 + + + + + 注解 + + + 参数direction和 + count的详细解释请见 + SQL 命令。 + + + + 如果该游标的计划不是用CURSOR_OPT_SCROLL + 选项创建的,除FETCH_FORWARD之外的方向值可能失败。 + + + + + + + + SPI_scroll_cursor_move + + + SPI_scroll_cursor_move + 3 + + + + SPI_scroll_cursor_move + 移动一个游标 + + + + +void SPI_scroll_cursor_move(Portal portal, FetchDirection direction, + long count) + + + + + 描述 + + + SPI_scroll_cursor_move在一个游标中跳过 + 一定数量的行。这等效于 SQL 命令MOVE。 + + + + + 参数 + + + + Portal portal + + + 包含该游标的 portal + + + + + + FetchDirection direction + + + FETCH_FORWARD、 + FETCH_BACKWARD、 + FETCH_ABSOLUTE或者 + FETCH_RELATIVE之一 + + + + + + long count + + + FETCH_FORWARD或者 + FETCH_BACKWARD方式中要移动的行数; + FETCH_ABSOLUTE方式中要移动到的绝对行号; + FETCH_RELATIVE方式中要移动到的相对行号 + + + + + + + + 返回值 + + + 成功时,SPI_processed 的设置方式与 + SPI_execute 相同。 + SPI_tuptable 会被设置为 NULL, + 因为本函数不会返回任何行。 + + + + + 注解 + + + 参数direction和 + count的详细解释请见 + SQL 命令。 + + + + 如果该游标的计划不是用CURSOR_OPT_SCROLL + 选项创建的,除FETCH_FORWARD之外的方向值可能失败。 + + + + + + + + SPI_cursor_close + + + SPI_cursor_close + 3 + + + + SPI_cursor_close + 关闭一个游标 + + + + +void SPI_cursor_close(Portal portal) + + + + + 描述 + + + SPI_cursor_close关闭一个之前创建的游标 + 并且释放它的 portal 存储。 + + + + 所有打开的游标会在事务结束时自动被关闭。 + 只有在希望尽快释放资源时,才需要调用 + SPI_cursor_close。 + + + + + 参数 + + + + Portal portal + + + 包含该游标的 portal + + + + + + + + + + + SPI_keepplan + + + SPI_keepplan + 3 + + + + SPI_keepplan + 保存一个预备语句 + + + + +int SPI_keepplan(SPIPlanPtr plan) + + + + + 描述 + + + SPI_keepplan 保存传入的预备语句(由 + SPI_prepare 准备),使其不会被 + SPI_finish 或事务管理器释放。这让你能够在当前会 + 话后续的过程调用中重用该预备语句。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 要保存的预备语句 + + + + + + + + 返回值 + + + 成功返回 0;如果planNULL + 或者无效则返回SPI_ERROR_ARGUMENT + + + + + 注解 + + + 这个函数通过调整指针(无需复制数据)的方式,将传入的预备语句重定位到 + 永久存储中。如果之后需要删除它,可以对其使用 + SPI_freeplan。 + + + + + + + + SPI_saveplan + + + SPI_saveplan + 3 + + + + SPI_saveplan + 保存一个预备语句 + + + + +SPIPlanPtr SPI_saveplan(SPIPlanPtr plan) + + + + + 描述 + + + SPI_saveplan 会把传入的预备语句(由 + SPI_prepare 准备)复制到不会被 + SPI_finish 或事务管理器释放的内存中,并返回该副 + 本的指针。这让你能够在当前会话后续的过程调用中重用预备语句。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 要保存的预备语句 + + + + + + + + 返回值 + + + 复制后语句的指针;如果未成功则返回NULL。 + 错误时,SPI_result会被这样设置: + + + + SPI_ERROR_ARGUMENT + + + 如果planNULL或无效 + + + + + + SPI_ERROR_UNCONNECTED + + + 如果从未连接的过程中调用 + + + + + + + + + 注解 + + + 原始传入的预备语句不会被释放,因此你可能需要对其调用 + SPI_freeplan,以避免在 + SPI_finish之前发生内存泄露。 + + + + 在大多数情况下,SPI_keepplan 比此函数更合适,因 + 为它基本能达到相同效果,同时无需实际复制该预备语句的数据结构。 + + + + + + + + + + + + + 接口支持函数 + + + 这里介绍的函数提供了一个接口,用于从 SPI_execute + 及其他 SPI 函数返回的结果集中提取信息。 + + + + 本节介绍的所有函数都可以在已连接和未连接的过程中使用。 + + + + + + SPI_fname + + + SPI_fname + 3 + + + + SPI_fname + 为指定的列号确定列名 + + + + +char * SPI_fname(TupleDesc rowdesc, int colnumber) + + + + + 描述 + + + SPI_fname 返回指定列列名的一个副本。(不再需要时, + 可以用 pfree 释放它。) + + + + + 参数 + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + int colnumber + + + 列号(从 1 开始计) + + + + + + + + 返回值 + + + 列名;如果 colnumber 超出范围则返回 + NULL。出错时, + SPI_result 会被设置为 + SPI_ERROR_NOATTRIBUTE。 + + + + + + + + SPI_fnumber + + + SPI_fnumber + 3 + + + + SPI_fnumber + 根据指定列名确定列号 + + + + +int SPI_fnumber(TupleDesc rowdesc, const char * colname) + + + + + 描述 + + + SPI_fnumber 返回指定列名对应的列号。 + + + + 如果 colname 指向的是系统列(例如 + oid),则会返回相应的负列号。调用者应当通过检查返 + 回值是否恰好等于 SPI_ERROR_NOATTRIBUTE 来判断错误; + 除非本就要拒绝系统列,否则测试结果是否小于等于 0 并不正确。 + + + + + 参数 + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + const char * colname + + + 列名 + + + + + + + + 返回值 + + + 列号(从 1 开始计),如果没有找到所提到的列名则返回 + SPI_ERROR_NOATTRIBUTE。 + + + + + + + + SPI_getvalue + + + SPI_getvalue + 3 + + + + SPI_getvalue + 返回指定列的字符串值 + + + + +char * SPI_getvalue(HeapTuple row, TupleDesc rowdesc, int colnumber) + + + + + 描述 + + + SPI_getvalue返回指定列的值的字符串表示。 + + + + 结果保存在由 palloc 分配的内存中返回。(不再需要 + 时,可以用 pfree 释放该内存。) + + + + + 参数 + + + + HeapTuple row + + + 要检查的输入行 + + + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + int colnumber + + + 列号(从 1 开始计) + + + + + + + + 返回值 + + + 列值;如果该列为空值、colnumber 超出范围 + (SPI_result 被设置为 + SPI_ERROR_NOATTRIBUTE)或者没有输出函数 + 可用(SPI_result 被设置为 + SPI_ERROR_NOOUTFUNC)则返回 + NULL。 + + + + + + + + SPI_getbinval + + + SPI_getbinval + 3 + + + + SPI_getbinval + 返回指定列的二进制值 + + + + +Datum SPI_getbinval(HeapTuple row, TupleDesc rowdesc, int colnumber, + bool * isnull) + + + + + 描述 + + + SPI_getbinval 以内部形式(即 Datum + 类型)返回指定列的值。 + + + + 此函数不会为该 datum 分配新空间。对于传引用的数据类型,返回值将是指向 + 传入行内部数据的指针。 + + + + + 参数 + + + + HeapTuple row + + + 要检查的输入行 + + + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + int colnumber + + + 列号(从 1 开始计) + + + + + + bool * isnull + + + 列中是否为空值的标志 + + + + + + + + 返回值 + + + 返回该列的二进制值。如果该列为空值,则 + isnull 指向的变量会被设为真,否则设为假。 + + + + 出错时,SPI_result 会被设置为 + SPI_ERROR_NOATTRIBUTE。 + + + + + + + + SPI_gettype + + + SPI_gettype + 3 + + + + SPI_gettype + 返回指定列的数据类型名称 + + + + +char * SPI_gettype(TupleDesc rowdesc, int colnumber) + + + + + 描述 + + + SPI_gettype返回该指定列的数据类型名称 + 的副本(当你不再需要该副本后,可以使用pfree + 释放它)。 + + + + + 参数 + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + int colnumber + + + 列号(从 1 开始计) + + + + + + + + 返回值 + + + 指定列的数据类型名称;出错时返回 NULL。 + 出错时,SPI_result 会被设置为 + SPI_ERROR_NOATTRIBUTE。 + + + + + + + + SPI_gettypeid + + + SPI_gettypeid + 3 + + + + SPI_gettypeid + 返回指定列的数据类型的OID + + + + +Oid SPI_gettypeid(TupleDesc rowdesc, int colnumber) + + + + + 描述 + + + SPI_gettypeid返回该指定列的数据类型的 + OID。 + + + + + 参数 + + + + TupleDesc rowdesc + + + 输入行描述 + + + + + + int colnumber + + + 列号(从 1 开始计) + + + + + + + + 返回值 + + + 指定列数据类型的 OID;出错时返回 + InvalidOid。出错时, + SPI_result 会被设置为 + SPI_ERROR_NOATTRIBUTE。 + + + + + + + + SPI_getrelname + + + SPI_getrelname + 3 + + + + SPI_getrelname + 返回指定关系的名称 + + + + +char * SPI_getrelname(Relation rel) + + + + + 描述 + + + SPI_getrelname返回该指定关系的名称 + 的副本(当你不再需要该副本后,可以使用pfree + 释放它)。 + + + + + 参数 + + + + Relation rel + + + 输入关系 + + + + + + + + 返回值 + + + 指定关系的名称。 + + + + + + SPI_getnspname + + + SPI_getnspname + 3 + + + + SPI_getnspname + 返回指定关系所属的名字空间 + + + + +char * SPI_getnspname(Relation rel) + + + + + 描述 + + + SPI_getnspname 返回指定 + Relation 所属名字空间名称的一个副本。这等价于 + 该关系的模式名。用完该返回值后,应调用 pfree 释 + 放它。 + + + + + 参数 + + + + Relation rel + + + 输入关系 + + + + + + + + 返回值 + + + 指定关系的名字空间的名称。 + + + + + + + + 内存管理 + + + + 内存上下文 + 在 SPI 中 + + PostgreSQL内存上下文 + 中分配内存。内存上下文为管理那些在许多不同位置创建、且生命周期各不相同 + 的内存分配提供了便捷手段。销毁某个上下文时,会释放其中分配的全部内存。 + 因此,不必为了避免内存泄漏而跟踪每个单独对象;只需管理数量相对较少的上 + 下文即可。palloc 及相关函数都从当前 + 上下文中分配内存。 + + + + SPI_connect 创建新的内存上下文并将其设为当前上下 + 文。SPI_finish 则恢复先前的当前上下文,并销毁由 + SPI_connect 创建的上下文。这些操作可以确保在过程内部所做的临时内存分配会在过程退出时被回收,从而避免内存泄漏。 + + + + 不过,如果过程需要返回位于已分配内存中的对象(例如传引用数据类型的 + 值),就不能使用 palloc 来分配这块内存,至少在连 + 接到 SPI 的期间不能这么做。否则,该对象会在 + SPI_finish 时被释放,过程也就无法可靠工作。解决 + 办法是用 SPI_palloc 为返回对象分配内存。 + SPI_palloc上层执行器上下文中分配 + 内存,也就是调用 SPI_connect 时的当前内存上下文; + 这正是从过程返回值最合适的上下文。 + + + + 如果在过程尚未连接到 SPI 时调用SPI_palloc,它的行为与普通的palloc相同。在过程连接到 SPI 管理器之前,当前内存上下文就是上层执行器上下文,因此过程通过palloc或 SPI 辅助函数所做的所有分配都位于这个上下文中。 + + + + 调用 SPI_connect 时,会把该过程的私有上下文 + (由 SPI_connect 创建)设为当前上下文。所有通过 + pallocrepalloc 或 SPI + 辅助函数分配的内存(但SPI_copytuple、 + SPI_returntupleSPI_modifytuple + 和SPI_palloc除外)都位于这个上下文中。当过程通 + 过 SPI_finish 与 SPI 管理器断开连接时,当前上下文 + 会恢复为上层执行器上下文,而在该过程内存上下文中分配的所有内存都会被 + 释放,之后就不能再使用。 + + + + 本节介绍的所有函数既可由已连接的过程使用,也可由未连接的过程使用。在未连接的过程中,它们的行为与底层普通服务器函数(palloc等)相同。 + + + + + + SPI_palloc + + + SPI_palloc + 3 + + + + SPI_palloc + 在上层执行器上下文中分配内存 + + + + +void * SPI_palloc(Size size) + + + + + 描述 + + + SPI_palloc 在上层执行器上下文中分配内存。 + + + + + + 参数 + + + + Size size + + + 要分配的存储空间大小(以字节计) + + + + + + + + 返回值 + + + 指向具有指定大小的新存储空间的指针 + + + + + + + + SPI_repalloc + + + SPI_repalloc + 3 + + + + SPI_repalloc + 在上层执行器上下文中重分配内存 + + + + +void * SPI_repalloc(void * pointer, Size size) + + + + + 描述 + + + SPI_repalloc改变之前用SPI_palloc + 分配的内存段的大小。 + + + + 这个函数如今与普通的repalloc已无区别。 + 保留它只是为了与现有代码保持向后兼容。 + + + + + 参数 + + + + void * pointer + + + 指向要改变的现有存储空间的指针 + + + + + + Size size + + + 要分配的存储空间大小(以字节计) + + + + + + + + 返回值 + + + 指向具有指定大小的新存储空间的指针,现有区域的内容会被复制到其中 + + + + + + + + SPI_pfree + + + SPI_pfree + 3 + + + + SPI_pfree + 在上层执行器上下文中释放内存 + + + + +void SPI_pfree(void * pointer) + + + + + 描述 + + + SPI_pfree释放之前使用 + SPI_palloc或者 + SPI_repalloc分配的内存。 + + + + 这个函数如今与普通的pfree已无区别。 + 保留它只是为了与现有代码保持向后兼容。 + + + + + 参数 + + + + void * pointer + + + 指向要释放的现有存储空间的指针 + + + + + + + + + + + SPI_copytuple + + + SPI_copytuple + 3 + + + + SPI_copytuple + 在上层执行器上下文中创建一行的副本 + + + + +HeapTuple SPI_copytuple(HeapTuple row) + + + + + 描述 + + + SPI_copytuple 在上层执行器上下文中创建一行的副本。 + 它通常用于从触发器中返回修改后的行。对于声明为返回复合类型的函数,应改 + 用 SPI_returntuple。 + + + + + + 参数 + + + + HeapTuple row + + + 要复制的行 + + + + + + + + 返回值 + + + 复制后的行;仅当tupleNULL 时 + 才返回 NULL + + + + + + + + SPI_returntuple + + + SPI_returntuple + 3 + + + + SPI_returntuple + 准备把一个元组返回为一个 Datum + + + + +HeapTupleHeader SPI_returntuple(HeapTuple row, TupleDesc rowdesc) + + + + + 描述 + + + SPI_returntuple 在上层执行器上下文中创建一行的副 + 本,并以行类型 Datum 的形式返回。返回的指针只需在返回前用 + PointerGetDatum 转换为 Datum 即可。 + + + + + 请注意,它应用于声明为返回复合类型的函数,而不用于触发器;触发器中返回 + 修改后的行应使用 SPI_copytuple。 + + + + + 参数 + + + + HeapTuple row + + + 要复制的行 + + + + + + TupleDesc rowdesc + + + 行描述符(若要获得最佳缓存效果,每次都传入同一个描述符) + + + + + + + + 返回值 + + + 指向复制后行的 HeapTupleHeader;仅当row + 或 rowdescNULL 时才返回 + NULL + + + + + + + + SPI_modifytuple + + + SPI_modifytuple + 3 + + + + SPI_modifytuple + 通过替换给定行的选定字段来创建新行 + + + + +HeapTuple SPI_modifytuple(Relation rel, HeapTuple row, int ncols, + int * colnum, Datum * values, const char * nulls) + + + + + 描述 + + + SPI_modifytuple 通过为选定列替换新值、并在其他位 + 置复制原行的列来创建一行新记录。输入行本身不会被修改。新行会在上层执行 + 器上下文中返回。 + + + + + + 参数 + + + + Relation rel + + + 仅用作该行的行描述符来源。(传递关系而不是行描述符是个设计缺陷。) + + + + + + HeapTuple row + + + 要被修改的行 + + + + + + int ncols + + + 要被修改的列数 + + + + + + int * colnum + + + 一个长度为ncols的数组,包含了要被修改的列号 + (列号从 1 开始) + + + + + + Datum * values + + + 一个长度为ncols的数组,包含了指定列的新值 + + + + + + const char * nulls + + + 一个长度为ncols的数组,描述哪些新值为空值 + + + + 如果 nullsNULL,则 + SPI_modifytuple 会假定没有新值为空值。 + 否则,如果对应新值非空值,则 nulls 数组中 + 的对应项应为 ' ';如果对应新值为空值,则 + 对应项应为 'n'。(后一种情况下,对应 + values 项中的实际值无关紧要。)注意, + nulls 不是文本字符串,而只是一个数组,因此不 + 需要 '\0' 终止符。 + + + + + + + + 返回值 + + + 修改后的新行,分配在上层执行器上下文中;仅当row + 为 NULL 时才返回 NULL + + + + 出错时,SPI_result被设置如下: + + + SPI_ERROR_ARGUMENT + + + 如果relNULL,或者 + rowNULL,或者ncols + 小于等于 0,或者colnumNULL, + 或者valuesNULL。 + + + + + + SPI_ERROR_NOATTRIBUTE + + + 如果colnum包含一个无效的列号(小于等于 0 或者大于 + row中的列数)。 + + + + + + + + + + + + SPI_freetuple + + + SPI_freetuple + 3 + + + + SPI_freetuple + 释放一个在上层执行器上下文中分配的行 + + + + +void SPI_freetuple(HeapTuple row) + + + + + 描述 + + + SPI_freetuple释放之前在上层执行器上下文中 + 分配的一行。 + + + + 这个函数如今与普通的heap_freetuple已无区别。 + 保留它只是为了与现有代码保持向后兼容。 + + + + + 参数 + + + + HeapTuple row + + + 要释放的行 + + + + + + + + + + + SPI_freetuptable + + + SPI_freetuptable + 3 + + + + SPI_freetuptable + 释放由 SPI_execute 或类似函数创建的结果行集 + + + + +void SPI_freetuptable(SPITupleTable * tuptable) + + + + + 描述 + + + SPI_freetuptable 释放由先前的 SPI 命令执行函数 + (例如 SPI_execute)创建的结果行集。因此,调用它 + 时经常会直接把全局变量 SPI_tuptable 作为参数。 + + + + 如果某个使用 SPI 的过程需要执行多条命令,并且不想将前面命令的 + 结果一直保留到过程结束,那么这个函数就很有用。注意,任何尚未释放的结果行集都会在 + SPI_finish 时被释放。此外,如果在使用 SPI 的过程执行过程中开启了一个子事务而后又中止,SPI 会自动释放该子事务运行期间 + 创建的所有结果行集。 + + + + 从 PostgreSQL 9.3 开始, + SPI_freetuptable 内置了保护逻辑,以避免对同一个结 + 果行集重复发出删除请求。在更早的版本中,重复删除会导致崩溃。 + + + + + 参数 + + + + SPITupleTable * tuptable + + + 要释放的结果行集指针,或传入 NULL 表示不执行任何操作 + + + + + + + + + + + SPI_freeplan + + + SPI_freeplan + 3 + + + + SPI_freeplan + 释放一个之前保存的预备语句 + + + + +int SPI_freeplan(SPIPlanPtr plan) + + + + + 描述 + + + SPI_freeplan释放一个之前由 + SPI_prepare返回的或者由 + SPI_keepplanSPI_saveplan + 保存的预备语句。 + + + + + 参数 + + + + SPIPlanPtr plan + + + 要释放的语句的指针 + + + + + + + + 返回值 + + + 成功返回 0;如果planNULL + 或者无效则返回SPI_ERROR_ARGUMENT + + + + + + + + 数据更改的可见性 + + + 下列规则决定了使用 SPI 的函数(以及其他任何 C 函数)中数据更改的可见 + 性: + + + + + 在某条 SQL 命令执行期间,该命令所做的任何数据更改对该命令本身都是不 + 可见的。例如,在 + +INSERT INTO a SELECT * FROM a; + + 中,被插入的行对SELECT部分不可见。 + + + + + + 某条命令 C 所做的更改,对所有在 C 之后启动的命令都可见,无论这些命令 + 是在 C 内部(即 C 执行期间)启动,还是在 C 完成之后启动。 + + + + + + 在由 SQL 命令调用的函数中(无论是普通函数还是触发器)通过 SPI 执行 + 的命令,究竟遵循上述哪条规则,取决于传给 SPI 的读写标志。只读模式下 + 执行的命令遵循第一条规则:它们看不到调用它们的命令所做的更改。读写模 + 式下执行的命令遵循第二条规则:它们能够看到截至当前为止的所有更改。 + + + + + + 所有标准过程语言都会根据函数的易变性属性设置 SPI 的读写模式。 + STABLEIMMUTABLE 函数中的命 + 令会以只读模式执行,而 VOLATILE 函数中的命令则以 + 读写模式执行。虽然 C 函数作者能够违背这一约定,但通常并不是好主意。 + + + + + + + 下一节给出了一个展示这些规则如何应用的示例。 + + + + + 示例 + + + 本节给出一个非常简单的 SPI 用法示例。过程 + execq 以一条 SQL 命令作为第一个参数、以行数计数作 + 为第二个参数,使用 SPI_exec 执行该命令,并返回该 + 命令实际处理的行数。更复杂的 SPI 示例可以在源码树的 + src/test/regress/regress.c 以及 + 模块中找到。 + + + +#include "postgres.h" + +#include "executor/spi.h" +#include "utils/builtins.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +int64 execq(text *sql, int cnt); + +int64 +execq(text *sql, int cnt) +{ + char *command; + int ret; + uint64 proc; + + /* Convert given text object to a C string */ + command = text_to_cstring(sql); + + SPI_connect(); + + ret = SPI_exec(command, cnt); + + proc = SPI_processed; + /* + * If some rows were fetched, print them via elog(INFO). + */ + if (ret > 0 && SPI_tuptable != NULL) + { + TupleDesc tupdesc = SPI_tuptable->tupdesc; + SPITupleTable *tuptable = SPI_tuptable; + char buf[8192]; + uint64 j; + + for (j = 0; j < proc; j++) + { + HeapTuple tuple = tuptable->vals[j]; + int i; + + for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++) + snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s", + SPI_getvalue(tuple, tupdesc, i), + (i == tupdesc->natts) ? " " : " |"); + elog(INFO, "EXECQ: %s", buf); + } + } + + SPI_finish(); + pfree(command); + + return (proc); +} + + + + (此函数使用版本 0 调用约定,以使示例更易于理解。在实际应用中,你应该使用新的版本 1 接口。) + + + + 将该函数编译成共享库之后(详见 ),可以这样声 + 明它: + + +CREATE FUNCTION execq(text, integer) RETURNS int8 + AS 'filename' + LANGUAGE C STRICT; + + + + + 以下是一个示例会话: + +=> SELECT execq('CREATE TABLE a (x integer)', 0); + execq +------- + 0 +(1 row) + +=> INSERT INTO a VALUES (execq('INSERT INTO a VALUES (0)', 0)); +INSERT 0 1 +=> SELECT execq('SELECT * FROM a', 0); +INFO: EXECQ: 0 -- 由 execq 插入 +INFO: EXECQ: 1 -- 由 execq 返回并被外层 INSERT 插入 + + execq +------- + 2 +(1 row) + +=> SELECT execq('INSERT INTO a SELECT x + 2 FROM a', 1); + execq +------- + 1 +(1 row) + +=> SELECT execq('SELECT * FROM a', 10); +INFO: EXECQ: 0 +INFO: EXECQ: 1 +INFO: EXECQ: 2 -- 0 + 2,按指定只插入一行 + + execq +------- + 3 -- 10 只是最大值,3 才是真正的行数 +(1 row) + +=> DELETE FROM a; +DELETE 3 +=> INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1); +INSERT 0 1 +=> SELECT * FROM a; + x +--- + 1 -- a 中没有行(0)+ 1 +(1 row) + +=> INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1); +INFO: EXECQ: 1 +INSERT 0 1 +=> SELECT * FROM a; + x +--- + 1 + 2 -- a 中原有一行 + 1 +(2 rows) + +-- 下面演示数据更改可见性规则: + +=> INSERT INTO a SELECT execq('SELECT * FROM a', 0) * x FROM a; +INFO: EXECQ: 1 +INFO: EXECQ: 2 +INFO: EXECQ: 1 +INFO: EXECQ: 2 +INFO: EXECQ: 2 +INSERT 0 2 +=> SELECT * FROM a; + x +--- + 1 + 2 + 2 -- 2 行 * 1(第一行中的 x) + 6 -- 3 行(前面的 2 行加上刚插入的 1 行)* 2(第二行中的 x) +(4 rows) ^^^^^^ + 不同调用中 execq() 可见的行 + + + + diff --git a/zh/9.6/sql.sgml b/zh/9.6/sql.sgml new file mode 100644 index 00000000..0384166e --- /dev/null +++ b/zh/9.6/sql.sgml @@ -0,0 +1,1883 @@ + + + + SQL + + + + 本章介绍关系数据库背后的数学概念。这不是必读内容,所以如果你觉得 + 陷入困境,或者想直接看一些简单的例子,尽管跳到下一章,等有了更多 + 时间和耐心时再回来。这些内容本来就应该是有趣的! + + + + 这些材料最初是 Stefan Simkovics 的硕士论文 + ()的一部分。 + + + + + SQL 已成为最流行的关系查询语言。 + SQL这个名字是 + 结构化查询语言(Structured Query Language)的缩写。 + 1974 年,Donald Chamberlin 等人在 IBM 研究部定义了语言 SEQUEL + (结构化英语查询语言,Structured English Query Language)。 + 这种语言于 1974-75 年在一个名为 SEQUEL-XRM 的 IBM 原型中首次实现。 + 1976-77 年,SEQUEL 的一个修订版 SEQUEL/2 被定义,随后名称改为 + SQL。 + + + + 1977 年,IBM 开发了一个名为 System R 的新原型。System R 实现了 + SEQUEL/2(即现在的 SQL)的一个很大的子集,并且在 + 项目期间对 SQL 做了大量修改。System R 被安装在许多 + 用户站点,既有 IBM 内部站点,也有一些选定的客户站点。得益于 System R + 在这些用户站点的成功和认可,IBM 开始基于 System R 技术开发实现 + SQL 语言的商业产品。 + + + + 在随后的几年里,IBM 以及其他许多厂商都发布了 SQL + 产品,例如 SQL/DS(IBM)、 + DB2(IBM)、ORACLE + (Oracle Corp.)、DG/SQL(Data General Corp.) + 和 SYBASE(Sybase Inc.)。 + + + + 如今 SQL 也是一个官方标准。1982 年,美国国家标准协会 + (ANSI)委托其数据库委员会 X3H2 制定关系语言标准的提案。 + 该提案于 1986 年获得批准,本质上采用 IBM 的 SQL 方言。 + 1987 年,这个 ANSI 标准又被国际标准化组织 + (ISO)接受为国际标准。SQL 的这个 + 最初标准版本常被非正式地称为SQL/86。 + 1989 年,原始标准得到扩展,这个新标准同样常被非正式地称为 + SQL/89。同样在 1989 年,还制定了一个相关标准, + 称为数据库语言嵌入式 SQL + (ESQL)。 + + + + 多年来,ISOANSI 委员会一直在 + 制定原始标准的一个大幅扩展版本,它被非正式地称为 + SQL2 或 + SQL/92。这个版本于 1992 年底成为 + 批准的标准——国际标准 ISO/IEC 9075:1992,数据库语言 + SQL。当人们提到SQL + 标准时,通常指的就是 SQL/92。 + SQL/92 的详细描述见 + 。在撰写本文档时,一个被非正式地 + 称为 SQL3 的新标准正在制定中。 + 其计划是使 SQL 成为图灵完备的语言,也就是说,所有 + 可计算的查询(例如递归查询)都将成为可能。这一目标现在已经以 SQL:2003 + 完成。 + + + + 关系数据模型 + + + 如前所述,SQL 是一种关系语言。也就是说,它基于 + E.F. Codd 于 1970 年首次发表的关系数据模型。 + 我们稍后(在中) + 给出关系模型的形式化描述,但首先我们想从更直观的角度看一看它。 + + + + 关系数据库是这样一种数据库:在它的用户看来,它是 + 一个表的集合(而且除了表之外没有别的东西)。 + 表由行和列组成,每一行代表一条记录,每一列代表表中记录的一个属性。 + 展示了一个由三张表 + 组成的数据库示例: + + + + + SUPPLIER 是一张存储供应商编号(SNO)、名称(SNAME)和所在城市 + (CITY)的表。 + + + + + + PART 是一张存储零件编号(PNO)、名称(PNAME)和价格(PRICE)的表。 + + + + + + SELLS 存储哪个供应商(SNO)销售哪个零件(PNO)的信息。 + 在某种意义上,它起着把另外两张表连接起来的作用。 + + + + + + 供应商与零件数据库 + +SUPPLIER: SELLS: + SNO | SNAME | CITY SNO | PNO +----+---------+-------- -----+----- + 1 | Smith | London 1 | 1 + 2 | Jones | Paris 1 | 2 + 3 | Adams | Vienna 2 | 4 + 4 | Blake | Rome 3 | 1 + 3 | 3 + 4 | 2 +PART: 4 | 3 + PNO | PNAME | PRICE 4 | 4 +----+---------+--------- + 1 | Screw | 10 + 2 | Nut | 8 + 3 | Bolt | 15 + 4 | Cam | 25 + + + + + + 表 PART 和 SUPPLIER 可以看作实体,而 SELLS + 可以看作特定零件与特定供应商之间的联系。 + + + + 正如我们稍后将看到的,SQL 就是在刚才定义的这类表上 + 操作的,但在此之前,我们先学习关系模型的理论。 + + + + + 关系数据模型的形式化定义 + + + 关系模型背后的数学概念是集合论的关系,即一组域的 + 笛卡尔积的子集。正是这个集合论意义上的关系赋予了该模型名字(不要把它与 + 实体-联系模型中的联系相混淆)。形式上,域只是 + 一个值的集合。例如整数集合就是一个域。长度为 20 的字符串集合和实数 + 集合也是域的例子。 + + + + + 域 D1、 + D2、 + ... + Dk 的 + 笛卡尔积记作 + D1 × + D2 × + ... × + Dk + 它是所有 k 元组 + v1、 + v2、 + ... + vk + 的集合,其中 + v1 ∈ + D1, + v2 ∈ + D2, + ... + vk ∈ + Dk. + + + + 例如,当我们有 + + k=2, + D1={0,1} and + D2={a,b,c} then + D1 × + D2 is + {(0,a),(0,b),(0,c),(1,a),(1,b),(1,c)}. + + + + + 关系是一个或多个域的笛卡尔积的任意子集:R ⊆ + D1 × + D2 × + ... × + Dk. + + + + 例如 {(0,a),(0,b),(1,a)} 是一个关系; + 它实际上是上文提到的 + D1 × + D2 + 笛卡尔积的一个子集。 + + + + 关系的成员称为元组。某个笛卡尔积 + D1 × + D2 × + ... × + Dk + 上的每个关系被称为元数为 k,因而它是 + k 元组的一个集合。 + + + + 关系可以被看作一张表(我们前面已经这样做了,回忆一下 + ),其中每个元组由 + 一行表示,而每一列对应元组的一个分量。给列命名(称为属性)就引出了 + 关系模式的定义。 + + + + + 关系模式 R 是属性的一个 + 有限集 + A1, + A2, + ... + Ak. + 对每个属性 + Ai + (1 <= i <= k)都有一个域 + Di, + 属性的值即取自该域。我们常把关系模式写成 + R(A1, + A2, + ... + Ak). + + + + 关系模式只是一种模板,而关系 + 是关系模式的一个实例。关系由元组组成(因此可以 + 看作一张表);关系模式则不然。 + + + + + + 域与数据类型 + + + 上一节我们经常谈到。回想一下,形式上域只是 + 一个值的集合(例如整数集或实数集)。在数据库系统的语境中,我们常常 + 谈论数据类型而不是域。定义一张表时,我们必须 + 决定要包含哪些属性。此外还必须决定属性值将要存储哪种数据。例如, + 表 SUPPLIERSNAME 的 + 值将是字符串,而 SNO 将存储整数。我们通过为 + 每个属性指定一个数据类型来定义这一点。SNAME 的 + 类型将是 VARCHAR(20)(这是长度 <= 20 的字符串的 + SQL 类型),SNO 的类型将是 + INTEGER。指定数据类型的同时,我们也就为属性选定了一个域。 + SNAME 的域是所有长度 <= 20 的字符串的集合, + SNO 的域是所有整数的集合。 + + + + + + 关系数据模型中的操作 + + + 在上一节()中, + 我们定义了关系模型的数学概念。现在我们知道如何用关系数据模型存储数据了, + 但还不知道对这些表做些什么才能从数据库中检索内容。例如有人可能询问销售 + 零件'Screw'的所有供应商的名称。为此,人们定义了两种相当不同的用于表达 + 关系上操作的记法: + + + + + 关系代数,一种代数记法,查询通过将专门的 + 操作符应用于关系来表达。 + + + + + + 关系演算,一种逻辑记法,查询通过表述答案中的 + 元组必须满足的某些逻辑限制来表达。 + + + + + + + 关系代数 + + + 关系代数由 E. F. Codd 于 1972 年提出。它由一组 + 关系上的操作组成: + + + + + SELECT(σ):从关系中提取满足给定限制的元组。 + 设 R 是一张包含属性 A + 的表。 +σA=a(R) = {t ∈ R ∣ t(A) = a} + 其中 t 表示 R 的一个元组, + 而 t(A) 表示元组 t 的属性 + A 的值。 + + + + + + PROJECT(π):从关系中提取指定的属性(列)。 + 设 R 是一个包含属性 X + 的关系。 + πX(R) = {t(X) ∣ t ∈ R}, + 其中 t(X) 表示元组 + t 的属性 X 的值。 + + + + + + PRODUCT(×):构建两个关系的笛卡尔积。设 + R 是元数为 k1 + 的表,S 是元数为 + k2 的表。 + R × S + 是所有这样的 + k1 + + k2 元组的集合:其前 + k1 个分量构成 + R 中的一个元组,后 + k2 个分量构成 + S 中的一个元组。 + + + + + + UNION(∪):构建两个表的集合论并集。给定表 + RS(两者的元数必须 + 相同),并集 RS + 是属于 RS 或两者 + 的元组的集合。 + + + + + + INTERSECT(∩):构建两个表的集合论交集。给定表 + RS, + RS 是既属于 + R 又属于 S 的元组的 + 集合。我们同样要求 R 和 + S 元数相同。 + + + + + + DIFFERENCE(− 或 ∖):构建两个表的集合差。设 + RS 仍是两张元数 + 相同的表。R - S 是 + 属于 R 但不属于 S 的 + 元组的集合。 + + + + + + JOIN(∏):通过公共属性连接两张表。设 + R 是具有属性 A、 + BC 的表, + S 是具有属性 C、 + DE 的表。两个关系 + 有一个公共属性,即属性 C。 + + R ∏ S = πR.A,R.B,R.C,S.D,S.ER.C=S.C(R × S)). + 我们在这里做了什么?首先计算笛卡尔积 + R × S。 + 然后选出公共属性 C 的值相等的那些元组 + (σR.C = S.C)。 + 现在我们得到一张包含两次属性 C 的表, + 再通过投影去掉重复的列来纠正这一点。 + + + + 一个内连接 + + + 让我们看一看执行连接所需的各个步骤所产生的表。给定下面两张表: + + +R: S: + A | B | C C | D | E +---+---+--- ---+---+--- + 1 | 2 | 3 3 | a | b + 4 | 5 | 6 6 | c | d + 7 | 8 | 9 + + + + + + 首先计算笛卡尔积 R × S, + 得到: + + +R x S: + A | B | R.C | S.C | D | E +---+---+-----+-----+---+--- + 1 | 2 | 3 | 3 | a | b + 1 | 2 | 3 | 6 | c | d + 4 | 5 | 6 | 3 | a | b + 4 | 5 | 6 | 6 | c | d + 7 | 8 | 9 | 3 | a | b + 7 | 8 | 9 | 6 | c | d + + + + + 经过选择 σR.C=S.C(R × S) + 后,得到: + + + A | B | R.C | S.C | D | E +---+---+-----+-----+---+--- + 1 | 2 | 3 | 3 | a | b + 4 | 5 | 6 | 6 | c | d + + + + + 为去掉重复的列 S.C, + 我们用下面的操作把它投影出去: + πR.A,R.B,R.C,S.D,S.ER.C=S.C(R × S)) + 并得到: + + + A | B | C | D | E +---+---+---+---+--- + 1 | 2 | 3 | a | b + 4 | 5 | 6 | c | d + + + + + + + DIVIDE(÷):设 R 是具有属性 A、B、C、 + D 的表,S 是具有属性 C 和 D 的表。除法定义为: + + +R ÷ S = {t ∣ ∀ ts ∈ S ∃ tr ∈ R + + + 使得 +tr(A,B)=t∧tr(C,D)=ts} + 其中 tr(x,y) 表示表 R + 中仅由分量 xy 组成的一个 + 元组。注意元组 t 只由关系 R + 的分量 AB 组成。 + + + + 给定下面的表 + + +R: S: + A | B | C | D C | D +---+---+---+--- ---+--- + a | b | c | d c | d + a | b | e | f e | f + b | c | e | f + e | d | c | d + e | d | e | f + a | b | d | e + + + R ÷ S 的结果推导为 + + + A | B +---+--- + a | b + e | d + + + + + + + + 关于关系代数更详细的描述和定义,参见 [] + 或 []。 + + + + 一个使用关系代数的查询 + + 回想一下,我们阐述所有这些关系操作符,是为了能够从数据库中检索数据。 + 让我们回到上一节()中的 + 例子:有人想知道销售零件 Screw 的所有供应商的名称。 + 使用关系代数,这个问题可以用下面的操作来回答: + +SUPPLIER.SNAMEPART.PNAME='Screw'(SUPPLIER ∏ SELLS ∏ PART)) + + + + + 我们把这样的操作称为查询。如果对示例表 + ()求值上述查询, + 将得到以下结果: + + + SNAME +------- + Smith + Adams + + + + + + + 关系演算 + + + 关系演算基于一阶逻辑。关系演算有两种变体: + + + + + 域关系演算DRC),其中 + 变量代表元组的分量(属性)。 + + + + + + 元组关系演算TRC),其中 + 变量代表元组。 + + + + + + + 我们只想讨论元组关系演算,因为它是大多数关系语言的基石。关于 + DRC(以及 TRC)的详细讨论,参见 + 或 + 。 + + + + + 元组关系演算 + + + TRC 中使用的查询具有如下形式: + + +x(A) ∣ F(x) + + + 其中 x 是元组变量,A 是一个 + 属性集合,F 是一个公式。结果关系由满足 + F(t) 的所有元组 t(A) 组成。 + + + + 如果我们想用 TRC 回答例子 + 中的问题, + 可以表述如下查询: + + +{x(SNAME) ∣ x ∈ SUPPLIER ∧ + ∃ y ∈ SELLS ∃ z ∈ PART (y(SNO)=x(SNO) ∧ + z(PNO)=y(PNO) ∧ + z(PNAME)='Screw')} + + + + + 对 中的表求值该查询, + 得到的结果与 + 中的相同。 + + + + + 关系代数与关系演算 + + + 关系代数和关系演算具有相同的表达能力;也就是说, + 所有能用关系代数表述的查询也都能用关系演算来表述,反之亦然。这一点最早 + 由 E. F. Codd 于 1972 年证明。该证明基于一个算法(Codd 归约 + 算法),通过它可以把关系演算的任意表达式归约为语义等价的 + 关系代数表达式。更详细的讨论参见 + 和 + 。 + + + + 有时人们说,基于关系演算的语言比基于关系代数的语言层次更高 + 或更具声明性,因为代数(部分地)规定了操作的顺序, + 而演算把确定最有效求值顺序的工作留给了编译器或解释器。 + + + + + + <acronym>SQL</acronym> 语言 + + + 与大多数现代关系语言一样,SQL 基于元组关系演算。 + 因此,每个能用元组关系演算(或者等价地,关系代数)表述的查询也都能用 + SQL 表述。不过,SQL 也有一些超出 + 关系代数或演算范围的能力。下面列出 SQL 提供的一些 + 不属于关系代数或演算的附加特性: + + + + + 用于插入、删除或修改数据的命令。 + + + + + + 算术能力:在 SQL 中可以使用算术运算和比较,例如: + + +A < B + 3. + + + 注意 + 或其他算术操作符既不出现在关系代数中,也不出现在关系演算中。 + + + + + + 赋值和打印命令:可以打印由查询构造出的关系,也可以把计算得到的关系 + 赋给一个关系名。 + + + + + + 聚合函数:可以关系的列应用诸如平均值、 + 求和最大值等操作, + 以获得单一的量。 + + + + + + + Select + + + SQL 中最常用的命令是用于检索数据的 + SELECT 语句。其语法为: + + +SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] + * | expression [ [ AS ] output_name ] [, ...] + [ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ] + [ FROM from_item [, ...] ] + [ WHERE condition ] + [ GROUP BY expression [, ...] ] + [ HAVING condition [, ...] ] + [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ] + [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] + [ LIMIT { count | ALL } ] + [ OFFSET start ] + [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] + + + + + 下面我们用各种例子来说明 SELECT 语句的复杂语法。 + 示例所用的表定义于。 + + + + 简单 Select + + + 下面是一些使用 SELECT 语句的简单例子: + + + 带限定条件的简单查询 + + 要检索表 PART 中属性 PRICE 大于 10 的所有元组,我们表述如下查询: + + +SELECT * FROM PART + WHERE PRICE > 10; + + + 并得到表: + + + PNO | PNAME | PRICE +-----+---------+-------- + 3 | Bolt | 15 + 4 | Cam | 25 + + + + + 在 SELECT 语句中使用*会给出表中 + 的所有属性。如果只想检索表 PART 的属性 PNAME 和 PRICE,我们使用语句: + + +SELECT PNAME, PRICE + FROM PART + WHERE PRICE > 10; + + + 这种情况下结果是: + + + PNAME | PRICE + --------+-------- + Bolt | 15 + Cam | 25 + + + 注意 SQLSELECT 对应的是 + 关系代数中的投影而不是选择 + (更多细节见)。 + + + + WHERE 子句中的限定条件也可以用关键字 OR、AND 和 NOT 进行逻辑连接: + + +SELECT PNAME, PRICE + FROM PART + WHERE PNAME = 'Bolt' AND + (PRICE = 0 OR PRICE <= 15); + + + 将得到结果: + + + PNAME | PRICE +--------+-------- + Bolt | 15 + + + + + 目标列表和 WHERE 子句中都可以使用算术运算。例如,如果我们想知道 + 买两个某种零件要花多少钱,可以使用以下查询: + + +SELECT PNAME, PRICE * 2 AS DOUBLE + FROM PART + WHERE PRICE * 2 < 50; + + + 并得到: + + + PNAME | DOUBLE +--------+--------- + Screw | 20 + Nut | 16 + Bolt | 30 + + + 注意关键字 AS 后面的 DOUBLE 是第二列的新标题。对目标列表中的每个 + 元素都可以使用这种技术为结果列指定新标题。这个新标题常被称为别名。 + 别名不能在查询的其余部分中使用。 + + + + + + + 连接 + + + 下面的例子展示连接SQL + 中是如何实现的。 + + + + 要通过公共属性连接 SUPPLIER、PART 和 SELLS 三张表,我们表述如下语句: + + +SELECT S.SNAME, P.PNAME + FROM SUPPLIER S, PART P, SELLS SE + WHERE S.SNO = SE.SNO AND + P.PNO = SE.PNO; + + + 并得到以下表作为结果: + + + SNAME | PNAME +-------+------- + Smith | Screw + Smith | Nut + Jones | Cam + Adams | Screw + Adams | Bolt + Blake | Nut + Blake | Bolt + Blake | Cam + + + + + 在 FROM 子句中,我们为每个关系引入了一个别名,因为这些关系之间存在 + 同名属性(SNO 和 PNO)。现在只需在属性名前加上别名和一个点号,就可以 + 区分这些同名属性。连接的计算方式与 + 中所示的相同。 + 首先导出笛卡尔积 + + SUPPLIER × PART × SELLS + + 然后只选出满足 WHERE 子句所给条件的元组(即同名属性必须相等)。 + 最后把除 S.SNAME 和 P.PNAME 之外的所有列都投影出去。 + + + + 执行连接的另一种方式是使用 SQL 的 JOIN 语法,如下所示: + +SELECT sname, pname from supplier + JOIN sells USING (sno) + JOIN part USING (pno); + + 同样得到: + + sname | pname +-------+------- + Smith | Screw + Adams | Screw + Smith | Nut + Blake | Nut + Adams | Bolt + Blake | Bolt + Jones | Cam + Blake | Cam +(8 rows) + + + + + 使用 JOIN 语法创建的连接表是出现在 FROM 子句中、并且位于任何 WHERE、 + GROUP BY 或 HAVING 子句之前的表引用列表项。其他表引用(包括表名或 + 其他 JOIN 子句)只要用逗号分隔,也可以包含在 FROM 子句中。连接表在 + 逻辑上与 FROM 子句中列出的其他任何表一样。 + + + + SQL 的 JOIN 分为两大类型:CROSS JOIN(非限定连接)和 + 限定 JOIN。限定连接还可以进一步按 + 连接条件的指定方式(ON、USING 或 NATURAL) + 以及应用方式(INNER 或 OUTER 连接)细分。 + + + + 连接类型 + + CROSS JOIN + + + T1 + CROSS JOIN + T2 + + + + 交叉连接取分别有 N 行和 M 行的两张表 T1 和 T2,返回包含所有 + N*M 种可能连接行的连接表。对 T1 的每一行 R1,T2 的每一行 R2 + 都与 R1 连接,产生由 R1 和 R2 的所有字段组成的连接表行 JR。 + CROSS JOIN 等价于 INNER JOIN ON TRUE。 + + + + + + Qualified JOINs + + + + T1 + NATURAL + + INNER + + + LEFT + RIGHT + FULL + + OUTER + + + JOIN + T2 + + ON search condition + USING ( join column list ) + + + + + 限定 JOIN 必须通过给出 NATURAL、ON 或 USING 三者之一(且仅一者) + 来指定其连接条件。ON 子句接受一个 + 搜索条件,它与 WHERE 子句中的相同。 + USING 子句接受一个逗号分隔的列名列表,这些列是两张被连接表所 + 共有的,连接在这些列相等的基础上进行。NATURAL 是 USING 子句的 + 简写形式,它列出两张表所有公共列名。USING 和 NATURAL 都有一个 + 副作用:每个被连接列只有一份副本会输出到结果表中(对比前面给出 + 的 JOIN 的关系代数定义)。 + + + + + + + + INNER + JOIN + + + + + 对 T1 的每一行 R1,连接表都为 T2 中满足与 R1 的连接条件的 + 每一行各包含一行。 + + + + 对所有 JOIN 而言,单词 INNER 和 OUTER 都是可选的。 + INNER 是默认值。LEFT、RIGHT 和 FULL 意味着 + OUTER JOIN。 + + + + + + + + LEFT + OUTER + JOIN + + + + + 首先执行 INNER JOIN。然后,对 T1 中不满足与 T2 任何行的 + 连接条件的每一行,额外返回一个连接行,其来自 T2 的列为 + 空值。 + + + + 连接表无条件地为 T1 的每一行各包含一行。 + + + + + + + + RIGHT + OUTER + JOIN + + + + + 首先执行 INNER JOIN。然后,对 T2 中不满足与 T1 任何行的 + 连接条件的每一行,额外返回一个连接行,其来自 T1 的列为 + 空值。 + + + + 连接表无条件地为 T2 的每一行各包含一行。 + + + + + + + + FULL + OUTER + JOIN + + + + + 首先执行 INNER JOIN。然后,对 T1 中不满足与 T2 任何行的 + 连接条件的每一行,额外返回一个连接行,其来自 T2 的列为 + 空值。 + 同样,对 T2 中不满足与 T1 任何行的连接条件的每一行,额外返回 + 一个连接行,其来自 T1 的列为空值。 + + + + 连接表无条件地为 T1 的每一行和 T2 的每一行各包含一行。 + + + + + + + + + + + + + 所有类型的 JOIN 都可以串联或嵌套在一起, + T1 和 + T2 之一或两者都可以是 + 连接表。可以在 JOIN 子句周围使用圆括号来控制 JOIN 的顺序, + 否则 JOIN 按从左到右的顺序处理。 + + + + + + 聚合函数 + + + SQL 提供聚合函数,例如 AVG、COUNT、SUM、MIN 和 + MAX。聚合函数的参数在满足 WHERE 子句的每一行上求值,聚合函数就在这组 + 输入值上进行计算。通常,聚合函数为整个 SELECT 语句 + 给出单个结果。但如果查询中指定了分组,则会对每个组的行分别进行计算, + 并为每个组给出一个聚合结果(见下一节)。 + + + 聚合 + + + 如果我们想知道表 PART 中所有零件的平均价格,使用以下查询: + + +SELECT AVG(PRICE) AS AVG_PRICE + FROM PART; + + + + + 结果是: + + + AVG_PRICE +----------- + 14.5 + + + + + 如果我们想知道表 PART 中定义了多少个零件,使用语句: + + +SELECT COUNT(PNO) + FROM PART; + + + and get: + + + COUNT +------- + 4 + + + + + + + + + 按组聚合 + + + SQL 允许把一张表的元组划分成组。然后就可以对组应用 + 上面描述的聚合函数——也就是说,聚合函数的值不再是对指定列 + 的所有值计算,而是对一个组的所有值计算。这样,聚合函数就为每个组分别 + 求值。 + + + + 把元组划分成组的工作通过关键字 GROUP BY 后跟定义 + 组的属性列表来完成。如果有 + GROUP BY A1, ⃛, Ak, + 我们就把关系划分为若干组,使得两个元组处于同一组当且仅当它们在所有 + 属性 A1, ⃛, Ak 上 + 都一致。 + + + 聚合 + + 如果我们想知道每个供应商销售多少个零件,可以表述如下查询: + + +SELECT S.SNO, S.SNAME, COUNT(SE.PNO) + FROM SUPPLIER S, SELLS SE + WHERE S.SNO = SE.SNO + GROUP BY S.SNO, S.SNAME; + + + and get: + + + SNO | SNAME | COUNT +-----+-------+------- + 1 | Smith | 2 + 2 | Jones | 1 + 3 | Adams | 2 + 4 | Blake | 3 + + + + + 现在来看看这里发生了什么。首先导出表 SUPPLIER 和 SELLS 的连接: + + + S.SNO | S.SNAME | SE.PNO +-------+---------+-------- + 1 | Smith | 1 + 1 | Smith | 2 + 2 | Jones | 4 + 3 | Adams | 1 + 3 | Adams | 3 + 4 | Blake | 2 + 4 | Blake | 3 + 4 | Blake | 4 + + + + + 接下来,把在 S.SNO 和 S.SNAME 两个属性上都一致的元组放在一起, + 将元组划分为组: + + + S.SNO | S.SNAME | SE.PNO +-------+---------+-------- + 1 | Smith | 1 + | 2 +-------------------------- + 2 | Jones | 4 +-------------------------- + 3 | Adams | 1 + | 3 +-------------------------- + 4 | Blake | 2 + | 3 + | 4 + + + + + 在我们的例子中得到了四个组,现在可以对每个组应用聚合函数 COUNT, + 从而得到上面给出的查询最终结果。 + + + + + + 注意,要让一个使用 GROUP BY 和聚合函数的查询有意义,目标列表只能直接 + 引用被分组的那些属性。其他属性只能用在聚合函数的参数内部。否则, + 其他属性就无法关联到一个唯一的值。 + + + + 还要注意,请求聚合的聚合(例如 AVG(MAX(sno)))是没有意义的,因为 + SELECT 只做一轮分组和聚合。你可以通过临时表或 + FROM 子句中的子 SELECT 来完成第一级聚合,从而得到这类结果。 + + + + + Having + + + HAVING 子句的工作方式与 WHERE 子句非常相似,用于只考虑满足 HAVING + 子句中给出的限定条件的那些组。本质上,WHERE 在分组和聚合之前过滤掉 + 不需要的输入行,而 HAVING 在分组之后过滤掉不需要的组行。因此, + WHERE 不能引用聚合函数的结果。另一方面,写一个不涉及聚合函数的 + HAVING 条件毫无意义!如果你的条件不涉及聚合,不如直接写到 WHERE + 中,这样就不必为那些反正要丢弃的组计算聚合了。 + + + Having + + + 如果我们只要销售多于一个零件的供应商,使用查询: + + +SELECT S.SNO, S.SNAME, COUNT(SE.PNO) + FROM SUPPLIER S, SELLS SE + WHERE S.SNO = SE.SNO + GROUP BY S.SNO, S.SNAME + HAVING COUNT(SE.PNO) > 1; + + + and get: + + + SNO | SNAME | COUNT +-----+-------+------- + 1 | Smith | 2 + 3 | Adams | 2 + 4 | Blake | 3 + + + + + + + + 子查询 + + + 在 WHERE 和 HAVING 子句中,凡期望一个值的地方都允许使用子查询 + (子选择)。这种情况下,必须先对子查询求值来得到该值。子查询的使用 + 扩展了 SQL 的表达能力。 + + + 子选择 + + + 如果我们想知道所有比名为'Screw'的零件价格更高的零件,使用查询: + + +SELECT * + FROM PART + WHERE PRICE > (SELECT PRICE FROM PART + WHERE PNAME='Screw'); + + + + + 结果是: + + + PNO | PNAME | PRICE +-----+---------+-------- + 3 | Bolt | 15 + 4 | Cam | 25 + + + + + 观察上面的查询,我们可以看到 SELECT 关键字出现了 + 两次。第一次在查询的开头——我们称之为外层 + SELECT——另一次在 WHERE 子句中,它开始 + 一个嵌套查询——我们称之为内层 SELECT。 + 对外层 SELECT 的每个元组,内层 + SELECT 都必须求值。每次求值之后,我们就知道了 + 名为'Screw'的元组的价格,可以检查当前元组的价格是否更大。 + (实际上,在这个例子中内层查询只需求值一次,因为它不依赖于外层 + 查询的状态。) + + + + 如果我们想知道没有销售任何零件的供应商(例如,以便能从数据库中删除 + 这些供应商),使用: + + +SELECT * + FROM SUPPLIER S + WHERE NOT EXISTS + (SELECT * FROM SELLS SE + WHERE SE.SNO = S.SNO); + + + + + 在我们的例子中,结果将为空,因为每个供应商至少销售一个零件。注意, + 我们在内层 SELECT 的 WHERE 子句中使用了外层 + SELECT 的 S.SNO。这里子查询必须对外层查询的 + 每个元组重新求值,也就是说,S.SNO 的值总是取自外层 + SELECT 的当前元组。 + + + + + + + FROM 中的子查询 + + + 使用子查询的一种略有不同的方式是把它们放到 FROM 子句中。这是一个 + 有用的特性,因为这种子查询可以输出多列多行,而用在表达式中的子查询 + 必须只给出单个结果。它还让我们无需借助临时表就能进行不止一轮的 + 分组/聚合。 + + + FROM 中的子选择 + + + 如果我们想知道所有供应商中最高的平均零件价格,不能写成 + MAX(AVG(PRICE)),但可以写成: + + +SELECT MAX(subtable.avgprice) + FROM (SELECT AVG(P.PRICE) AS avgprice + FROM SUPPLIER S, PART P, SELLS SE + WHERE S.SNO = SE.SNO AND + P.PNO = SE.PNO + GROUP BY S.SNO) subtable; + + + 子查询为每个供应商返回一行(因为它有 GROUP BY),然后我们在外层 + 查询中对这些行进行聚合。 + + + + + + + Union、Intersect、Except + + + 这些操作计算两个子查询所得元组的并集、交集和集合论差。 + + + Union、Intersect、Except + + + 下面的查询是 UNION 的例子: + + +SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNAME = 'Jones' +UNION + SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNAME = 'Adams'; + + +给出结果: + + + SNO | SNAME | CITY +-----+-------+-------- + 2 | Jones | Paris + 3 | Adams | Vienna + + + + + 下面是 INTERSECT 的例子: + + +SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNO > 1 +INTERSECT + SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNO < 3; + + + 给出结果: + + + SNO | SNAME | CITY +-----+-------+-------- + 2 | Jones | Paris + + + 查询两部分都返回的唯一元组是 SNO=2 的那个。 + + + + 最后是 EXCEPT 的例子: + + +SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNO > 1 +EXCEPT + SELECT S.SNO, S.SNAME, S.CITY + FROM SUPPLIER S + WHERE S.SNO > 3; + + + 给出结果: + + + SNO | SNAME | CITY +-----+-------+-------- + 2 | Jones | Paris + 3 | Adams | Vienna + + + + + + + + + 数据定义 + + + SQL 语言中包含一组用于数据定义的命令。 + + + + 创建表 + + + 数据定义中最基本的命令就是创建新关系(新表)的命令。 + CREATE TABLE 命令的语法为: + + +CREATE TABLE table_name + (name_of_attr_1 type_of_attr_1 + [, name_of_attr_2 type_of_attr_2 + [, ...]]); + + + + 创建表 + + + 要创建中定义的表, + 使用以下 SQL 语句: + + +CREATE TABLE SUPPLIER + (SNO INTEGER, + SNAME VARCHAR(20), + CITY VARCHAR(20)); + + + +CREATE TABLE PART + (PNO INTEGER, + PNAME VARCHAR(20), + PRICE DECIMAL(4 , 2)); + + + +CREATE TABLE SELLS + (SNO INTEGER, + PNO INTEGER); + + + + + + + + <acronym>SQL</acronym> 中的数据类型 + + + 下面是 SQL 支持的一些数据类型的列表: + + + + + INTEGER:有符号全字二进制整数(31 位精度)。 + + + + + + SMALLINT:有符号半字二进制整数(15 位精度)。 + + + + + + DECIMAL(p[,q]): + 有符号压缩十进制数,最多 p + 位数字,其中小数点右边有 + q 位数字。如果省略 + q,则假定为 0。 + + + + + + FLOAT:有符号双字浮点数。 + + + + + + VARCHAR(n): + 最大长度为 n 的 + 变长字符串。 + + + + + + CHAR(n): + 长度为 n 的定长 + 字符串。 + + + + + + + + + 创建索引 + + + 索引用来加快对关系的访问。如果关系 R 在属性 + A 上有一个索引,那么检索所有满足 + t(A) = a + 的元组 t 时,所需时间大致与这类元组 + t 的数量成正比,而不再与 + R 的大小成正比。 + + + + 在 SQL 中,使用 CREATE INDEX + 命令创建索引。语法为: + + +CREATE INDEX index_name + ON table_name ( name_of_attribute ); + + + + + + 创建索引 + + + 要在关系 SUPPLIER 的属性 SNAME 上创建名为 I 的索引,使用以下语句: + + +CREATE INDEX I ON SUPPLIER (SNAME); + + + + + 创建的索引会被自动维护,也就是说,每当有新元组插入关系 SUPPLIER 时, + 索引 I 都会随之调整。注意,存在索引时用户能察觉到的唯一变化是 + SELECT 速度的提升和更新速度的下降。 + + + + + + + 创建视图 + + + 视图可以被看作一张虚拟表,即一张在数据库中 + 并不物理存在、但在用户看来好像存在的表。相比之下, + 当我们谈论基表时,表的每一行在物理存储中的 + 某处确实有一个物理存储的对应物。 + + + + 视图没有自己的、物理上独立、可区分的存储数据。系统只是把视图的定义 + (即如何访问物理存储的基表以物化该视图的规则)存储在系统目录的某个 + 地方(见 + )。 + 关于实现视图的不同技术的讨论,参见 + + SIM98. + + + + 在 SQL 中使用 CREATE VIEW + 命令定义视图。语法为: + + +CREATE VIEW view_name + AS select_stmt + + + 其中 select_stmt 是一个 + 如中所定义的有效的 + select 语句。注意,创建视图时并不执行 + select_stmt,它只是被存储在 + 系统目录中,每当对视图进行查询时才被执行。 + + + + 给定下面的视图定义(我们再次使用 + 中的表): + + +CREATE VIEW London_Suppliers + AS SELECT S.SNAME, P.PNAME + FROM SUPPLIER S, PART P, SELLS SE + WHERE S.SNO = SE.SNO AND + P.PNO = SE.PNO AND + S.CITY = 'London'; + + + + + 现在我们就可以像使用另一张基表一样使用这个 + 虚拟关系 London_Suppliers: + + +SELECT * FROM London_Suppliers + WHERE PNAME = 'Screw'; + + + 它将返回以下表: + + + SNAME | PNAME +-------+------- + Smith | Screw + + + + + 为了计算这个结果,数据库系统必须先隐蔽地访问 + 基表 SUPPLIER、SELLS 和 PART。它通过对这些基表执行视图定义中给出的 + 查询来完成这一步。之后,再应用(针对视图的查询中给出的)附加限定条件, + 即可得到结果表。 + + + + + Drop Table、Drop Index、Drop View + + + 要销毁一张表(包括该表中存储的所有元组),使用 + DROP TABLE 命令: + + +DROP TABLE table_name; + + + + + 要销毁 SUPPLIER 表,使用以下语句: + + +DROP TABLE SUPPLIER; + + + + + DROP INDEX 命令用于销毁索引: + + +DROP INDEX index_name; + + + + + 最后,要销毁一个给定的视图,使用 DROP VIEW 命令: + + +DROP VIEW view_name; + + + + + + + 数据操纵 + + + Insert Into + + + 一旦创建了表(见 + ),就可以使用 + INSERT INTO 命令向其中填入元组。语法为: + + +INSERT INTO table_name (name_of_attr_1 + [, name_of_attr_2 [, ...]]) + VALUES (val_attr_1 [, val_attr_2 [, ...]]); + + + + + 要向关系 SUPPLIER(来自 + )插入第一个元组, + 使用以下语句: + + +INSERT INTO SUPPLIER (SNO, SNAME, CITY) + VALUES (1, 'Smith', 'London'); + + + + + 要向关系 SELLS 插入第一个元组,使用: + + +INSERT INTO SELLS (SNO, PNO) + VALUES (1, 1); + + + + + + Update + + + 要修改关系中元组的一个或多个属性值,使用 UPDATE + 命令。语法为: + + +UPDATE table_name + SET name_of_attr_1 = value_1 + [, ... [, name_of_attr_k = value_k]] + WHERE condition; + + + + + 要修改关系 PART 中零件'Screw'的属性 PRICE 的值,使用: + + +UPDATE PART + SET PRICE = 15 + WHERE PNAME = 'Screw'; + + + + + 名为'Screw'的元组的属性 PRICE 的新值现在是 15。 + + + + + Delete + + + 要从特定的表中删除元组,使用 DELETE FROM 命令。语法为: + + +DELETE FROM table_name + WHERE condition; + + + + + 要删除表 SUPPLIER 中名为'Smith'的供应商,使用以下语句: + + +DELETE FROM SUPPLIER + WHERE SNAME = 'Smith'; + + + + + + + 系统目录 + + + 在每个 SQL 数据库系统中,都使用 + 系统目录来记录数据库中定义了哪些表、视图、 + 索引等。这些系统目录可以像普通关系一样被查询。例如,有一个用于视图 + 定义的目录,该目录存储视图定义中的查询。每当对视图进行查询时,系统 + 首先从目录中取出视图定义查询并物化该视图, + 然后再继续处理用户的查询(更详细的描述见 + + + )。关于系统目录的更多信息,参见 + 。 + + + + + 嵌入式 <acronym>SQL</acronym> + + + 本节将概述如何把 SQL 嵌入到宿主语言 + (例如 C)中。我们想从宿主语言中使用 + SQL 有两个主要原因: + + + + + 有些查询无法用纯 SQL 表述(即递归查询)。要能够 + 执行这类查询,我们需要一种表达能力比 SQL 更强 + 的宿主语言。 + + + + + + 我们只是想从用宿主语言编写的应用程序访问数据库(例如,一个带图形 + 用户界面的订票系统用 C 编写,而关于还剩哪些票的信息存储在可以用 + 嵌入式 SQL 访问的数据库中)。 + + + + + + + 在宿主语言中使用嵌入式 SQL 的程序由宿主语言的 + 语句和嵌入式 SQL + (ESQL)语句组成。每条 ESQL + 语句都以关键字 EXEC SQL 开头。 + ESQL 语句由预编译器转换成 + 宿主语言的语句(预编译器通常会插入对库例程的调用,由这些例程执行各种 + SQL 命令)。 + + + + 回顾中的各个例子, + 我们会发现查询的结果经常是一个元组集合。大多数宿主语言并非为操作集合 + 而设计,因此我们需要一种机制来访问 SELECT 语句返回的元组集合中的每一个 + 单独的元组。这种机制可以通过声明一个游标来提供。 + 之后我们可以使用 FETCH 命令检索一个元组,并把游标 + 移到下一个元组。 + + + + 关于嵌入式 SQL 的详细讨论,参见 + 、 + 或 + 。 + + + + diff --git a/zh/9.6/sslinfo.sgml b/zh/9.6/sslinfo.sgml new file mode 100644 index 00000000..b075fa1b --- /dev/null +++ b/zh/9.6/sslinfo.sgml @@ -0,0 +1,227 @@ + + + + sslinfo + + + sslinfo + + + + sslinfo 模块提供关于当前客户端在连接到 + PostgreSQL 时所提供 SSL 证书的信息。如果当前连接 + 没有使用 SSL,则该模块没有用处(大多数函数将返回 NULL)。 + + + 除非安装时使用--with-openssl配置,否则此扩展根本无法构建。 + + + 提供的函数 + + + + + ssl_is_used() returns boolean + + ssl_is_used + + + + 如果当前到服务器的连接使用 SSL,则返回 true,否则返回 false。 + + + + + + ssl_version() returns text + + ssl_version + + + + 返回 SSL 连接使用的协议名称(例如 SSLv2、SSLv3 或 TLSv1)。 + + + + + + ssl_cipher() returns text + + ssl_cipher + + + + + 返回 SSL 连接所用密码的名称(例如 DHE-RSA-AES256-SHA)。 + + + + + + + ssl_client_cert_present() returns boolean + + ssl_client_cert_present + + + + 如果当前客户端已向服务器提供了有效的 SSL 客户端证书,则返回 true,否则返回 false。(服务器可能配置为要求客户端证书,也可能没有这样配置。) + + + + + + ssl_client_serial() returns numeric + + ssl_client_serial + + + + + 返回当前客户端证书的序列号。证书序列号与证书颁发者的组合可保证唯一 + 标识一个证书(但不能保证唯一标识其所有者 — 所有者应当定期更换其 + 密钥,并从颁发者处获取新证书)。 + + + + 因此,如果你运行自己的 CA,并且只允许服务器接受由该 CA 签发的证书, + 那么序列号就是识别用户的最可靠手段(尽管并不便于记忆)。 + + + + + + + ssl_client_dn() returns text + + ssl_client_dn + + + + + 返回当前客户端证书的完整主题,并将字符数据转换为当前数据库编码。 + 假定如果你在证书名称中使用了非 ASCII 字符,你的数据库也能表示这些 + 字符。如果你的数据库使用 SQL_ASCII 编码,名称中的非 ASCII 字符将表示为 + UTF-8 序列。 + + + + 结果类似于 + /CN=Somebody /C=Some country/O=Some organization。 + + + + + + + ssl_issuer_dn() returns text + + ssl_issuer_dn + + + + + 返回当前客户端证书的完整颁发者名称,并将字符数据转换为当前数据库 + 编码。编码转换的处理方式与 ssl_client_dn 相同。 + + + 该函数的返回值与证书序列号的组合可唯一标识该证书。 + + 只有在服务器的root.crt文件中包含多个受信任的 CA 证书,或者此 CA 颁发了一些中间证书颁发机构证书时,此函数才真正有用。 + + + + + + ssl_client_dn_field(fieldname text) returns text + + ssl_client_dn_field + + + + + 该函数返回证书主题中指定字段的值;如果该字段不存在,则返回 NULL。 + 字段名是字符串常量,它们使用 OpenSSL 对象 + 数据库转换为 ASN1 对象标识符。可接受的值如下: + + +commonName (alias CN) +surname (alias SN) +name +givenName (alias GN) +countryName (alias C) +localityName (alias L) +stateOrProvinceName (alias ST) +organizationName (alias O) +organizationalUnitName (alias OU) +title +description +initials +postalCode +streetAddress +generationQualifier +description +dnQualifier +x500UniqueIdentifier +pseudonym +role +emailAddress + + + 除 commonName 外,所有这些字段都是可选的。 + 究竟包含哪些字段、哪些字段不包含,完全取决于你的 CA 策略。不过,这些 + 字段的含义由 X.500 和 X.509 标准严格定义,因此你不能随意赋予它们任意 + 含义。 + + + + + + + ssl_issuer_field(fieldname text) returns text + + ssl_issuer_field + + + + + 与 ssl_client_dn_field 相同,只是针对证书颁发者 + 而不是证书主题。 + + + + + + + ssl_extension_info() returns setof record + + ssl_extension_info + + + + + 提供客户端证书扩展的信息:扩展名、扩展值,以及该扩展是否为关键扩展。 + + + + + + + + 作者 + + + Victor Wagner vitus@cryptocom.ru, Cryptocom LTD + + + + Dmitry Voronin carriingfate92@yandex.ru + + + + Cryptocom OpenSSL 开发组的电子邮件: + openssl@cryptocom.ru + + + + diff --git a/zh/9.6/standalone-install.sgml b/zh/9.6/standalone-install.sgml new file mode 100644 index 00000000..1942f9dc --- /dev/null +++ b/zh/9.6/standalone-install.sgml @@ -0,0 +1,28 @@ + + + + + +%version; + + + + + + + +]> diff --git a/zh/9.6/start.sgml b/zh/9.6/start.sgml new file mode 100644 index 00000000..8363f1a8 --- /dev/null +++ b/zh/9.6/start.sgml @@ -0,0 +1,307 @@ + + + + 从头开始 + + + 安装 + + + 当然,在你开始使用PostgreSQL之前, + 必须先安装它。PostgreSQL很可能已经安装在你的站点上, + 可能是因为它随操作系统发行版一起提供, + 也可能是因为系统管理员已经安装了它。如果是这样, + 你应该从操作系统文档或系统管理员那里了解如何访问PostgreSQL。 + + + + 如果你不确定PostgreSQL是否已经可用, + 或者是否可以用它来做实验,那么你可以自行安装。 + 这并不难,而且也是一个不错的练习。PostgreSQL可由任何非特权用户安装, + 不需要超级用户(root)权限。 + + + + 如果你要自己安装PostgreSQL, + 请参阅中的安装说明, + 待安装完成后再回到本指南。务必仔细遵循关于设置适当环境变量的小节。 + + + + 如果你的站点管理员没有按默认方式完成设置, + 你可能还需要做一些额外工作。例如,如果数据库服务器所在的机器是远程机器, + 你就需要把PGHOST环境变量设置为数据库服务器机器的名称。 + 环境变量PGPORT也可能需要设置。归根结底就是: + 如果你试着启动某个应用程序,而它抱怨无法连接到数据库, + 那么你应该去咨询站点管理员;如果你自己就是管理员, + 那就查阅文档以确保你的环境已经正确设置。 + 如果你没有理解上一段的意思,请阅读下一节。 + + + + + + 架构基础 + + + 在继续之前,你应该先了解PostgreSQL的基本系统架构。 + 了解PostgreSQL各部分如何交互,会让本章更容易理解。 + + + + 用数据库术语来说,PostgreSQL采用客户端/服务器模型。一次PostgreSQL会话由以下协同工作的进程(程序)组成: + + + + + 一个服务器进程,负责管理数据库文件、接受客户端应用到数据库的连接, + 并代表客户端执行数据库操作。 + 数据库服务器程序叫做postgres。 + postgres + + + + + + 用户用来执行数据库操作的客户端(前端)应用。 + 客户端应用的形式可以非常多样:可以是一个文本工具, + 也可以是一个图形应用程序,或者是一个通过访问数据库来显示网页的 Web 服务器, + 还可以是专门的数据库维护工具。 + 有些客户端应用随PostgreSQL发行版一起提供;大多数则由用户开发。 + + + + + + + + 和典型的客户端/服务器应用一样,客户端和服务器可以位于不同的主机上。 + 在这种情况下,它们通过 TCP/IP 网络连接通信。 + 你应该记住这一点,因为在客户端机器上可访问的文件,在数据库服务器机器上可能不可访问 + (或者只能使用不同的文件名访问)。 + + + + PostgreSQL服务器可以处理来自客户端的多个并发连接。 + 为此,它会为每个连接启动(forks)一个新进程。 + 从那时起,客户端和新的服务器进程就无需原始 + postgres进程介入而直接通信。 + 因此,这个主服务器进程始终在运行,等待客户端连接, + 而客户端及其关联的服务器进程则不断创建和退出。 + (当然,这一切对用户都是不可见的。这里只是为求完整而提及。) + + + + + + 创建一个数据库 + + + database + creating + + + + createdb + + + + 检验你能否访问数据库服务器的第一步,就是试着创建一个数据库。 + 一个正在运行的PostgreSQL服务器可以管理许多数据库。 + 通常会为每个项目或每个用户使用一个独立的数据库。 + + + + 你的站点管理员可能已经为你创建了一个可用的数据库。 + 如果是这样,你就可以省略这一步,直接跳到下一节。 + + + + 要创建一个新数据库(本例中命名为 + mydb),请使用以下命令: + +$ createdb mydb + + 如果这条命令没有任何输出,就说明这一步成功了,你可以跳过本节余下的内容。 + + + + 如果你看到类似下面这样的信息: + +createdb: command not found + + 那么说明PostgreSQL没有正确安装。要么根本没有安装, + 要么就是你的 shell 搜索路径没有把它包含进去。试着改用绝对路径调用该命令: + +$ /usr/local/pgsql/bin/createdb mydb + + 你所在站点上的路径可能不同。请联系站点管理员,或者查看安装说明来修正这个问题。 + + + 另一种响应可能是这样: +createdb: could not connect to database postgres: could not connect to server: No such file or directory + Is the server running locally and accepting + connections on Unix domain socket "/tmp/.s.PGSQL.5432"? +这意味着服务器尚未启动,或者它没有在 createdb 期望的位置启动。同样,请查看安装说明或咨询管理员。 + + 另一种响应可能是这样: +createdb: could not connect to database postgres: FATAL: role "joe" does not exist +其中提到了你自己的登录名。如果管理员没有为你创建 PostgreSQL 用户帐号,就会发生这种情况。(PostgreSQL用户帐号与操作系统用户帐号是分开的。)如果你是管理员,请参阅 了解如何创建帐号。你需要切换成安装 PostgreSQL 时所用的操作系统用户(通常是 postgres),才能创建第一个用户帐号。也可能分配给你的 PostgreSQL 用户名与操作系统用户名不同;在这种情况下,你需要使用 选项,或者设置 PGUSER 环境变量,来指定你的 PostgreSQL 用户名。 + + 如果你有用户帐号,但它没有创建数据库所需的权限,那么你会看到下面的信息: +createdb: database creation failed: ERROR: permission denied to create database +并非每个用户都被授权创建新数据库。如果 PostgreSQL 拒绝为你创建数据库,那么站点管理员就需要授予你创建数据库的权限。遇到这种情况时,请咨询站点管理员。如果 PostgreSQL 是你自己安装的,那么在本教程中,你应当以启动服务器时所用的用户帐号登录。 + + 这之所以可行,是因为PostgreSQL用户名与操作系统用户帐号是分开的。 + 当你连接到数据库时,可以选择以哪个PostgreSQL用户名连接; + 如果不指定,默认就会使用你当前操作系统帐号的名称。 + 恰好总会有一个PostgreSQL用户帐号, + 其名称与启动服务器的操作系统用户相同,而该用户也总是有权创建数据库。 + 除了直接以该用户身份登录之外, + 你还可以在各处使用选项来选择连接时使用的PostgreSQL用户名。 + + + + + + 你也可以创建其他名称的数据库。PostgreSQL允许你在一个给定站点上创建任意数量的数据库。 + 数据库名的首字符必须是字母,并且长度限制为 63 字节。 + 一种方便的做法是创建一个与当前用户名同名的数据库。 + 许多工具都将该数据库名视为默认值,因此这样可以少打一些字。 + 要创建这个数据库,只需输入: + +$ createdb + + + + + 如果你不再想使用某个数据库,可以删除它。 + 例如,如果你是数据库mydb的所有者(创建者), + 就可以使用下面的命令销毁它: + +$ dropdb mydb + + (对于这条命令,数据库名不会默认成用户帐号名,你始终需要显式指定它。) + 这个操作会从物理上删除与该数据库关联的所有文件,而且无法撤销, + 因此务必三思而后行。 + + + + 关于createdbdropdb的更多信息, + 可分别参见。 + + + + + + 访问数据库 + + + psql + + + + 一旦你创建了数据库,你就可以通过以下方式访问它: + + + + + 运行PostgreSQL的交互式终端程序 + psql, + 它允许你以交互方式输入、编辑并执行SQL命令。 + + + + + + 使用现有的图形前端工具,例如pgAdmin, + 或者使用具备ODBCJDBC支持的办公套件来创建和操作数据库。 + 这些方式不在本教程讨论范围内。 + + + + + + 使用若干可用语言绑定之一编写自定义应用程序。 + 关于这些方式的进一步讨论,请见。 + + + + + 你可能需要启动psql来试验本教程中的示例。 + 可以通过输入下面的命令连接到mydb数据库: + +$ psql mydb + + 如果你不提供数据库名,那么它默认就是你的用户帐号名。 + 在上一节使用createdb时,你已经见识过这种机制了。 + + + + 在psql中,你将看到下面的欢迎信息: + +psql (&version;) +Type "help" for help. + +mydb=> + + superuser + 最后一行也可能是: + +mydb=# + + 这表示你是数据库超级用户;如果这个 + PostgreSQL实例是你自己安装的,那很可能就是这种情况。 + 超级用户意味着你不受访问控制限制。对本教程而言,这一点并不重要。 + + + + 如果你在启动psql时遇到问题,请回到前一节。 + createdbpsql的诊断信息很相似, + 如果前者能工作,后者通常也应该能工作。 + + + psql 打印出的最后一行就是提示符,它表示 psql正在等待你的输入,你可以将 SQL 查询输入到由 psql 维护的工作区中。试试这些命令:version + +mydb=> SELECT version(); + version +------------------------------------------------------------------------------------------ + PostgreSQL &version; on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit +(1 row) + +mydb=> SELECT current_date; + date +------------ + 2016-01-07 +(1 row) + +mydb=> SELECT 2 + 2; + ?column? +---------- + 4 +(1 row) + + + + + psql程序有一些不属于 SQL 命令的内部命令。 + 它们以反斜线 \ 开头。 + 例如,你可以输入下面的命令来查看各种PostgreSQL SQL命令的语法帮助: + +mydb=> \h + + + + + 要退出psql,输入: + +mydb=> \q + + psql将退出并返回到命令 shell。 + (要了解更多内部命令,可输入\?,在psql提示符下执行。) + psql的完整功能见。在本教程中,我们不会明确使用这些功能, + 但在有帮助时你可以自行使用它们。 + + + + diff --git a/zh/9.6/storage.sgml b/zh/9.6/storage.sgml new file mode 100644 index 00000000..7dc6f8cb --- /dev/null +++ b/zh/9.6/storage.sgml @@ -0,0 +1,884 @@ + + + + +数据库物理存储 + + +本章概述 PostgreSQL 数据库所使用的物理存储格式。 + + + + +数据库文件布局 + + +本节从文件和目录的层次描述存储格式。 + + + +传统上,数据库集簇所使用的配置文件和数据文件都存放在集簇的数据目录中, +这个目录通常称为 PGDATA(名字来自可用于定义它的环境变量)。 +PGDATA 的一个常见位置是 +/var/lib/pgsql/data。同一台机器上可以存在多个由不同服务器实例 +管理的集簇。 + + + +PGDATA 目录包含若干子目录和控制文件,如 + 所示。除这些必需项之外,集簇配置文件 +postgresql.confpg_hba.conf 和 +pg_ident.conf 传统上也存放在 PGDATA +中,不过也可以把它们放在其他地方。 + + + +<varname>PGDATA</varname> 的内容 + + + + +项 + +描述 + + + + + + + PG_VERSION + 包含 PostgreSQL 主版本号的文件 + + + + base + 包含各数据库子目录的子目录 + + + + global + 包含集簇范围内表的子目录,例如 + pg_database + + + + pg_commit_ts + 包含事务提交时间戳数据的子目录 + + + + pg_clog + 包含事务提交状态数据的子目录 + + + + pg_dynshmem + 包含动态共享内存子系统所用文件的子目录 + + + + pg_logical + 包含逻辑解码状态数据的子目录 + + + + pg_multixact + 包含多事务状态数据的子目录 + (用于共享行锁) + + + + pg_notify + 包含 LISTEN/NOTIFY 状态数据的子目录 + + + + pg_replslot + 包含复制槽数据的子目录 + + + + pg_serial + 包含已提交可串行化事务信息的子目录 + + + + pg_snapshots + 包含导出快照的子目录 + + + + pg_stat + 包含统计子系统永久文件的子目录 + + + + pg_stat_tmp + 包含统计子系统临时文件的子目录 + + + + pg_subtrans + 包含子事务状态数据的子目录 + + + + pg_tblspc + 包含指向表空间的符号链接的子目录 + + + + pg_twophase + 包含预备事务状态文件的子目录 + + + + pg_xlog + 包含 WAL(预写式日志)文件的子目录 + + + + postgresql.auto.conf + 用于存储通过 +ALTER SYSTEM 设置的配置参数的文件 + + + + postmaster.opts + 记录服务器上次启动时所用命令行选项的文件 + + + + postmaster.pid + 记录当前 postmaster 进程 ID(PID)、 + 集簇数据目录路径、 + postmaster 启动时间戳、 + 端口号、 + Unix 域套接字目录路径(在 Windows 上为空)、 + 第一个有效的 listen_address(IP 地址或 *,若未监听 TCP 则为空), + 以及共享内存段 ID 的锁文件 + (服务器关闭后该文件不存在) + + + + +
+ + +对于集簇中的每个数据库,PGDATA/base +中都有一个子目录,其名称是该数据库在 pg_database +中的 OID。这个子目录是该数据库文件的默认位置;特别是,它的系统目录就存放在这里。 + + + +每个表和索引都存储在单独的文件中。对于普通关系,这些文件以表或索引的 +filenode 编号命名,该编号可在 +pg_class.relfilenode +中找到。但对于临时关系,文件名的形式为 +tBBB_FFF, +其中 BBB 是创建该文件的后端进程号, +FFF 是 filenode 编号。无论哪种情况,除了主文件 +(也称主分支)之外,每个表和索引还有一个 +空闲空间映射(见 ), +用于存储该关系可用空闲空间的信息。空闲空间映射存放在以 filenode 编号加上 +_fsm 后缀命名的文件中。表还拥有 +可见性映射,存放在带有 _vm +后缀的分支中,用于跟踪哪些页面已知不含死元组。可见性映射在 + 中有进一步说明。不记录 WAL 的表和索引还有第三个分支, +称为初始化分支,存放在带有 _init 后缀的分支中 +(见 )。 + + + + +请注意,虽然表的 filenode 往往与其 OID 相同,但这并非 +必然如此;有些操作,例如 TRUNCATE、 +REINDEXCLUSTER 以及某些形式的 +ALTER TABLE,会在保留 OID 的同时改变 filenode。 +不要假定 filenode 和表 OID 一定相同。此外,对于某些系统目录(包括 +pg_class 本身), +pg_class.relfilenode +中存放的是零。这些目录的实际 filenode 编号保存在更底层的数据结构中, +可以使用 pg_relation_filenode() 函数取得。 + + + + +当表或索引超过 1 GB 时,会被划分成若干个 1 GB 大小的 +。第一段的文件名与 filenode 相同,后续各段则命名为 +filenode.1、filenode.2,依此类推。这样的安排避免了在文件大小受限的平台上出现问题。 +(实际上,1 GB 只是默认段大小。段大小可以通过配置选项 + 在构建 PostgreSQL +时调整。) +原则上,空闲空间映射和可见性映射分支也可能需要多个段,不过在实践中这不太可能发生。 + + + +如果一个表包含可能具有很大条目的列,它就会有关联的 +TOAST 表,用于对那些大到无法直接保存在表行中的字段值 +进行线外存储。pg_class.reltoastrelid +会把该表关联到其 TOAST 表(如果存在的话)。更多信息见 +。 + + + +表和索引的内容将在 中进一步讨论。 + + + +表空间使情况变得更复杂。每个用户定义的表空间都在 +PGDATA/pg_tblspc 目录中有一个符号链接, +指向其物理表空间目录(即该表空间的 CREATE TABLESPACE +命令中指定的位置)。该符号链接以表空间的 OID 命名。在物理表空间目录中, +有一个名称依赖于 PostgreSQL 服务器版本的子目录, +例如 PG_9.0_201008051。(使用这个子目录的原因是,为了让数据库的 +后续版本可以在不发生冲突的情况下复用同一个 +CREATE TABLESPACE 位置值。)在这个版本相关的子目录中, +对于每个在该表空间中拥有对象的数据库,都有一个以该数据库 OID 命名的子目录。 +表和索引就使用 filenode 命名方案存储在该目录中。 +pg_default 表空间不通过 pg_tblspc +访问,而是对应于 PGDATA/base。 +同样,pg_global 表空间也不通过 +pg_tblspc 访问,而是对应于 +PGDATA/global。 + + + +pg_relation_filepath() 函数会显示任意关系的完整路径 +(相对于 PGDATA)。它常可用作记住上述众多规则的替代方法。 +但请记住,该函数只给出该关系主分支第一段的名字,若要找出与该关系关联的所有文件, +你可能还需要追加段号以及/或者 _fsm_vm +或 _init。 + + + +临时文件(用于诸如排序的数据多于内存可容纳量之类的操作)会创建在 +PGDATA/base/pgsql_tmp 中;或者创建在表空间目录下的 +pgsql_tmp 子目录中,如果为其指定的是不同于 +pg_default 的表空间。临时文件的名称形式为 +pgsql_tmpPPP.NNN, +其中 PPP 是所属后端的 PID, +NNN 用来区分该后端的不同临时文件。 + + +
+ + + +TOAST + + + TOAST + + 切片面包TOAST + + +本节概述TOAST(超长属性存储技术)。 + + + +PostgreSQL 使用固定页面大小(通常为 8 kB),并且不允许 +元组跨越多个页面。因此,无法直接存储非常大的字段值。为克服这一限制,大字段值会被 +压缩并且/或者拆分成多个物理行。这个过程对用户是透明的,而且对大部分后端代码的影响 +很小。这项技术被亲切地称为 TOAST(或者 +自切片面包问世以来最棒的东西)。TOAST +基础设施也被用于改进内存中大型数据值的处理。 + + + +只有某些数据类型支持 TOAST,因为没有必要让那些不可能产生大字段值的 +数据类型承担这部分额外开销。要支持 TOAST,数据类型必须具有变长 +(varlena)表示形式,在通常情况下,任何已存储值的第一个四字节字 +都包含该值以字节计的总长度(包括其自身)。TOAST 对该数据类型表示形式的 +其余部分没有约束。统称为 TOAST 化值 的特殊表示形式, +是通过修改或重新解释这个初始长度字来工作的。因此,支持可 TOAST 数据类型的 +C 级函数,必须小心处理可能已经 TOAST 化的输入值:在被 +去 TOAST 化之前,一个输入值未必真正由四字节长度字和内容组成。 +(通常是在对输入值做任何处理之前调用 PG_DETOAST_DATUM, +但在某些情况下也可以采用更高效的方法。详见 。) + + + +TOAST 会占用 varlena 长度字中的两个位 +(在大端机器上是最高位,在小端机器上是最低位),从而把任何可 +TOAST 数据类型值的逻辑大小限制为 1 GB +(230 - 1 字节)。当这两个位都为零时, +该值就是这种数据类型的普通、未经 TOAST 处理的值,长度字的其余位 +给出该 datum 的总大小(包括长度字),以字节计。当最高位或最低位被置位时, +该值使用的不是通常的四字节头部,而是单字节头部;该字节的其余位给出 datum +的总大小(包括长度字节),以字节计。这样既能高效存储短于 127 字节的值, +又仍然允许数据类型在需要时增长到 1 GB。带单字节头部的值不按任何特定边界对齐, +而带四字节头部的值至少按四字节边界对齐;省去这部分对齐填充后,相对于短值而言, +可以显著节省空间。作为特殊情况,如果单字节头部的其余位全为零 +(对于自包含长度而言这是不可能的),则该值是一个指向线外数据的指针, +它可能有下面将描述的几种形式。这样一种 TOAST 指针 的类型和大小, +由 datum 第二个字节中存储的编码决定。最后,当最高位或最低位为零、但相邻的一位被置位时, +该 datum 的内容已被压缩,使用前必须先解压。在这种情况下,四字节长度字的其余位给出的 +是压缩后 datum 的总大小,而不是原始数据的大小。请注意,线外数据也可能被压缩, +但 varlena 头部不会告诉你是否发生了压缩,这一点要由 TOAST +指针的内容来说明。 + + + +如前所述,TOAST 指针 datum 有多种类型。最早、也最常见的一种, +是指向存储在 TOAST中的线外数据的指针, +该表与包含该 TOAST 指针 datum 本身的表相关联,但与之分离存储。 +当一个要存储到磁盘上的元组过大而无法原样存储时, +这些磁盘上的指针 datum 会由 +TOAST 管理代码(位于 +access/heap/tuptoaster.c)创建。更多细节见 +。另一种情况是, +TOAST 指针 datum 可以包含指向内存中其他位置的线外数据的指针。 +这类 datum 必然是短命的,永远不会出现在磁盘上,但它们对于避免大型数据值的复制和 +冗余处理非常有用。更多细节见 。 + + + +无论是线内压缩数据还是线外压缩数据,其所用的压缩技术都是 LZ 压缩技术族中一种相当简单且非常快速的方法。 +详情见 src/common/pg_lzcompress.c。 + + + + 线外、磁盘上的 TOAST 存储 + + +如果一个表的任意列是可 TOAST 的,该表就会有关联的 +TOAST 表,其 OID 存放在表的 +pg_class.reltoastrelid +项中。磁盘上的 TOAST 化值保存在该 +TOAST 表中,下文会更详细地描述。 + + + +线外值会被划分成最多 TOAST_MAX_CHUNK_SIZE 字节的块 +(如果启用了压缩,则在压缩之后再分块;默认情况下该值的选取方式是让四个块行 +可以装入一页,因此大约是 2000 字节)。每个块都作为独立的一行,存储在所属表的 +TOAST 表中。每个 TOAST 表都有 +chunk_id 列(标识某个特定 +TOAST 化值的 OID)、chunk_seq 列 +(该块在其值内的序号)以及 chunk_data 列 +(该块的实际数据)。在 chunk_id 和 +chunk_seq 上建立的唯一索引可提供快速检索。因此,一个表示 +线外、磁盘上的 TOAST 化值的指针 datum,需要存储要查找的 +TOAST 表的 OID,以及该特定值的 OID(即其 +chunk_id)。为方便起见,指针 datum 还会存储逻辑 datum 大小 +(原始未压缩数据长度)和物理存储大小(如果应用了压缩,这两者会不同)。再加上 varlena 头部字节,磁盘上的 +TOAST 指针 datum 的总大小因此恒为 18 字节,与所表示值的实际大小无关。 + + + +TOAST 管理代码只有在要存入表中的行值宽于 +TOAST_TUPLE_THRESHOLD 字节(通常为 2 kB)时才会被触发。 +TOAST 代码会压缩并且/或者把字段值移到线外,直到该行值短于 +TOAST_TUPLE_TARGET 字节(通常也为 2 kB), +或者已经无法再获得更多收益。在 UPDATE 操作中,未更改字段的值通常会原样保留; +因此,如果更新一行时其线外值都没有变化,便不会产生 TOAST 成本。 + + + +该 TOAST 管理代码为在磁盘上存储可 TOAST 的列提供四种不同的策略: + + + + + PLAIN 既不允许压缩,也不允许线外存储;此外,它还会禁止 varlena 类型使用单字节头部。 + 这是不可 TOAST 数据类型列唯一可能的策略。 + + + + + EXTENDED 同时允许压缩和线外存储。 + 这是大多数可 TOAST 数据类型的默认策略。 + 系统会先尝试压缩,如果行仍然过大,再使用线外存储。 + + + + + EXTERNAL 允许线外存储,但不允许压缩。 + 使用 EXTERNAL 会让宽 text 和 + bytea 列上的子串操作更快(代价是占用更多存储空间), + 因为在值未压缩时,这些操作经过优化,只需提取线外值中所需的部分。 + + + + + MAIN 允许压缩,但不允许线外存储。 + (实际上,对于这类列,仍然可能进行线外存储,但只有在别无他法、 + 必须这样做才能让行足够小以放入页面时,才会把它作为最后手段。) + + + + +每种可 TOAST 的数据类型都会为该数据类型的列指定默认策略,但可以通过以下命令更改某个表列的策略: +ALTER TABLE SET STORAGE。 + + + +与允许行值跨页之类更直接的方法相比,这种方案有不少优点。假定查询通常是通过与 +相对较小的键值比较来限定的,执行器的大部分工作都会只使用主行项完成。 +TOAST 化属性的大值只有在结果集发送给客户端时才会被取出 +(前提是它们确实被选中了)。因此,主表会小得多,其更多的行能够装入共享缓冲区缓存, +这是没有线外存储时做不到的。排序集也会缩小,因此排序更常能够完全在内存中完成。 +一个小测试表明,一个包含典型 HTML 页面及其 URL 的表,其总存储量(包括 +TOAST 表)大约只有原始数据大小的一半,而主表只包含全部数据的 +大约 10%(URL 以及一些较小的 HTML 页面)。与未进行 +TOAST 处理的对照表相比,运行时没有差异;在那个对照表中, +所有 HTML 页面都被裁剪到 7 kB 以内以便放得下。 + + + + + + 线外、内存中的 TOAST 存储 + + +TOAST 指针可以指向不在磁盘上、而位于当前服务器进程内存中其他位置的数据。 +这类指针显然不可能长期存在,但仍然很有用。目前有两个子情况: +指向间接数据的指针,以及指向展开数据的指针。 + + + +间接 TOAST 指针只是简单地指向某处内存中存放的一个非间接 varlena 值。 +这一情况最初只是作为概念验证而创建,但目前在逻辑解码期间会用到它,以避免可能不得不 +创建超过 1 GB 的物理元组(把所有线外字段值都拉入元组就可能导致这种情况)。这种机制 +的用途有限,因为创建该指针 datum 的一方必须完全负责确保被引用数据在指针可能存在的 +整个期间都保持有效,而且系统并没有任何基础设施来帮助做到这一点。 + + + +展开的 TOAST 指针对那些磁盘表示形式并不特别适合计算用途的复杂数据类型 +很有用。以标准的 PostgreSQL 数组 varlena 表示为例,它包含维度 +信息、一个空值位图(如果存在空元素),然后依次保存所有元素的值。当元素类型本身也是变长 +时,定位第 N 个元素的唯一办法就是扫描它前面的所有元素。 +这种表示形式因其紧凑性而适合磁盘存储,但对于数组计算而言,一种展开或 +解构的表示会更好,因为其中所有元素的起始位置都已识别出来。 +TOAST 指针机制通过允许按引用传递的 Datum 指向标准 varlena 值 +(即磁盘表示)或者指向内存中某处展开表示的 TOAST 指针,来满足这一需要。 +这种展开表示的具体细节由数据类型自行决定,不过它必须具有标准头部,并满足 +src/include/utils/expandeddatum.h 中给出的其他 API 要求。 +处理该数据类型的 C 级函数可以选择支持任一种表示。不知道展开表示、但只是对输入应用 +PG_DETOAST_DATUM 的函数,会自动获得传统的 varlena 表示; +因此,对展开表示的支持可以逐步引入,一次增加一个函数即可。 + + + +指向展开值的 TOAST 指针还可进一步分为 +可读写(read-write)和 +只读(read-only)指针。无论哪种方式,被指向的表示是相同的; +但收到可读写指针的函数可以原地修改所引用的值,而收到只读指针的函数则不可以, +如果它想生成该值的修改版本,必须先创建一个副本。 +这种区分以及一些相关约定,使得在查询执行期间可以避免不必要的展开值复制。 + + + +对于所有类型的内存中 TOAST 指针, +TOAST 管理代码都会确保这类指针 datum 不会意外地被存储到磁盘上。 +内存中的 TOAST 指针在存储前会自动展开为普通的线内 varlena 值, +然后如果承载它的元组否则会过大,可能还会进一步转换为磁盘上的 +TOAST 指针。 + + + + + + + + +空闲空间映射 + + + 空闲空间映射 + +FSM空闲空间映射 + + +每个堆关系和索引关系(哈希索引除外)都有一个空闲空间映射 +(FSM),用于跟踪该关系中的可用空间。 +它与主关系数据并列存放在一个独立的关系分支中,其名称由该关系的 filenode 编号加上 +_fsm 后缀构成。例如,如果某关系的 filenode 是 12345, +那么 FSM 就存储在名为 12345_fsm +的文件中,与主关系文件位于同一目录。 + + + +空闲空间映射被组织成一棵由 FSM 页面构成的树。最底层的 +FSM 页面存储每个堆页面(或索引页面)上的可用空闲空间, +每个这样的页面用一个字节表示。上层页面则聚合来自下层的信息。 + + + +每个 FSM 页面内部都有一棵二叉树,以数组形式存储,每个节点占一个字节。 +每个叶子节点表示一个堆页面或下层 FSM 页面。每个非叶子节点中保存其子节点值 +中较大的那个,因此叶子节点中的最大值会保存在根节点。 + + + +请参见 src/backend/storage/freespace/README, +了解 FSM 的结构以及如何更新和搜索它的更多细节。 + 模块可用于检查空闲空间映射中存储的信息。 + + + + + + +可见性映射 + + + 可见性映射 + +VM可见性映射 + + +每个堆关系都有一个可见性映射(VM),用于跟踪哪些页面只包含已知对所有活动事务都可见的 +元组;它还会跟踪哪些页面只包含已冻结的元组。它与主关系数据并列存放在一个独立的关系分支中, +名称由关系的 filenode 编号加上 _vm 后缀构成。例如,如果某关系的 +filenode 是 12345,那么 VM 会存储在名为 12345_vm 的文件中, +与主关系文件位于同一目录。请注意,索引没有 VM。 + + + +可见性映射为每个堆页面存储两个位。第一位若被置位,表示该页面是全部可见(all-visible), +换句话说,该页面不包含任何需要被清理的元组。这些信息还可以被 +仅索引扫描 +用来只依据索引元组回答查询。第二位若被置位,则表示页面上的所有元组都已经被冻结。 +这意味着,即使是防回卷清理也不必重新访问该页面。 + + + +这个映射是保守的,也就是说,只要某个位被置位,我们就能确定相应条件确实成立; +但如果某个位没有置位,该条件既可能为真,也可能不为真。可见性映射位只会由 +VACUUM 设置,但页面上的任何数据修改操作都会把它们清除。 + + + + 模块可用于检查可见性映射中存储的信息。 + + + + + + +初始化分支 + + + 初始化分支 + + + +每个不记录 WAL 的表以及该表上的每个索引,都有一个初始化分支。初始化分支是一个适当类型的 +空表或空索引。当不记录 WAL 的表因崩溃而必须重置为空时,初始化分支会被复制覆盖到主分支上, +而其他分支都会被擦除(它们会在需要时自动重建)。 + + + + + + +数据库页面布局 + + +本节概述 PostgreSQL 表和索引内部所使用的页面格式。 + + 实际上,索引访问方法不要求必须使用这种页面格式。 + 现有的所有索引方法都使用这种 + 基本格式,但索引元页中保存的数据通常并不遵循项布局规则。 + + +序列和 TOAST 表的格式与普通表相同。 + + + +在下面的说明中,假定一个 字节 包含 8 个位。另外, +术语 指的是存储在页面上的单个数据值。在表中,项是一行; +在索引中,项是一条索引条目。 + + + +每个表和索引都存储为固定大小的页面数组 +(通常为 8 kB,不过在编译服务器时可以选择不同的页面大小)。在表中,所有页面在逻辑上 +都是等价的,因此某个特定项(行)可以存放在任意页面中。在索引中,第一页通常保留为 +元页,用于保存控制信息;并且根据索引访问方法的不同,索引中 +还可能存在不同类型的页面。 + + + + 展示了页面的总体布局。每个页面包含五个部分。 + + + +总体页面布局 +页面布局 + + + + +项 + +描述 + + + + + + + PageHeaderData + 长度为 24 字节。包含页面的一般信息,包括空闲空间指针。 + + + +ItemIdData +指向实际项的项标识符数组。每个条目都是一个(偏移量、长度)对。每项 4 字节。 + + + +空闲空间 +尚未分配的空间。新的项标识符从该区域起始处分配,新的项从末尾处分配。 + + + + +项本身。 + + + +特殊空间 +索引访问方法特有的数据。不同方法存储不同数据。普通表中为空。 + + + + +
+ + + + 每个页面的前 24 个字节由页头(PageHeaderData)组成。 + 它的格式详见 。第一个字段跟踪与该页面有关的最新 + WAL 记录。第二个字段在启用了 时 + 包含页面校验和。接下来是一个 2 字节字段,包含标志位。随后是三个 2 字节整数字段 + (pd_lowerpd_upper + 和 pd_special)。它们分别保存从页面起始位置到 + 未分配空间起始位置、未分配空间结束位置以及特殊空间起始位置的字节偏移量。 + 页头接下来的 2 个字节 pd_pagesize_version + 同时存储页面大小和版本指示器。从 PostgreSQL 8.3 起, + 版本号为 4;PostgreSQL 8.1 和 8.2 使用版本号 3; + PostgreSQL 8.0 使用版本号 2; + PostgreSQL 7.3 和 7.4 使用版本号 1; + 更早版本使用版本号 0。(这些版本中的大多数,其基本页面布局和页头格式并未改变, + 但堆行头部的布局发生过变化。)页面大小字段基本上只是用于交叉检查; + 一个安装中并不支持同时存在多种页面大小。最后一个字段是一个提示,用于显示对页面进行 + 剪枝是否可能有利:它跟踪页面上最老的、尚未剪枝的 XMAX。 + + + + + PageHeaderData 布局 + PageHeaderData 布局 + + + + 字段 + 类型 + 长度 + 描述 + + + + + pd_lsn + PageXLogRecPtr + 8 字节 + LSN:上次修改此页面的 xlog 记录最后一个字节之后的下一个字节 + + + pd_checksum + uint16 + 2 字节 + 页面校验和 + + + pd_flags + uint16 + 2 字节 + 标志位 + + + pd_lower + LocationIndex + 2 字节 + 到空闲空间起始位置的偏移量 + + + pd_upper + LocationIndex + 2 字节 + 到空闲空间结束位置的偏移量 + + + pd_special + LocationIndex + 2 字节 + 到特殊空间起始位置的偏移量 + + + pd_pagesize_version + uint16 + 2 字节 + 页面大小和布局版本号信息 + + + pd_prune_xid + TransactionId + 4 字节 + 页面上最老的未剪枝 XMAX,若无则为零 + + + +
+ + + 所有细节都可以在 + src/include/storage/bufpage.h 中找到。 + + + + 页头之后是项标识符(ItemIdData),每个需要四个字节。 + 一个项标识符包含项起始位置的字节偏移量、其字节长度,以及若干影响解释方式的属性位。 + 新的项标识符会根据需要从未分配空间的起始处分配。当前已有多少个项标识符,可以通过查看 + pd_lower 得知;分配新标识符时它会增加。因为一个项标识符在被释放之前永远不会移动, + 所以即使项本身为了压缩空闲空间而在页面内移动,其索引仍然可以被长期用来引用该项。 + 事实上,每个指向项的指针(ItemPointer,也称 CTID) + 在 PostgreSQL 中都由页号和项标识符索引构成。 + + + + + + 项本身存储在从未分配空间末尾开始、向后分配的区域中。其确切结构取决于表要包含什么内容。 + 表和序列都使用一种名为 HeapTupleHeaderData 的结构体,如下所述。 + + + + + + 最后一部分是特殊部分,其中可以存放访问方法希望保存的任何内容。例如, + B-树索引会在这里保存指向页面左、右兄弟的链接,以及其他一些与索引结构相关的数据。 + 普通表完全不使用特殊部分(通过将 pd_special 设为页面大小来表示)。 + + + + + + 所有表行的结构都相同。它们都有一个固定大小的头部(在大多数机器上占 23 字节), + 后面跟着可选的空值位图、可选的对象 ID 字段以及用户数据。头部的详细格式见 + 。实际用户数据(行的各列)从 + t_hoff 指示的偏移位置开始,它必须始终是该平台 + MAXALIGN 对齐单位的整数倍。只有当 HEAP_HASNULL 位在 + t_infomask 中被置位时,空值位图才存在。若存在,它紧随固定头部之后, + 并占用足够多的字节,以便为每个数据列提供一位 + (也就是说,总共为 t_natts 位)。 + 在这个位图中,1 表示非空,0 表示空值。当位图不存在时,假定所有列都非空。 + 只有当 HEAP_HASOID 位在 + t_infomask 中被置位时,对象 ID 才存在。若存在,它位于 + t_hoff 边界之前。为了让 t_hoff + 成为 MAXALIGN 的整数倍所需的任何填充,都会出现在空值位图与对象 ID 之间。 + (这又反过来保证了对象 ID 的对齐是合适的。) + + + + + HeapTupleHeaderData 布局 + HeapTupleHeaderData 布局 + + + + 字段 + 类型 + 长度 + 描述 + + + + + t_xmin + TransactionId + 4 字节 + 插入 XID 标记 + + + t_xmax + TransactionId + 4 字节 + 删除 XID 标记 + + + t_cid + CommandId + 4 字节 + 插入和/或删除 CID 标记(与 t_xvac 重叠) + + + t_xvac + TransactionId + 4 字节 + VACUUM 操作移动某个行版本时的 XID + + + t_ctid + ItemPointerData + 6 字节 + 本行版本或更新行版本的当前 TID + + + t_infomask2 + uint16 + 2 字节 + 属性个数以及各种标志位 + + + t_infomask + uint16 + 2 字节 + 各种标志位 + + + t_hoff + uint8 + 1 字节 + 到用户数据的偏移量 + + + +
+ + + 所有细节都可以在 + src/include/access/htup_details.h 中找到。 + + + + + 要解释实际数据,必须借助从其他表中获得的信息,其中大部分来自 + pg_attribute。识别字段位置所需的关键值是 + attlenattalign。 + 除非所有字段都是定宽且没有空值,否则没有办法直接取得某个特定属性。 + 所有这些技巧都封装在 heap_getattr、 + fastgetattrheap_getsysattr + 这些函数中。 + + + + + 读取数据时,需要依次检查每个属性。首先根据空值位图判断该字段是否为 NULL。 + 如果是,就继续下一个。然后确认对齐是否正确。如果字段是定宽字段,那么它的所有字节 + 都是直接摆放的;如果它是变长字段(attlen = -1),情况就会稍复杂一些。所有变长 + 数据类型都共享一个通用头部结构体 struct varlena,其中包含已存储值的 + 总长度以及一些标志位。根据这些标志,数据可能是线内存储的,也可能位于 + TOAST 表中;它也可能是经过压缩的(见 + )。 + + +
+ +
diff --git a/zh/9.6/stylesheet-common.xsl b/zh/9.6/stylesheet-common.xsl new file mode 100644 index 00000000..a2e3db67 --- /dev/null +++ b/zh/9.6/stylesheet-common.xsl @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + 1 + 0 + + + + +yes +2 + + + + + + + + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ? + + ? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/stylesheet-fo.xsl b/zh/9.6/stylesheet-fo.xsl new file mode 100644 index 00000000..41a436f5 --- /dev/null +++ b/zh/9.6/stylesheet-fo.xsl @@ -0,0 +1,539 @@ + + +%common.entities; +]> + + + + + + + +3 + + + + + + + + + + + + + + +2em + + + wrap + + + + solid + 1pt + black + 12pt + 12pt + 6pt + 6pt + + + + center + + + + + left + + + + + 1em + 0.8em + 1.2em + + + + + + + + + + + + , + + + + + + + + ISBN + + + + + + + + + + + + + + + + -3.5em + + + + + + + + + + -3.5em + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ientry- + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ientry- + + + + + + + + + ( + + + + + + + + + + + + + + + + + + + + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ientry- + + + + + + + + + ( + + + + + + + + + + + + + + + + + + + + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed + 100% + + + + + + + + + always + + + + + + + + + + + + + + + + + + + + + + + + 9pt + + + + + wrap + + + + + + + + diff --git a/zh/9.6/stylesheet-hh.xsl b/zh/9.6/stylesheet-hh.xsl new file mode 100644 index 00000000..ae9c0c47 --- /dev/null +++ b/zh/9.6/stylesheet-hh.xsl @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + +pgsql-docs@lists.postgresql.org + + + + + + + + + + + + + + + + +
+
+ +
diff --git a/zh/9.6/stylesheet-html-common.xsl b/zh/9.6/stylesheet-html-common.xsl new file mode 100644 index 00000000..cd622437 --- /dev/null +++ b/zh/9.6/stylesheet-html-common.xsl @@ -0,0 +1,656 @@ + + +%common.entities; +]> + + + + + + + + +pgsql-docs@lists.postgresql.org +2 + + + stylesheet.css.xml + + + https://www.postgresql.org/media/css/docs-complete.css + + + + + + docContent + container-fluid col-10 + + + + + + + + + + + + + + , + + + + + + + + + + ISBN + + + + + + + + + +appendix toc,title +article/appendix nop +article toc,title +book toc,title +chapter toc,title +part toc,title +preface toc,title +qandadiv toc +qandaset toc +reference toc,title +sect1 toc +sect2 toc +sect3 toc +sect4 toc +sect5 toc +section toc +set toc,title + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

+ + + +

+
+ + + + + + + +
+
+
+ + + + + +
+

+ + + +

+
+ + + + + + + +
+
+
+
+
+ + + + + + + + +
+
+ + + + + + + + + +
+ + + + + + +

+ +

+
+
+ + + + + + + +
+
+
+
+ + + + + + + + + + + + | + + + + + + + + + + + + + + + + + + + + + + + + + + id- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6 + + + + + + + + + + clear: both + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + id_link + + # + + + + + + + + + + + + + + + + + + +
+ + + + + ientry- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + #ientry- + + + + + + + + ( + + + + + + + + + + + + + + + + + + + + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ientry- + + + + + + + +
+ ( + + + + + + + + + + + + + + + + + + + + + + + + + ) +
+ + +
+
+
+
+ +
diff --git a/zh/9.6/stylesheet-html-nochunk.xsl b/zh/9.6/stylesheet-html-nochunk.xsl new file mode 100644 index 00000000..5a0bb4ea --- /dev/null +++ b/zh/9.6/stylesheet-html-nochunk.xsl @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/stylesheet-man.xsl b/zh/9.6/stylesheet-man.xsl new file mode 100644 index 00000000..fcb485c2 --- /dev/null +++ b/zh/9.6/stylesheet-man.xsl @@ -0,0 +1,226 @@ + + + + + + + + + +0 +0 +0 + + + +32 +40 + + + + + + + + + + + + + + < + + > + + + + + + ^ + + + + + + + + + + + + + + + + ( + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + Note: + + + + + + + + + + + + + + + + + + + + + + + + + Note: + (soelim stub) + + + + + + + + + + + + + + + + + + + + + + + + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/stylesheet-speedup-common.xsl b/zh/9.6/stylesheet-speedup-common.xsl new file mode 100644 index 00000000..403f350c --- /dev/null +++ b/zh/9.6/stylesheet-speedup-common.xsl @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +zh_cn + + diff --git a/zh/9.6/stylesheet-speedup-xhtml.xsl b/zh/9.6/stylesheet-speedup-xhtml.xsl new file mode 100644 index 00000000..da0f2b5a --- /dev/null +++ b/zh/9.6/stylesheet-speedup-xhtml.xsl @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Error: If you change $chunk.section.depth, then you must update the performance-optimized chunk-all-sections-template. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/stylesheet-text.xsl b/zh/9.6/stylesheet-text.xsl new file mode 100644 index 00000000..529cc9ec --- /dev/null +++ b/zh/9.6/stylesheet-text.xsl @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + +
  • + + + + +
  • +
    + + + + + * + + * + + + + + + + + + + + + + + +
    + + + + + + +

    + + + : + +

    +
    + + +
    +
    + + + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    diff --git a/zh/9.6/stylesheet.css b/zh/9.6/stylesheet.css new file mode 100644 index 00000000..83761916 --- /dev/null +++ b/zh/9.6/stylesheet.css @@ -0,0 +1,185 @@ +/* doc/src/sgml/stylesheet.css */ + +/* Prefer official pg.org docs styles when online. */ +@import url('https://www.postgresql.org/media/css/docs-complete.css'); + +/* color scheme similar to www.postgresql.org */ + +body { + color: #000000; + background: #FFFFFF; + font-family: verdana, sans-serif; +} + +a:link { color:#0066A2; } +a:visited { color:#004E66; } +a:active { color:#0066A2; } +a:hover { color:#000000; } + +h1 { + font-size: 1.4em; + font-weight: bold; + margin-top: 0em; + margin-bottom: 0em; + color: #EC5800; +} + +h2 { + font-size: 1.2em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; + color: #666; +} + +.titlepage h2.title, +.refnamediv h2 { + color: #EC5800; +} + +h3 { + font-size: 1.1em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; + color: #666; +} + +h4 { + font-size: 0.95em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; + color: #666; +} + +h5 { + font-size: 0.9em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +h6 { + font-size: 0.85em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +/* center some titles */ + +.book .title, .book .corpauthor, .book .copyright { + text-align: center; +} + +/* decoration for formal examples */ + +div.example { + padding-left: 15px; + border-style: solid; + border-width: 0px; + border-left-width: 2px; + border-color: black; + margin: 0.5ex; +} + +/* Additional formatting for "simplelist" structures */ +table.simplelist td { + padding-left: 2em; + padding-right: 2em; +} + +/* formatting for entries in tables of functions: indent all but first line */ + +th.func_table_entry p, +td.func_table_entry p { + margin-top: 0.1em; + margin-bottom: 0.1em; + padding-left: 4em; + text-align: left; +} + +p.func_signature { + text-indent: -3.5em; +} + +td.func_table_entry pre.programlisting { + margin-top: 0.1em; + margin-bottom: 0.1em; + padding-left: 4em; +} + +/* formatting for entries in tables of catalog/view columns */ + +th.catalog_table_entry p, +td.catalog_table_entry p { + margin-top: 0.1em; + margin-bottom: 0.1em; + padding-left: 4em; + text-align: left; +} + +th.catalog_table_entry p.column_definition { + text-indent: -3.5em; + word-spacing: 0.25em; +} + +td.catalog_table_entry p.column_definition { + text-indent: -3.5em; +} + +p.column_definition code.type { + padding-left: 0.25em; + padding-right: 0.25em; +} + +td.catalog_table_entry pre.programlisting { + margin-top: 0.1em; + margin-bottom: 0.1em; + padding-left: 4em; +} + +/* Put these here instead of inside the HTML (see unsetting of + admon.style in XSL) so that the web site stylesheet can set its own + style. */ + +.tip, +.note, +.important, +.caution, +.warning { + margin-left: 0.5in; + margin-right: 0.5in; +} + +/* miscellaneous */ + +pre.literallayout, .screen, .synopsis, .programlisting { + margin-left: 4ex; +} + +ul.itemizedlist { + margin-left: 2.5rem; +} + +.comment { color: red; } + +var { font-family: monospace; font-style: italic; } +/* Konqueror's standard style for ACRONYM is italic. */ +acronym { font-style: inherit; } + +.option { white-space: nowrap; } + +/* make images not too wide on larger screens */ +@media (min-width: 800px) { + .mediaobject { + width: 75%; + } +} + +/* links to ids of headers and definition terms */ + +a.id_link { + color: inherit; + visibility: hidden; +} + +*:hover > a.id_link { + visibility: visible; +} diff --git a/zh/9.6/stylesheet.css.xml b/zh/9.6/stylesheet.css.xml new file mode 100644 index 00000000..a21fcca5 --- /dev/null +++ b/zh/9.6/stylesheet.css.xml @@ -0,0 +1,8 @@ + + +]> + diff --git a/zh/9.6/stylesheet.xsl b/zh/9.6/stylesheet.xsl new file mode 100644 index 00000000..9b039895 --- /dev/null +++ b/zh/9.6/stylesheet.xsl @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/9.6/syntax.sgml b/zh/9.6/syntax.sgml new file mode 100644 index 00000000..e50d40e0 --- /dev/null +++ b/zh/9.6/syntax.sgml @@ -0,0 +1,1778 @@ + + + + SQL语法 + + + syntax + SQL + + + + 本章描述 SQL 的语法。它是理解后续各章的基础,后续各章将详细介绍如何使用 SQL 命令定义和修改数据。 + + + + 我们也建议已经熟悉 SQL 的用户仔细阅读本章,因为其中包含一些在各 SQL 数据库之间实现并不一致的规则和概念,以及一些 + PostgreSQL 特有的规则和概念。 + + + + 词法结构 + + + 词元 + + + + SQL 输入由一系列命令组成。一个命令由一系列词元组成,并以分号(;)终止。输入流的结束也会终止一个命令。哪些词元是合法的,取决于具体命令的语法。 + + + + 一个词元可以是关键词标识符带引号的标识符字面量(或常量),或者特殊字符符号。词元通常由空白(空格、制表符、换行)分隔,但如果不存在歧义,则不必如此(一般只有特殊字符与其他类型的词元相邻时才会这样)。 + + + + 例如,下面是一个(语法上)合法的SQL输入: + +SELECT * FROM MY_TABLE; +UPDATE MY_TABLE SET A = 5; +INSERT INTO MY_TABLE VALUES (3, 'hi there'); + + 这是一个由三个命令组成的序列,每行一个命令(虽然这并非必须;一行中可以包含多个命令,命令也可以有意义地跨多行书写)。 + + + + 此外,注释也可以出现在 SQL 输入中。它们不是词元,实际上等价于空白。 + + + + 在哪些词元标识命令、哪些词元是操作数或参数这一点上,SQL 语法并不十分一致。最前面的几个词元通常是命令名,因此在上面的示例中,我们通常会说是 + SELECTUPDATEINSERT 命令。但是例如 + UPDATE 命令总是要求在特定位置出现一个 SET 词元,而这种形式的 + INSERT 也要求有一个 VALUES 才算完整。每个命令的精确语法规则见 + 。 + + + + 标识符和关键词 + + + 标识符 + 语法 + + + + 名称 + 语法 + + + + 关键词 + 语法 + + + + 上例中的 SELECTUPDATEVALUES 这类词元都是关键词,也就是在 SQL 语言中具有固定含义的词。词元 + MY_TABLEA 则是标识符的例子。它们标识表、列或其他数据库对象的名称,这取决于它们所在的命令。因此,它们有时也简称为名称。关键词和标识符具有相同的词法结构,这意味着如果不了解该语言,就无法判断一个词元究竟是标识符还是关键词。完整的关键词列表可以在中找到。 + + + + SQL 标识符和关键词必须以字母(a-z,也包括带变音符的字母和非拉丁字母)或下划线(_)开头。标识符或关键词中的后续字符可以是字母、下划线、数字(0-9)或美元符号($)。注意,按照 SQL 标准的字面规定,标识符中不允许使用美元符号,因此它可能会降低应用的可移植性。SQL 标准不会定义包含数字、以下划线开头或以下划线结尾的关键词,因此这种形式的标识符可以安全地避免与未来标准扩展发生冲突。 + + + + 标识符长度 + 系统对标识符最多只使用 NAMEDATALEN-1 个字节;在命令中可以写更长的名称,但它们会被截断。默认情况下, + NAMEDATALEN 为 64,因此标识符的最大长度是 63 字节。如果这个限制有问题,可以通过修改 + src/include/pg_config_manual.h 中的 NAMEDATALEN 常量来提高。 + + + + + 大小写敏感性 + SQL 命令 + + 关键词和未加引号的标识符不区分大小写。因此: + +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 转义 + 在标识符中 + + + + 带引号标识符的一种变体允许包含按代码点标识的转义 Unicode 字符。这种变体在开头双引号之前紧接着写 + U&(大写或小写的字母 U 后接和号),中间没有空格,例如 U&"foo"。(注意这会与操作符 + & 产生歧义。请在该操作符两侧加空格以避免这个问题。)在引号内,可以通过写一个反斜线后跟 4 位十六进制代码点编号,或者一个反斜线后跟加号再跟 6 位十六进制代码点编号,来指定 Unicode 字符。例如,标识符 "data" 可以写成: + +U&"d\0061t\+000061" + + 下面这个稍微复杂一些的示例,用西里尔字母写出了俄语单词 slon(大象): + +U&"\0441\043B\043E\043D" + + + + 如果想使用反斜线以外的转义字符,可以在字符串后用 UESCAPEUESCAPE 子句指定,例如: +U&"d!0061t!+000061" UESCAPE '!' +转义字符可以是除十六进制数字、加号、单引号、双引号或空白字符之外的任何单个字符。注意,转义字符要写在单引号内,而不是双引号内。 + + + 如果要在标识符中按字面包含转义字符本身,把它写两次即可。 + + + Unicode 转义语法只有在服务器编码为 UTF8 时才有效。使用其他服务器编码时,只能指定 ASCII 范围内的代码点(不超过 \007F)。4 位或 6 位转义形式都可以用来指定 UTF-16 代理对,以组合出代码点大于 U+FFFF 的字符,尽管从技术上说,由于有 6 位形式,这样做并无必要。(代理对不会被直接存储,而是会合并成一个代码点,然后以 UTF-8 编码。) + + + 给标识符加上引号也会使其区分大小写,而不加引号的名称总是会被折叠成小写。例如,标识符 + FOOfoo"foo" 在 + PostgreSQL 中被视为相同,但 "Foo" 和 + "FOO" 与这三个都不同,彼此之间也不同。(PostgreSQL 将不加引号的名称折叠为小写,这与 SQL 标准不兼容;标准规定,不加引号的名称应折叠为大写。因此,按照标准, + foo 应当等价于 "FOO",而不是 "foo"。如果你想编写可移植的应用,建议对某个特定名称要么始终加引号,要么始终不加引号。) + + + + + + 常量 + + + 常量 + + + + 在PostgreSQL中有三种隐式类型常量:字符串、位串和数字。也可以为常量指定显式类型,这样系统就能更准确地表示并更高效地处理它们。这些方式将在后续小节中讨论。 + + + + 字符串常量 + + + 字符串 + 常量 + + + + 引号 + 转义 + + SQL 中的字符串常量是由单引号(')括起的任意字符序列,例如 'This is a string'。要在字符串常量中包含单引号字符,请写两个相邻的单引号,例如 'Dianne''s horse'。注意,这不同于双引号字符(")。 + + + 两个只由空白及至少一个新行分隔的字符串常量会被连接在一起,并且将作为一个写在一起的字符串常量来对待。例如: + +SELECT 'foo' +'bar'; + + 等同于: + +SELECT 'foobar'; + + 但是: + +SELECT 'foo' 'bar'; + + 则不是合法的语法(这种有些奇怪的行为是SQL指定的,PostgreSQL遵循了该标准)。 + + + + + C风格转义的字符串常量 + + + 转义字符串语法 + + + 反斜线转义 + + + + PostgreSQL也接受转义字符串常量,这也是SQL标准的一个扩展。一个转义字符串常量可以通过在开单引号前面写一个字母E(大写或小写形式)来指定,例如E'foo'(当一个转义字符串常量跨行时,只在第一个开引号之前写E)。在一个转义字符串内部,一个反斜线字符(\)会开始一个 C 风格的反斜线转义序列,在其中反斜线和后续字符的组合表示一个特殊的字节值(如中所示)。 + + + + 反斜线转义序列 + + + + 反斜线转义序列 + 解释 + + + + + + \b + 退格 + + + \f + 换页 + + + \n + 换行 + + + \r + 回车 + + + \t + 制表符 + + + + \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 转义或者 中说明的另一种 Unicode 转义语法。(另一种做法是手工进行 UTF-8 编码并写出各字节,但那会非常繁琐。) + + Unicode 转义语法只有在服务器编码为 UTF8 时才能完全有效。使用其他服务器编码时,只能指定 ASCII 范围内的代码点(不超过 \u007F)。4 位和 8 位形式都可以用来指定 UTF-16 代理对,以组合出代码点大于 U+FFFF 的字符,尽管从技术上说,由于有 8 位形式,这样做并无必要。(在服务器编码为 UTF8 时使用代理对,它们会先合并成一个代码点,然后以 UTF-8 编码。) + + + + 如果配置参数off,那么PostgreSQL对常规字符串常量和转义字符串常量中的反斜线转义都识别。不过,从PostgreSQL 9.1 开始,该参数的默认值为on,意味着只在转义字符串常量中识别反斜线转义。这种行为更兼容标准,但是可能使依赖历史行为(始终识别反斜线转义)的应用无法正常工作。作为一种变通,你可以设置该参数为off,但最好修改应用,不再使用反斜线转义。如果你需要使用一个反斜线转义来表示一个特殊字符,为该字符串常量写上一个E。 + + + + 在standard_conforming_strings之外,配置参数也决定了如何对待字符串常量中的反斜线。 + + + + + 编码为零的字符不能出现在字符串常量中。 + +
    + + + 带有 Unicode 转义的字符串常量 + + + Unicode 转义 + 在字符串常量中 + + + + PostgreSQL 还支持另一种字符串转义语法,它允许用代码点指定任意 Unicode 字符。Unicode 转义字符串常量在开引号前紧接着写 + U&(大写或小写字母 U 后跟和号),中间没有任何空白,例如 U&'foo'。(注意这会与操作符 + & 产生歧义。请在该操作符两侧加空格以避免这个问题。)在引号内,可以通过写一个反斜线后跟 4 位十六进制代码点编号,或者一个反斜线后跟加号再跟 6 位十六进制代码点编号,来指定 Unicode 字符。例如,字符串 + 'data' 可以写成 + +U&'d\0061t\+000061' + + 下面这个稍微复杂一些的示例,用西里尔字母写出了俄语单词 slon(大象): + +U&'\0441\043B\043E\043D' + + + + + 如果想要一个不是反斜线的转义字符,可以在字符串之后使用UESCAPEUESCAPE子句来指定,例如: + +U&'d!0061t!+000061' UESCAPE '!' + + 转义字符可以是除十六进制数字、加号、单引号、双引号或空白字符之外的任意单个字符。 + + + Unicode 转义语法只有在服务器编码为 UTF8 时才有效。使用其他服务器编码时,只能指定 ASCII 范围内的代码点(不超过 \007F)。4 位和 6 位形式都可以用来指定 UTF-16 代理对,以组合出代码点大于 U+FFFF 的字符,尽管从技术上说,由于有 6 位形式,这样做并无必要。(在服务器编码为 UTF8 时使用代理对,它们会先合并成一个代码点,然后以 UTF-8 编码。) + + + 此外,字符串常量的 Unicode 转义语法只有在配置参数 开启时才有效。这是因为否则这种语法可能会混淆解析 SQL 语句的客户端,从而导致 SQL 注入以及类似的安全问题。如果该参数被设置为 off,这种语法将被拒绝并报错。 + + + + 如果要在字符串中按字面包含转义字符本身,把它写两次即可。 + + + + + 美元引用的字符串常量 + + + 美元引用 + + + 指定字符串常量的标准语法通常很方便,但当所需字符串包含很多单引号或反斜线时,就可能难以理解,因为每个这样的字符都必须写成双份。为了在这种情况下让查询更易读,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$ 表示一个美元引用的字面字符串 + [\t\r\n\v\\],当函数体被 PostgreSQL 执行时,它会被识别。但由于该序列与外层美元引用定界符 $function$ 不匹配,就外层字符串而言,它只不过是常量中的一部分字符。 + + + + 美元引用字符串的标签(如果有)遵循与未加引号标识符相同的规则,但不能包含美元符号。标签区分大小写,因此 $tag$String content$tag$ 是正确的,而 $TAG$String content$tag$ 则不正确。 + + + + 如果美元引用字符串位于某个关键词或标识符之后,必须用空白把它们分隔开,否则美元引用定界符会被当作前一个标识符的一部分。 + + + + 美元引用不是 SQL 标准的一部分,但在书写复杂字符串字面量时,它往往比符合标准的单引号语法更方便。当要表示的字符串常量位于其他常量内部时,它尤其有用,这种情况在过程函数定义中很常见。如果使用单引号语法,上一个示例中的每个反斜线都必须写成四个反斜线;在解析原始字符串常量时它们会缩减为两个反斜线,而在函数执行期间重新解析内层字符串常量时又会变成一个。 + + + + + 位串常量 + + + 位串 + 常量 + + + + 位串常量看起来像常规字符串常量在开引号之前(中间无空白)加了一个B(大写或小写形式),例如B'1001'。位串常量中允许的字符只有01。 + + + + 或者,也可以用十六进制记法指定位串常量,即在开头写一个 X(大写或小写形式),例如 X'1FF'。这种记法等价于把每个十六进制位替换成四个二进制位得到的位串常量。 + + + + 两种形式的位串常量可以以常规字符串常量相同的方式跨行继续。美元引用不能被用在位串常量中。 + + + + + 数字常量 + + + 数字 + 常量 + + + + 数字常量接受下列一般形式: + +digits +digits.digitse+-digits +digits.digitse+-digits +digitse+-digits + + 其中 digits 是一个或多个十进制数字(0 到 9)。 + 若使用小数点,则小数点前后至少有一侧必须有数字。若使用指数标记 + (e),其后至少必须有一位数字。常量中不能嵌入空格或 + 其他字符。请注意,前导正号或负号 + 实际上不属于常量本身,而是作用于常量的操作符。 + + + + 这些是合法数字常量的示例: + +42 +3.5 +4. +.001 +5e2 +1.925e-3 + + + + + integer + bigint + numeric + 如果一个不包含小数点和指数的数字常量的值适合类型integer(32 位),它首先被假定为类型integer。否则如果它的值适合类型bigint(64 位),它被假定为类型bigint。再否则它会被取做类型numeric。包含小数点和/或指数的常量总是首先被假定为类型numeric。 + + + + 为数字常量最初指定的数据类型,只是类型解析算法的起点。在大多数情况下,常量会根据上下文被自动强制转换成最合适的类型。必要时,你可以通过类型转换强制把一个数值解释为特定的数据类型。类型转换 + 例如,可以通过下面的写法强制把一个数值当作类型 realfloat4)处理: + + +REAL '1.23' -- 字符串形式 +1.23::REAL -- PostgreSQL(历史)形式 + + + 这些实际上只是下面将要讨论的一般类型转换记法的特例。 + + + + + 其他类型的常量 + + + 数据类型 + 常量 + + + + 任意类型的常量都可以用下列记法之一输入: + +type 'string' +'string'::type +CAST ( 'string' AS type ) + + 字符串常量的文本会被传递给名为 type 的类型的输入转换例程。其结果是一个指定类型的常量。如果该常量必须具有的类型不存在歧义(例如,它被直接赋给某个表列时),就可以省略显式类型转换,在这种情况下它会被自动强制转换。 + + + + 字符串常量可以使用常规 SQL 记法或美元引用书写。 + + + + 也可以用类似函数调用的语法来指定类型强制转换: + +typename ( 'string' ) + + 但并非所有类型名都可以这样使用,详见。 + + + + 如中所述,::CAST() 以及函数调用语法也可以用来指定任意表达式的运行时类型转换。为避免语法歧义,type 'string' 语法只能用来指定简单字面常量的类型。type 'string' 语法的另一个限制是不能用于数组类型;要指定数组常量的类型,请使用 ::CAST()。 + + + + CAST()语法符合 SQL。type 'string'语法是该标准的一般化:SQL 指定这种语法只用于一些数据类型,但是PostgreSQL允许它用于所有类型。带有::的语法是PostgreSQL的历史用法,就像函数调用语法一样。 + + +
    + + + 操作符 + + + 操作符 + 语法 + + + + 操作符名称由下列列表中的字符组成,长度最多为 NAMEDATALEN-1 个字符(默认是 63): + ++ - * / < > = ~ ! @ # % ^ & | ` ? + + + 不过,操作符名称也有一些限制: + + + + --/* 不能出现在操作符名称中的任何位置,因为它们会被视为注释的开始。 + + + + + + 多字符操作符名称不能以 +- 结尾,除非该名称中还至少包含下列字符中的一个: + +~ ! @ # % ^ & | ` ? + + 例如,@- 是合法的操作符名称,但 *- 不是。这个限制使 + PostgreSQL 能够在不要求词元之间必须有空格的情况下解析符合 SQL 的查询。 + + + + + + + 当使用非 SQL 标准的操作符名时,你通常需要用空格分隔相邻的操作符来避免歧义。例如,如果你定义了一个名为@的左一元操作符,你不能写X*@Y,你必须写X* @Y来确保PostgreSQL把它读作两个操作符名而不是一个。 + + + + + 特殊字符 + + + 某些非字母数字字符具有不同于操作符的特殊含义。关于它们的详细用法,可以在描述相应语法元素的位置找到。本节只是为了提示它们的存在,并概述这些字符的用途。 + + + + + 美元符号($)后跟数字时,用来表示函数定义体或预备语句中的位置参数。在其他上下文中,美元符号可以是标识符的一部分,也可以是美元引用字符串常量的一部分。 + + + + + + 圆括号(())具有它们通常的含义,用来对表达式分组并确定运算优先级。在某些情况中,圆括号被要求作为一个特定 SQL 命令的固定语法的一部分。 + + + + + + 方括号([])被用来选择一个数组中的元素。更多关于数组的信息见。 + + + + + + 逗号(,)被用在某些语法结构中来分割一个列表的元素。 + + + + + + 分号(;)结束一个 SQL 命令。它不能出现在一个命令中间的任何位置,除了在一个字符串常量中或者一个带引号的标识符中。 + + + + + + 冒号(:)被用来从数组中选择切片(见)。在某些 SQL 的“方言”(例如嵌入式 SQL)中,冒号被用来作为变量名的前缀。 + + + + + + 星号(*)在某些上下文中用来表示表行或复合值的所有字段。当它被用作聚合函数的参数时,还有一种特殊含义,即该聚合不需要任何显式参数。 + + + + + + 句点(.)被用在数字常量中,并且被用来分割模式、表和列名。 + + + + + + + + + 注释 + + + 注释 + 在 SQL 中 + + + + 注释是一串以双连字符开始并延伸到行尾的字符,例如: + +-- 这是一条标准 SQL 注释 + + + + + 另外,也可以使用 C 风格注释块: + +/* 多行注释 + * 包含嵌套:/* 嵌套块注释 */ + */ + + 这里该注释开始于/*并且延伸到匹配出现的*/。这些注释块可按照 SQL 标准中指定的方式嵌套,但和 C 中不同。这样我们可以注释掉一大段可能包含注释块的代码。 + + + + 在进一步进行语法分析之前,注释会从输入流中移除,并实际被替换为空白。 + + + + + 操作符优先级 + + + 操作符 + 优先级 + + + 展示了 PostgreSQL 中操作符的优先级和结合性。大多数操作符具有相同的优先级,并且是左结合的。操作符的优先级和结合性是固定写在解析器中的。 + + 在组合使用二元和一元操作符时,有时需要添加圆括号。例如: +SELECT 5 ! - 6; +将被解析为: +SELECT 5 ! (- 6); +因为解析器并不知道 — 等到知道时已经太迟了 — !被定义为后缀操作符,而不是中缀操作符。要在这种情况下得到所需的行为,必须写成: +SELECT (5 !) - 6; +这是为可扩展性付出的代价。 + + + 操作符优先级(从高到低) + + + + + 操作符/元素 + 结合性 + 描述 + + + + + + . + + 表/列名分隔符 + + + + :: + + PostgreSQL-风格的类型转换 + + + + [ ] + + 数组元素选择 + + + + + - + + 一元正号、一元负号 + + + + ^ + + 求幂 + + + + * / % + + 乘、除、取模 + + + + + - + + 加、减 + + + + (任意其他操作符) + + 所有其他内置以及用户定义的操作符 + + + + BETWEEN IN LIKE ILIKE SIMILAR + + 范围包含、集合成员关系、字符串匹配 + + + + < > = <= >= <> + + + 比较操作符 + + + + IS ISNULL NOTNULL + + IS TRUE, IS FALSE, IS + NULL, IS DISTINCT FROM, 等。 + + + + NOT + + 逻辑否定 + + + + AND + + 逻辑合取 + + + + OR + + 逻辑析取 + + + +
    + + + 注意,这些操作符优先级规则也适用于名称与上述内置操作符相同的用户定义操作符。例如,如果你为某种自定义数据类型定义了一个 + 操作符,那么无论你的操作符做什么,它都将具有与内置 + 操作符相同的优先级。 + + + + 当一个模式限定的操作符名被用在OPERATOR语法中时,如下面的示例: + +SELECT 3 OPERATOR(pg_catalog.+) 4; + + OPERATOR 结构在优先级上会被视为 中所示的任意其他操作符。无论 OPERATOR() 中指定的是哪个具体操作符,这一点都成立。 + + + + PostgreSQL 9.5 之前的版本使用的操作符优先级规则略有不同。特别是,<=>=<> 过去被当作普通操作符;IS 测试过去具有较高的优先级;而 NOT BETWEEN 和相关结构的处理不一致,在某些情况下被认为具有 NOT 而不是 BETWEEN 的优先级。为了更好地符合 SQL 标准,并减少对逻辑等价结构的不一致处理造成的困惑,这些规则得到了修改。在大部分情况下,这些变化不会导致行为变化,或者可能会产生 no such operator 错误,但可以通过增加圆括号解决。不过在一些极端情况下,查询可能在没有报告任何解析错误的情况下改变行为。如果你担心这些变化是否悄悄破坏了某些功能,可以在开启配置参数 的情况下测试应用程序,查看是否记录了任何警告。 + +
    +
    + + + 值表达式 + + + 表达式 + 语法 + + + + 值表达式 + + + + 标量 + 表达式 + + + + 值表达式被用于各种各样的环境中,例如在SELECT命令的目标列表中,作为INSERTUPDATE中的新列值,或者作为若干命令中的搜索条件。为了与表表达式(其结果是一张表)区分开来,值表达式的结果有时被称为标量。因此,值表达式也称为标量表达式(甚至简称为表达式)。表达式语法允许使用算术、逻辑、集合以及其他操作,从基本部分计算出值。 + + + + 一个值表达式是下列之一: + + + + + 一个常量或字面值 + + + + + + 一个列引用 + + + + + + 在一个函数定义体或预备语句中的一个位置参数引用 + + + + + + 一个下标表达式 + + + + + + 一个字段选择表达式 + + + + + + 一个操作符调用 + + + + + + 一个函数调用 + + + + + + 一个聚合表达式 + + + + + + 一个窗口函数调用 + + + + + + 一个类型转换 + + + + + + 一个排序规则表达式 + + + + + + 一个标量子查询 + + + + + + 一个数组构造器 + + + + + + 一个行构造器 + + + + + + 另一个圆括号中的值表达式(用于对子表达式分组并覆盖优先级圆括号) + + + + + + + 在这个列表之外,还有一些结构可以被分类为一个表达式,但是它们不遵循任何一般语法规则。这些通常具有一个函数或操作符的语义并且在中的合适位置解释。一个示例是IS NULL子句。 + + + + 我们已经在中讨论过常量。下面的小节会讨论剩下的选项。 + + + + 列引用 + + + 列引用 + + + + 一个列可以以下面的形式被引用: + +correlation.columnname + + + + + correlation 是一个表的名字(可能带有模式名限定),或者是在 FROM 子句中为某个表定义的别名。如果该列名在当前查询所用的所有表中都是唯一的,则关联名称和分隔用的句点都可以省略(另见)。 + + + + + 位置参数 + + + 参数 + 语法 + + + + $ + + + + 位置参数引用用来表示一个由 SQL 语句外部提供的值。参数可用于 SQL 函数定义和预备语句中。某些客户端库还支持把数据值与 SQL 命令字符串分开指定,在这种情况下,参数就用来引用这些命令字符串之外的数据值。参数引用的形式是: + +$number + + + + + 例如,考虑一个函数dept的定义: + + +CREATE FUNCTION dept(text) RETURNS dept + AS $$ SELECT * FROM dept WHERE name = $1 $$ + LANGUAGE SQL; + + + 这里$1引用函数被调用时第一个函数参数的值。 + + + + + 下标 + + + 下标 + + + + 如果一个表达式得到了一个数组类型的值,那么可以抽取出该数组值的一个特定元素: + +expression[subscript] + + 或者抽取出多个相邻元素(一个数组切片): + +expression[lower_subscript:upper_subscript] + + (这里,方括号[ ]表示其字面意思)。每一个下标自身是一个表达式,它将四舍五入到最接近的整数值。 + + + + 通常,数组表达式必须加上圆括号,但如果要加下标的表达式只是列引用或位置参数,则圆括号可以省略。另外,当原始数组是多维数组时,多个下标可以连写。例如: + + +mytable.arraycolumn[4] +mytable.two_d_column[17][34] +$1[10:42] +(arrayfunction(a,b))[42] + + + 最后一个示例中的圆括号是必需的。详见。 + + + + + 字段选择 + + + 字段选择 + + + + 如果一个表达式得到一个复合类型(行类型)的值,那么可以抽取该行的指定字段: + +expression.fieldname + + + + + 通常,行表达式必须加上圆括号,但如果要取字段的表达式只是表引用或位置参数,则圆括号可以省略。例如: + + +mytable.mycolumn +$1.somecolumn +(rowfunction(a,b)).col3 + + + (因此,限定列引用实际上只是字段选择语法的一种特例。)一种重要的特例是从复合类型的表列中抽取字段: + + +(compositecol).somefield +(mytable.compositecol).somefield + + + 这里需要圆括号来显示compositecol是一个列名而不是一个表名,在第二种情况中则是显示mytable是一个表名而不是一个模式名。 + + + + 你可以通过书写.*来请求一个组合值的所有字段: + +(compositecol).* + + 这种记法的行为根据上下文会有不同,详见。 + + + + + 操作符调用 + + + 操作符 + 调用 + + + 对于一次操作符调用,有三种可能的语法: + expression operator expression(二元中缀操作符) + operator expression(一元前缀操作符) + expression operator(一元后缀操作符) + 其中 operator 词元遵循 的语法规则,或者是关键词 ANDORNOT 之一,或者是一个如下形式的模式限定操作符名: +OPERATOR(schema.operatorname) +具体存在哪些操作符以及它们是一元还是二元,取决于系统或用户定义了哪些操作符。描述了内置操作符。 + + + + 函数调用 + + + 函数 + 调用 + + + + 函数调用的语法是:函数名(可能带有模式名限定)后跟一组放在圆括号内的参数列表: + + +function_name (expression , expression ... ) + + + + + 例如,下面会计算 2 的平方根: + +sqrt(2) + + + + + 内置函数的列表在中。其他函数可以由用户增加。 + + + + 当在某些用户不信任其他用户的数据库中发出查询时,在编写函数调用时应遵守中的安全防范措施。 + + + + 参数也可以选择附带名称。详见。 + + + + + 接受单个复合类型参数的函数,可以选择使用字段选择语法来调用;反过来,字段选择也可以写成函数风格。也就是说,col(table)table.col 这两种记法可以互换。这种行为不是 SQL 标准的一部分,但 PostgreSQL 提供了它,因为这允许使用函数来模拟计算字段。详见。 + + + + + + 聚合表达式 + + + 聚合函数 + 调用 + + + + 有序集聚合 + + + + WITHIN GROUP + + + + FILTER + + + 一个聚合表达式表示将聚合函数应用于查询选中的各行。聚合函数将多个输入归约为单个输出值,例如输入的和或平均值。聚合表达式的语法可以是以下形式之一: +aggregate_name (expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] +aggregate_name (ALL expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] +aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] +aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ] +aggregate_name ( [ expression [ , ... ] ] ) WITHIN GROUP ( order_by_clause ) [ FILTER ( WHERE filter_clause ) ] +其中,aggregate_name 是先前定义的聚合(可以带模式名限定),而 expression 是任何本身不包含聚合表达式或窗口函数调用的值表达式。可选的 order_by_clausefilter_clause 将在下文说明。 + + + 第一种形式的聚合表达式为每一个输入行调用一次聚合。第二种形式和第一种相同,因为ALL是默认选项。第三种形式为输入行中表达式的每一个可区分值(或者对于多个表达式是值的可区分集合)调用一次聚合。第四种形式为每一个输入行调用一次聚合,因为没有特定的输入值被指定,它通常只对于count(*)聚合函数有用。最后一种形式被用于有序集聚合函数,其描述如下。 + + + + 大部分聚合函数忽略空输入,这样其中一个或多个表达式得到空值的行将被丢弃。除非另有说明,对于所有内置聚合都是这样。 + + + + 例如,count(*)得到输入行的总数。count(f1)得到输入行中f1为非空的数量,因为count忽略空值。而count(distinct f1)得到f1的非空可区分值的数量。 + + + 通常,输入行以未指定的顺序传递给聚合函数。在许多情况下这无关紧要;例如,min无论以什么顺序接收输入都会产生相同的结果。但是,有些聚合函数(例如 array_aggstring_agg)的结果取决于输入行的顺序。使用这类聚合时,可选的 order_by_clause 可以用来指定所需的顺序。order_by_clause的语法与查询级 ORDER BY 子句相同,详见 ,但其中的表达式只能是表达式,不能是输出列的名称或编号。例如: +SELECT array_agg(a ORDER BY b DESC) FROM table; + + + + 使用多参数聚合函数时,请注意 ORDER BY 子句位于所有聚合参数之后。例如,应写成: +SELECT string_agg(a, ',' ORDER BY a) FROM table; +而不是: +SELECT string_agg(a ORDER BY a, ',') FROM table; -- incorrect +后者在语法上是有效的,但它表示调用一个带有两个 ORDER BY 排序键的单参数聚合函数(第二个排序键是常量,因此没什么用处)。 + + 如果在 DISTINCT 之外还指定了 order_by_clause,那么所有 ORDER BY 表达式都必须与聚合的常规参数相匹配;也就是说,不能根据未包含在 DISTINCT 列表中的表达式排序。 + + + 在聚合函数中同时指定 DISTINCTORDER BY 的能力是 PostgreSQL 的扩展。 + + + + 按照到目前为止的描述,把 + ORDER BY 放在该聚合的常规参数列表中的做法,用于为排序可选的 + 普通聚合排序输入行。有一类聚合函数称为 + 有序集聚合,它们必须带有 + order_by_clause,通常是因为这些聚合只有在输入行具有特定顺序时其计算才有意义。有序集聚合的典型例子包括排名和百分位点计算。对于有序集聚合,按照上面最后一种语法, + order_by_clause 要写在 WITHIN GROUP (...) 之中。order_by_clause 中的表达式会像普通聚合参数一样,对每个输入行计算一次,按 order_by_clause 的要求排序,然后作为输入参数传给聚合函数。(这不同于不带 + WITHIN GROUPorder_by_clause;在那种情况下,表达式结果不会被视为聚合函数的参数。)如果在 + WITHIN GROUP 之前还有参数表达式,它们称为直接参数,以区别于列在 + order_by_clause 中的聚合参数。与普通聚合参数不同,直接参数在每次聚合调用中只计算一次,而不是对每个输入行都计算一次。这意味着,只有在这些变量被 + GROUP BY 分组时,直接参数中才能包含它们;这一限制与这些直接参数根本不在聚合表达式内时是一样的。直接参数通常用于诸如百分位分数之类的值,因为这类值只有在每次聚合计算中作为单一值时才有意义。直接参数列表可以为空;在这种情况下,应写成 () 而不是 (*)。(实际上 + PostgreSQL 两种写法都接受,但只有前者符合 SQL 标准。) + + + + + median + 百分位点 + + 有序集聚合调用的一个示例如下: + + +SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households; + percentile_cont +----------------- + 50489 + + + 这会得到表 householdsincome 列的第 50 百分位点,也就是中位数。在这里,0.5 是一个直接参数;如果百分位分数在不同行之间变化,那就没有意义了。 + + + 如果指定了 FILTER,那么只有使 filter_clause 计算为真的输入行才会被交给聚合函数;其他行会被丢弃。例如: +SELECT + count(*) AS unfiltered, + count(*) FILTER (WHERE i < 5) AS filtered +FROM generate_series(1,10) AS s(i); + unfiltered | filtered +------------+---------- + 10 | 4 +(1 row) + + + + + 预定义的聚合函数在中描述。其他聚合函数可以由用户增加。 + + + + 一个聚合表达式只能出现在SELECT命令的结果列表或是HAVING子句中。在其他子句(如WHERE)中禁止使用它,因为那些子句的计算在逻辑上是在聚合的结果被形成之前。 + + + + 当一个聚合表达式出现在一个子查询中(见),聚合通常在该子查询的行上被计算。但是如果该聚合的参数(以及filter_clause,如果有)只包含外层变量则会产生一个异常:该聚合则属于最近的那个外层,并且会在那个查询的行上被计算。该聚合表达式从整体上则是对其所出现于的子查询的一种外层引用,并且在那个子查询的任意一次计算中都作为一个常量。只出现在结果列表或HAVING子句的限制适用于该聚合所属的查询层次。 + + + + + 窗口函数调用 + + + 窗口函数 + invocation + + + + OVER clause + + + 一次窗口函数调用表示在查询选出的部分行上应用一个类似聚合的函数。与普通聚合函数调用不同,这并不意味着把选出的行分组为单个输出行 — 每一行在查询输出中仍然独立存在。不过,窗口函数能够扫描根据该窗口函数调用的分组说明(PARTITION BY 列表)属于当前行所在组的所有行。窗口函数调用的语法是下列之一: +function_name (expression , expression ... ) [ FILTER ( WHERE filter_clause ) ] OVER window_name +function_name (expression , expression ... ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition ) +function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name +function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition ) +其中 window_definition 的语法为: +[ existing_window_name ] +[ PARTITION BY expression [, ...] ] +[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] +[ frame_clause ] +而可选的 frame_clause 可以是以下形式之一: +{ RANGE | ROWS } frame_start +{ RANGE | ROWS } BETWEEN frame_start AND frame_end +其中 frame_startframe_end 可以是以下形式之一: +UNBOUNDED PRECEDING +value PRECEDING +CURRENT ROW +value FOLLOWING +UNBOUNDED FOLLOWING + + + + + 这里,expression表示任何自身不含有窗口函数调用的值表达式。 + + + + window_name是对定义在查询的WINDOW子句中的一个命名窗口声明的引用。还可以使用在WINDOW子句中定义命名窗口的相同语法在圆括号内给定一个完整的window_definition,详见参考页。值得指出的是,OVER wname并不严格地等价于OVER (wname),后者表示复制并修改窗口定义,并且在被引用窗口声明包括一个帧子句时会被拒绝。 + + + + PARTITION BY选项把查询中的行分组成分区,窗口函数会分别处理这些分区。PARTITION BY 的工作方式类似于查询级别的 GROUP BY 子句,不过它的表达式始终只是表达式,不能是输出列名或列编号。如果没有 PARTITION BY,该查询生成的所有行都会被当作单个分区处理。ORDER BY 选项决定窗口函数处理某个分区中的行时所采用的顺序。它的工作方式也类似于查询级别的 ORDER BY 子句,但同样不能使用输出列名或列编号。如果没有 ORDER BY,行将按未指定的顺序处理。 + + + frame_clause指定构成窗口帧的行集合,它是当前分区的一个子集,供那些作用于帧而不是整个分区的窗口函数使用。可以在RANGEROWS模式中指定帧;无论哪种情况,帧的范围都是从frame_startframe_end。如果frame_end被省略,则默认为CURRENT ROW + + + UNBOUNDED PRECEDING的一个frame_start表示该帧开始于分区的第一行,类似地UNBOUNDED FOLLOWING的一个frame_end表示该帧结束于分区的最后一行。 + + + RANGE模式中,frame_startCURRENT ROW表示帧从当前行的第一个同等行(即ORDER BY认为与当前行等价的行)开始,而frame_endCURRENT ROW表示帧在最后一个等价的ORDER BY同等行处结束。在ROWS模式中,CURRENT ROW就表示当前行。 + + value PRECEDINGvalue FOLLOWING目前只允许用于ROWS模式。它们表示帧开始或结束于当前行之前或之后指定行数的位置。value必须是一个不包含任何变量、聚合函数或窗口函数的整数表达式。其值不能为空值或负数;但可以为零,这会只选择当前行。 + + + 默认的帧选项是RANGE UNBOUNDED PRECEDING,它和RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。如果使用ORDER BY,这会把该帧设置为从分区开始一直到当前行的最后一个ORDER BY同等行的所有行。如果不使用ORDER BY,就意味着分区中所有的行都被包括在窗口帧中,因为所有行都成为了当前行的同等行。 + + + 限制是frame_start不能为UNBOUNDED FOLLOWINGframe_end不能为UNBOUNDED PRECEDING,并且在上述列表中,frame_end选项不能出现在frame_start选项之前 — 例如,不允许RANGE BETWEEN CURRENT ROW AND value PRECEDING + + + 如果指定了FILTER,那么只有对filter_clause计算为真的输入行会被交给该窗口函数,其他行会被丢弃。只有是聚合的窗口函数才接受FILTER 。 + + + + 内置的窗口函数在中介绍。用户可以加入其他窗口函数。此外,任何内置的或者用户定义的普通聚合函数都可以被用作窗口函数。但有序集聚合当前不能被用作窗口函数。 + + + + 使用*的语法被用来把参数较少的聚合函数当作窗口函数调用,例如count(*) OVER (PARTITION BY x ORDER BY y)。星号(*)通常不被用于非聚合的窗口函数。与普通聚合函数不同,聚合窗口函数不允许在函数参数列表中使用DISTINCTORDER BY。 + + + + 只有在SELECT列表和查询的ORDER BY子句中才允许窗口函数调用。 + + + + 更多关于窗口函数的信息可以在以及中找到。 + + + + + 类型转换 + + + data type + type cast + + + + type cast + + + + :: + + + + 一个类型转换指定从一种数据类型到另一种数据类型的转换。PostgreSQL接受两种等价的类型转换语法: + +CAST ( expression AS type ) +expression::type + + CAST语法遵从 SQL,而用::的语法是PostgreSQL的历史用法。 + + + + 当类型转换被应用于一种已知类型的值表达式时,它表示一次运行时类型转换。只有在已经定义了合适的类型转换操作时,该类型转换才会成功。注意,这与常量上的类型转换(如中所示)略有不同。应用于未修饰字符串字面量的类型转换,表示为一个字面常量值赋予初始类型,因此它对任意类型都能成功(前提是该字符串字面量的内容符合该数据类型的输入语法)。 + + + + 如果一个值表达式必须产生的类型没有歧义(例如当它被赋给一个表列时),通常可以省略显式类型转换,在这种情况下系统会自动应用类型转换。但是,自动类型转换只会对那些在系统目录中被标记为可以隐式应用的类型转换执行。其他类型转换必须使用显式类型转换语法来调用。施加这一限制的目的是防止出人意料的转换被悄无声息地应用。 + + + + 还可以用类似函数调用的语法来指定类型转换: + +typename ( expression ) + + 不过,这只对那些名称本身也可作为函数名使用的类型有效。例如,double precision 不能用这种方式,但等效的 float8 可以。另外,由于语法冲突,名称 intervaltimetimestamp 只有在使用双引号引用时才能采用这种写法。因此,函数风格的类型转换语法会带来不一致性,通常应尽量避免。 + + + + + 函数风格的语法事实上只是一次函数调用。当两种标准类型转换语法之一被用来做一次运行时转换时,它将在内部调用一个已注册的函数来执行该转换。简而言之,这些转换函数具有和它们的输出类型相同的名字,并且因此函数风格的语法无非是对底层转换函数的一次直接调用。显然,一个可移植的应用不应当依赖于它。详见。 + + + + + + 排序规则表达式 + + + COLLATE + + + + COLLATE 子句会覆盖表达式的排序规则。它附加在所作用的表达式之后: + +expr COLLATE collation + + 这里 collation 可以是一个带模式名限定的标识符。COLLATE 子句比操作符绑定得更紧,必要时可以使用圆括号。 + + + + 如果没有显式指定排序规则,数据库系统会从表达式所涉及的列中得到一个排序规则,如果该表达式没有涉及列,则会默认采用数据库的默认排序规则。 + + + + COLLATE 子句的两种常见用途是覆盖 ORDER BY 子句中的排序顺序,例如: + +SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C"; + + 以及覆盖具有区域敏感结果的函数或操作符调用的排序规则,例如: + +SELECT * FROM tbl WHERE a > 'foo' COLLATE "C"; + + 注意,在后一种情况中,COLLATE 子句是附加到我们希望影响的那个操作符的某个输入参数上的。把 COLLATE 子句附加到该操作符或函数调用的哪个参数上并不重要,因为操作符或函数实际采用的排序规则是综合所有参数得出的,而显式的 COLLATE 子句会覆盖其他所有参数的排序规则。(不过,若把不匹配的 COLLATE 子句附加到多个参数上,则会报错。详见。)因此,这会得到与前一个示例相同的结果: + +SELECT * FROM tbl WHERE a COLLATE "C" > 'foo'; + + 但是这是一个错误: + +SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C"; + + 因为它尝试把一个排序规则应用到>操作符的结果,而它的数据类型是不支持排序规则的数据类型boolean。 + + + + + 标量子查询 + + + 子查询 + + + + 标量子查询是一个放在圆括号中的普通 SELECT 查询,它恰好返回一行一列(关于如何编写查询,见)。该 SELECT 查询会被执行,其返回的单个值会用在外围值表达式中。把一个返回多于一行或多于一列的查询当作标量子查询来用是错误的。(但如果在某次具体执行中该子查询没有返回任何行,则不算错误;其标量结果会被视为空值。)该子查询可以引用外围查询中的变量,这些变量在该子查询的每一次求值期间都会作为常量。有关其他涉及子查询的表达式,另见。 + + + + 例如,下列语句会寻找每个州中最大的城市人口: + +SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name) + FROM states; + + + + + + 数组构造器 + + + 数组 + 构造器 + + + + ARRAY + + + + 数组构造器是一个表达式,它使用其成员元素的值来构造一个数组值。简单的数组构造器由关键字 + ARRAY、左方括号 [、一个用于指定数组元素值的表达式列表(以逗号分隔)以及最后的右方括号 ] 组成。例如: + +SELECT ARRAY[1,2,3+4]; + array +--------- + {1,2,7} +(1 row) + + 默认情况下,数组元素类型是成员表达式的公共类型,其确定规则与 UNION 或 + CASE 结构相同(见 )。你可以通过把数组构造器显式转换为所需类型来覆盖这一点,例如: + +SELECT ARRAY[1,2,22.7]::integer[]; + array +---------- + {1,2,23} +(1 row) + + 这与把每个表达式分别转换为数组元素类型具有相同的效果。关于类型转换的更多信息,见 。 + + + + 多维数组值可以通过嵌套数组构造器来构造。在内部构造器中,关键字 ARRAY 可以省略。例如,下面两种写法产生相同的结果: + + +SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]]; + array +--------------- + {{1,2},{3,4}} +(1 row) + +SELECT ARRAY[[1,2],[3,4]]; + array +--------------- + {{1,2},{3,4}} +(1 row) + + + 由于多维数组必须是长方形的,因此同一级别的内部构造器必须生成维度完全相同的子数组。施加到外层 ARRAY 构造器上的任何类型转换,都会自动传播到所有内层构造器。 + + + + 多维数组构造器中的元素可以是任何能够生成适当类型数组的表达式,而不仅仅是子 ARRAY 构造。例如: + +CREATE TABLE arr(f1 int[], f2 int[]); + +INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]); + +SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr; + array +------------------------------------------------ + {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}} +(1 row) + + + + + 你也可以构造空数组,但由于不可能存在没有类型的数组,因此必须把空数组显式转换为所需类型。例如: + +SELECT ARRAY[]::integer[]; + array +------- + {} +(1 row) + + + + 也可以根据子查询的结果构造数组。在这种形式中,数组构造器写为关键字 ARRAY 后跟一个用圆括号(而不是方括号)括起的子查询。例如: +SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); + array +----------------------------------------------------------------------- + {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413} +(1 row) + +SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS a(i)); + array +---------------------------------- + {{1,2},{2,4},{3,6},{4,8},{5,10}} +(1 row) +子查询必须返回单列。如果子查询的输出列不是数组类型,那么生成的一维数组会为子查询结果中的每一行包含一个元素,元素类型与子查询输出列的类型相匹配。如果子查询的输出列是数组类型,那么结果就是同一类型但维数高一维的数组;在这种情况下,所有子查询行都必须产生维度完全相同的数组,否则结果就不是矩形的。 + + + 用 ARRAY 构造出来的数组值,其下标总是从 1 开始。关于数组的更多信息,见。 + + + + + + 行构造器 + + + 复合类型 + 构造器 + + + + 行类型 + 构造器 + + + + ROW + + + + 行构造器是一种表达式,它使用成员字段的值来构造一个行值(也称为组合值)。行构造器由关键字 ROW、左圆括号、零个或多个用于指定行各字段值的表达式(以逗号分隔),以及最后的右圆括号组成。例如: + +SELECT ROW(1,2.5,'this is a test'); + + 当列表中有多个表达式时,关键字 ROW 是可选的。 + + + + 一个行构造器可以包括语法rowvalue.*,它将被扩展为该行值的元素的一个列表,就像在一个顶层SELECT列表(见)中使用.*时发生的事情一样。例如,如果表t有列f1f2,那么这些是相同的: + +SELECT ROW(t.*, 42) FROM t; +SELECT ROW(t.f1, t.f2, 42) FROM t; + + + + + + 在PostgreSQL 8.2 以前,.* 语法不会在行构造器中展开,因此写成 ROW(t.*, 42) 会创建一个具有两个字段的行,其中第一个字段是另一个行值。新的行为通常更有用。如果你需要旧版本那种嵌套行值的行为,写内层行值时不要使用 .*,例如 ROW(t, 42)。 + + + + + 默认情况下,ROW 表达式创建的值具有匿名记录类型。如有需要,可以把它转换为具名复合类型,也就是某个表的行类型,或者使用 CREATE TYPE AS 创建的复合类型。为避免歧义,可能需要显式类型转换。例如: + +CREATE TABLE mytable(f1 int, f2 float, f3 text); + +CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; + +-- No cast needed since only one getf1() exists +SELECT getf1(ROW(1,2.5,'this is a test')); + getf1 +------- + 1 +(1 row) + +CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric); + +CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; + +-- Now we need a cast to indicate which function to call: +SELECT getf1(ROW(1,2.5,'this is a test')); +ERROR: function getf1(record) is not unique + +SELECT getf1(ROW(1,2.5,'this is a test')::mytable); + getf1 +------- + 1 +(1 row) + +SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype)); + getf1 +------- + 11 +(1 row) + + + + 行构造器可以用来构造复合值,以存储在复合类型的表列中,或者传递给接受复合参数的函数。此外,还可以比较两个行值,或者用 IS NULL 或 IS NOT NULL 来测试一个行,例如: +SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same'); + +SELECT ROW(table.*) IS NULL FROM table; -- detect all-null rows +更多详情见 。行构造器也可以与子查询结合使用,详见 。 + + + + + + 表达式计算规则 + + + 表达式 + 计算的顺序 + + + + 子表达式的计算顺序是未定义的。特别是,操作符或函数的输入不一定会按照从左到右或任何其他固定顺序计算。 + + + + 此外,如果一个表达式的结果可以通过只计算其一部分来决定,那么其他子表达式可能完全不需要被计算。例如,如果我们写: + +SELECT true OR somefunc(); + + 那么somefunc()将(可能)完全不被调用。如果我们写成下面这样也是一样: + +SELECT somefunc() OR true; + + 注意这和一些编程语言中布尔操作符从左至右的短路不同。 + + + + 因此,在复杂表达式中使用带有副作用的函数是不明智的。在WHEREHAVING子句中依赖副作用或计算顺序尤其危险,因为在建立一个执行计划时这些子句会被广泛地重新处理。这些子句中布尔表达式(AND/OR/NOT的组合)可能会以布尔代数定律所允许的任何方式被重组。 + + + + 当有必要强制计算顺序时,可以使用一个CASE结构(见)。例如,在一个WHERE子句中使用下面的方法尝试避免除零是不可靠的: + +SELECT ... WHERE x > 0 AND y/x > 1.5; + + 但是这是安全的: + +SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; + + 一个以这种风格使用的CASE结构将使得优化尝试失败,因此只有必要时才这样做(在这个特别的示例中,最好通过写y > 1.5*x来回避这个问题)。 + + + + 不过,CASE 并不是这类问题的万灵药。上述技术的一个限制是, + 它无法阻止常量子表达式被提前计算。如 + 中所述,当查询被规划而不是被执行时,被标记成 + IMMUTABLE的函数和操作符可以被计算。因此 + +SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab; + + 很可能会导致一次除零失败,因为规划器尝试简化常量子表达式。即便是 + 表中的每一行都有x > 0(这样运行时永远不会进入到 + ELSE分支)也是这样。 + + + + 虽然这个例子看起来有些可笑,但在函数内部执行的查询中,也可能出现一些看上去并未明显涉及常量的相关情况,因为函数参数值和局部变量值为了规划目的,可能会作为常量插入到查询中。例如,在 PL/pgSQL 函数中,使用 IF-THEN-ELSE 语句来保护一项有风险的计算,要比仅仅把它嵌套进一个 CASE 表达式安全得多。 + + + + 另一个同类型的限制是,一个CASE无法阻止其所包含的聚合表达式 + 的计算,因为在考虑SELECT列表或HAVING子句中的 + 其他表达式之前,会先计算聚合表达式。例如,下面的查询可能会导致除零错误, + 尽管看起来似乎已经对这种情况做了防护: + +SELECT CASE WHEN min(employees) > 0 + THEN avg(expenses / employees) + END + FROM departments; + + min()avg()聚合会在所有输入行上并行地计算, + 因此如果任何行有employees等于零,在有机会测试 + min()的结果之前,就会发生除零错误。取而代之的是,可以使用 + 一个WHEREFILTER子句来首先阻止有问题的输入行到达 + 一个聚合函数。 + + + + + + 调用函数 + + + notation + functions + + + + PostgreSQL 允许对带命名参数的函数使用位置记法或命名记法来调用。命名记法对于拥有大量参数的函数尤其有用,因为它能让形参与实参之间的对应关系更明确、更可靠。在位置记法中,函数调用中的参数值按照它们在函数声明中定义的顺序书写;在命名记法中,实参与函数参数按名称匹配,因此可以按任意顺序书写。对于每一种记法,还要考虑函数参数类型的影响,这一点在中有说明。 + + + + 在任意一种记法中,在函数声明中给出了默认值的参数都可以在调用中省略。不过这在命名记法中特别有用,因为任意组合的参数都可以被省略;而在位置记法中,参数只能从右向左省略。 + + + + PostgreSQL 也支持混合记法,它结合了位置记法和命名记法。在这种情况下,位置参数先写,命名参数写在后面。 + + + + 下面的示例将展示这三种记法的用法,所用函数定义如下: + +CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false) +RETURNS text +AS +$$ + SELECT CASE + WHEN $3 THEN UPPER($1 || ' ' || $2) + ELSE LOWER($1 || ' ' || $2) + END; +$$ +LANGUAGE SQL IMMUTABLE STRICT; + + 函数 concat_lower_or_upper 有两个必需参数 ab。此外还有一个可选参数 uppercase,其默认值为 falseab 的输入将被连接起来,并根据 uppercase 参数的值转换成大写或小写。该函数定义的其余细节在这里并不重要(详见)。 + + + + 使用位置记法 + + + 函数 + 位置记法 + + + + 位置记法是在 PostgreSQL 中向函数传递参数的传统机制。例如: + +SELECT concat_lower_or_upper('Hello', 'World', true); + concat_lower_or_upper +----------------------- + HELLO WORLD +(1 row) + + 所有参数都按顺序给出。由于 uppercase 被指定为 true,因此结果是大写。另一个例子是: + +SELECT concat_lower_or_upper('Hello', 'World'); + concat_lower_or_upper +----------------------- + hello world +(1 row) + + 这里省略了 uppercase 参数,因此它接收默认值 false,结果是小写输出。在位置记法中,只要参数具有默认值,就可以从右向左依次省略。 + + + + + 使用命名记法 + + + 函数 + 命名记法 + + + + 在命名记法中,每个参数名都使用 => 与其参数表达式分隔。例如: + +SELECT concat_lower_or_upper(a => 'Hello', b => 'World'); + concat_lower_or_upper +----------------------- + hello world +(1 row) + + 同样,uppercase 参数被省略,因此它被隐式设置为 false。使用命名记法的一个优点是,参数可以按任意顺序指定,例如: + +SELECT concat_lower_or_upper(a => 'Hello', b => 'World', uppercase => true); + concat_lower_or_upper +----------------------- + HELLO WORLD +(1 row) + +SELECT concat_lower_or_upper(a => 'Hello', uppercase => true, b => 'World'); + concat_lower_or_upper +----------------------- + HELLO WORLD +(1 row) + + + + 为保持向后兼容,也支持基于 ":=" 的旧语法: +SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World'); + concat_lower_or_upper +----------------------- + HELLO WORLD +(1 row) + + + + + + 使用混合记法 + + + 函数 + 混合记法 + + + + 混合记法结合了位置记法和命名记法。不过,如前所述,命名参数不能出现在位置参数之前。例如: + +SELECT concat_lower_or_upper('Hello', 'World', uppercase => true); + concat_lower_or_upper +----------------------- + HELLO WORLD +(1 row) + + 在上面的查询中,参数 ab 采用位置方式指定,而 uppercase 采用命名方式指定。在这个示例中,这样做除了起到一定说明作用外并没有太多额外价值。但对于拥有大量带默认值参数的更复杂函数,命名记法或混合记法可以大幅减少书写量,并降低出错机会。 + + + + + 命名调用记法和混合调用记法目前不能用于调用聚合函数(但当聚合函数被用作窗口函数时,它们是可以使用的)。 + + + + + +
    diff --git a/zh/9.6/tablefunc.sgml b/zh/9.6/tablefunc.sgml new file mode 100644 index 00000000..015602f3 --- /dev/null +++ b/zh/9.6/tablefunc.sgml @@ -0,0 +1,651 @@ + + + + tablefunc + + + tablefunc + + + + tablefunc 模块包含多种返回表(即多行结果)的函数。这些函数本身很有用,也可作为如何编写返回多行的 C 函数的示例。 + + + + 提供的函数 + + 总结了 tablefunc 模块提供的函数。 + + + <filename>tablefunc</filename>函数 + + + + 函数 + 返回值 + 描述 + + + + + normal_rand(int numvals, float8 mean, float8 stddev) + setof float8 + + 生成一组正态分布的随机值。 + + + + crosstab(text sql) + setof record + + 生成一个透视表,其中包含行名以及 N 个值列,其中 N 由调用查询中指定的行类型决定。 + + + + crosstabN(text sql) + setof table_crosstab_N + + 生成一个透视表,其中包含行名以及 N 个值列。crosstab2crosstab3crosstab4 是预定义的,但也可以按下文所述创建额外的 crosstabN 函数。 + + + + crosstab(text source_sql, text category_sql) + setof record + + 生成一个透视表,其值列由第二个查询指定。 + + + + crosstab(text sql, int N) + setof record + + 这是 crosstab(text) 的过时版本。参数 N 现已被忽略,因为值列的数量始终由调用查询决定。 + + + + connectby(text relname, text keyid_fld, text parent_keyid_fld [, text orderby_fld ], text start_with, int max_depth [, text branch_delim ]) connectby + setof record + + 生成层次树结构的表示形式。 + + + + +
    + + + <function>normal_rand</function> + + + normal_rand + + + +normal_rand(int numvals, float8 mean, float8 stddev) returns setof float8 + + + + normal_rand 生成一组正态分布(高斯分布)的随机值。 + + + + numvals 是该函数要返回的值的个数。mean 是这些值所服从正态分布的均值,stddev 是这些值所服从正态分布的标准偏差。 + + + + 例如,下面这个调用请求生成 1000 个值,均值为 5,标准偏差为 3: + + + +test=# SELECT * FROM normal_rand(1000, 5, 3); + normal_rand +---------------------- + 1.56556322244898 + 9.10040991424657 + 5.36957140345079 + -0.369151492880995 + 0.283600703686639 + . + . + . + 4.82992125404908 + 9.71308014517282 + 2.49639286969028 +(1000 rows) + + + + + <function>crosstab(text)</function> + + + crosstab + + + +crosstab(text sql) +crosstab(text sql, int N) + + + crosstab函数用于产生透视显示形式,其中数据沿页面横向排列,而不是纵向排列。例如,我们可能有这样的数据 +row1 val11 +row1 val12 +row1 val13 +... +row2 val21 +row2 val22 +row2 val23 +... +并希望将它们显示成这样 +row1 val11 val12 val13 ... +row2 val21 val22 val23 ... +... +crosstab函数接受一个文本参数,该参数是一个 SQL 查询,用于产生按第一种方式格式化的原始数据,并生成一个按第二种方式格式化的表。 + + + sql 参数是一个产生源数据集的 SQL 语句。该语句必须返回一个 row_name 列、一个 category 列和一个 value 列。N 是一个过时参数,即使提供也会被忽略(以前它必须与输出值列的个数匹配,但现在这一点由调用查询决定)。 + + + + 例如,给出的查询可能会产生如下结果: + + row_name cat value +----------+-------+------- + row1 cat1 val1 + row1 cat2 val2 + row1 cat3 val3 + row1 cat4 val4 + row2 cat1 val5 + row2 cat2 val6 + row2 cat3 val7 + row2 cat4 val8 + + + + crosstab 函数声明的返回类型为 setof record,因此必须在 FROM 子句中定义输出列的实际名称和类型,该子句属于调用它的 SELECT 语句。例如: +SELECT * FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text); +此示例生成的集合类似于: + <== value columns ==> + row_name category_1 category_2 +----------+------------+------------ + row1 val1 val2 + row2 val5 val6 + + + + + FROM 子句必须把输出定义为一个 row_name 列(其数据类型与 SQL 查询的第一列结果相同),其后跟着 N 个 value 列(它们的数据类型都与 SQL 查询的第三列结果相同)。可以按需设置任意数量的输出值列,输出列的名称也可自行决定。 + + + + crosstab 函数会为输入行中具有相同 row_name 值的每个连续分组生成一行输出。它会用这些行中的 value 字段从左到右填充输出的 value 列。如果某个分组中的行数少于输出 value 列的个数,多余的输出列会填充为空值;如果行数更多,多出来的输入行会被跳过。 + + + + 实际上,SQL 查询应始终指定 ORDER BY 1,2,以确保输入行按正确顺序排列,也就是使具有相同 row_name 的值聚在一起,并在行内正确排序。注意,crosstab 本身并不会关注查询结果的第二列;该列只是为了排序而存在,用来控制第三列的值在页面上横向排列的顺序。 + + + + 下面是一个完整示例: + +CREATE TABLE ct(id SERIAL, rowid TEXT, attribute TEXT, value TEXT); +INSERT INTO ct(rowid, attribute, value) VALUES('test1','att1','val1'); +INSERT INTO ct(rowid, attribute, value) VALUES('test1','att2','val2'); +INSERT INTO ct(rowid, attribute, value) VALUES('test1','att3','val3'); +INSERT INTO ct(rowid, attribute, value) VALUES('test1','att4','val4'); +INSERT INTO ct(rowid, attribute, value) VALUES('test2','att1','val5'); +INSERT INTO ct(rowid, attribute, value) VALUES('test2','att2','val6'); +INSERT INTO ct(rowid, attribute, value) VALUES('test2','att3','val7'); +INSERT INTO ct(rowid, attribute, value) VALUES('test2','att4','val8'); + +SELECT * +FROM crosstab( + 'select rowid, attribute, value + from ct + where attribute = ''att2'' or attribute = ''att3'' + order by 1,2') +AS ct(row_name text, category_1 text, category_2 text, category_3 text); + + row_name | category_1 | category_2 | category_3 +----------+------------+------------+------------ + test1 | val2 | val3 | + test2 | val6 | val7 | +(2 rows) + + + + + 可以定义一个在其定义中固定了所需输出行类型的自定义 crosstab 函数,以避免每次都必须写出用于定义输出列的 FROM 子句。下一节会介绍这种做法。另一种可能性是在视图定义中嵌入所需的 FROM 子句。 + + + + + 另见 psql 中的 \crosstabview 命令,它提供了与 crosstab() 类似的功能。 + + + + + + + <function>crosstab<replaceable>N</replaceable>(text)</function> + + + crosstab + + + +crosstabN(text sql) + + + + crosstabN 函数展示了如何为通用的 crosstab 函数设置自定义包装器,这样就不必在调用的 SELECT 查询中写出列名和类型。tablefunc 模块包含 crosstab2crosstab3crosstab4,其输出行类型定义为: + + + +CREATE TYPE tablefunc_crosstab_N AS ( + row_name TEXT, + category_1 TEXT, + category_2 TEXT, + . + . + . + category_N TEXT +); + + + + 因此,当输入查询产生类型为 textrow_name 列和 value 列,并且需要 2、3 或 4 个输出值列时,这些函数可以直接使用。除此之外,它们在其他方面的行为与上文描述的通用 crosstab 函数完全相同。 + + + + 例如,前一节中的示例也可以这样写: + +SELECT * +FROM crosstab3( + 'select rowid, attribute, value + from ct + where attribute = ''att2'' or attribute = ''att3'' + order by 1,2'); + + + + + 这些函数主要用于说明。也可以基于底层的 crosstab() 函数创建自己的返回类型和函数。做法有两种: + + + + + 创建一个描述所需输出列的复合类型,类似于 contrib/tablefunc/tablefunc--1.0.sql 中的示例。然后定义一个具有唯一名称的函数,该函数接受单个 text 参数并返回 setof your_type_name,但底层链接到同一个 crosstab C 函数。例如,如果源数据生成的行名是 text,值是 float8,而希望有 5 个值列: + +CREATE TYPE my_crosstab_float8_5_cols AS ( + my_row_name text, + my_category_1 float8, + my_category_2 float8, + my_category_3 float8, + my_category_4 float8, + my_category_5 float8 +); + +CREATE OR REPLACE FUNCTION crosstab_float8_5_cols(text) + RETURNS setof my_crosstab_float8_5_cols + AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT; + + + + + + + 使用 OUT 参数隐式定义返回类型。同一个示例也可以这样实现: + +CREATE OR REPLACE FUNCTION crosstab_float8_5_cols( + IN text, + OUT my_row_name text, + OUT my_category_1 float8, + OUT my_category_2 float8, + OUT my_category_3 float8, + OUT my_category_4 float8, + OUT my_category_5 float8) + RETURNS setof record + AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT; + + + + + + + + + + <function>crosstab(text, text)</function> + + + crosstab + + + +crosstab(text source_sql, text category_sql) + + + + crosstab 单参数形式的主要限制在于,它会把同一组中的所有值一视同仁,并把每个值插入第一个可用列中。如果希望值列对应于特定的数据类别,而某些分组可能没有某些类别的数据,这种形式就不太适用了。crosstab 的双参数形式通过提供一个与输出列对应的显式类别列表来处理这种情况。 + + + + source_sql 是一个产生源数据集的 SQL 语句。该语句必须返回一个 row_name 列、一个 category 列和一个 value 列。它也可以有一个或多个extra列。row_name 列必须位于第一列。category 列和 value 列必须是最后两列,并且顺序必须如此。位于 row_namecategory 之间的任何列都被视为extra。对于具有相同 row_name 值的所有行,这些extra列预期应当相同。 + + + + 例如,source_sql可能会产生如下结果: + +SELECT row_name, extra_col, cat, value FROM foo ORDER BY 1; + + row_name extra_col cat value +----------+------------+-----+--------- + row1 extra1 cat1 val1 + row1 extra1 cat2 val2 + row1 extra1 cat4 val4 + row2 extra2 cat1 val5 + row2 extra2 cat2 val6 + row2 extra2 cat3 val7 + row2 extra2 cat4 val8 + + + + + category_sql是一个生成类别集合的 SQL 语句。该语句必须只返回一列。它必须至少生成一行,否则会产生错误。此外,它不得生成重复值,否则会产生错误。category_sql可以是这样的: +SELECT DISTINCT cat FROM foo ORDER BY 1; + cat + ------- + cat1 + cat2 + cat3 + cat4 + + + + crosstab 函数声明的返回类型为 setof record,因此必须在 FROM 子句中定义输出列的实际名称和类型,该子句属于调用它的 SELECT 语句。例如: +SELECT * FROM crosstab('...', '...') + AS ct(row_name text, extra text, cat1 text, cat2 text, cat3 text, cat4 text); + + + + + 这将产生如下结果: + + <== value columns ==> +row_name extra cat1 cat2 cat3 cat4 +---------+-------+------+------+------+------ + row1 extra1 val1 val2 val4 + row2 extra2 val5 val6 val7 val8 + + + + + FROM 子句必须定义数量和数据类型都正确的输出列。如果 source_sql 查询结果有 N 列,那么其中前 N-2 列必须与前 N-2 个输出列相匹配。其余输出列必须具有 source_sql 查询结果最后一列的数据类型,并且这些输出列的数量必须与 category_sql 查询结果的行数完全相同。 + + + + crosstab 函数会为输入行中具有相同 row_name 值的每个连续分组生成一行输出。输出的 row_name 列以及任何extra列都从该分组的第一行复制而来。输出的 value 列则使用那些具有匹配 category 值的行中的 value 字段来填充。如果某一行的 categorycategory_sql 查询的任何输出都不匹配,那么它的 value 会被忽略。那些匹配类别未出现在该分组任何输入行中的输出列会填充为空值。 + + + + 实际上,source_sql 查询应始终指定 ORDER BY 1,以确保具有相同 row_name 的值被聚在一起。不过,分组内类别的顺序并不重要。另外,必须确保 category_sql 查询输出的顺序与指定的输出列顺序一致。 + + + + 下面给出两个完整示例: + +create table sales(year int, month int, qty int); +insert into sales values(2007, 1, 1000); +insert into sales values(2007, 2, 1500); +insert into sales values(2007, 7, 500); +insert into sales values(2007, 11, 1500); +insert into sales values(2007, 12, 2000); +insert into sales values(2008, 1, 1000); + +select * from crosstab( + 'select year, month, qty from sales order by 1', + 'select m from generate_series(1,12) m' +) as ( + year int, + "Jan" int, + "Feb" int, + "Mar" int, + "Apr" int, + "May" int, + "Jun" int, + "Jul" int, + "Aug" int, + "Sep" int, + "Oct" int, + "Nov" int, + "Dec" int +); + year | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec +------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+------+------ + 2007 | 1000 | 1500 | | | | | 500 | | | | 1500 | 2000 + 2008 | 1000 | | | | | | | | | | | +(2 rows) + + + +CREATE TABLE cth(rowid text, rowdt timestamp, attribute text, val text); +INSERT INTO cth VALUES('test1','01 March 2003','temperature','42'); +INSERT INTO cth VALUES('test1','01 March 2003','test_result','PASS'); +INSERT INTO cth VALUES('test1','01 March 2003','volts','2.6987'); +INSERT INTO cth VALUES('test2','02 March 2003','temperature','53'); +INSERT INTO cth VALUES('test2','02 March 2003','test_result','FAIL'); +INSERT INTO cth VALUES('test2','02 March 2003','test_startdate','01 March 2003'); +INSERT INTO cth VALUES('test2','02 March 2003','volts','3.1234'); + +SELECT * FROM crosstab +( + 'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1', + 'SELECT DISTINCT attribute FROM cth ORDER BY 1' +) +AS +( + rowid text, + rowdt timestamp, + temperature int4, + test_result text, + test_startdate timestamp, + volts float8 +); + rowid | rowdt | temperature | test_result | test_startdate | volts +-------+--------------------------+-------------+-------------+--------------------------+-------- + test1 | Sat Mar 01 00:00:00 2003 | 42 | PASS | | 2.6987 + test2 | Sun Mar 02 00:00:00 2003 | 53 | FAIL | Sat Mar 01 00:00:00 2003 | 3.1234 +(2 rows) + + + + + 可以创建预定义函数,以避免在每个查询中都写出结果列的名称和类型。请参见前一节中的示例。这种形式的 crosstab 所对应的底层 C 函数名为 crosstab_hash。 + + + + + + <function>connectby</function> + + + connectby + + + +connectby(text relname, text keyid_fld, text parent_keyid_fld + [, text orderby_fld ], text start_with, int max_depth + [, text branch_delim ]) + + + + connectby 函数会显示存储在表中的层次数据。该表必须有一个唯一标识各行的键字段,以及一个引用每一行的父行(如果有)的父键字段。connectby 可以显示从任意一行开始向下展开的子树。 + + + + 解释了这些参数。 + + + + <function>connectby</function>参数 + + + + 参数 + 描述 + + + + + relname + 源关系名称 + + + keyid_fld + 键字段名称 + + + parent_keyid_fld + 父键字段名称 + + + orderby_fld + 用于对同级节点排序的字段名称(可选) + + + start_with + 起始行的键值 + + + max_depth + 向下遍历的最大深度,零表示深度不受限制 + + + branch_delim + 在分支输出中分隔各键值的字符串(可选) + + + +
    + + + 键字段和父键字段可以是任意数据类型,但它们必须是同一类型。注意,无论键字段的类型是什么,start_with 值都必须作为文本字符串输入。 + + + + connectby 函数被声明为返回 setof record,因此输出列的实际名称和类型必须在调用 SELECT 语句的 FROM 子句中定义,例如: + + + +SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~') + AS t(keyid text, parent_keyid text, level int, branch text, pos int); + + + + 前两个输出列用于当前行的键和其父行的键,它们必须与该表键字段的类型匹配。第三个输出列表示树中的深度,必须是 integer 类型。如果给出了 branch_delim 参数,下一个输出列就是分支路径显示,必须是 text 类型。最后,如果给出了 orderby_fld 参数,最后一个输出列就是一个序列号,必须是 integer 类型。 + + + + branch 输出列显示了到达当前行所经过的键路径。各键之间用指定的 branch_delim 字符串分隔。如果不需要分支显示,则在输出列列表中同时省略 branch_delim 参数和 branch 列即可。 + + + + 如果同一父节点下各同级节点的顺序很重要,可以包含 orderby_fld 参数来指定按哪个字段对同级节点排序。该字段可以是任何可排序的数据类型。当且仅当指定了 orderby_fld 时,输出列列表才必须包含最后那个整数类型的序列号列。 + + + + 表名和字段名参数会原样复制到 connectby 在内部生成的 SQL 查询中。因此,如果名称是大小写混合的,或者包含特殊字符,就应包含双引号。还可能需要对表名进行模式限定。 + + + + 在大表中,除非父键字段上建有索引,否则性能会很差。 + + + + 重要的是,branch_delim 字符串不要出现在任何键值中,否则 connectby 可能会错误地报告无限递归错误。注意,如果没有提供 branch_delim,为了递归检测会使用默认值 ~。 + + + + + 下面是一个示例: + +CREATE TABLE connectby_tree(keyid text, parent_keyid text, pos int); + +INSERT INTO connectby_tree VALUES('row1',NULL, 0); +INSERT INTO connectby_tree VALUES('row2','row1', 0); +INSERT INTO connectby_tree VALUES('row3','row1', 0); +INSERT INTO connectby_tree VALUES('row4','row2', 1); +INSERT INTO connectby_tree VALUES('row5','row2', 0); +INSERT INTO connectby_tree VALUES('row6','row4', 0); +INSERT INTO connectby_tree VALUES('row7','row3', 0); +INSERT INTO connectby_tree VALUES('row8','row6', 0); +INSERT INTO connectby_tree VALUES('row9','row5', 0); + +-- with branch, without orderby_fld (order of results is not guaranteed) +SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0, '~') + AS t(keyid text, parent_keyid text, level int, branch text); + keyid | parent_keyid | level | branch +-------+--------------+-------+--------------------- + row2 | | 0 | row2 + row4 | row2 | 1 | row2~row4 + row6 | row4 | 2 | row2~row4~row6 + row8 | row6 | 3 | row2~row4~row6~row8 + row5 | row2 | 1 | row2~row5 + row9 | row5 | 2 | row2~row5~row9 +(6 rows) + +-- without branch, without orderby_fld (order of results is not guaranteed) +SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0) + AS t(keyid text, parent_keyid text, level int); + keyid | parent_keyid | level +-------+--------------+------- + row2 | | 0 + row4 | row2 | 1 + row6 | row4 | 2 + row8 | row6 | 3 + row5 | row2 | 1 + row9 | row5 | 2 +(6 rows) + +-- with branch, with orderby_fld (notice that row5 comes before row4) +SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~') + AS t(keyid text, parent_keyid text, level int, branch text, pos int); + keyid | parent_keyid | level | branch | pos +-------+--------------+-------+---------------------+----- + row2 | | 0 | row2 | 1 + row5 | row2 | 1 | row2~row5 | 2 + row9 | row5 | 2 | row2~row5~row9 | 3 + row4 | row2 | 1 | row2~row4 | 4 + row6 | row4 | 2 | row2~row4~row6 | 5 + row8 | row6 | 3 | row2~row4~row6~row8 | 6 +(6 rows) + +-- without branch, with orderby_fld (notice that row5 comes before row4) +SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0) + AS t(keyid text, parent_keyid text, level int, pos int); + keyid | parent_keyid | level | pos +-------+--------------+-------+----- + row2 | | 0 | 1 + row5 | row2 | 1 | 2 + row9 | row5 | 2 | 3 + row4 | row2 | 1 | 4 + row6 | row4 | 2 | 5 + row8 | row6 | 3 | 6 +(6 rows) + + +
    + +
    + + + 作者 + + + Joe Conway + + + + +
    diff --git a/zh/9.6/tablesample-method.sgml b/zh/9.6/tablesample-method.sgml new file mode 100644 index 00000000..36718168 --- /dev/null +++ b/zh/9.6/tablesample-method.sgml @@ -0,0 +1,242 @@ + + + + 编写一种表采样方法 + + + 表采样方法 + + + + TABLESAMPLE 方法 + + + + PostgreSQLTABLESAMPLE 子句的实现, + 除了支持 SQL 标准要求的 BERNOULLI 和 + SYSTEM 方法之外,还支持自定义表采样方法。采样方法决定了 + 在使用 TABLESAMPLE 子句时会选取表中的哪些行。 + + + 在 SQL 层,表采样方法由单个 SQL 函数表示,该函数通常用 C 实现,其签名为 +method_name(internal) RETURNS tsm_handler +该函数的名称就是出现在TABLESAMPLE子句中的方法名。internal参数是一个占位值(其值始终为零),仅用于防止从 SQL 命令直接调用此函数。函数的结果必须是由 palloc 分配的TsmRoutine结构体,其中包含指向采样方法支持函数的指针。这些支持函数是普通的 C 函数,在 SQL 层既不可见也不可调用。有关这些支持函数的说明见。 + + + + 除了函数指针之外,TsmRoutine 结构体还必须提供以下额外字段: + + + + + List *parameterTypes + + + + 这是一个 OID 列表,包含该采样方法在 TABLESAMPLE + 子句中可接受参数的数据类型 OID。例如,对于内置方法,该列表只包含一个值为 + FLOAT4OID 的项,它表示采样百分比。自定义采样方法可以 + 有更多参数,也可以有不同的参数。 + + + + + + bool repeatable_across_queries + + + + 如果为 true,只要每次都提供相同的参数和 + REPEATABLE 种子值,并且表内容未发生变化,该采样方法就 + 能在连续查询之间返回相同的样本。如果该字段为 false, + 则该采样方法不接受 REPEATABLE 子句。 + + + + + + bool repeatable_across_scans + + + + 如果为 true,该采样方法就能在同一查询中的连续扫描之间 + 返回相同的样本(假定参数、种子值和快照都保持不变)。如果该字段为 + false,规划器将不会选择那些需要对被采样表扫描多次的 + 计划,因为那可能导致查询输出不一致。 + + + + + + + TsmRoutine 结构体类型声明在 + src/include/access/tsmapi.h 中,更多细节见该文件。 + + + + 标准发行版中包含的表采样方法,是尝试自行编写方法时的良好参考。内置 + 采样方法位于源代码树的 src/backend/access/tablesample + 子目录中,附加方法位于 contrib 子目录中。 + + + + 采样方法支持函数 + + + TSM 处理器函数返回一个通过 palloc 分配的 TsmRoutine 结构体, + 其中包含下文所述支持函数的指针。大多数函数是必需的,但有些是可选的, + 对应指针可以为 NULL。 + + + + +void +SampleScanGetSampleSize (PlannerInfo *root, + RelOptInfo *baserel, + List *paramexprs, + BlockNumber *pages, + double *tuples); + + + 该函数在规划阶段调用。它必须估计采样扫描期间将读取的关系页数,以及 + 该扫描将选出的元组数。(例如,可以先估计采样比例,再将 + baserel->pagesbaserel->tuples + 的值乘以该比例,并确保结果舍入为整数值。) + paramexprs 列表保存 TABLESAMPLE + 子句参数对应的表达式。如果出于估算目的需要这些值,建议使用 + estimate_expression_value() 尝试将这些表达式化简为 + 常量;但即使无法化简,该函数也必须给出大小估计,而且即便这些值看起来无效 + 也不应失败(别忘了,它们只是对运行时取值的估计)。 + pagestuples 参数是输出参数。 + + + + +void +InitSampleScan (SampleScanState *node, + int eflags); + + + 为 SampleScan 计划节点的执行进行初始化。该函数在执行器启动期间调用。 + 它应执行开始处理前所需的任何初始化工作。 + SampleScanState 节点已经创建,但其 + tsm_state 字段为 NULL。 + InitSampleScan 函数可以通过 palloc 分配采样方法所需的 + 任何内部状态数据,并将其指针存入 node->tsm_state。 + 待扫描表的信息可通过 SampleScanState 节点的其他 + 字段访问(但注意 node->ss.ss_currentScanDesc 扫描 + 描述符尚未设置)。 + eflags 包含描述执行器对此计划节点工作模式的标志位。 + + + + 当 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 为真时,不会 + 实际执行该扫描,因此这个函数只应做使节点状态对 EXPLAIN + 和 EndSampleScan 有效所需的最少工作。 + + + + 该函数可以省略(将指针设为 NULL),此时 + BeginSampleScan 必须执行采样方法所需的全部初始化工作。 + + + + +void +BeginSampleScan (SampleScanState *node, + Datum *params, + int nparams, + uint32 seed); + + + 开始执行一次采样扫描。它会在第一次尝试提取一个元组之前调用;如果该扫描需要 + 重启,也可能再次调用。待扫描表的信息可通过 + SampleScanState 节点的字段访问(但注意 + node->ss.ss_currentScanDesc 扫描描述符尚未设置)。 + 长度为 nparamsparams 数组包含 + TABLESAMPLE 子句中提供的参数值。这些参数的个数和类型 + 与该采样方法的 parameterTypes 列表中指定的一致,并且 + 已确认不为 NULL。seed 包含采样方法内部生成随机数时 + 要使用的种子; + 如果给定了 REPEATABLE 值,它就是从该值派生出的哈希值, + 否则就是 random() 的结果。 + + + + 该函数可以调整 node->use_bulkread 和 + node->use_pagemode 字段。 + 如果 node->use_bulkreadtrue + (默认如此),扫描将使用一种鼓励在使用后回收缓冲区的缓冲区访问策略。 + 如果该扫描只会访问该表页的一小部分,把它设为 false + 可能更合理。 + 如果 node->use_pagemodetrue + (默认如此),扫描将对每个访问页上的所有元组以单遍方式执行可见性检查。 + 如果该扫描只会从每个访问页中选出一小部分元组,把它设为 + false 可能更合理。这样执行的元组可见性检查会更少, + 但每次检查的代价会更高,因为需要更多加锁。 + + + + 如果采样方法被标记为 repeatable_across_scans,那么在 + 重新扫描期间它必须能够像最初那样选出相同的一组元组;也就是说,重新调用 + BeginSampleScan 必须像之前一样选出相同的元组 + (如果 TABLESAMPLE 参数和种子没有变化)。 + + + + +BlockNumber +NextSampleBlock (SampleScanState *node); +返回下一个要扫描的页的块号,或者返回InvalidBlockNumber,表示已经没有待扫描的页。 + + + 该函数可以省略(将指针设为 NULL),此时核心代码将对整个关系执行顺序 + 扫描。这样的扫描可能使用同步扫描,因此采样方法不能假定每次扫描都会按 + 相同顺序访问关系页。 + + + + +OffsetNumber +NextSampleTuple (SampleScanState *node, + BlockNumber blockno, + OffsetNumber maxoffset); + + + 返回指定页上下一个要采样的元组的偏移号;如果没有剩余元组可采样,则返回 + InvalidOffsetNumbermaxoffset 是该页上 + 正在使用的最大偏移号。 + + + + NextSampleTuple 不会被显式告知在 1 .. maxoffset 范围内哪些偏移号实际上包含有效元组。通常这不是问题,因为核心代码会忽略对缺失或不可见元组的采样请求;这不应在样本中引入任何偏差。不过,如有需要,该函数可以检查 node->ss.ss_currentScanDesc->rs_vistuples[] 来确定哪些元组有效且可见。(这要求 node->use_pagemodetrue。) + + + + + + NextSampleTuple 绝不能假定 + blockno 与最近一次 NextSampleBlock + 调用返回的页号相同。它是由之前某次 + NextSampleBlock 调用返回的,但核心代码可以在真正 + 扫描页之前就调用 NextSampleBlock,以支持预取。 + 可以假定的是,一旦开始对某个给定页进行采样,后续连续的 + NextSampleTuple 调用在返回 + InvalidOffsetNumber 之前都指向同一页。 + + + + + +void +EndSampleScan (SampleScanState *node); + + + 结束扫描并释放资源。通常不必专门释放通过 palloc 分配的内存,但任何对外 + 可见的资源都应清理掉。如果通常不存在此类资源,则这个函数可以省略(将 + 指针设为 NULL)。 + + + + + diff --git a/zh/9.6/tcn.sgml b/zh/9.6/tcn.sgml new file mode 100644 index 00000000..301855e9 --- /dev/null +++ b/zh/9.6/tcn.sgml @@ -0,0 +1,58 @@ + + + + tcn + + + tcn + + + + triggered_change_notification + + + + tcn模块提供一个触发器函数,用于将其所附着任意表的内容变更通知给监听者。它必须作为AFTER触发器,并以FOR EACH ROW方式使用。 + + + + 在CREATE TRIGGER语句中,至多可以为该函数提供一个参数,而且该参数是可选的。如果提供该参数,它将用作通知的通道名;如果省略,则使用tcn作为通道名。 + + + + 通知的载荷由表名、一个用于指示执行了哪种操作的字母,以及主键列的列名/值对组成。各部分之间都以逗号分隔。为了便于用正则表达式解析,表名和列名始终用双引号括起,数据值始终用单引号括起。嵌入的引号会被双写。 + + + 下面是一个使用该扩展的简短示例。 +test=# create table tcndata +test-# ( +test(# a int not null, +test(# b date not null, +test(# c text, +test(# primary key (a, b) +test(# ); +CREATE TABLE +test=# create trigger tcndata_tcn_trigger +test-# after insert or update or delete on tcndata +test-# for each row execute procedure triggered_change_notification(); +CREATE TRIGGER +test=# listen tcn; +LISTEN +test=# insert into tcndata values (1, date '2012-12-22', 'one'), +test-# (1, date '2012-12-23', 'another'), +test-# (2, date '2012-12-23', 'two'); +INSERT 0 3 +Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-22'" received from server process with PID 22770. +Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-23'" received from server process with PID 22770. +Asynchronous notification "tcn" with payload ""tcndata",I,"a"='2',"b"='2012-12-23'" received from server process with PID 22770. +test=# update tcndata set c = 'uno' where a = 1; +UPDATE 2 +Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-22'" received from server process with PID 22770. +Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-23'" received from server process with PID 22770. +test=# delete from tcndata where a = 1 and b = date '2012-12-22'; +DELETE 1 +Asynchronous notification "tcn" with payload ""tcndata",D,"a"='1',"b"='2012-12-22'" received from server process with PID 22770. + + + + diff --git a/zh/9.6/test-decoding.sgml b/zh/9.6/test-decoding.sgml new file mode 100644 index 00000000..5d392f69 --- /dev/null +++ b/zh/9.6/test-decoding.sgml @@ -0,0 +1,37 @@ + + + + test_decoding + + + test_decoding + + + + test_decoding 是一个逻辑解码输出插件示例。它本身并没有什么特别有用的功能,但可作为开发自定义输出插件的起点。 + + + + test_decoding 通过逻辑解码机制接收 WAL,并将其解码为所执行操作的文本表示形式。 + + + + 通过 SQL 逻辑解码接口使用该插件时,其典型输出可能如下: + + +postgres=# SELECT * FROM pg_logical_slot_get_changes('test_slot', NULL, NULL, 'include-xids', '0'); + location | xid | data +-----------+-----+-------------------------------------------------- + 0/16D30F8 | 691 | BEGIN + 0/16D32A0 | 691 | table public.data: INSERT: id[int4]:2 data[text]:'arg' + 0/16D32A0 | 691 | table public.data: INSERT: id[int4]:3 data[text]:'demo' + 0/16D32A0 | 691 | COMMIT + 0/16D32D8 | 692 | BEGIN + 0/16D3398 | 692 | table public.data: DELETE: id[int4]:2 + 0/16D3398 | 692 | table public.data: DELETE: id[int4]:3 + 0/16D3398 | 692 | COMMIT +(8 rows) + + + + diff --git a/zh/9.6/textsearch.sgml b/zh/9.6/textsearch.sgml new file mode 100644 index 00000000..c5b32d04 --- /dev/null +++ b/zh/9.6/textsearch.sgml @@ -0,0 +1,2940 @@ + + + + 全文搜索 + + + 全文搜索 + + + + 文本搜索 + + + + 介绍 + + + 全文检索(或简称文本搜索)提供了识别满足 + 查询条件的自然语言文档的能力, + 并且可按它们与查询的相关度进行排序。最常见的搜索类型,是找出所有包含给定 + 查询词的文档,并按它们与查询的相似性 + 排序返回。查询相似性的概念都很灵活, + 取决于具体应用。最简单的搜索把查询视为一组词,把 + 相似性视为查询词在文档中的出现频率。 + + + + 文本搜索操作符在数据库中已经存在很多年了。 + PostgreSQL为文本数据类型提供了 + ~~*LIKE 和 + ILIKE 操作符,但它们缺少现代信息系统所要求的许多关键特性: + + + + + + 缺少语言学支持,即便对英语也是如此。正则表达式并不足够,因为它们难以轻松 + 处理词形变化,例如 satisfiessatisfy。 + 搜索 satisfy 时,你大概也希望找到包含 + satisfies 的文档,但实际上可能会漏掉它们。虽然可以用 + OR 搜索多个派生形式,但这样既繁琐又容易出错 + (有些词甚至可能有数千种派生形式)。 + + + + + + 它们不会对搜索结果排序(排名),因此当找到成千上万条匹配文档时就难以实用。 + + + + + + 它们往往很慢,因为没有索引支持,所以每次搜索都必须处理全部文档。 + + + + + + 全文索引允许先对文档进行预处理,并保存索引以供后续快速搜索。 + 预处理包括: + + + + + + 将文档解析成词元。识别出不同类别的词元 + 很有帮助,例如数字、单词、复合词、电子邮件地址等,这样就可以分别处理。 + 原则上,词元类别取决于具体应用,但在大多数场景中,一套预定义的类别已经足够。 + PostgreSQL使用解析器执行这一步。 + 系统提供了标准解析器,也可以根据特定需求创建自定义解析器。 + + + + + + 将词元转换成词位。词位和词元一样都是字符串, + 但它已经过正规化,使同一个词的不同形式归为一致。例如, + 正规化几乎总是包含把大写字母折叠为小写,也常常会去掉后缀 + (如英语中的 ses)。这样,搜索时 + 无需繁琐地输入所有可能的变体,就能匹配同一个词的不同形式。此外,这一步通常 + 还会去除停用词,也就是那些过于常见、对搜索没有帮助的词。 + (简言之,词元是文档文本的原始片段,而词位则是被认为适合用于索引和搜索的词。) + PostgreSQL使用词典执行这一步。 + 系统提供了多种标准词典,也可以按需创建自定义词典。 + + + + + + 以适合搜索的形式存储预处理后的文档。例如,每个文档都可以表示为 + 由正规化词位组成的有序数组。除词位之外,通常还希望存储位置信息,以便用于 + 邻近排名;这样,查询词分布更密集的文档 + 会比查询词分散出现的文档得到更高的排名。 + + + + + + 词典允许对词元的正规化方式进行细粒度控制。借助合适的词典,你可以: + + + + + + 定义不应该被索引的停用词。 + + + + + + 使用 Ispell 把同义词映射为同一个词。 + + + + + + 使用分类词典把短语映射为同一个词。 + + + + + + 使用 Ispell 词典把一个词的不同变体映射到一种规范形式。 + + + + + + 使用 Snowball 词干分析规则把一个词的不同变体映射到一种规范形式。 + + + + + + 系统提供了数据类型 tsvector 用于存储预处理后的文档,也提供了 + 数据类型 tsquery 用于表示处理过的查询 + ()。围绕这两种数据类型还有许多函数和 + 操作符(),其中最重要的是匹配操作符 + @@,我们将在中介绍。 + 全文搜索还可以借助索引加速()。 + + + + + 什么是文档? + + + 文档 + 全文搜索 + + + + 文档是全文搜索系统中的搜索单位,例如一篇杂志文章或一封电子邮件。文本搜索引擎必须能够解析文档,并保存词位(关键字)与其所属文档之间的关联。随后,就可以利用这些关联来搜索包含查询词的文档。 + + + + 在 PostgreSQL 中进行搜索时,文档通常是数据库表某一行中的一个文本字段,或者是这类字段的组合(串接),这些字段可能分布在多个表里,也可能是动态取得的。换句话说,文档可以由不同部分拼接而成以供索引,而且未必会以一个整体存储在任何地方。例如: + + +SELECT title || ' ' || author || ' ' || abstract || ' ' || body AS document +FROM messages +WHERE mid = 12; + +SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document +FROM messages m, docs d +WHERE m.mid = d.did AND m.mid = 12; + + + + + + 实际上,这些示例查询中应使用 coalesce,以避免某个单独的 NULL 属性导致整个文档结果变成 NULL。 + + + + + 另一种做法是把文档存储为文件系统中的普通文本文件。在这种情况下,数据库可以用于保存全文索引并执行搜索,而文档本身则借助某个唯一标识符从文件系统中取回。不过,从数据库外部检索文件需要超级用户权限或特殊函数支持,因此这种方法通常不如把所有数据都保存在 PostgreSQL 内部方便。此外,把所有内容都放在数据库中,也便于访问文档元数据来辅助索引和展示。 + + + + 为了进行文本搜索,每个文档都必须被化简为预处理后的 tsvector 格式。搜索和排名完全基于文档的 tsvector 表示来执行 — 只有当文档被选中并准备展示给用户时,才需要取回原始文本。因此,我们常常把 tsvector 直接称作文档,但它当然只是完整文档的一种紧凑表示。 + + + + + 基本文本匹配 + + + 在PostgreSQL中,全文搜索基于匹配操作符@@。如果一个tsvector(文档)匹配一个tsquery(查询),它就返回true。哪一种数据类型写在前面并不重要: + + +SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery; + ?column? +---------- + t + +SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector; + ?column? +---------- + f + + + + + 正如上例所示,tsquery并不只是原始文本,tsvector也不是。tsquery包含搜索术语,这些术语必须已经是正规化后的词位,并且可以用 AND、OR、NOT 和 FOLLOWED BY 操作符把多个术语组合起来。(语法细节见。)to_tsqueryplainto_tsqueryphraseto_tsquery 有助于把用户输入的文本转换为合适的 tsquery,其主要工作就是对文本中的词做正规化。类似地,to_tsvector 用于解析并正规化文档字符串。因此在实践中,文本搜索匹配更像是这样: + + +SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat'); + ?column? +---------- + t + + + 请注意,如果写成 + + +SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat'); + ?column? +---------- + f + + + 就不会匹配成功,因为这里不会对单词 rats 做正规化。tsvector 的元素是词位,默认假定已经正规化,因此 rats 不会匹配 rat。 + + + + @@操作符也支持text输入,因此在简单场景下可以跳过把文本字符串显式转换为tsvectortsquery。可用的形式有: + + +tsvector @@ tsquery +tsquery @@ tsvector +text @@ tsquery +text @@ text + + + + + 前两种形式我们已经见过。text @@ tsquery 等价于 to_tsvector(x) @@ ytext @@ text 等价于 to_tsvector(x) @@ plainto_tsquery(y)。 + + + + 在tsquery中,&(AND)操作符指定它的两个参数都必须出现在文档中才表示匹配。类似地,|(OR)操作符指定至少一个参数必须出现,而!(NOT)操作符指定它的参数出现才能匹配。例如,查询fat & ! rat匹配包含fat但不包含rat的文档。 + + + + 借助<->(FOLLOWED BY)tsquery操作符,也可以搜索短语。只有当它的参数在文档中有相邻且顺序符合要求的匹配时,查询才算匹配。例如: + + +SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error'); + ?column? +---------- + t + +SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error'); + ?column? +---------- + f + + + FOLLOWED BY 操作符还有一个更一般的形式 <N>,其中 N 是表示匹配词位位置差的整数。<1><-> 相同,而 <2> 允许两个匹配之间恰好出现一个其他词位,依此类推。phraseto_tsquery 函数利用这种操作符构造 tsquery,从而在某些词是停用词时仍能匹配多词短语。例如: + + +SELECT phraseto_tsquery('cats ate rats'); + phraseto_tsquery +------------------------------- + 'cat' <-> 'ate' <-> 'rat' + +SELECT phraseto_tsquery('the cats ate the rats'); + phraseto_tsquery +------------------------------- + 'cat' <-> 'ate' <2> 'rat' + + + + + 有一个特殊情形有时很有用:<0> 可用于要求两个模式匹配同一个词。 + + + + 圆括号可以被用来控制tsquery操作符的嵌套。如果没有圆括号,|的计算优先级最低,然后从低到高依次是&<->!。 + + + + 值得注意的是,当 AND/OR/NOT 操作符位于 FOLLOWED BY 操作符的参数内部时,它们的语义与位于外部时会有细微差别,因为在 FOLLOWED BY 中,匹配的确切位置是有意义的。例如,通常 !x 只匹配完全不包含 x 的文档;但 !x <-> y 会在 y 前面没有紧邻一个 x 时匹配,文档其他位置出现的 x 并不会阻止匹配。再例如,x & y 通常只要求 xy 都在文档某处出现,而 (x & y) <-> z 则要求 xy 在同一位置匹配,并且紧挨在 z 之前。因此,这个查询的行为不同于 x <-> z & y <-> z,后者会匹配同时包含两个独立序列 x zy z 的文档。(按字面写出时,这个特定查询其实没有什么用,因为 xy 不可能在同一位置匹配;但在更复杂的场景中,例如前缀匹配模式,这种形式就可能有用。) + + + + + 配置 + + + 前述的都是简单的文本搜索示例。正如前面所提到的,全文搜索功能包括做更多事情的能力:跳过索引特定词(停用词)、处理同义词并使用更高级的解析,例如基于空白之外的解析。这个功能由文本搜索配置控制。PostgreSQL中有多种语言的预定义配置,并且你可以很容易地创建你自己的配置(psql\dF命令显示所有可用的配置)。 + + + + 在安装期间会选择一个合适的配置,并据此在postgresql.conf中设置。如果整个集簇都使用同一种文本搜索配置,你可以直接使用postgresql.conf中的这个值。若要在整个集簇中使用不同配置,但保证每个数据库内部使用同一种配置,可以使用ALTER DATABASE ... SET。否则,你也可以在每个会话中设置default_text_search_config。 + + + + 依赖一个配置的每一个文本搜索函数都有一个可选的regconfig参数,因此要使用的配置可以被显式指定。只有当这个参数被忽略时,default_text_search_config才被使用。 + + + + 为了让建立自定义文本搜索配置更容易,一个配置可以从更简单的数据库对象来建立。PostgreSQL的文本搜索功能提供了四类配置相关的数据库对象: + + + + + + 文本搜索解析器将文档拆分成词元并分类每个词元(例如,作为词或者数字)。 + + + + + + 文本搜索词典将词元转变成正规化的形式并拒绝停用词。 + + + + + + 文本搜索模板提供位于词典底层的函数(一个词典简单地指定一个模板和一组用于模板的参数)。 + + + + + + 文本搜索配置选择一个解析器和一组用于将解析器产生的词元正规化的词典。 + + + + + + 文本搜索解析器和模板是从低层 C 函数构建而来,因此它要求 C 编程能力来开发新的解析器和模板,并且还需要超级用户权限来把它们安装到一个数据库中(在PostgreSQL发布的contrib/区域中有一些附加的解析器和模板的示例)。由于词典和配置只是对底层解析器和模板的参数化和连接,不需要特殊的权限来创建一个新词典或配置。创建定制词典和配置的示例将在本章稍后的部分给出。 + + + + + + + + 表和索引 + + + 在前一节中的示例演示了使用简单常数字符串进行全文匹配。本节展示如何搜索表数据,以及可选择地使用索引。 + + + + 搜索表 + + + 即使没有索引,也可以执行全文搜索。下面这个简单查询会输出每一行的title,其中对应的body字段包含单词friend: + + +SELECT title +FROM pgweb +WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend'); + + + 这还会找到相关词,例如friendsfriendly,因为这些词都会被约简为同一个正规化词位。 + + + + 上述查询指定使用 english 配置来解析并正规化字符串。我们也可以省略配置参数: + + +SELECT title +FROM pgweb +WHERE to_tsvector(body) @@ to_tsquery('friend'); + + + 该查询将使用由 设置的配置。 + + + + 更复杂一点的例子,是选出最近的 10 个文档,它们的 titlebody 中同时包含 createtable: + + +SELECT title +FROM pgweb +WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table') +ORDER BY last_mod_date DESC +LIMIT 10; + + + 为简洁起见,这里省略了 coalesce 调用;如果希望在这两个字段之一为 NULL 时,另一字段仍能参与搜索,就需要加上它。 + + + + 虽然这些查询在没有索引的情况下也能工作,但除偶尔的临时搜索外,大多数应用都会觉得这种方式太慢。文本搜索在实际使用中通常都需要建立索引。 + + + + + + 创建索引 + + 我们可以创建一个GIN索引()来加速文本搜索: +CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', body)); +注意这里使用的是to_tsvector的双参数版本。只有显式指定配置名称的文本搜索函数,才能用于表达式索引()。这是因为索引内容必须不受的影响。否则,索引内容就可能不一致,因为不同的索引项可能包含tsvector,它们使用不同的文本搜索配置创建,而且无法判断各自使用了哪一种配置。这样的索引也不可能被正确地转储和恢复。 + + + 由于上面的索引使用了 to_tsvector 的双参数版本,因此只有同样使用相同配置名的双参数版 to_tsvector 查询,才能使用该索引。也就是说,WHERE to_tsvector('english', body) @@ 'a & b' 可以使用该索引,而 WHERE to_tsvector(body) @@ 'a & b' 则不能。这样可以保证索引只会和创建索引项时所用的同一配置配合使用。 + + + 还可以建立更复杂的表达式索引,其中配置名由另一个列指定,例如: +CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector(config_name, body)); +这里config_namepgweb表中的一个列。这样就允许在同一个索引中混合使用不同配置,同时记录每个索引项使用的是哪一种配置。例如,如果文档集合中包含不同语言的文档,这就会很有用。同样,打算使用该索引的查询也必须写成对应的形式,例如WHERE to_tsvector(config_name, body) @@ 'a & b'。 + + + 索引甚至可以串接多个列: +CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', title || ' ' || body)); + + + + 另一种方法是创建一个单独的tsvector列来保存to_tsvector的输出。下面的示例把titlebody串接起来,并用coalesce保证一个字段仍然可以被建立索引,即使另一个字段为NULL: + + +ALTER TABLE pgweb ADD COLUMN textsearchable_index_col tsvector; +UPDATE pgweb SET textsearchable_index_col = + to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')); +然后我们创建一个GIN索引来加速搜索: +CREATE INDEX textsearch_idx ON pgweb USING GIN (textsearchable_index_col); +现在可以执行快速全文检索了: +SELECT title +FROM pgweb +WHERE textsearchable_index_col @@ to_tsquery('create & table') +ORDER BY last_mod_date DESC +LIMIT 10; + + + + 当使用一个单独的列来存储 tsvector 表示时,需要创建一个触发器来使 tsvector 列保持最新,以应对 titlebody 的任何更改。 说明了如何做到这一点。 + + + 与表达式索引相比,单独列方法的一个优点是,为了利用索引,查询中不必显式指定文本搜索配置。正如上面的例子所示,查询可以依赖default_text_search_config。另一个优点是搜索会更快,因为它不必重新执行to_tsvector调用来验证索引匹配(使用 GiST 索引时这一点比使用 GIN 索引时更重要;见)。不过,表达式索引方法更容易设置,而且占用更少磁盘空间,因为tsvector表示并没有被显式存储。 + + + + + + + + 控制文本搜索 + + + 要实现全文搜索,必须有函数能够从文档创建 tsvector,并从用户查询创建 tsquery。此外,我们还希望结果能按有意义的顺序返回,因此还需要函数根据文档与查询的相关性进行比较。同样重要的,是把结果良好地展示出来。PostgreSQL为这些能力都提供了支持。 + + + + 解析文档 + + + PostgreSQL 提供了 to_tsvector 函数,用于把文档转换成 tsvector 数据类型。 + + + + to_tsvector + + + +to_tsvector( config regconfig, document text) returns tsvector + + + + to_tsvector 会把文本文档解析为词元,将词元归约为词位,并返回一个 tsvector,其中列出各词位及其在文档中的位置。文档会按指定的或默认的文本搜索配置进行处理。下面是一个简单示例: + + +SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats'); + to_tsvector +----------------------------------------------------- + 'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4 + + + + + 在上面的示例中可以看到,结果 tsvector 不包含 aonit,单词 rats 变成了 rat,标点符号 - 也被忽略了。 + + + + to_tsvector 在内部会调用解析器,把文档文本拆分成词元并为每类词元分配类型。对于每个词元,系统都会查询一个词典列表(),而这个列表会随词元类型而变化。第一个能够识别该词元的词典,会输出一个或多个正规化后的词位来表示它。例如,rats 之所以变成 rat,是因为某个词典识别出 ratsrat 的复数形式。某些词会被识别为停用词),于是被忽略,因为它们出现得过于频繁,对搜索没有帮助。在这个示例中,aonit 就是停用词。如果列表中的词典都无法识别某个词元,它也会被忽略。示例里的标点符号 - 就属于这种情况,因为其词元类型(Space symbols)实际上没有分配任何词典,也就是说,空白类词元永远不会被索引。解析器、词典以及需要索引哪些词元类型,都由所选的文本搜索配置()决定。一个数据库里可以同时存在多种不同配置,并且系统已经为多种语言提供了预定义配置。在本例中,我们使用的是英语的默认配置 english。 + + + + setweight 函数可用于给 tsvector 中的项打上指定的权重标签,权重可以是四个字母之一:ABCD。这通常用于标记来自文档不同部分的项,例如标题和正文。稍后,这些信息可以用于搜索结果排名。 + + + + 由于 to_tsvector(NULL) 会返回 NULL,因此只要字段可能为空,就建议使用 coalesce。下面是从结构化文档创建 tsvector 的推荐方法: + + +UPDATE tt SET ti = + setweight(to_tsvector(coalesce(title,'')), 'A') || + setweight(to_tsvector(coalesce(keyword,'')), 'B') || + setweight(to_tsvector(coalesce(abstract,'')), 'C') || + setweight(to_tsvector(coalesce(body,'')), 'D'); + + + 这里我们使用 setweight 为最终 tsvector 中每个词位标注来源,然后再用 tsvector 连接操作符 || 把这些已标注的 tsvector 值合并起来(关于这些操作的细节,见 )。 + + + + + + 解析查询 + + + PostgreSQL 提供了 to_tsqueryplainto_tsquery以及 phraseto_tsquery,用于把查询转换成 tsquery 数据类型。to_tsquery 提供的特性比 plainto_tsqueryphraseto_tsquery 更丰富,但对输入也更严格。 + + + + to_tsquery + + + +to_tsquery( config regconfig, querytext text) returns tsquery + + + + to_tsquery创建一个tsquery值,其来源为querytext,其中必须是由以下 tsquery 操作符分隔的单个词元:&(AND)、|(OR)、!(NOT)以及 <->(FOLLOWED BY),也可以使用括号分组。换句话说,to_tsquery 的输入必须已经遵循 tsquery 输入的一般规则,如 所述。区别在于,基本的 tsquery 输入会直接使用词元,而 to_tsquery 会使用指定或默认的配置将每个词元正规化为词位,并丢弃根据该配置判定为停用词的词元。例如: +SELECT to_tsquery('english', 'The & Fat & Rats'); + to_tsquery +--------------- + 'fat' & 'rat' +与基本的 tsquery 输入一样,可以给每个词位附加权重,以限制它只匹配 tsvector 中具有这些权重的词位。例如: +SELECT to_tsquery('english', 'Fat | Rats:AB'); + to_tsquery +------------------ + 'fat' | 'rat':AB +此外,可以把 * 附加到词位上来指定前缀匹配: +SELECT to_tsquery('supern:*A & star:A*B'); + to_tsquery +-------------------------- + 'supern':*A & 'star':*AB +这样的词位将匹配 tsvector 中以给定字符串开头的任何单词。 + + + to_tsquery也可以接受单引号括起来的短语。当配置中包含可能在这类短语上触发的分类词典时,这一点尤其有用。在下面的例子中,一个分类词典包含规则 supernovae + stars : sn: + + +SELECT to_tsquery('''supernovae stars'' & !crab'); + to_tsquery +--------------- + 'sn' & !'crab' + + + 如果不加引号,对于那些未被 AND、OR 或 FOLLOWED BY 操作符分隔的词元,to_tsquery会产生语法错误。 + + + + plainto_tsquery + + + +plainto_tsquery( config regconfig, querytext text) returns tsquery + + + + plainto_tsquery把未格式化的文本querytext转换成一个tsquery值。该文本会像to_tsvector那样被解析并正规化,然后在保留下来的词之间插入&(AND)tsquery操作符。 + + + 示例: +SELECT plainto_tsquery('english', 'The Fat Rats'); + plainto_tsquery +----------------- + 'fat' & 'rat' +注意,plainto_tsquery不会识别其输入中的tsquery操作符、权重标签或前缀匹配标签: +SELECT plainto_tsquery('english', 'The Fat & Rats:C'); + plainto_tsquery +--------------------- + 'fat' & 'rat' & 'c' +这里,输入中的所有标点符号都被当作空白符丢弃。 + + + phraseto_tsquery + + + +phraseto_tsquery( config regconfig, querytext text) returns tsquery + + + + phraseto_tsquery的行为很像plainto_tsquery,不过它会在保留下来的词之间插入<->(FOLLOWED BY)操作符,而不是&(AND)操作符。此外,停用词也不是简单地丢弃,而是通过插入<N>操作符(而不是<->操作符)来体现。在搜索精确词位序列时,这个函数很有用,因为 FOLLOWED BY 操作符不仅检查所有词位是否存在,还检查词位的顺序。 + + + 示例: +SELECT phraseto_tsquery('english', 'The Fat Rats'); + phraseto_tsquery +------------------ + 'fat' <-> 'rat' +plainto_tsquery一样,phraseto_tsquery函数也不会识别其输入中的tsquery操作符、权重标签或前缀匹配标签: +SELECT phraseto_tsquery('english', 'The Fat & Rats:C'); + phraseto_tsquery +----------------------------- + 'fat' <-> 'rat' <-> 'c' + + + + + + + 搜索结果排名 + + + 排名旨在衡量文档与特定查询的相关程度,以便在匹配很多时优先显示最相关的结果。PostgreSQL提供了两种预定义的排名函数,它们会综合考虑词法信息、邻近关系和结构信息;也就是说,会考虑查询词在文档中出现的频率、这些词彼此之间的距离,以及它们出现于文档中哪个部分。不过,相关性这一概念本身就比较模糊,而且高度依赖具体应用。不同应用可能还需要额外信息参与排名,例如文档修改时间。内置排名函数仅仅是示例。你可以编写自己的排名函数,或者把它们的结果与其他因素结合起来,以满足特定需求。 + + + 目前可用的两种排名函数是: + + + + + + ts_rank + + + ts_rank( weights float4[], vector tsvector, query tsquery , normalization integer ) returns float4 + + + + + 根据向量中匹配词位的频率对向量进行排名。 + + + + + + + + + ts_rank_cd + + + ts_rank_cd( weights float4[], vector tsvector, query tsquery , normalization integer ) returns float4 + + + + + 该函数为给定的文档向量和查询计算覆盖密度排名。该方法见 Clarke、Cormack 和 Tudhope 于 1999 年发表于期刊 Information Processing and Management 的文章 Relevance Ranking for One to Three Term Queries。覆盖密度排名与 ts_rank 类似,但还会考虑匹配词位彼此之间的接近程度。 + + + + 该函数需要词位位置信息才能完成计算。因此,它会忽略 tsvector 中任何被剥离的词位。如果输入中根本没有未剥离的词位,结果就会是零(有关 strip 函数以及 tsvector 中位置信息的更多内容,见 )。 + + + + + + + + + 对这两个函数来说,可选的weights参数允许根据词实例的标注情况赋予它们不同权重。权重数组按如下顺序指定各类词的权重: +{D-weight, C-weight, B-weight, A-weight} +如果没有提供weights,则使用如下默认值: +{0.1, 0.2, 0.4, 1.0} +通常,权重用于标注文档中特殊部分的词,例如标题或开头的摘要,从而使它们相对于正文中的词具有更高或更低的重要性。 + + + 由于较长的文档更有机会包含查询词,因此把文档大小纳入考量是合理的。例如,一个一百词的文档里某个搜索词出现五次,通常会比一个一千词的文档里同一搜索词也只出现五次更相关。两种排名函数都接受一个整数 normalization 选项,用于指定文档长度是否影响排名,以及具体如何影响。该整数选项控制多种行为,因此它是一个位掩码:你可以使用 | 指定一种或多种行为(例如 2|4)。 + + + + + 0(默认值)忽略文档长度 + + + + + 1 用 1 + 文档长度的对数除排名 + + + + + 2 用文档长度除排名 + + + + + 4 用匹配范围之间的平均调和距离除排名(仅由 ts_rank_cd 实现) + + + + + 8 用文档中唯一词的数量除排名 + + + + + 16 用 1 + 文档中唯一词数量的对数除排名 + + + + + 32 用排名 + 1 除排名 + + + + + 如果指定了多个标志位,这些变换将按上面列出的顺序依次应用。 + + + + 需要注意的是,排名函数不会使用任何全局信息,因此不可能像有时人们希望的那样,给出一种能够公平归一化到 1% 或 100% 的结果。正规化选项 32(rank/(rank+1))可以把所有排名缩放到 0 到 1 的范围内,但这当然只是表面上的变化,不会影响搜索结果的顺序。 + + + + 下面是只选择排名最高的十个匹配的示例: + + +SELECT title, ts_rank_cd(textsearch, query) AS rank +FROM apod, to_tsquery('neutrino|(dark & matter)') query +WHERE query @@ textsearch +ORDER BY rank DESC +LIMIT 10; + title | rank +-----------------------------------------------+---------- + Neutrinos in the Sun | 3.1 + The Sudbury Neutrino Detector | 2.4 + A MACHO View of Galactic Dark Matter | 2.01317 + Hot Gas and Dark Matter | 1.91171 + The Virgo Cluster: Hot Plasma and Dark Matter | 1.90953 + Rafting for Solar Neutrinos | 1.9 + NGC 4650A: Strange Galaxy and Dark Matter | 1.85774 + Hot Gas and Dark Matter | 1.6123 + Ice Fishing for Cosmic Neutrinos | 1.6 + Weak Lensing Distorts the Universe | 0.818218 + + + 下面是使用归一化排名的同一示例: + + +SELECT title, ts_rank_cd(textsearch, query, 32 /* rank/(rank+1) */ ) AS rank +FROM apod, to_tsquery('neutrino|(dark & matter)') query +WHERE query @@ textsearch +ORDER BY rank DESC +LIMIT 10; + title | rank +-----------------------------------------------+------------------- + Neutrinos in the Sun | 0.756097569485493 + The Sudbury Neutrino Detector | 0.705882361190954 + A MACHO View of Galactic Dark Matter | 0.668123210574724 + Hot Gas and Dark Matter | 0.65655958650282 + The Virgo Cluster: Hot Plasma and Dark Matter | 0.656301290640973 + Rafting for Solar Neutrinos | 0.655172410958162 + NGC 4650A: Strange Galaxy and Dark Matter | 0.650072921219637 + Hot Gas and Dark Matter | 0.617195790024749 + Ice Fishing for Cosmic Neutrinos | 0.615384618911517 + Weak Lensing Distorts the Universe | 0.450010798361481 + + + + + 排名计算可能非常昂贵,因为它需要访问每个匹配文档的 tsvector,这往往会受 I/O 限制而变慢。不幸的是,这几乎无法避免,因为实际查询常常会产生大量匹配。 + + + + + + 高亮结果 + + + 在呈现搜索结果时,理想的方式是展示每个文档的一段摘录,并说明它与查询的关系。通常,搜索引擎会在文档片段中标出搜索词。PostgreSQL 提供了 ts_headline 函数来实现这一功能。 + + + + ts_headline + + + +ts_headline( config regconfig, document text, query tsquery , options text ) returns text + + + + ts_headline 接收文档和查询,并返回文档中一段 + 高亮查询词条的摘录。具体而言,该函数会先用查询选择相关文本片段,然后 + 高亮查询中出现的所有词,即使这些词的位置并不满足查询本身的位置限制。 + 用于解析文档的配置可通过 config 指定; + 若省略 config,则使用 + default_text_search_config 配置。 + + + 如果指定了options字符串,它必须由一个或多个用逗号分隔的option=value对组成。可用选项有: + + + MaxWordsMinWords(整数):这两个数值决定输出摘要的最长和最短长度。默认值分别为 35 和 15。 + + + + + ShortWord(整数):长度不超过该值的词,如果不是查询词,就会从摘要的开头和结尾处被丢弃。默认值 3 会去掉常见的英语冠词。 + + + + + HighlightAll(布尔值):如果为 true,则整个文档都会被用作摘要,而忽略前面三个参数。默认值是 false。 + + + + + MaxFragments(整数):要显示的最大文本片段数。默认值 0 选择一种非基于片段的摘要生成方法。大于 0 的值选择基于片段的摘要生成方法(见下文)。 + + + + + StartSelStopSel(字符串): + 用于界定文档中查询词的字符串,以便与摘录中的其他词区分。默认值分别为 + <b> 和 + </b>,可以用于 HTML 输出。 + + + + + FragmentDelimiter(字符串):当显示多个片段时,片段之间将以该字符串分隔。默认值是 ... 。 + + + 这些选项名不区分大小写。如果字符串值包含空格或逗号,则必须用双引号括起来。 + + + 在非片段模式下,ts_headline 会为给定的 query 定位匹配,并从中选择一处显示,优先选择在允许摘要长度内包含更多查询词的匹配。在基于片段的摘要生成模式下,ts_headline 会定位查询匹配,并把每个匹配切分成多个不超过 MaxWords 个词的片段,优先选择包含更多查询词的片段,并在可能时把片段扩展到周围词语。因此,当查询匹配跨越文档中较大区域,或希望显示多个匹配时,片段模式会更有用。无论哪种模式,如果无法识别出查询匹配,都会显示由文档前 MinWords 个词组成的单个片段。 + + + + 例如: + + +SELECT ts_headline('english', + 'The most common type of search +is to find all documents containing given query terms +and return them in order of their similarity to the +query.', + to_tsquery('english', 'query & similarity')); + ts_headline +------------------------------------------------------------ + containing given <b>query</b> terms + + and return them in order of their <b>similarity</b> to the+ + <b>query</b>. + +SELECT ts_headline('english', + 'Search terms may occur +many times in a document, +requiring ranking of the search matches to decide which +occurrences to display in the result.', + to_tsquery('english', 'search & term'), + 'MaxFragments=10, MaxWords=7, MinWords=3, StartSel=<<, StopSel=>>'); + ts_headline +------------------------------------------------------------ + <<Search>> <<terms>> may occur + + many times ... ranking of the <<search>> matches to decide + + + + + ts_headline 使用的是原始文档,而不是 tsvector 摘要,因此它可能较慢,使用时应当谨慎。 + + + + + + + + 附加特性 + + + 本节介绍一些在文本搜索中很有用的附加函数和操作符。 + + + + 操纵文档 + + + 展示了原始文本文档如何被转换成 tsvector 值。PostgreSQL 也提供了用于操纵已经处于 tsvector 形式文档的函数和操作符。 + + + + + + + + + tsvector 连接 + + + tsvector || tsvector + + + + + tsvector 连接操作符返回一个向量,它结合了两个参数向量中的词位和位置信息。位置和权重标签在连接过程中会被保留。右侧向量中的位置会按左侧向量中出现的最大位置做偏移,因此结果几乎等同于对两个原始文档字符串连接后的结果执行 to_tsvector。(这种等价并不完全成立,因为从左侧参数末尾移除的停用词不会影响结果;而如果直接做文本连接,它们本来会影响右侧参数中词位的位置。) + + + + 相比先连接文本再应用 to_tsvector,直接连接向量的一个优点是,你可以用不同配置来解析文档的不同部分。此外,由于 setweight 会以相同方式标记给定向量中的全部词位,如果希望给文档不同部分打上不同权重,就必须先解析文本并调用 setweight,然后再做连接。 + + + + + + + + + setweight + + + setweight(vector tsvector, weight "char") returns tsvector + + + + + setweight 返回输入向量的一个副本,其中每个位置都被标注为给定的 weightABCDD 是新向量的默认值,因此不会在输出中显示)。这些标签在向量连接时会被保留,从而允许排名函数对来自文档不同部分的词赋予不同权重。 + + + + 注意权重标签是应用到位置而不是词位。如果输入向量已经被剥离了位置,则setweight什么也不会做。 + + + + + + + + length(tsvector) + + + length(vector tsvector) returns integer + + + + + 返回存储在向量中的词位数。 + + + + + + + + + strip + + + strip(vector tsvector) returns tsvector + + + + + 返回一个向量,其中列出与给定向量相同的词位,但不包含任何位置或权重信息。其结果通常比未剥离的向量小得多,但也没那么有用。相关度排名在已剥离向量上的效果不如未剥离向量。此外,<->(FOLLOWED BY)tsquery 操作符永远不会匹配已剥离的输入,因为它无法确定词位出现之间的距离。 + + + + + + + + + 中有tsvector相关函数的完整列表。 + + + + + + 操纵查询 + + + 展示了原始文本查询如何被转换成 tsquery 值。PostgreSQL 也提供了用于操纵已经处于 tsquery 形式查询的函数和操作符。 + + + + + + + + tsquery && tsquery + + + + + 返回用 AND 结合的两个给定查询。 + + + + + + + + + tsquery || tsquery + + + + + 返回用 OR 结合的两个给定查询。 + + + + + + + + + !! tsquery + + + + + 返回给定查询的否定(NOT)。 + + + + + + + + + tsquery <-> tsquery + + + + + 返回一个查询,它使用 <->(FOLLOWED BY)tsquery 操作符,搜索第一个给定查询的匹配后紧跟着第二个给定查询的匹配。例如: + + +SELECT to_tsquery('fat') <-> to_tsquery('cat | rat'); + ?column? +---------------------------- + 'fat' <-> ( 'cat' | 'rat' ) + + + + + + + + + + + tsquery_phrase + + + tsquery_phrase(query1 tsquery, query2 tsquery [, distance integer ]) returns tsquery + + + + + 返回一个查询,它使用 <N> tsquery 操作符,搜索第一个给定查询的匹配,并在距离正好为 distance 个词位处搜索第二个给定查询的匹配。例如: + + +SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10); + tsquery_phrase +------------------ + 'fat' <10> 'cat' + + + + + + + + + + + numnode + + + numnode(query tsquery) returns integer + + + + + 返回tsquery中的节点数(词位加操作符)。这个函数可用于判断query是否有意义(返回值 > 0),或者是否只包含停用词(返回 0)。例如: + + +SELECT numnode(plainto_tsquery('the any')); +NOTICE: query contains only stopword(s) or doesn't contain lexeme(s), ignored + numnode +--------- + 0 + +SELECT numnode('foo & bar'::tsquery); + numnode +--------- + 3 + + + + + + + + + + querytree + + + querytree(query tsquery) returns text + + + + 返回一个tsquery中可用于搜索索引的部分。此函数可用于检测无法使用索引的查询,例如只包含停用词或只包含否定词项的查询。例如: +SELECT querytree(to_tsquery('!defined')); + querytree +----------- + + + + + + + + + + 查询重写 + + + ts_rewrite + + + + ts_rewrite 函数族会在给定的 tsquery 中搜索目标子查询的出现,并把每一次出现都替换为替换子查询。本质上,这就是面向 tsquery 的一种子串替换。目标与替换的组合可以看作一条查询重写规则。一组这样的重写规则可以成为非常强大的搜索辅助工具。例如,你可以利用同义词扩展搜索(如 new yorkbig applenycgotham),或者收窄搜索,把用户引向某个热门主题。该特性与分类词典()在功能上有一定重叠。不过,重写规则可以随时修改而无需重建索引,而更新分类词典则必须重建索引后才能生效。 + + + + + + + + ts_rewrite (query tsquery, target tsquery, substitute tsquery) returns tsquery + + + + 这种形式的ts_rewrite 只应用一条重写规则:target 会被替换成 substitute,替换范围是整个 query。例如: +SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); + ts_rewrite +------------ + 'b' & 'c' + + + + + + + + + ts_rewrite (query tsquery, select text) returns tsquery + + + + 这种形式的ts_rewrite接受一个起始query和一个 SQLselect命令,该命令以文本字符串给出。该select必须产生两列tsquery类型的值。对于select结果中的每一行,第一列值(目标)的各次出现都会被第二列值(替换)取代,替换范围为当前query值。例如: +CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery); +INSERT INTO aliases VALUES('a', 'c'); + +SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases'); + ts_rewrite +------------ + 'b' & 'c' + + + + + 注意,当以这种方式应用多个重写规则时,应用顺序可能很重要;因此在实际使用中,你通常会要求源查询按某个排序键 ORDER BY。 + + + + + + + + 我们来看一个现实中的天文示例。我们将使用表驱动的重写规则来扩展查询 supernovae: + + +CREATE TABLE aliases (t tsquery primary key, s tsquery); +INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn')); + +SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); + ts_rewrite +--------------------------------- + 'crab' & ( 'supernova' | 'sn' ) + + + 我们只需更新表,就可以修改这些重写规则: + + +UPDATE aliases +SET s = to_tsquery('supernovae|sn & !nebulae') +WHERE t = to_tsquery('supernovae'); + +SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); + ts_rewrite +--------------------------------------------- + 'crab' & ( 'supernova' | 'sn' & !'nebula' ) + + + + + 当重写规则很多时,重写过程可能会很慢,因为它需要检查每一条规则是否可能匹配。为了过滤掉显然不可能命中的规则,可以使用 tsquery 类型的包含操作符。在下面的例子中,我们只选择那些可能匹配原始查询的规则: + + +SELECT ts_rewrite('a & b'::tsquery, + 'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t'); + ts_rewrite +------------ + 'b' & 'c' + + + + + + + + + 用于自动更新的触发器 + + + 触发器 + 用于更新一个派生的 tsvector 列 + + + + 当使用单独一列来存储文档的 tsvector 表示时,就需要创建触发器,以便在文档内容列发生变化时更新该 tsvector 列。系统为此提供了两个内置触发器函数,你也可以自行编写触发器函数。 + + + +tsvector_update_trigger(tsvector_column_name, config_name, text_column_name , ... ) +tsvector_update_trigger_column(tsvector_column_name, config_column_name, text_column_name , ... ) + + + 这些触发器函数会自动计算一个tsvector列,其值来自一个或多个文本列,并受以下命令中所指定参数的控制:CREATE TRIGGER。下面是一个用法示例: +CREATE TABLE messages ( + title text, + body text, + tsv tsvector +); + +CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE +ON messages FOR EACH ROW EXECUTE PROCEDURE +tsvector_update_trigger(tsv, 'pg_catalog.english', title, body); + +INSERT INTO messages VALUES('title here', 'the body text is here'); + +SELECT * FROM messages; + title | body | tsv +------------+-----------------------+---------------------------- + title here | the body text is here | 'bodi':4 'text':5 'titl':1 + +SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title & body'); + title | body +------------+----------------------- + title here | the body text is here +创建这个触发器后,titlebody中的任何更改都会自动反映到tsv中,应用无需为此操心。 + + + 第一个触发器参数必须是要更新的 tsvector 列名。第二个参数指定执行转换时要使用的文本搜索配置。对于 tsvector_update_trigger,配置名直接作为第二个触发器参数给出。如上所示,它必须带模式限定,这样触发器行为就不会随着 search_path 的变化而变化。对于 tsvector_update_trigger_column,第二个触发器参数则是另一个表列的名称,该列必须是 regconfig 类型。这样就可以按行选择配置。其余参数是文本列的名称(类型为 textvarcharchar),它们会按给定顺序并入文档。NULL 值会被跳过(但其他列仍会被索引)。 + + + 这些内置触发器有一个限制,即它们会一视同仁地处理所有输入列。要对不同列采用不同处理方式 — 例如,给标题赋予与正文不同的权重 — 就需要编写自定义触发器。下面是一个使用PL/pgSQL作为触发器语言的示例: +CREATE FUNCTION messages_trigger() RETURNS trigger AS $$ +begin + new.tsv := + setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') || + setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D'); + return new; +end +$$ LANGUAGE plpgsql; + +CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE + ON messages FOR EACH ROW EXECUTE PROCEDURE messages_trigger(); + + + + + 请记住,在触发器中创建 tsvector 值时,明确指定配置名称非常重要, + 这样列的内容才不会受到 default_text_search_config 变化的影响。 + 否则,很可能会导致问题,例如在转储并恢复之后搜索结果发生变化。 + + + + + + 收集文档统计数据 + + + ts_stat + + + + ts_stat 可用于检查配置,并寻找候选停用词。 + + + +ts_stat(sqlquery text, weights text, + OUT word text, OUT ndoc integer, + OUT nentry integer) returns setof record + + + + sqlquery 是一个文本值,其中包含一条必须返回单一 tsvector 列的 SQL 查询。ts_stat 会执行该查询,并返回该 tsvector 数据中每个不同词位(单词)的统计信息。返回的列如下: + + + + + word text — 一个词位的值 + + + + + ndoc integer — 词出现过的文档(tsvector)的数量 + + + + + nentry integer — 词出现的总次数 + + + + + 如果提供了 weights,则只统计具有这些权重之一的出现。 + + + + 例如,要在一个文档集合中查找十个最频繁的词: + + +SELECT * FROM ts_stat('SELECT vector FROM apod') +ORDER BY nentry DESC, ndoc DESC, word +LIMIT 10; + + + 同样的查询,但只统计权重为 AB 的出现次数: + + +SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab') +ORDER BY nentry DESC, ndoc DESC, word +LIMIT 10; + + + + + + + + + 解析器 + + + 文本搜索解析器负责把未处理的文档文本划分成词元并标识每个词元的类型,而可能的类型集合由解析器本身定义。注意,解析器完全不会修改文本 — 它只是识别看似合理的词边界。由于作用范围有限,相比自定义词典,对应用相关的自定义解析器的需求没有那么强烈。目前PostgreSQL只提供一种内置解析器,而它已经被证明对广泛的应用都很有用。 + + + + 内置解析器被称为pg_catalog.default。它识别 23 种词元类型,如所示。 + + + + 默认解析器的词元类型 + + + + 别名 + 描述 + 示例 + + + + + asciiword + 单词,所有 ASCII 字母 + elephant + + + word + 单词,所有字母 + mañana + + + numword + 单词,字母和数字 + beta1 + + + asciihword + 带连字符的单词,所有 ASCII + up-to-date + + + hword + 带连字符的单词,所有字母 + lógico-matemática + + + numhword + 带连字符的单词,字母和数字 + postgresql-beta1 + + + hword_asciipart + 带连字符的单词部分,所有 ASCII + postgresql-beta1 上下文中的 postgresql + + + hword_part + 带连字符的单词部分,所有字母 + lógico-matemática 上下文中的 lógicomatemática + + + hword_numpart + 带连字符的单词部分,字母和数字 + postgresql-beta1 上下文中的 beta1 + + + email + 电子邮件地址 + foo@example.com + + + protocol + 协议头部 + http:// + + + url + URL + example.com/stuff/index.html + + + host + 主机 + example.com + + + url_path + URL 路径 + /stuff/index.html,在 URL 上下文中 + + + file + 文件或路径名 + /usr/local/foo.txt,如果不在 URL 中 + + + sfloat + 科学记数法 + -1.234e56 + + + float + 十进制记数法 + -1.234 + + + int + 有符号整数 + -1234 + + + uint + 无符号整数 + 1234 + + + version + 版本号 + 8.3.0 + + + tag + XML 标签 + <a href="dictionaries.html"> + + + entity + XML 实体 + &amp; + + + blank + 空格符号 + (其他不识别的任意空白或标点符号) + + + +
    + + + + 解析器的一个字母的概念由数据库的区域设置决定,具体是lc_ctype。只包含基本 ASCII 字母的词被报告为一个单独的词元类型,因为有时可以用来区别它们。在大部分欧洲语言中,词元类型wordasciiword应该被同样对待。 + + + email 不支持 RFC 5322 定义的所有有效电子邮件字符。具体来说,电子邮件用户名中支持的非字母数字字符只有句点、短横线和下划线。 + + + + 解析器有可能从同一段文本中产生重叠的词元。举例来说,一个带连字符的词既会被报告为整个单词,也会被报告为各个组成部分: + + +SELECT alias, description, token FROM ts_debug('foo-bar-beta1'); + alias | description | token +-----------------+------------------------------------------+--------------- + numhword | Hyphenated word, letters and digits | foo-bar-beta1 + hword_asciipart | Hyphenated word part, all ASCII | foo + blank | Space symbols | - + hword_asciipart | Hyphenated word part, all ASCII | bar + blank | Space symbols | - + hword_numpart | Hyphenated word part, letters and digits | beta1 + + + 这种行为是可取的,因为它既允许针对整个复合词搜索,也允许针对各个组成部分搜索。下面是另一个有启发性的示例: + + +SELECT alias, description, token FROM ts_debug('http://example.com/stuff/index.html'); + alias | description | token +----------+---------------+------------------------------ + protocol | Protocol head | http:// + url | URL | example.com/stuff/index.html + host | Host | example.com + url_path | URL path | /stuff/index.html + + + +
    + + + 词典 + + + 词典用于消除不应参与搜索的词(stop words),并用于对词进行正规化,以便同一个词的不同派生形式能够匹配。成功完成正规化的词被称为词位。除了改善搜索质量,正规化和移除停用词还会减小文档的tsvector表示,从而提高性能。正规化并不总是具有语言学意义,而且通常依赖于应用的语义。 + + + 一些正规化的示例: + + + + 语言学上的例子 — Ispell 词典尝试把输入词约简为一种正规化形式;词干分析器词典则去掉词尾 + + + + + URL地址可以被正规化,以便让等价的 URL 匹配: + + + + + http://www.pgsql.ru/db/mw/index.html + + + + + http://www.pgsql.ru/db/mw/ + + + + + http://www.pgsql.ru/db/../db/mw/index.html + + + + + + + + 颜色名可以被它们的十六进制值替换,例如red, green, blue, magenta -> FF0000, 00FF00, 0000FF, FF00FF + + + + + 如果要索引数字,我们可以去掉某些小数位,以缩小可能数值的范围。因此,如果只保留小数点后两位,那么例如 3.14159265359、3.1415926 和 3.14 在正规化后就会变成相同的值。 + + + + + + + + 一个词典是一个程序,它接受一个词元作为输入,并返回: + + + + 如果输入的词元对词典是已知的,则返回一个词位数组(注意一个词元可能产生多于一个词位) + + + + + 一个带有 TSL_FILTER 标志的单个词位,它会用一个新词元替换原始词元,并把它传递给后续词典(执行这种工作的词典称为过滤字典) + + + + + 如果字典知道该词元但它是一个停用词,则返回一个空数组 + + + + + 如果字典不识别该输入词元,则返回NULL + + + + + + + PostgreSQL为许多语言提供了预定义的字典。也有多种预定义模板可以被用于创建带自定义参数的新词典。每一种预定义词典模板在下面描述。如果没有合适的现有模板,可以创建新的;示例见PostgreSQL发布的contrib/区域。 + + + + 文本搜索配置把一个解析器与一组用于处理解析器输出词元的词典绑定在一起。对于解析器可能返回的每一种词元类型,配置都会指定一个单独的词典列表。当解析器找到该类型的词元时,会按顺序依次查询列表中的每个词典,直到有某个词典把它识别为已知词。如果它被识别为停用词,或者没有任何词典识别它,那么该词元就会被丢弃,既不会建立索引,也不会参与搜索。通常,第一个返回非 NULL 输出的词典就决定结果,后续词典不会再被查询;但过滤词典可以把给定单词替换为一个修改后的单词,再传递给后续词典。 + + + + 配置词典列表的一般规则是,把最窄、最专门的词典放在最前面,然后是更通用的词典,最后以一个非常通用的词典收尾,例如 Snowball 词干分析器,或能识别所有内容的 simple。例如,对于一个天文学相关搜索(配置名为 astro_en),可以把词元类型 asciiword(ASCII 词)绑定到一个天文学术语分类词典、一个通用英语词典,以及一个 Snowball 英语词干分析器: + + +ALTER TEXT SEARCH CONFIGURATION astro_en + ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem; + + + + + 过滤词典可以放在列表中的任何位置,只是不能放在最后,因为放在最后就没有意义了。过滤词典可用于先对词做部分正规化,以简化后续词典的工作。例如,可以用过滤词典去掉带重音字母中的重音符号,就像模块所做的那样。 + + + + 停用词 + + + 停用词是非常常见、几乎出现在每个文档中的词,没有区分价值。因此,在全文搜索中可以忽略它们。例如,每篇英文文本都包含像 athe 这样的词,所以把它们存储在索引中没有用处。不过,停用词确实会影响 tsvector 中的位置,而这又会影响排名: + + +SELECT to_tsvector('english', 'in the list of stop words'); + to_tsvector +---------------------------- + 'list':3 'stop':5 'word':6 + + + 缺失的位置 1、2、4 就是由停用词造成的。对包含停用词和不包含停用词的文档,计算出来的排名会明显不同: + + +SELECT ts_rank_cd (to_tsvector('english', 'in the list of stop words'), to_tsquery('list & stop')); + ts_rank_cd +------------ + 0.05 + +SELECT ts_rank_cd (to_tsvector('english', 'list stop words'), to_tsquery('list & stop')); + ts_rank_cd +------------ + 0.1 + + + + + + 如何处理停用词取决于具体词典。例如,ispell词典会先正规化单词,再查看停用词列表,而Snowball词干分析器则先检查停用词列表。这种不同行为是为了尽量减少噪声。 + + + + + + 简单词典 + + + simple 词典模板的工作方式是先把输入词元转换为小写,再根据停用词文件检查它。如果在文件中找到该词元,就返回一个空数组,从而丢弃该词元;否则,返回其小写形式作为正规化后的词位。或者,也可以把该词典配置为把非停用词报告为未识别,从而允许它们传递给列表中的下一个词典。 + + + + 下面是一个使用 simple 模板的词典定义示例: + + +CREATE TEXT SEARCH DICTIONARY public.simple_dict ( + TEMPLATE = pg_catalog.simple, + STOPWORDS = english +); + + + 其中,english 是停用词文件的基名。文件完整名称将是 $SHAREDIR/tsearch_data/english.stop,其中 $SHAREDIR 表示 PostgreSQL 安装的共享数据目录,通常是 /usr/local/share/postgresql(如果不确定,可用 pg_config --sharedir 来确定)。文件格式只是一个单词列表,每行一个单词。空行和行尾空格会被忽略,大写会折叠为小写,但不会对文件内容做其他处理。 + + + + 现在我们可以测试这个词典: + + +SELECT ts_lexize('public.simple_dict', 'YeS'); + ts_lexize +----------- + {yes} + +SELECT ts_lexize('public.simple_dict', 'The'); + ts_lexize +----------- + {} + + + + + 如果在停用词文件中找不到该词,我们也可以选择返回 NULL,而不是返回它的小写形式。这种行为可以通过把词典的 Accept 参数设置为 false 来启用。继续上面的例子: + + +ALTER TEXT SEARCH DICTIONARY public.simple_dict ( Accept = false ); + +SELECT ts_lexize('public.simple_dict', 'YeS'); + ts_lexize +----------- + + +SELECT ts_lexize('public.simple_dict', 'The'); + ts_lexize +----------- + {} + + + + + 在默认设置 Accept = true 下,只有把 simple 词典放在词典列表末尾才有意义,因为它不会把任何词元传递给后续词典。相反,Accept = false 只有在后面至少还有一个词典时才有用。 + + + + + 大多数词典类型都依赖配置文件,例如停用词文件。这些文件必须采用 UTF-8 编码保存。当它们被读入服务器时,如果数据库编码不同,就会被转换为实际的数据库编码。 + + + + + + 通常,词典配置文件在某个数据库会话中第一次被使用时,只会被读取一次。如果你修改了配置文件,并希望强制现有会话加载新内容,可以对该词典执行一条 ALTER TEXT SEARCH DICTIONARY 命令。这可以是一条更新,也就是并不实际修改任何参数值的更新。 + + + + + + + 同义词词典 + + + 这个词典模板用于创建把一个单词替换为同义词的词典。不支持短语(对此请使用分类词典模板 )。同义词词典可以用来克服语言学上的问题,例如防止英语词干分析词典把 Paris 约简成 pari。只要在同义词词典中加入一行 Paris paris,并把该词典放在 english_stem 词典之前即可。例如: + + +SELECT * FROM ts_debug('english', 'Paris'); + alias | description | token | dictionaries | dictionary | lexemes +-----------+-----------------+-------+----------------+--------------+--------- + asciiword | Word, all ASCII | Paris | {english_stem} | english_stem | {pari} + +CREATE TEXT SEARCH DICTIONARY my_synonym ( + TEMPLATE = synonym, + SYNONYMS = my_synonyms +); + +ALTER TEXT SEARCH CONFIGURATION english + ALTER MAPPING FOR asciiword + WITH my_synonym, english_stem; + +SELECT * FROM ts_debug('english', 'Paris'); + alias | description | token | dictionaries | dictionary | lexemes +-----------+-----------------+-------+---------------------------+------------+--------- + asciiword | Word, all ASCII | Paris | {my_synonym,english_stem} | my_synonym | {paris} + + + + + synonym模板要求的唯一参数是SYNONYMS,它是其配置文件的基本名 — 上例中的my_synonyms。该文件的完整名称将是$SHAREDIR/tsearch_data/my_synonyms.syn(其中$SHAREDIR表示PostgreSQL安装的共享数据目录)。该文件格式是每行一个要被替换的词,后面跟着它的同义词,用空白分隔。空行和结尾的空格会被忽略。 + + + + synonym模板还有一个可选参数CaseSensitive,默认值为false。当CaseSensitivefalse时,同义词文件中的词会像输入词元一样折叠为小写。当它为true时,词和词元都不会被折叠为小写,而是按原样比较。 + + + + 可以在配置文件中把星号(*)放在同义词末尾,表示该同义词是一个前缀。当该条目用于 to_tsvector() 时,星号会被忽略;但当它用于 to_tsquery() 时,结果将是一个带有前缀匹配标记的查询项(见 )。例如,假设在 $SHAREDIR/tsearch_data/synonym_sample.syn 中有以下条目: + +postgres pgsql +postgresql pgsql +postgre pgsql +gogle googl +indices index* + + 那么将得到如下结果: + +mydb=# CREATE TEXT SEARCH DICTIONARY syn (template=synonym, synonyms='synonym_sample'); +mydb=# SELECT ts_lexize('syn', 'indices'); + ts_lexize +----------- + {index} +(1 row) + +mydb=# CREATE TEXT SEARCH CONFIGURATION tst (copy=simple); +mydb=# ALTER TEXT SEARCH CONFIGURATION tst ALTER MAPPING FOR asciiword WITH syn; +mydb=# SELECT to_tsvector('tst', 'indices'); + to_tsvector +------------- + 'index':1 +(1 row) + +mydb=# SELECT to_tsquery('tst', 'indices'); + to_tsquery +------------ + 'index':* +(1 row) + +mydb=# SELECT 'indexes are very useful'::tsvector; + tsvector +--------------------------------- + 'are' 'indexes' 'useful' 'very' +(1 row) + +mydb=# SELECT 'indexes are very useful'::tsvector @@ to_tsquery('tst', 'indices'); + ?column? +---------- + t +(1 row) + + + + + + 分类词典 + + + 一个分类词典(有时被简写成TZ)是一个词的集合,其中包括了词与短语之间的联系,即广义词(BT)、狭义词(NT)、首选词、非首选词、相关词等。 + + + + 基本上一个分类词典会用一个首选词替换所有非首选词,并且也可选择地保留原始术语用于索引。PostgreSQL的分类词典的当前实现是同义词词典的一个扩展,并增加了短语支持。一个分类词典要求一个下列格式的配置文件: + + +# this is a comment +sample word(s) : indexed word(s) +more sample word(s) : more indexed word(s) +... + + + 其中冒号(:)符号扮演了一个短语及其替换之间的定界符。 + + + + 分类词典会使用一个子词典(在词典配置中指定)在检查短语匹配之前正规化输入文本。只能选择一个子词典。如果子词典无法识别某个词,就会报错。在这种情况下,你应当避免使用该词,或者让子词典学会它。你可以在某个被索引词的开头放置一个星号(*),以跳过对子词典的应用,但所有样例词都必须能被子词典识别。 + + + + 如果有多个短语匹配输入,则分类词典选择最长的那一个,并且使用最后的定义打破连结。 + + + + 由子词典识别的特定停用词不能够被指定;改用?标记任何可以出现停用词的地方。例如,假定根据子词典athe是停用词: + + +? one ? two : swsw + + + 匹配a one the twothe one a two;两者都将被swsw替换。 + + + + 由于分类词典具备识别短语的能力,因此它必须记住自身状态并与解析器交互。分类词典会利用这些映射关系来判断自己是应当处理下一个词,还是停止累积。分类词典必须经过仔细配置。例如,如果分类词典只被映射到 asciiword 词元,那么像 one 7 这样的分类词典定义就无法工作,因为词元类型 uint 并没有映射给该分类词典。 + + + + + 分类词典会在建立索引时使用,因此其参数发生任何变化都要求重新建立索引。对于大多数其他词典类型,像增加或移除停用词这样的细小改动则不会强制重新建立索引。 + + + + + 分类词典配置 + + + 要定义一个新的分类词典,可使用thesaurus模板。例如: + + +CREATE TEXT SEARCH DICTIONARY thesaurus_simple ( + TEMPLATE = thesaurus, + DictFile = mythesaurus, + Dictionary = pg_catalog.english_stem +); + + + 这里: + + + + thesaurus_simple是新词典的名称 + + + + + mythesaurus是分类词典配置文件的基础名称(它的全名将是$SHAREDIR/tsearch_data/mythesaurus.ths,其中$SHAREDIR表示安装的共享数据目录)。 + + + + + pg_catalog.english_stem是要用于分类词典正规化的子词典(这里是一个 Snowball 英语词干分析器)。注意子词典将拥有它自己的配置(例如停用词),但这里没有展示。 + + + + + 现在可以在配置中把分类词典thesaurus_simple绑定到想要的词元类型上,例如: + + +ALTER TEXT SEARCH CONFIGURATION russian + ALTER MAPPING FOR asciiword, asciihword, hword_asciipart + WITH thesaurus_simple; + + + + + + + 分类词典示例 + + + 考虑简单的天文词库 thesaurus_astro,其中包含一些天文单词组合: + + +supernovae stars : sn +crab nebulae : crab + + + 下面我们创建一个词典,并把一些词元类型绑定到天文分类词典和英语词干分析器上: + + +CREATE TEXT SEARCH DICTIONARY thesaurus_astro ( + TEMPLATE = thesaurus, + DictFile = thesaurus_astro, + Dictionary = english_stem +); + +ALTER TEXT SEARCH CONFIGURATION russian + ALTER MAPPING FOR asciiword, asciihword, hword_asciipart + WITH thesaurus_astro, english_stem; + + + 现在我们可以看看它是如何工作的。ts_lexize 对测试分类词典并不十分有用,因为它把输入当作一个单独的词元。相反,我们可以使用 plainto_tsqueryto_tsvector,它们会把输入字符串拆分成多个词元: + + +SELECT plainto_tsquery('supernova star'); + plainto_tsquery +----------------- + 'sn' + +SELECT to_tsvector('supernova star'); + to_tsvector +------------- + 'sn':1 + + + 原则上,如果把参数用引号括起来,也可以使用 to_tsquery: + + +SELECT to_tsquery('''supernova star'''); + to_tsquery +------------ + 'sn' + + + 请注意,supernova star 能匹配 thesaurus_astro 中的 supernovae stars,因为我们在分类词典定义中指定了 english_stem 词干分析器。它去掉了词尾的 es。 + + + + 如果既要为替换词建立索引,也要为原始短语建立索引,只需把原始短语也写到定义右侧即可: + + +supernovae stars : sn supernovae stars + +SELECT plainto_tsquery('supernova star'); + plainto_tsquery +----------------------------- + 'sn' & 'supernova' & 'star' + + + + + + + + + <application>Ispell</application> 词典 + + + Ispell词典模板支持形态词典,它可以把一个词的许多不同语言学形式正规化为同一个词位。例如,一个英语 Ispell 词典可以把搜索词 bank 的词尾变化和词形变化对应起来,例如 bankingbankedbanksbanks'bank's。 + + + + 标准PostgreSQL发行版不包含任何Ispell配置文件。 + 大量语言的词典可从Ispell获取。 + 此外,还支持一些更现代的词典文件格式 — MySpell (OO < 2.0.1) + 和Hunspell(OO >= 2.0.2)。在OpenOffice + Wiki上有大量词典可用。 + + + + 要创建一个Ispell词典,执行这三步: + + + + + 下载词典配置文件。OpenOffice扩展文件的扩展名是.oxt。有必要抽取.aff.dic文件,把扩展改为.affix.dict。对于某些词典文件,还需要使用下面的命令把字符转换成 UTF-8 编码(例如挪威语词典): + +iconv -f ISO_8859-1 -t UTF-8 -o nn_no.affix nn_NO.aff +iconv -f ISO_8859-1 -t UTF-8 -o nn_no.dict nn_NO.dic + + + + + + 拷贝文件到$SHAREDIR/tsearch_data目录 + + + + + 用下面的命令把文件载入到 PostgreSQL: + +CREATE TEXT SEARCH DICTIONARY english_hunspell ( + TEMPLATE = ispell, + DictFile = en_us, + AffFile = en_us, + Stopwords = english); + + + + + + + 这里,DictFileAffFileStopWords指定词典、词缀和停用词文件的基础名称。停用词文件的格式和前面解释的simple词典类型相同。其他文件的格式在这里没有指定,但是也可以从上面提到的网站获得。 + + + + Ispell 词典通常识别一个有限集合的词,这样它们后面应该跟着另一个更广义的词典;例如,一个 Snowball 词典,它可以识别所有东西。 + + + + Ispell.affix文件具有下面的结构: + +prefixes +flag *A: + . > RE # As in enter > reenter +suffixes +flag T: + E > ST # As in late > latest + [^AEIOU]Y > -Y,IEST # As in dirty > dirtiest + [AEIOU]Y > EST # As in gray > grayest + [^EY] > EST # As in small > smallest + + + + .dict文件具有下面的结构: + +lapse/ADGRS +lard/DGRS +large/PRTY +lark/MRS + + + + + .dict文件的格式是: + +basic_form/affix_class_name + + + + + 在.affix文件中,每一个词缀标志以下面的格式描述: + +condition > [-stripping_letters,] adding_affix + + + + + 这里的条件具有和正则表达式相似的格式。它可以使用分组[...][^...]。例如,[AEIOU]Y表示词的最后一个字母是"y"并且倒数第二个字母是"a""e""i""o"或者"u"[^EY]表示最后一个字母既不是"e"也不是"y"。 + + + + Ispell 词典支持划分复合词,这是一个有用的特性。注意词缀文件应该用compoundwords controlled语句指定一个特殊标志,它标记可以参与到复合格式中的词典词: + + +compoundwords controlled z + + + 下面是挪威语的一些示例: + + +SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent'); + {over,buljong,terning,pakk,mester,assistent} +SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk'); + {sjokoladefabrikk,sjokolade,fabrikk} + + + + + MySpell格式是Hunspell格式的一个子集。Hunspell.affix文件具有下面的结构: + +PFX A Y 1 +PFX A 0 re . +SFX T N 4 +SFX T 0 st e +SFX T y iest [^aeiou]y +SFX T 0 est [aeiou]y +SFX T 0 est [^ey] + + + + + 一个词缀类的第一行是头部。头部后面列出了词缀规则的域: + + + + + 参数名(PFX 或者 SFX) + + + + + 标志(词缀类的名称) + + + + + 从该词的开始(前缀)或者结尾(后缀)剥离字符 + + + + + 增加词缀 + + + + + 和正则表达式格式类似的条件。 + + + + + + .dict文件看起来和Ispell.dict文件相似: + +larder/M +lardy/RT +large/RSPMYT +largehearted + + + + + + MySpell 不支持复合词。Hunspell则对复合词有更好的支持。当前,PostgreSQL只实现了 Hunspell 中基本的复合词操作。 + + + + + + + <application>Snowball</application> 词典 + + + Snowball词典模板基于 Martin Porter 的一个项目,他是流行的英语 Porter 词干分析算法的发明者。Snowball 现在对许多语言提供词干分析算法(详见Snowball 站点)。每一个算法懂得按照其语言中的拼写,如何缩减词的常见变体形式为一个基础或词干。一个 Snowball 词典要求一个language参数来标识要用哪种词干分析器,并且可以选择地指定一个stopword文件名来给出一个要被消除的词列表(PostgreSQL的标准停用词列表也是由 Snowball 项目提供的)。例如,有一个内置的定义等效于 + + +CREATE TEXT SEARCH DICTIONARY english_stem ( + TEMPLATE = snowball, + Language = english, + StopWords = english +); + + + 停用词文件格式和已经解释的一样。 + + + + 一个Snowball词典识别所有的东西,不管它能不能简化该词,因此它应当被放置在词典列表的最后。把它放在任何其他词典前面是没有用处的,因为一个词元永远不会穿过它而进入到下一个词典。 + + + + + + + + 配置示例 + + + 一个文本搜索配置指定了将一个文档转换成一个tsvector所需的所有选项:用于把文本分解成词元的解析器,以及用于将每一个词元转换成词位的词典。每一次to_tsvectorto_tsquery的调用都需要一个文本搜索配置来执行其处理。配置参数指定了默认配置的名称,如果忽略了显式的配置参数,文本搜索函数将会使用它。它可以在postgresql.conf中设置,或者使用SET命令为一个单独的会话设置。 + + + + 有一些预定义的文本搜索配置可用,并且你可以容易地创建自定义的配置。为了便于管理文本搜索对象,可以使用一组SQL命令,并且有多个psql命令可以显示有关文本搜索对象()的信息。 + + + + 作为一个示例,我们将创建一个配置pg,从复制内置的english配置开始: + + +CREATE TEXT SEARCH CONFIGURATION public.pg ( COPY = pg_catalog.english ); + + + + + 我们将使用一个 PostgreSQL 相关的同义词列表,并将它存储在$SHAREDIR/tsearch_data/pg_dict.syn中。文件内容看起来像: + + +postgres pg +pgsql pg +postgresql pg + + + 我们定义同义词词典如下: + + +CREATE TEXT SEARCH DICTIONARY pg_dict ( + TEMPLATE = synonym, + SYNONYMS = pg_dict +); + + + 接下来我们注册Ispell词典english_ispell,它有其自己的配置文件: + + +CREATE TEXT SEARCH DICTIONARY english_ispell ( + TEMPLATE = ispell, + DictFile = english, + AffFile = english, + StopWords = english +); + + + 现在我们可以在配置pg中建立词的映射: + + +ALTER TEXT SEARCH CONFIGURATION pg + ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, + word, hword, hword_part + WITH pg_dict, english_ispell, english_stem; + + + 我们选择不索引或搜索某些内置配置确实处理的词元类型: + + +ALTER TEXT SEARCH CONFIGURATION pg + DROP MAPPING FOR email, url, url_path, sfloat, float; + + + + + 现在我们可以测试我们的配置: + + +SELECT * FROM ts_debug('public.pg', ' +PostgreSQL, the highly scalable, SQL compliant, open source object-relational +database management system, is now undergoing beta testing of the next +version of our software. +'); + + + + + 下一步是把会话设置为使用这个新配置,该配置是在 public 模式中创建的: + + +=> \dF + List of text search configurations + Schema | Name | Description +---------+------+------------- + public | pg | + +SET default_text_search_config = 'public.pg'; +SET + +SHOW default_text_search_config; + default_text_search_config +---------------------------- + public.pg + + + + + + + 测试和调试文本搜索 + + + 一个自定义文本搜索配置的行为很容易变得混乱。本节中描述的函数对于测试文本搜索对象有用。你可以测试一个完整的配置,或者独立测试解析器和词典。 + + + + 配置测试 + + + 函数ts_debug允许简单地测试一个文本搜索配置。 + + + + ts_debug + + + +ts_debug( config regconfig, document text, + OUT alias text, + OUT description text, + OUT token text, + OUT dictionaries regdictionary[], + OUT dictionary regdictionary, + OUT lexemes text[]) + returns setof record + + + + ts_debug显示document的每一个词元的信息,词元由解析器产生并由配置的词典处理过。该函数使用由config指定的配置,如果该参数被忽略则使用default_text_search_config指定的配置。 + + + + ts_debug为解析器在文本中标识的每一个词元返回一行。被返回的列是: + + + + + alias text — 词元类型的短名称 + + + + + description text — 词元类型的描述 + + + + + token text — 词元的文本 + + + + + dictionaries regdictionary[] — 配置为这种词元类型选择的词典 + + + + + dictionary regdictionary — 识别该词元的词典,如果没有词典能识别则为NULL + + + + + lexemes text[] — 识别该词元的词典产生的词位,如果没有词典能识别则为NULL;一个空数组({})表示该词元被识别为一个停用词 + + + + + + + 以下是一个简单的示例: + + +SELECT * FROM ts_debug('english', 'a fat cat sat on a mat - it ate a fat rats'); + alias | description | token | dictionaries | dictionary | lexemes +-----------+-----------------+-------+----------------+--------------+--------- + asciiword | Word, all ASCII | a | {english_stem} | english_stem | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | fat | {english_stem} | english_stem | {fat} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | cat | {english_stem} | english_stem | {cat} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | sat | {english_stem} | english_stem | {sat} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | on | {english_stem} | english_stem | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | a | {english_stem} | english_stem | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | mat | {english_stem} | english_stem | {mat} + blank | Space symbols | | {} | | + blank | Space symbols | - | {} | | + asciiword | Word, all ASCII | it | {english_stem} | english_stem | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | ate | {english_stem} | english_stem | {ate} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | a | {english_stem} | english_stem | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | fat | {english_stem} | english_stem | {fat} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | rats | {english_stem} | english_stem | {rat} + + + + + 为了一个更广泛的示范,我们先为英语语言创建一个public.english配置和 Ispell 词典: + + + +CREATE TEXT SEARCH CONFIGURATION public.english ( COPY = pg_catalog.english ); + +CREATE TEXT SEARCH DICTIONARY english_ispell ( + TEMPLATE = ispell, + DictFile = english, + AffFile = english, + StopWords = english +); + +ALTER TEXT SEARCH CONFIGURATION public.english + ALTER MAPPING FOR asciiword WITH english_ispell, english_stem; + + + +SELECT * FROM ts_debug('public.english', 'The Brightest supernovaes'); + alias | description | token | dictionaries | dictionary | lexemes +-----------+-----------------+-------------+-------------------------------+----------------+------------- + asciiword | Word, all ASCII | The | {english_ispell,english_stem} | english_ispell | {} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | Brightest | {english_ispell,english_stem} | english_ispell | {bright} + blank | Space symbols | | {} | | + asciiword | Word, all ASCII | supernovaes | {english_ispell,english_stem} | english_stem | {supernova} + + + + 在这个示例中,词Brightest被解析器识别为一个ASCII word(别名asciiword)。对于这种词元类型,词典列表是english_ispellenglish_stem。该词被english_ispell识别,并被这个词典归约为名词bright。词supernovaesenglish_ispell词典来说是未知的,因此它会被传递给下一个词典;幸运的是,它随后被识别了。(实际上,english_stem是一个 Snowball 词典,它能够识别所有输入;这也是为什么它被放在词典列表末尾。) + + + + 词Theenglish_ispell词典识别为一个停用词()并且将不会被索引。空格也被丢弃,因为该配置没有为它们提供词典。 + + + + 你可以通过明确指定要查看的列来减少输出宽度: + + +SELECT alias, token, dictionary, lexemes +FROM ts_debug('public.english', 'The Brightest supernovaes'); + alias | token | dictionary | lexemes +-----------+-------------+----------------+------------- + asciiword | The | english_ispell | {} + blank | | | + asciiword | Brightest | english_ispell | {bright} + blank | | | + asciiword | supernovaes | english_stem | {supernova} + + + + + + + 解析器测试 + + + 下列函数允许直接测试一个文本搜索解析器。 + + + + ts_parse + + + +ts_parse(parser_name text, document text, + OUT tokid integer, OUT token text) returns setof record +ts_parse(parser_oid oid, document text, + OUT tokid integer, OUT token text) returns setof record + + + + ts_parse解析给定的document,并返回一组记录,每个由解析过程产生的词元对应一条记录。每条记录都包含一个 tokid,显示所分配的词元类型,以及一个 token,即该词元的文本。例如: + + +SELECT * FROM ts_parse('default', '123 - a number'); + tokid | token +-------+-------- + 22 | 123 + 12 | + 12 | - + 1 | a + 12 | + 1 | number + + + + + ts_token_type + + + +ts_token_type(parser_name text, OUT tokid integer, + OUT alias text, OUT description text) returns setof record +ts_token_type(parser_oid oid, OUT tokid integer, + OUT alias text, OUT description text) returns setof record + + + + ts_token_type返回一个表,描述指定解析器能够识别的每一种词元。对于每种词元类型,该表给出解析器用来标记该类词元的整数 tokid、在配置命令中命名该词元类型的 alias,以及简短的 description。例如: + + +SELECT * FROM ts_token_type('default'); + tokid | alias | description +-------+-----------------+------------------------------------------ + 1 | asciiword | Word, all ASCII + 2 | word | Word, all letters + 3 | numword | Word, letters and digits + 4 | email | Email address + 5 | url | URL + 6 | host | Host + 7 | sfloat | Scientific notation + 8 | version | Version number + 9 | hword_numpart | Hyphenated word part, letters and digits + 10 | hword_part | Hyphenated word part, all letters + 11 | hword_asciipart | Hyphenated word part, all ASCII + 12 | blank | Space symbols + 13 | tag | XML tag + 14 | protocol | Protocol head + 15 | numhword | Hyphenated word, letters and digits + 16 | asciihword | Hyphenated word, all ASCII + 17 | hword | Hyphenated word, all letters + 18 | url_path | URL path + 19 | file | File or path name + 20 | float | Decimal notation + 21 | int | Signed integer + 22 | uint | Unsigned integer + 23 | entity | XML entity + + + + + + + 词典测试 + + + ts_lexize函数帮助词典测试。 + + + + ts_lexize + + + +ts_lexize(dict regdictionary, token text) returns text[] + + + + 如果输入的token是该词典已知的,则ts_lexize返回一个词位数组;如果词元是词典已知的但是它是一个停用词,则返回一个空数组;或者如果它对词典是未知词,则返回NULL。 + + + + 示例: + + +SELECT ts_lexize('english_stem', 'stars'); + ts_lexize +----------- + {star} + +SELECT ts_lexize('english_stem', 'a'); + ts_lexize +----------- + {} + + + + + + ts_lexize函数期望的是单个词元,而不是文本。下面这种情况可能会让人困惑: + + +SELECT ts_lexize('thesaurus_astro', 'supernovae stars') is null; + ?column? +---------- + t + + + 分类词典 thesaurus_astro 确实知道短语 supernovae stars,但 ts_lexize 会失败,因为它不会解析输入文本,而是把它当作一个单独的词元。测试分类词典时应使用 plainto_tsqueryto_tsvector,例如: + + +SELECT plainto_tsquery('supernovae stars'); + plainto_tsquery +----------------- + 'sn' + + + + + + + + + + GIN 和 GiST 索引类型 + + + 文本搜索 + 索引 + + + + 有两种索引可以用来加速全文搜索。 + 请注意,索引对于全文搜索并非强制要求,但在定期搜索某一列的情况下,通常是可取的。 + + + + + + + + 索引 + GIN + 文本搜索 + + + CREATE INDEX name ON table USING GIN (column); + + + + + 创建基于 GIN(广义倒排索引)的索引。 + column必须是tsvector类型。 + + + + + + + + + 索引 + GiST + 文本搜索 + + + CREATE INDEX name ON table USING GIST (column); + + + + + 创建基于 GiST(广义搜索树)的索引。 + column可以是tsvectortsquery类型。 + + + + + + + + + GIN 索引是文本搜索的首选索引类型。作为倒排索引,每个词(词位)在 + 其中都有一个索引项,其中有压缩过的匹配位置的列表。多词搜索可以找到 + 第一个匹配,然后使用该索引移除缺少额外词的行。GIN 索引只存储 + tsvector值的词(词位),并且不存储它们的权重标签。因此, + 在使用涉及权重的查询时需要一次在表行上的重新检查。 + + + + 一个 GiST 索引是有损的,这表示索引可能产生假匹配,并且有必要检查真实的表行来消除这种假匹配(PostgreSQL在需要时会自动做这一步)。GiST 索引之所以是有损的,是因为每一个文档在索引中被表示为一个定长的签名。该签名通过哈希每一个词到一个 n 位串中的一个单一位来产生,通过将所有这些位 OR 在一起产生一个 n 位的文档签名。当两个词哈希到同一个位位置时就会产生假匹配。如果查询中所有词都有匹配(真或假),则必须检索表行查看匹配是否正确。 + + + + 有损性导致的性能下降归因于不必要的表记录(即被证实为假匹配的记录)获取。因为表记录的随机访问是较慢的,这限制了 GiST 索引的可用性。假匹配的可能性取决于几个因素,特别是唯一词的数量,因此推荐使用词典来缩减这个数量。 + + + + 注意GIN索引的构件时间常常可以通过增加来改进,而GiST索引的构建时间则与该参数无关。 + + + 对大集合分区并正确使用 GIN 和 GiST 索引允许实现带在线更新的快速搜索。分区可以在数据库层面上使用表继承来完成,或者通过将文档分布在服务器上,并使用 模块收集搜索结果来完成。后者是可能的,因为排名函数只使用本地信息。 + + + + + <application>psql</application> 支持 + + + 关于文本搜索配置对象的信息可以在psql中使用一组命令获得: + +\dF{d,p,t}+ PATTERN + + 可选的+能产生更多细节。 + + + + 可选参数 PATTERN 可以是文本搜索对象的名称,也可以可选地带上模式限定。如果省略 PATTERN,则会显示所有可见对象的信息。PATTERN 还可以是正则表达式,并且可以为模式名和对象名分别提供独立的模式。下面的示例说明了这一点: + + +=> \dF *fulltext* + List of text search configurations + Schema | Name | Description +--------+--------------+------------- + public | fulltext_cfg | + + + +=> \dF *.fulltext* + List of text search configurations + Schema | Name | Description +----------+---------------------------- + fulltext | fulltext_cfg | + public | fulltext_cfg | + + + 可用命令如下: + + + + + \dF+ PATTERN + + 列出文本搜索配置(添加 +可获得更详细的信息)。 +=> \dF russian + List of text search configurations + Schema | Name | Description +------------+---------+------------------------------------ + pg_catalog | russian | configuration for russian language + +=> \dF+ russian +Text search configuration "pg_catalog.russian" +Parser: "pg_catalog.default" + Token | Dictionaries +-----------------+-------------- + asciihword | english_stem + asciiword | english_stem + email | simple + file | simple + float | simple + host | simple + hword | russian_stem + hword_asciipart | english_stem + hword_numpart | simple + hword_part | russian_stem + int | simple + numhword | simple + numword | simple + sfloat | simple + uint | simple + url | simple + url_path | simple + version | simple + word | russian_stem + + + + + + + \dFd+ PATTERN + + 列出文本搜索词典(加上+可获得更详细的信息)。 +=> \dFd + List of text search dictionaries + Schema | Name | Description +------------+-----------------+----------------------------------------------------------- + pg_catalog | danish_stem | snowball stemmer for danish language + pg_catalog | dutch_stem | snowball stemmer for dutch language + pg_catalog | english_stem | snowball stemmer for english language + pg_catalog | finnish_stem | snowball stemmer for finnish language + pg_catalog | french_stem | snowball stemmer for french language + pg_catalog | german_stem | snowball stemmer for german language + pg_catalog | hungarian_stem | snowball stemmer for hungarian language + pg_catalog | italian_stem | snowball stemmer for italian language + pg_catalog | norwegian_stem | snowball stemmer for norwegian language + pg_catalog | portuguese_stem | snowball stemmer for portuguese language + pg_catalog | romanian_stem | snowball stemmer for romanian language + pg_catalog | russian_stem | snowball stemmer for russian language + pg_catalog | simple | simple dictionary: just lower case and check for stopword + pg_catalog | spanish_stem | snowball stemmer for spanish language + pg_catalog | swedish_stem | snowball stemmer for swedish language + pg_catalog | turkish_stem | snowball stemmer for turkish language + + + + + + + \dFp+ PATTERN + + 列出文本搜索解析器(添加 +可获得更详细的信息)。 +=> \dFp + List of text search parsers + Schema | Name | Description +------------+---------+--------------------- + pg_catalog | default | default word parser +=> \dFp+ + Text search parser "pg_catalog.default" + Method | Function | Description +-----------------+----------------+------------- + Start parse | prsd_start | + Get next token | prsd_nexttoken | + End parse | prsd_end | + Get headline | prsd_headline | + Get token types | prsd_lextype | + + Token types for parser "pg_catalog.default" + Token name | Description +-----------------+------------------------------------------ + asciihword | Hyphenated word, all ASCII + asciiword | Word, all ASCII + blank | Space symbols + email | Email address + entity | XML entity + file | File or path name + float | Decimal notation + host | Host + hword | Hyphenated word, all letters + hword_asciipart | Hyphenated word part, all ASCII + hword_numpart | Hyphenated word part, letters and digits + hword_part | Hyphenated word part, all letters + int | Signed integer + numhword | Hyphenated word, letters and digits + numword | Word, letters and digits + protocol | Protocol head + sfloat | Scientific notation + tag | XML tag + uint | Unsigned integer + url | URL + url_path | URL path + version | Version number + word | Word, all letters +(23 rows) + + + + + + + \dFt+ PATTERN + + 列出文本搜索模板(添加 +可获得更详细的信息)。 +=> \dFt + List of text search templates + Schema | Name | Description +------------+-----------+----------------------------------------------------------- + pg_catalog | ispell | ispell dictionary + pg_catalog | simple | simple dictionary: just lower case and check for stopword + pg_catalog | snowball | snowball stemmer + pg_catalog | synonym | synonym dictionary: replace word by its synonym + pg_catalog | thesaurus | thesaurus dictionary: phrase by phrase substitution + + + + + + + + + + 限制 + + 目前,PostgreSQL的文本搜索功能存在以下限制: + + 每个词位的长度必须小于 2K 字节 + + + tsvector 的长度(词位加位置)必须小于 1 兆字节 + + + + 词位数量必须小于 264 + + + tsvector 中的位置值必须大于 0 且不超过 16,383 + + + <N>(FOLLOWED BY)tsquery 操作符中的匹配距离不能超过 16,384 + + + 每个词位的位置数不能超过 256 个 + + + tsquery 中的节点数(词位加操作符)必须小于 32,768 + + + + + + 为了对比,PostgreSQL 8.1 的文档包含 10,441 个唯一词,总数 335,420 个词,并且最频繁的词postgresql在 655 个文档中被提到 6,127 次。 + + + + + 另一个示例 — PostgreSQL的邮件列表归档在 461,020 条消息的 57,491,343 个词位中包含 910,989 个唯一词。 + + + + + + 从 8.3 之前的文本搜索迁移 + + + 使用模块进行文本搜索的应用需要进行一些调整, + 才能使用内置功能: + + + + + + 一些函数已被重命名,或在其参数列表上做了小调整,而且它们现在都位于 + pg_catalog模式中,而在以前的安装中它们可能位于 + public或其他非系统模式中。新版本的tsearch2 + 提供了一个兼容层,可解决这一领域的大多数问题。 + + + + + + 从 8.3 之前的数据库加载pg_dump输出时, + 必须屏蔽旧的tsearch2函数和其他对象。 + 虽然其中很多本来就无法加载,但少数可以加载并随后引发问题。 + 处理这个问题的一种简单方法是在恢复转储之前先加载新的 + tsearch2模块;这样它就会阻止旧对象被加载。 + + + + + + 文本搜索配置的设置方式现在完全不同了。不再需要手动向配置表中插入行, + 而是通过本章前面介绍的专用 SQL 命令来配置搜索。没有将现有自定义配置 + 转换为 8.3 形式的自动化支持;这一步需要你自己完成。 + + + + + + 大多数类型的词典依赖于数据库之外的一些配置文件。这些文件与 8.3 之前的 + 用法大体兼容,但请注意以下差异: + + + + + 配置文件现在必须放在一个指定的目录中 + ($SHAREDIR/tsearch_data),并且根据文件类型必须有特定的 + 扩展名,如前面各种词典类型的描述中所述。添加这一限制是为了防止 + 安全问题。 + + + + + + 无论使用什么数据库编码,配置文件都必须以 UTF-8 编码。 + + + + + + 在分类词典配置文件中,停用词必须用?标记。 + + + + + + + + + + +
    diff --git a/zh/9.6/trigger.sgml b/zh/9.6/trigger.sgml new file mode 100644 index 00000000..0a6c6cc1 --- /dev/null +++ b/zh/9.6/trigger.sgml @@ -0,0 +1,649 @@ + + + + 触发器 + + + 触发器 + + + + 本章提供有关编写触发器函数的一般信息。触发器函数可以用大多数可用的过程语言编写,包括 + PL/pgSQL)、 + PL/Tcl)、 + PL/Perl)和 + PL/Python)。读完本章后,你还应查阅所用过程语言对应的章节,了解用该语言编写触发器的语言相关细节。 + + + + 也可以用 C 编写触发器函数,不过大多数人会觉得使用某种过程语言更容易。目前还不能用普通 SQL 函数语言编写触发器函数。 + + + + 触发器行为概述 + + + 触发器是一种规定:每当执行某种类型的操作时,数据库就自动执行某个特定函数。触发器可以附加到表、视图和外部表上。 + + + + 在表和外部表上,触发器可以定义为在任何 INSERT、 + UPDATEDELETE 操作之前或之后执行; + 既可以对每个被修改的行执行一次,也可以对每个 SQL 语句执行一次。 + 如果一条 INSERT 包含 ON CONFLICT DO UPDATE + 子句,那么当引用了 EXCLUDED 列时,BEFORE 插入触发器和 + BEFORE 更新触发器的效果有可能都会被一起应用。 + UPDATE 触发器还可以设置成只有在 UPDATE + 语句的 SET 子句中提到特定列时才触发。触发器也可以针对 + TRUNCATE 语句触发。一旦发生触发器事件,就会在适当的时机调用该触发器的函数来处理该事件。外部表完全不支持 TRUNCATE 语句。 + + + + 在视图上,触发器可以定义为代替 INSERT、 + UPDATEDELETE 操作执行。这种 + INSTEAD OF 触发器会对视图中每个需要修改的行触发一次。触发器函数负责对视图底层的基表执行必要的修改,并在适当时返回该修改后的行,使其在视图中呈现为应有的样子。视图上的触发器也可以定义为在 + INSERTUPDATE 或 + DELETE 操作之前或之后,对每个 SQL 语句执行一次。不过,这种触发器只有在该视图上还存在一个 + INSTEAD OF 触发器时才会触发。否则,任何以该视图为目标的语句都必须被重写成作用于其底层基表的语句,此时触发的将是附加在那些基表上的触发器。 + + + + 必须先定义触发器函数,之后才能创建触发器本身。触发器函数必须声明为不接受参数并返回类型 trigger 的函数。(触发器函数通过特殊传入的 + TriggerData 结构体接收输入,而不是通过普通函数参数。) + + + + 创建出合适的触发器函数后,就可以使用 创建触发器。同一个触发器函数可以供多个触发器使用。 + + + + PostgreSQL 同时提供 每行 触发器和 + 每语句 触发器。对于每行触发器,触发它的语句所影响的每一行都会调用一次触发器函数。相反,每语句触发器只会在执行相应语句时调用一次,不管该语句影响了多少行。特别是,即使某个语句一行也没有影响,仍会执行所有适用的每语句触发器。这两类触发器有时也分别称为 + 行级 触发器和 语句级 触发器。 + TRUNCATE 的触发器只能定义为语句级。在视图上,之前或之后触发的触发器只能定义为语句级,而代替 + INSERTUPDATE 或 + DELETE 触发的触发器只能定义为行级。 + + + + 触发器还可以按照它们是在操作 之前、 + 之后,还是 取代 该操作而触发来分类。这些分别称为 BEFORE 触发器、 + AFTER 触发器和 INSTEAD OF 触发器。语句级 + BEFORE 触发器自然会在语句开始执行任何操作之前触发,而语句级 + AFTER 触发器则在语句末尾触发。这些类型的触发器可以定义在表或视图上。行级 BEFORE 触发器会在对某一行执行操作之前立即触发,而行级 + AFTER 触发器会在语句结束时触发(但早于任何语句级 + AFTER 触发器)。这些类型的触发器只能定义在表和外部表上。行级 INSTEAD OF 触发器只能定义在视图上;当视图中的每一行被识别为需要执行操作时,它们就会立即触发。 + + + INSERT 包含 ON CONFLICT DO UPDATE 子句,则所有行级 BEFORE INSERT 触发器和所有行级 BEFORE UPDATE 触发器的效果可能都会体现在更新后行的最终状态中,前提是引用了 EXCLUDED 列。不过,即使没有引用 EXCLUDED 列,这两组行级 BEFORE 触发器也可能都会执行。如果同时存在会更改正在插入/更新的行的 BEFORE INSERTBEFORE UPDATE 行级触发器,就应考虑可能出现令人意外的结果(即使它们的修改大致等效,如果不具有幂等性,这也仍可能成为问题)。注意,语句级 UPDATE 触发器会在指定 ON CONFLICT DO UPDATE 时执行,不管 UPDATE 是否实际影响了行(也不管是否走到了 UPDATE 分支)。INSERT 如果带有 ON CONFLICT DO UPDATE 子句,会先执行语句级 BEFORE INSERT 触发器,再执行语句级 BEFORE UPDATE 触发器,然后执行语句级 AFTER UPDATE 触发器,最后执行语句级 AFTER INSERT 触发器。 + + + 由每语句触发器调用的触发器函数应始终返回 NULL。由每行触发器调用的触发器函数则可以根据需要向调用它的执行器返回一个表行(即类型 + HeapTuple 的值)。在操作之前触发的行级触发器可以作出如下选择: + + + + + 它可以返回 NULL,以跳过对当前行的操作。这会指示执行器不要执行引发该触发器的行级操作(即对某个具体表行的插入、修改或删除)。 + + + + + + 仅对于行级 INSERTUPDATE 触发器,返回的行会成为将要插入的行,或替换正在更新的行。这使得触发器函数可以修改即将插入或更新的行。 + + + + + 不打算产生上述任何一种行为的行级 BEFORE 触发器,必须小心地返回传入的同一行作为结果(也就是说,INSERT 和 + UPDATE 触发器返回 NEW 行, + DELETE 触发器返回 OLD 行)。 + + + + 行级 INSTEAD OF 触发器应当要么返回 NULL,表示它没有修改视图底层基表中的任何数据;要么返回传入的视图行(INSERTUPDATE 操作返回 + NEW 行,DELETE 操作返回 + OLD 行)。非空返回值用来表明触发器已经在视图中完成了必要的数据修改。这会使该命令所影响的行数计数增加。仅对于 + INSERTUPDATE 操作,触发器在返回 + NEW 行之前还可以修改它。这会改变 + INSERT RETURNINGUPDATE RETURNING + 返回的数据;当视图显示的数据与提供给它的数据不完全相同时,这很有用。 + + + + 对于在操作之后触发的行级触发器,其返回值会被忽略,因此它们可以返回 + NULL。 + + + + 如果为同一关系上的同一事件定义了多个触发器,它们会按触发器名称的字母顺序触发。对于 BEFOREINSTEAD OF + 触发器,每个触发器返回的、可能已经被修改过的行都会成为下一个触发器的输入。如果任何一个 BEFORE 或 + INSTEAD OF 触发器返回 NULL,则会放弃对该行执行该操作,并且后续触发器(针对该行)都不会再触发。 + + + + 触发器定义还可以指定一个布尔 WHEN 条件,用来测试是否应当触发该触发器。对于行级触发器,WHEN 条件可以检查该行各列的旧值和/或新值。(语句级触发器也可以有 WHEN + 条件,不过这个特性对它们用处不大。)在 BEFORE 触发器中, + WHEN 条件就在函数即将执行或本会执行之前求值,因此使用 + WHEN 与在触发器函数开头测试同样的条件并无实质差别。不过,在 + AFTER 触发器中,WHEN 条件会在行更改发生后立即求值,并决定是否要将一个事件排入队列,以便在语句末尾触发该触发器。因此,当 AFTER 触发器的 WHEN + 条件不返回真时,就不必排队该事件,也不必在语句末尾重新取出该行。如果触发器只需要针对少数几行触发,这会让修改大量行的语句显著加快。 + INSTEAD OF 触发器不支持 WHEN 条件。 + + + + 通常,行级 BEFORE 触发器用于检查或修改将要插入或更新的数据。例如,BEFORE 触发器可以用来把当前时间写入某个 + timestamp 列,或者检查该行的两个元素是否一致。行级 + AFTER 触发器最适合用于把更新传播到其他表,或对其他表执行一致性检查。之所以这样分工,是因为 AFTER 触发器可以确定自己看到的是该行的最终值,而 BEFORE 触发器不能;它之后可能还会有其他 BEFORE 触发器触发。如果没有特定理由把触发器做成 + BEFOREAFTER,那么 + BEFORE 形式更高效,因为关于该操作的信息无需一直保存到语句末尾。 + + + + 如果触发器函数执行 SQL 命令,那么这些命令可能会再次触发触发器。这就是所谓的级联触发器。对级联层数没有直接限制。级联还有可能导致同一个触发器被递归调用;例如,一个 INSERT 触发器可能执行一条向同一个表再插入一行的命令,从而导致该 INSERT 触发器再次触发。因此,在这种场景下,避免无限递归是触发器编写者自己的责任。 + + + + + 触发器 + 触发器函数的参数 + + 在定义触发器时,可以为它指定参数。在触发器定义中包含参数,是为了让需求相似的不同触发器能够调用同一个函数。举例来说,可以有一个通用触发器函数,它接受两个列名作为参数,把当前用户写入其中一个列,把当前时间戳写入另一个列。只要编写得当,这个触发器函数就应独立于它所作用的具体表。因此,同一个函数可用于任何具有适当列的表上的 INSERT 事件,例如自动跟踪某个事务表中记录的创建。如果把它定义成 + UPDATE 触发器,还可以用来跟踪最近更新事件。 + + + + 每种支持触发器的编程语言都有自己的方法,使触发器输入数据可供触发器函数使用。这些输入数据包括触发器事件的类型(例如 INSERT 或 + UPDATE),以及 CREATE TRIGGER + 中列出的任何参数。对于行级触发器,输入数据还包括 + INSERTUPDATE 触发器的 + NEW 行,以及/或者 UPDATE 和 + DELETE 触发器的 OLD 行。语句级触发器目前无法检查该语句修改的各个行。 + + + + + + 数据更改的可见性 + + + 如果你在触发器函数中执行 SQL 命令,而这些命令又访问该触发器所属的表,就需要了解数据可见性规则,因为这些规则决定了这些 SQL 命令是否能看到引发触发器的数据更改。简而言之: + + + + + + 语句级触发器遵循简单的可见性规则:语句所做的任何更改对语句级 + BEFORE 触发器都不可见,而所有修改对语句级 + AFTER 触发器都可见。 + + + + + + 导致触发器触发的数据更改(插入、更新或删除)对于在行级 + BEFORE 触发器中执行的 SQL 命令自然是 + 可见的,因为它尚未发生。 + + + + + + 然而,在行级 BEFORE 触发器中执行的 SQL 命令 + 看到同一外层命令中先前已处理各行上的数据更改结果。这里必须小心,因为这些更改事件的顺序通常不可预测;一条影响多行的 SQL 命令可能按任意顺序访问这些行。 + + + + + + 类似地,行级 INSTEAD OF 触发器会看到同一外层命令中此前触发的 INSTEAD OF 触发器所造成的数据更改结果。 + + + + + + 当行级 AFTER 触发器触发时,外层命令所做的所有数据更改都已经完成,并且对被调用的触发器函数可见。 + + + + + + + 如果你的触发器函数是用任何一种标准过程语言编写的,那么只有在该函数被声明为 VOLATILE 时,上述说法才成立。被声明为 + STABLEIMMUTABLE 的函数在任何情况下都看不到调用命令所做的更改。 + + + + 有关数据可见性规则的更多信息可见 。 + 中的示例展示了这些规则。 + + + + + 用 C 编写触发器函数 + + + 触发器 + 在 C 中 + + + + 本节说明触发器函数接口的底层细节。这些信息仅在用 C 编写触发器函数时才需要。如果你使用更高层语言,这些细节会由系统代为处理。在多数情况下,在用 C 编写触发器之前,应先考虑使用过程语言。每种过程语言的文档都会说明如何用该语言编写触发器。 + + + + 触发器函数必须使用 版本 1 函数管理器接口。 + + + + 当函数由触发器管理器调用时,不会向它传递任何普通参数,但会传递一个指向 TriggerData 结构体的 上下文 指针。C 函数可以通过执行下列宏来检查自己是否由触发器管理器调用: + +CALLED_AS_TRIGGER(fcinfo) + + 它会展开为: + +((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData)) + + 如果该宏返回真,就可以安全地把 fcinfo->context + 转换为 TriggerData * 类型,并使用其所指向的 + TriggerData 结构体。函数 + 绝不能 修改 TriggerData + 结构体本身,也不能修改它所指向的任何数据。 + + + + struct TriggerData定义在commands/trigger.h: + + +typedef struct TriggerData +{ + NodeTag type; + TriggerEvent tg_event; + Relation tg_relation; + HeapTuple tg_trigtuple; + HeapTuple tg_newtuple; + Trigger *tg_trigger; + Buffer tg_trigtuplebuf; + Buffer tg_newtuplebuf; +} TriggerData; +其中各成员的定义如下: + + type + + + 始终是 T_TriggerData。 + + + + + + tg_event + + + 描述调用函数的事件。可以使用以下宏检查 tg_event: + + + + TRIGGER_FIRED_BEFORE(tg_event) + + + 如果触发器在操作之前触发,则返回真。 + + + + + + TRIGGER_FIRED_AFTER(tg_event) + + + 如果触发器在操作之后触发,则返回真。 + + + + + + TRIGGER_FIRED_INSTEAD(tg_event) + + + 如果触发器是取代该操作而触发,则返回真。 + + + + + + TRIGGER_FIRED_FOR_ROW(tg_event) + + + 如果触发器是因行级事件触发,则返回真。 + + + + + + TRIGGER_FIRED_FOR_STATEMENT(tg_event) + + + 如果触发器是因语句级事件触发,则返回真。 + + + + + + TRIGGER_FIRED_BY_INSERT(tg_event) + + + 如果触发器是由 INSERT 命令触发,则返回真。 + + + + + + TRIGGER_FIRED_BY_UPDATE(tg_event) + + + 如果触发器是由 UPDATE 命令触发,则返回真。 + + + + + + TRIGGER_FIRED_BY_DELETE(tg_event) + + + 如果触发器是由 DELETE 命令触发,则返回真。 + + + + + + TRIGGER_FIRED_BY_TRUNCATE(tg_event) + + + 如果触发器是由 TRUNCATE 命令触发,则返回真。 + + + + + + + + + + tg_relation + + + 指向一个描述该触发器所针对关系的结构体。有关此结构体的细节见 + utils/rel.h。其中最值得关注的是 + tg_relation->rd_att(关系元组的描述符)和 + tg_relation->rd_rel->relname(关系名;其类型不是 + char*,而是 NameData;如果需要名称的副本,请使用 + SPI_getrelname(tg_relation) 取得一个 char*)。 + + + + + + tg_trigtuple + + + 指向触发该触发器的那一行。这是正在被插入、更新或删除的行。如果该触发器因 INSERTDELETE + 而触发,那么如果你不想用另一行替换该行(在 INSERT + 的情形下)或跳过该操作,就应从函数中返回它。对于外部表上的触发器,此处系统列的值未指定。 + + + + + + tg_newtuple + + + 如果触发器因 UPDATE 而触发,则指向该行的新版本;如果因 INSERTDELETE + 而触发,则为 NULL。如果事件是 UPDATE,而你不想用另一行替换该行或跳过该操作,就必须从函数中返回它。对于外部表上的触发器,此处系统列的值未指定。 + + + + + + tg_trigger + + 一个指向Trigger类型结构体的指针,该结构体定义在utils/reltrigger.h: + + +typedef struct Trigger +{ + Oid tgoid; + char *tgname; + Oid tgfoid; + int16 tgtype; + char tgenabled; + bool tgisinternal; + Oid tgconstrrelid; + Oid tgconstrindid; + Oid tgconstraint; + bool tgdeferrable; + bool tginitdeferred; + int16 tgnargs; + int16 tgnattr; + int16 *tgattr; + char **tgargs; + char *tgqual; +} Trigger; +其中tgname是触发器名称,tgnargstgargs中参数的数量,而tgargs是一个指针数组,指向CREATE TRIGGER语句中指定的参数。其他成员仅供内部使用。 + + + + + tg_trigtuplebuf + + 包含 tg_trigtuple 的缓冲区;如果没有这样的元组,或它没有存储在磁盘缓冲区中,则为 InvalidBuffer + + + + + tg_newtuplebuf + + 包含 tg_newtuple 的缓冲区;如果没有这样的元组,或它没有存储在磁盘缓冲区中,则为 InvalidBuffer + + + + + + + + + + 触发器函数必须返回一个 HeapTuple 指针或一个 + NULL 指针(不是 SQL 空值,也就是说不要把 + isNull 设为真)。如果你不想修改正在处理的行,就要小心地根据情况返回适当的 tg_trigtuple 或 + tg_newtuple。 + + + + + 一个完整的触发器示例 + + + 这里给出一个非常简单的、用 C 编写的触发器函数示例。(用过程语言编写的触发器示例可见各过程语言的文档。) + + + + 函数 trigf 会报告表 ttest 中的行数, + 并在命令试图向列 x 中插入空值时跳过实际操作。 + (这样,这个触发器就起到了非空约束的作用,但不会中止事务。) + + + + 首先,表定义: + +CREATE TABLE ttest ( + x integer +); + + + + + 下面是触发器函数的源代码: +context; + TupleDesc tupdesc; + HeapTuple rettuple; + char *when; + bool checknull = false; + bool isnull; + int ret, i; + + /* make sure it's called as a trigger at all */ + if (!CALLED_AS_TRIGGER(fcinfo)) + elog(ERROR, "trigf: not called by trigger manager"); + + /* tuple to return to executor */ + if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) + rettuple = trigdata->tg_newtuple; + else + rettuple = trigdata->tg_trigtuple; + + /* check for null values */ + if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) + && TRIGGER_FIRED_BEFORE(trigdata->tg_event)) + checknull = true; + + if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) + when = "before"; + else + when = "after "; + + tupdesc = trigdata->tg_relation->rd_att; + + /* connect to SPI manager */ + if ((ret = SPI_connect()) < 0) + elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret); + + /* get number of rows in table */ + ret = SPI_exec("SELECT count(*) FROM ttest", 0); + + if (ret < 0) + elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret); + + /* count(*) returns int8, so be careful to convert */ + i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0], + SPI_tuptable->tupdesc, + 1, + &isnull)); + + elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i); + + SPI_finish(); + + if (checknull) + { + SPI_getbinval(rettuple, tupdesc, 1, &isnull); + if (isnull) + rettuple = NULL; + } + + return PointerGetDatum(rettuple); +} +]]> + + + + 编译完源代码后(参见),声明函数和触发器: +CREATE FUNCTION trigf() RETURNS trigger + AS 'filename' + LANGUAGE C; + +CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest + FOR EACH ROW EXECUTE PROCEDURE trigf(); + +CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest + FOR EACH ROW EXECUTE PROCEDURE trigf(); + + + + + 现在可以测试该触发器的行为: + +=> INSERT INTO ttest VALUES (NULL); +INFO: trigf (fired before): there are 0 rows in ttest +INSERT 0 0 + +-- Insertion skipped and AFTER trigger is not fired + +=> SELECT * FROM ttest; + x +--- +(0 rows) + +=> INSERT INTO ttest VALUES (1); +INFO: trigf (fired before): there are 0 rows in ttest +INFO: trigf (fired after ): there are 1 rows in ttest + ^^^^^^^^ + remember what we said about visibility. +INSERT 167793 1 +vac=> SELECT * FROM ttest; + x +--- + 1 +(1 row) + +=> INSERT INTO ttest SELECT x * 2 FROM ttest; +INFO: trigf (fired before): there are 1 rows in ttest +INFO: trigf (fired after ): there are 2 rows in ttest + ^^^^^^ + remember what we said about visibility. +INSERT 167794 1 +=> SELECT * FROM ttest; + x +--- + 1 + 2 +(2 rows) + +=> UPDATE ttest SET x = NULL WHERE x = 2; +INFO: trigf (fired before): there are 2 rows in ttest +UPDATE 0 +=> UPDATE ttest SET x = 4 WHERE x = 2; +INFO: trigf (fired before): there are 2 rows in ttest +INFO: trigf (fired after ): there are 2 rows in ttest +UPDATE 1 +vac=> SELECT * FROM ttest; + x +--- + 1 + 4 +(2 rows) + +=> DELETE FROM ttest; +INFO: trigf (fired before): there are 2 rows in ttest +INFO: trigf (fired before): there are 1 rows in ttest +INFO: trigf (fired after ): there are 0 rows in ttest +INFO: trigf (fired after ): there are 0 rows in ttest + ^^^^^^ + remember what we said about visibility. +DELETE 2 +=> SELECT * FROM ttest; + x +--- +(0 rows) + + + + + + 更复杂的示例可见 src/test/regress/regress.c 和 + 。 + + + diff --git a/zh/9.6/tsearch2.sgml b/zh/9.6/tsearch2.sgml new file mode 100644 index 00000000..30a94b66 --- /dev/null +++ b/zh/9.6/tsearch2.sgml @@ -0,0 +1,173 @@ + + + + tsearch2 + + + tsearch2 + + + + tsearch2 模块为那些在文本搜索于 8.3 版集成进 + PostgreSQL 核心之前使用过 + tsearch2 的应用提供向后兼容的文本搜索功能。 + + + + 可移植性问题 + + + 尽管内置的文本搜索特性以 tsearch2 为基础, + 并且与它大体相似,但仍存在许多细微差异,会给现有应用带来可移植性问题: + + + + + + 一些函数的名称发生了变化,例如 rank 改为 + ts_rank。替代的 tsearch2 模块 + 提供了使用旧名称的别名。 + + + + + + 内置文本搜索的数据类型和函数都存在于系统模式 pg_catalog 中。 + 在使用 tsearch2 的安装中,这些对象通常位于 + public 模式,不过也有些用户选择把它们放在自己单独的模式里。 + 因此,无论哪种情况,对这些对象的显式模式限定引用都会失败。替代的 + tsearch2 模块提供了存储在 public + (必要时也可放在其他模式)中的别名对象,使这类引用仍能工作。 + + + + + + 内置文本搜索特性中没有当前解析器当前词典 + 的概念,只有当前搜索配置(由 default_text_search_config + 参数设置)的概念。虽然当前解析器和当前词典只被用于调试目的的函数使用, + 但在某些情况下这仍可能构成移植障碍。替代的 tsearch2 + 模块模拟了这些额外的状态变量,并提供用于设置和获取它们的向后兼容函数。 + + + + + + 还有一些问题是替代的 tsearch2 模块没有解决的, + 因而无论如何都需要修改应用代码: + + + + + + 旧的 tsearch2 触发器函数允许其参数列表中的项是函数名, + 这些函数会在文本数据被转换为 tsvector 格式之前对其调用。 + 这项功能因为属于安全漏洞而被移除,因为无法保证被调用的函数就是原本想要调用的那个。如果数据在建立索引之前必须经过处理,推荐的做法是编写一个自定义 + 触发器自行完成这项工作。 + + + + + + 文本搜索配置信息已被移入核心系统目录中,这些目录与 + tsearch2 使用的表明显不同。任何检查或修改那些表 + 的应用都需要调整。 + + + + + + 如果应用使用了任何自定义文本搜索配置,就需要使用新的文本搜索配置 SQL + 命令在核心目录中重建它们。替代的 tsearch2 模块对此 + 提供了一点支持:它使旧的一组 tsearch2 配置表 + 可以装载到 PostgreSQL 8.3 中。(如果没有该模块, + 配置数据将无法装载,因为 regprocedure 列中的值无法解析为函数。) + 虽然这些配置表实际上不会任何事情,但至少在 8.3 中 + 设置等效的自定义配置时可以查阅它们的内容。 + + + + + + 不支持旧的 reset_tsearch() 和 + get_covers() 函数。 + + + + + + 替代的 tsearch2 模块没有定义任何别名操作符, + 而是完全依赖内置操作符。只有当应用使用了显式模式限定的操作符名称时 + 这才会成为问题,而这种情况非常少见。 + + + + + + + + 转换 8.3 之前的安装 + + + 更新一个使用 tsearch2 的 8.3 之前安装的推荐方法是: + + + + + + 按通常方式从旧安装中做一个转储,但务必不要使用 + pg_dumppg_dumpall + 的 -c--clean)选项。 + + + + + + 在新安装中创建空数据库,并将替代的 tsearch2 模块安装到 + 每个要使用文本搜索的数据库中。这必须在装载转储数据之前 + 完成!如果你的旧安装把 tsearch2 对象放在了 + public 以外的模式中,请务必调整 + CREATE EXTENSION 命令,使替代对象创建在同一模式中。 + + + + + + 装载转储数据。由于无法重新创建原始的 tsearch2 + 对象,会报告相当多的错误。这些错误可以忽略,但这意味着你不能在单个事务中 + 恢复转储(例如,不能使用 pg_restore 的 + 开关)。 + + + + + + 检查恢复出的 tsearch2 配置表 + (pg_ts_cfg 等)的内容,并按需创建等效的内置 + 文本搜索配置。从中提取出所有有用信息之后,就可以删除旧的配置表。 + + + + + + 测试你的应用。 + + + + + + 之后你也许希望把应用中对别名文本搜索对象的引用改名,以便最终卸载替代的 + tsearch2 模块。 + + + + + + 参考文献 + + Tsearch2 开发站点 + + + + + diff --git a/zh/9.6/tsm-system-rows.sgml b/zh/9.6/tsm-system-rows.sgml new file mode 100644 index 00000000..dd9023dd --- /dev/null +++ b/zh/9.6/tsm-system-rows.sgml @@ -0,0 +1,56 @@ + + + + tsm_system_rows + + + tsm_system_rows + + + + tsm_system_rows模块提供表采样方法 + SYSTEM_ROWS,它可用于 + 命令的TABLESAMPLE子句中。 + + + + 这种表采样方法接受一个整数参数,表示最多读取多少行。除非表中没有足够的行,否则生成的样本将始终恰好包含这么多行;如果表中的行数不足,则会选取整个表中的所有行。 + + + + 与内置的SYSTEM采样方法一样, + SYSTEM_ROWS执行块级采样,因此采样并不是完全随机的, + 可能会受到聚簇效应的影响,特别是在只请求少量行时。 + + + + SYSTEM_ROWS不支持 + REPEATABLE子句。 + + + + 示例 + + + 下面是一个使用SYSTEM_ROWS从表中选取样本的示例。 + 首先安装扩展: + + + +CREATE EXTENSION tsm_system_rows; + + + + 然后就可以在SELECT命令中使用它,例如: + + +SELECT * FROM my_table TABLESAMPLE SYSTEM_ROWS(100); + + + + + 这个命令将从表my_table中返回一个包含 100 行的样本(除非该表没有 100 个可见行,在这种情况下将返回其中所有行)。 + + + + diff --git a/zh/9.6/tsm-system-time.sgml b/zh/9.6/tsm-system-time.sgml new file mode 100644 index 00000000..477b2c1e --- /dev/null +++ b/zh/9.6/tsm-system-time.sgml @@ -0,0 +1,58 @@ + + + + tsm_system_time + + + tsm_system_time + + + + tsm_system_time模块提供表采样方法 + SYSTEM_TIME,它可用于 + 命令的TABLESAMPLE子句中。 + + + + 这种表采样方法接受一个浮点参数,表示读取该表时最多可花费多少毫秒。 + 这使你能够直接控制查询耗时,但代价是样本大小会变得难以预测。 + 除非先读完整个表,否则所得样本将包含在指定时间内能够读取到的尽可能多的行。 + + + + 与内置的SYSTEM采样方法一样, + SYSTEM_TIME执行块级采样,因此采样并不是完全随机的, + 可能会受到聚簇效应的影响,特别是在只选取少量行时。 + + + + SYSTEM_TIME不支持 + REPEATABLE子句。 + + + + 示例 + + + 下面是一个使用SYSTEM_TIME从表中选取样本的示例。 + 首先安装扩展: + + + +CREATE EXTENSION tsm_system_time; + + + + 然后就可以在SELECT命令中使用它,例如: + + +SELECT * FROM my_table TABLESAMPLE SYSTEM_TIME(1000); + + + + + 这个命令将返回在 1 秒(1000 毫秒)内能够从my_table读取到的尽可能大的样本。当然,如果整个表在不到 1 秒内就能读完,则会返回其中所有行。 + + + + diff --git a/zh/9.6/typeconv.sgml b/zh/9.6/typeconv.sgml new file mode 100644 index 00000000..7431b109 --- /dev/null +++ b/zh/9.6/typeconv.sgml @@ -0,0 +1,866 @@ + + + +类型转换 + + + 数据类型 + 转换 + + + +SQL语句可能有意或无意地要求在同一表达式中混用不同的数据类型。PostgreSQL提供了丰富的机制来求值这类混合类型表达式。 + + + +在很多情况下,用户并不需要理解类型转换机制的细节。不过,PostgreSQL执行的隐式类型转换会影响查询结果。必要时,可以使用显式类型转换来控制这些结果。 + + + +本章介绍PostgreSQL的类型转换机制和约定。有关特定数据类型以及允许使用的函数和操作符的更多信息,请参阅中的相关章节。 + + + +概述 + + +SQL是一种强类型语言。也就是说,每个数据项都关联着一个决定其行为和允许用法的数据类型。PostgreSQL拥有可扩展的类型系统,比其他SQL实现更通用也更灵活。因此,PostgreSQL中的大多数类型转换行为都由通用规则控制,而不是依赖临时性的启发式规则。这使得即使表达式中包含用户定义类型,也能够使用混合类型表达式。 + + + +PostgreSQL扫描器/解析器将词法元素分成五个基本类别:整数、非整数数字、字符串、标识符和关键字。大多数非数值类型的常量会先被归类为字符串。SQL语言定义允许给字符串指定类型名,而PostgreSQL可以利用这一机制让解析器从一开始就沿着正确的路径处理。例如,查询: + + +SELECT text 'Origin' AS "label", point '(0,0)' AS "value"; + + label | value +--------+------- + Origin | (0,0) +(1 row) + + +包含两个字面量,它们的类型分别是textpoint。如果没有为某个字符串字面量指定类型,那么它最初会被赋予占位类型unknown,并在后文所述的后续阶段解析。 + + + +在PostgreSQL解析器中,有四种基本的SQL结构需要采用不同的类型转换规则: + + + + +函数调用 + + + +PostgreSQL类型系统的很大一部分建立在一套丰富的函数之上。函数可以有一个或多个参数。由于PostgreSQL支持函数重载,单凭函数名无法唯一确定要调用哪个函数;解析器必须根据所提供参数的数据类型选择正确的函数。 + + + + + +操作符 + + + +PostgreSQL允许表达式使用前缀和后缀一元(单参数)操作符,以及二元(双参数)操作符。与函数一样,操作符也可以重载,因此同样存在选择正确操作符的问题。 + + + + + +值存储 + + + +SQLINSERTUPDATE语句会把表达式的结果放入表中。语句中的表达式必须与目标列的类型匹配,并且在必要时转换为目标列类型。 + + + + + +UNIONCASE和相关结构 + + + +由于经UNION合并的SELECT语句的所有查询结果都必须出现在同一组列中,因此每个SELECT子句的结果类型必须彼此匹配,并转换为统一的一组类型。类似地,CASE结构中的结果表达式必须转换为某种公共类型,这样整个CASE表达式才有确定的输出类型。其他一些结构,如ARRAY[]以及GREATESTLEAST函数,也同样需要为若干子表达式确定公共类型。 + + + + + + + +系统目录保存了哪些数据类型之间存在哪些类型转换,以及如何执行这些转换的信息。用户还可以使用命令添加额外的类型转换。(这通常与定义新数据类型一并完成。内置类型之间的类型转换集合经过精心设计,最好不要改动。) + + + + 数据类型 + 分类 + + + +解析器还提供了一种额外的启发式规则,可在存在隐式类型转换的一组类型之间更准确地判定适当的转换行为。数据类型被划分为若干基本的类型分类,包括booleannumericstringbitstringdatetimetimespangeometricnetwork以及用户定义类型。(列表见;不过也可以创建自定义类型分类。)每个分类中可以有一个或多个首选类型,当存在多种可能类型可选时,会优先选择它们。通过仔细选择首选类型和可用的隐式类型转换,可以确保有歧义的表达式(即存在多个候选解析方案的表达式)能够以有用的方式被解析。 + + + +所有类型转换规则都以以下几个原则为基础: + + + + +隐式转换绝不应产生令人意外或无法预测的结果。 + + + + + +如果查询不需要隐式类型转换,解析器和执行器就不应承担额外开销。也就是说,如果查询本身书写正确且类型已经匹配,那么执行时不应让解析器额外耗时,也不应在查询中引入不必要的隐式类型转换调用。 + + + + + +此外,如果某个查询通常需要先对参数做隐式类型转换才能调用某个函数,而此后用户又定义了一个参数类型正确的新函数,那么解析器应改为使用这个新函数,而不再通过隐式转换去调用旧函数。 + + + + + + + + +操作符 + + + 操作符 + 调用中的类型解析 + + + + 操作符表达式所引用的具体操作符按照下述过程确定。注意,该过程还会间接受到相关操作符优先级的影响,因为优先级决定了哪些子表达式会被视为哪些操作符的输入。详见。 + + + +操作符类型解析 + + + + +从系统目录pg_operator中选取要考虑的操作符。如果使用的是未带模式限定的操作符名(通常如此),则考虑当前搜索路径中可见且名称和参数个数匹配的操作符(见)。如果给出了限定名,则只考虑指定模式中的操作符。 + + + + + + +如果搜索路径中找到多个参数类型完全相同的操作符,只考虑路径中最早出现的那一个。参数类型不同的操作符则不受搜索路径位置影响,处于同等地位。 + + + + + + + +检查是否存在一个恰好接受输入参数类型的操作符。如果存在(在被考虑的操作符集合中至多只有一个精确匹配),就使用它。若通过限定名调用(这种情况并不常见)某个允许不受信任的用户创建对象的模式中的操作符,而又缺少精确匹配,就会带来安全隐患 + + + + 对于未带模式限定的名称,不会出现这种隐患,因为包含允许不受信任的用户创建对象的模式的搜索路径不是一种模式的安全使用方式。 + + +。在这种情况下,应通过对参数进行类型转换来强制获得精确匹配。 + + + + + +如果二元操作符调用的一个参数是unknown类型,则在本次检查中假定它与另一个参数类型相同。涉及两个unknown输入的调用,或带有unknown输入的一元操作符,在这一步都不可能找到匹配。 + + + + + +如果二元操作符调用的一个参数是unknown类型,而另一个是域类型,则接着检查是否存在两边都恰好接受该域基础类型的操作符;如果有,就使用它。 + + + + + + + +寻找最佳匹配。 + + + + + +丢弃那些输入类型不匹配,且也无法通过隐式类型转换实现匹配的候选操作符。为此,假定unknown字面量可以转换为任何类型。如果只剩一个候选操作符,就使用它;否则继续下一步。 + + + + + +如果任何输入参数是域类型,则在后续所有步骤中都把它当作该域的基础类型。这样可以确保在解决操作符歧义时,域的行为与其基础类型一致。 + + + + + +遍历所有候选操作符,保留那些在输入类型上精确匹配数最多的候选项。如果没有任何候选项存在精确匹配,则保留全部候选项。如果只剩一个候选操作符,就使用它;否则继续下一步。 + + + + + +遍历所有候选操作符,在需要类型转换的位置上,保留接受首选类型(属于输入数据类型的类型分类)的位置数最多的候选项。如果没有候选项接受首选类型,则保留全部候选项。如果只剩一个候选操作符,就使用它;否则继续下一步。 + + + + +如果有任何输入参数是unknown,就检查剩余候选操作符在这些参数位置上接受哪些类型分类。在每个位置上,如果有任何候选项接受string分类,就选择该分类。(这种偏向字符串的做法是合适的,因为unknown类型的字面量看起来像字符串。)否则,如果所有剩余候选项都接受同一种类型分类,就选择该分类;否则失败,因为在缺少更多线索的情况下无法推断出正确选择。然后丢弃不接受所选类型分类的候选项。此外,如果有任何候选项接受该分类中的首选类型,就丢弃在该参数上接受非首选类型的候选项。如果没有候选项通过这些测试,则保留全部候选项。如果只剩一个候选操作符,就使用它;否则继续下一步。 + + + + + +如果同时存在unknown参数和已知类型参数,且所有已知类型参数都具有相同类型,则假定unknown参数也属于该类型,并检查哪些候选项能在unknown参数位置接受该类型。如果恰好只有一个候选项通过测试,就使用它;否则失败。 + + + + + + + +下面是一些示例。 + + + +阶乘操作符类型解析 + + +标准目录中只定义了一个阶乘操作符(后缀 !),它接受一个类型为 +bigint 的参数。扫描器为以下查询表达式中的参数赋予初始类型 integer: + +SELECT 40 ! AS "40 factorial"; + + 40 factorial +-------------------------------------------------- + 815915283247897734345611269596115894272000000000 +(1 row) + + +因此,解析器会对操作数进行类型转换,该查询等价于: + + +SELECT CAST(40 AS bigint) ! AS "40 factorial"; + + + + + +字符串连接操作符类型解析 + + +处理字符串类型以及复杂的扩展类型时,常常会使用类似字符串的语法。未指定类型的字符串会与可能的操作符候选项进行匹配。 + + + +一个参数未指定类型的例子: + +SELECT text 'abc' || 'def' AS "text and unknown"; + + text and unknown +------------------ + abcdef +(1 row) + + + + +在这种情况下,解析器会查看是否存在一个两边参数都接受text的操作符。既然存在,它就会假定第二个参数应解释为text类型。 + + + +下面是两个未指定类型值的连接: + +SELECT 'abc' || 'def' AS "unspecified"; + + unspecified +------------- + abcdef +(1 row) + + + + +这里对使用哪种类型没有初始提示,因为查询中没有指定任何类型。因此,解析器会查找所有候选操作符,并发现既有接受字符串分类输入的候选项,也有接受位串分类输入的候选项。由于在可用时优先选择字符串分类,所以会选中该分类,再使用字符串的首选类型text作为解析这些unknown类型字面量的具体类型。 + + + + +绝对值与取反操作符类型解析 + + +PostgreSQL操作符目录中为前缀操作符@提供了多个条目,它们分别实现各种数值数据类型的绝对值操作。其中一个条目对应float8,它是数值分类中的首选类型。因此,当遇到一个unknown输入时,PostgreSQL会使用该条目: + +SELECT @ '-4.5' AS "abs"; + abs +----- + 4.5 +(1 row) + +这里,系统在应用所选操作符之前,已经把unknown类型字面量隐式解析为float8。我们可以验证使用的确实是float8,而不是其他类型: + +SELECT @ '-4.5e500' AS "abs"; + +ERROR: "-4.5e500" is out of range for type double precision + + + + +另一方面,前缀操作符~(按位取反)只对整数数据类型定义,并没有为float8定义。因此,如果对~试一个类似的例子,会得到: + +SELECT ~ '20' AS "negation"; + +ERROR: operator is not unique: ~ "unknown" +HINT: Could not choose a best candidate operator. You might need to add +explicit type casts. + +这是因为系统无法决定几个可能的~操作符中应优先选择哪一个。我们可以通过显式类型转换来帮助它: + +SELECT ~ CAST('20' AS int8) AS "negation"; + + negation +---------- + -21 +(1 row) + + + + + +数组包含操作符类型解析 + + +下面是另一个解析带有一个已知类型输入和一个未知类型输入的操作符的例子: + +SELECT array[1,2] <@ '{1,2,3}' as "is subset"; + + is subset +----------- + t +(1 row) + +PostgreSQL操作符目录中为中缀操作符<@定义了多个条目,但左侧能够接受整数数组的只有两种:数组包含(anyarray <@ anyarray)和范围包含(anyelement <@ anyrange)。由于这些多态伪类型(见)都不被视为首选类型,解析器无法据此消除歧义。不过,要求它假定unknown类型字面量与另一输入具有相同类型,也就是整数数组。这样两个操作符中只有一个能够匹配,因此会选择数组包含。(如果选中范围包含,就会报错,因为该字符串的格式并不是合法的范围字面量。) + + + + + +域类型上的自定义操作符 + + +用户有时会尝试声明只适用于某个域类型的操作符。这是可行的,但远没有看上去那样有用,因为操作符解析规则被设计成优先选择作用于域基础类型的操作符。考虑下面的例子: + +CREATE DOMAIN mytext AS text CHECK(...); +CREATE FUNCTION mytext_eq_text (mytext, text) RETURNS boolean AS ...; +CREATE OPERATOR = (procedure=mytext_eq_text, leftarg=mytext, rightarg=text); +CREATE TABLE mytable (val mytext); + +SELECT * FROM mytable WHERE val = 'foo'; + +这个查询不会使用自定义操作符。解析器首先会检查是否存在mytext = mytext操作符(),但并不存在;然后它会考虑该域的基础类型text,并检查是否存在text = text操作符(),而这是存在的;于是它会把unknown类型的字面量解析为text,并使用text = text操作符。要让自定义操作符被使用,唯一的方法是显式地对该字面量做类型转换: + +SELECT * FROM mytable WHERE val = text 'foo'; + +这样就会根据精确匹配规则立即找到mytext = text操作符。如果解析过程进入最佳匹配规则,它们会主动排斥作用于域类型的操作符。如果不是这样,这类操作符会导致过多的操作符歧义失败,因为类型转换规则总是把域视为可以转换到其基础类型或从其基础类型转换而来,因此域操作符会在所有与基础类型上同名操作符相同的场景中都被视为可用。 + + + + + + +函数 + + + 函数 + 调用中的类型解析 + + + + 函数调用所引用的具体函数按照下述过程确定。 + + + +函数类型解析 + + + +从系统目录pg_proc中选取要考虑的函数。如果使用的是未带模式限定的函数名,则考虑当前搜索路径中可见且名称和参数个数匹配的函数(见)。如果给出了限定名,则只考虑指定模式中的函数。 + + + + + + +如果搜索路径中找到多个参数类型完全相同的函数,只考虑路径中最早出现的那一个。参数类型不同的函数则不受搜索路径位置影响,处于同等地位。 + + + + +如果函数声明了一个VARIADIC数组参数,而调用时没有使用VARIADIC关键字,则把数组参数视为一个或多个其元素类型的参数,个数按匹配该调用的需要确定。扩展之后,该函数的有效参数类型可能与某个非VARIADIC函数完全相同。在这种情况下,使用搜索路径中较早出现的函数;如果两个函数位于同一模式,则优先选择非VARIADIC函数。 + + +通过限定名调用某个允许不受信任的用户创建对象的模式中找到的可变参数函数时,会带来安全隐患 + + + + 对于未带模式限定的名称,不会出现这种隐患,因为包含允许不受信任的用户创建对象的模式的搜索路径不是一种模式的安全使用方式。 + + 。恶意用户可以借此劫持调用,并以你的身份执行任意 SQL 函数。改为使用带有VARIADIC关键字的调用可以绕过这一隐患。对于填充VARIADIC "any"参数的调用,往往没有包含VARIADIC关键字的等价写法。要安全地发出这类调用,该函数所在模式必须只允许受信任的用户创建对象。 + + + + + +带有参数默认值的函数,被认为可以匹配任何省略了零个或多个可使用默认值的参数位置的调用。如果不止一个这样的函数能匹配某个调用,则使用搜索路径中最早出现的那一个。如果同一模式中存在两个或更多这样的函数,并且它们在未使用默认值的参数位置上的参数类型相同(如果它们具有不同的带有默认值的参数集合,这种情况是可能的),系统就无法判断该偏好哪一个;如果找不到更好的匹配,就会报有歧义的函数调用错误。 + + + +通过限定名调用某个允许不受信任的用户创建对象的模式中的任意函数时,会带来可用性隐患。恶意用户可以用现有函数的名字创建一个新函数,复制该函数的参数,并附加带有默认值的新参数。这样就会阻止对原始函数的新调用。为避免这种隐患,应把函数放在只允许受信任的用户创建对象的模式中。 + + + + + + + + +检查是否存在一个恰好接受输入参数类型的函数。如果存在(在被考虑的函数集合中至多只有一个精确匹配),就使用它。若通过限定名调用某个允许不受信任的用户创建对象的模式中的函数,而又缺少精确匹配,就会带来安全隐患。在这种情况下,应通过对参数进行类型转换来强制获得精确匹配。(涉及unknown的情况在这一步永远找不到匹配。) + + + + + +如果没有找到精确匹配,就检查该函数调用是否看起来像一种特殊的类型转换请求。这种情况发生在:函数调用只有一个参数,且函数名与某个数据类型的(内部)名称相同。此外,函数参数必须是unknown类型字面量,或者是一种可以二进制强制转换为该命名数据类型的类型,或者是一种可以通过应用其 I/O 函数转换为该命名数据类型的类型(也就是说,该转换要么转到某种标准字符串类型,要么从某种标准字符串类型转来)。满足这些条件时,该函数调用会被当作一种CAST形式。 + + + 之所以有这一步,是为了在不存在实际类型转换函数的情况下仍支持函数风格的类型转换写法。如果存在类型转换函数,按惯例它会以其输出类型命名,因此无需为此设置特殊情况。更多说明见。 + + + + + + +寻找最佳匹配。 + + + + + +丢弃那些输入类型不匹配,且也无法通过隐式类型转换实现匹配的候选函数。为此,假定unknown字面量可以转换为任何类型。如果只剩一个候选函数,就使用它;否则继续下一步。 + + + + + +如果任何输入参数是域类型,则在后续所有步骤中都把它当作该域的基础类型。这样可以确保在解决函数歧义时,域的行为与其基础类型一致。 + + + + + +遍历所有候选函数,保留那些在输入类型上精确匹配数最多的候选项。如果没有任何候选项存在精确匹配,则保留全部候选项。如果只剩一个候选函数,就使用它;否则继续下一步。 + + + + + +遍历所有候选函数,在需要类型转换的位置上,保留接受首选类型(属于输入数据类型的类型分类)的位置数最多的候选项。如果没有候选项接受首选类型,则保留全部候选项。如果只剩一个候选函数,就使用它;否则继续下一步。 + + + + +如果有任何输入参数是unknown,就检查剩余候选函数在这些参数位置上接受哪些类型分类。在每个位置上,如果有任何候选项接受string分类,就选择该分类。(这种偏向字符串的做法是合适的,因为unknown类型的字面量看起来像字符串。)否则,如果所有剩余候选项都接受同一种类型分类,就选择该分类;否则失败,因为在缺少更多线索的情况下无法推断出正确选择。然后丢弃不接受所选类型分类的候选项。此外,如果有任何候选项接受该分类中的首选类型,就丢弃在该参数上接受非首选类型的候选项。如果没有候选项通过这些测试,则保留全部候选项。如果只剩一个候选函数,就使用它;否则继续下一步。 + + + + + +如果同时存在unknown参数和已知类型参数,且所有已知类型参数都具有相同类型,则假定unknown参数也属于该类型,并检查哪些候选项能在unknown参数位置接受该类型。如果恰好只有一个候选项通过测试,就使用它;否则失败。 + + + + + + + +注意,操作符类型解析和函数类型解析的最佳匹配规则是完全相同的。下面是一些示例。 + + + + +round 函数参数类型解析 + + +只有一个接受两个参数的round函数;它的第一个参数类型是numeric,第二个参数类型是integer。因此下面的查询会自动把类型为integer的第一个参数转换为numeric: + + +SELECT round(4, 4); + + round +-------- + 4.0000 +(1 row) + + +该查询实际上会被解析器改写成: + +SELECT round(CAST (4 AS numeric), 4); + + + + +由于带小数点的数字常量最初会被赋予numeric类型,下面的查询不需要类型转换,因此可能稍微更高效一些: + +SELECT round(4.0, 4); + + + + + +可变参数函数解析 + + + +CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int + LANGUAGE sql AS 'SELECT 1'; +CREATE FUNCTION + + +这个函数接受但不要求使用VARIADIC关键字。它既能接受integer参数,也能接受numeric参数: + + +SELECT public.variadic_example(0), + public.variadic_example(0.0), + public.variadic_example(VARIADIC array[0.0]); + variadic_example | variadic_example | variadic_example +------------------+------------------+------------------ + 1 | 1 | 1 +(1 row) + + +但是,如果有更具体的函数可用,第一和第二个调用会优先选择它们: + + +CREATE FUNCTION public.variadic_example(numeric) RETURNS int + LANGUAGE sql AS 'SELECT 2'; +CREATE FUNCTION + +CREATE FUNCTION public.variadic_example(int) RETURNS int + LANGUAGE sql AS 'SELECT 3'; +CREATE FUNCTION + +SELECT public.variadic_example(0), + public.variadic_example(0.0), + public.variadic_example(VARIADIC array[0.0]); + variadic_example | variadic_example | variadic_example +------------------+------------------+------------------ + 3 | 2 | 1 +(1 row) + + +在默认配置下,且只存在第一个函数时,第一和第二个调用并不安全。任何用户都可以通过创建第二个或第三个函数来劫持它们。第三个调用由于参数类型精确匹配且使用了VARIADIC关键字,因此是安全的。 + + + + + +substr 函数类型解析 + + +存在多个substr函数,其中一个接受textinteger。如果以未指定类型的字符串常量调用,系统会选择接受首选类型分类string参数的候选函数(也就是text类型的那个)。 + + +SELECT substr('1234', 3); + + substr +-------- + 34 +(1 row) + + + + +如果字符串被声明为varchar类型,例如它来自表中的某一列,那么解析器会尝试把它转换为text: + +SELECT substr(varchar '1234', 3); + + substr +-------- + 34 +(1 row) + + +解析器会把它改写成如下形式: + +SELECT substr(CAST (varchar '1234' AS text), 3); + + + + + + +解析器从pg_cast目录得知textvarchar二进制兼容,这意味着把其中一种类型传给接受另一种类型的函数时,无需做任何物理转换。因此,这种情况下实际上不会插入类型转换调用。 + + + + + +如果用integer类型参数调用该函数,解析器会尝试把它转换为text: + +SELECT substr(1234, 3); +ERROR: function substr(integer, integer) does not exist +HINT: No function matches the given name and argument types. You might need +to add explicit type casts. + + +这行不通,因为integer并没有到text的隐式类型转换。不过,显式类型转换可以: + +SELECT substr(CAST (1234 AS text), 3); + + substr +-------- + 34 +(1 row) + + + + + + + +值存储 + + + 要插入表中的值会按照以下步骤转换为目标列的数据类型。 + + + +值存储的类型转换 + + + + +检查是否与目标类型精确匹配。 + + + + + +否则,尝试把表达式转换为目标类型。如果这两种类型之间在pg_cast目录中登记了赋值类型转换(见),就可以这样做。另一种情况是,如果表达式是unknown类型字面量,则会把字符串字面量的内容送入目标类型的输入转换例程。 + + + + + + +检查目标类型是否有尺寸调整类型转换。尺寸调整类型转换是该类型到其自身的类型转换。如果在pg_cast目录中找到了这种转换,就在把表达式存入目标列之前先应用它。此类类型转换的实现函数总是额外接受一个integer参数,用来接收目标列的atttypmod值(通常是其声明长度,尽管不同数据类型对atttypmod的解释各不相同);它还可能接受第三个boolean参数,用来说明该类型转换是显式还是隐式的。类型转换函数负责应用所有与长度相关的语义,例如长度检查或截断。 + + + + + + +<type>character</type> 的存储类型转换 + + +对于声明为character(20)的目标列,下面的语句表明存储值会被调整到正确的长度: + + +CREATE TABLE vv (v character(20)); +INSERT INTO vv SELECT 'abc' || 'def'; +SELECT v, octet_length(v) FROM vv; + + v | octet_length +----------------------+-------------- + abcdef | 20 +(1 row) + + + + +这里实际发生的是,两个unknown类型字面量默认都被解析为text,从而使||操作符被解析为text的连接操作。然后,操作符得到的text结果被转换为bpchar用空格填充的字符,blank-padded char,即character数据类型的内部名称),以匹配目标列类型。(由于从textbpchar的转换是二进制可强制转换的,这一步不会插入任何真实的函数调用。)最后,系统从系统目录中找到尺寸调整函数bpchar(bpchar, integer, boolean),并把它应用到操作符结果以及存储列长度上。这个特定于类型的函数会执行所需的长度检查并补齐填充空格。 + + + + + +<literal>UNION</literal>、<literal>CASE</literal>及相关结构 + + + UNION + 结果类型的确定 + + + + CASE + 结果类型的确定 + + + + ARRAY + 结果类型的确定 + + + + VALUES + 结果类型的确定 + + + + GREATEST + 结果类型的确定 + + + + LEAST + 结果类型的确定 + + + +SQL 的UNION结构必须让可能不同的类型彼此匹配,以形成单一结果集。该解析算法会分别应用到联合查询的每个输出列。INTERSECTEXCEPT结构以与UNION相同的方式解析不同类型。其他一些结构,包括CASEARRAYVALUES以及GREATESTLEAST函数,也使用完全相同的算法来匹配其组成表达式并选择结果数据类型。 + + + +<literal>UNION</literal>、<literal>CASE</literal>及相关结构的类型解析 + + + + +如果所有输入都是同一类型,且该类型不是unknown,就解析为该类型。 + + + + + + +如果任何输入是域类型,则在后续所有步骤中都把它当作该域的基础类型。 + + + 这在某种程度上类似于操作符和函数对域输入的处理方式;只要用户确保所有输入都隐式或显式地正好属于该域类型,这种行为就能让域类型在UNION或类似结构中得以保留。否则,就会使用该域的基础类型。 + + + + + + + + +如果所有输入都是unknown类型,则解析为text类型(字符串分类的首选类型)。否则,忽略unknown输入。 + + + + + +如果非unknown输入并非全都属于同一类型分类,则失败。 + + + + + +选择第一个非unknown输入的类型作为候选类型,然后按从左到右的顺序考虑其余每个非unknown输入类型。 + + + 出于历史原因,CASE会把其ELSE子句(如果有)视为第一个输入,THEN子句则在其后考虑。其他所有情况中,从左到右都指表达式在查询文本中出现的顺序。 + + +如果候选类型可以隐式转换为另一类型,而反过来不行,则把另一类型选为新的候选类型。然后继续考虑剩余输入。如果在此过程的任何阶段选中了首选类型,就停止继续考虑其他输入。 + + + + + + +把所有输入都转换为最终候选类型。如果某个输入类型到候选类型不存在隐式转换,则失败。 + + + + + +下面是一些示例。 + + + +<literal>UNION</literal> 中未充分指定类型时的类型解析 + + + +SELECT text 'a' AS "text" UNION SELECT 'b'; + + text +------ + a + b +(2 rows) + +这里,unknown类型字面量'b'会被解析为text类型。 + + + + +简单 <literal>UNION</literal> 中的类型解析 + + + +SELECT 1.2 AS "numeric" UNION SELECT 1; + + numeric +--------- + 1 + 1.2 +(2 rows) + +字面量1.2的类型是numeric,而integer1可以隐式转换为numeric,因此使用该类型。 + + + + +次序对调的 <literal>UNION</literal> 中的类型解析 + + + +SELECT 1 AS "real" UNION SELECT CAST('2.2' AS REAL); + + real +------ + 1 + 2.2 +(2 rows) + +这里,由于real类型不能隐式转换为integer,而integer可以隐式转换为real,因此UNION结果类型被解析为real。 + + + + +嵌套 <literal>UNION</literal> 中的类型解析 + + + +SELECT NULL UNION SELECT NULL UNION SELECT 1; + +ERROR: UNION types text and integer cannot be matched + +之所以失败,是因为PostgreSQL把多个UNION视为由成对操作组成的嵌套;也就是说,这个输入等同于: + +(SELECT NULL UNION SELECT NULL) UNION SELECT 1; + +根据上述规则,内层UNION会被解析为输出text类型。随后外层UNION的输入类型分别是textinteger,于是就得到上面的错误。解决办法是确保最左边的UNION至少有一个输入属于期望的结果类型。 + + + +INTERSECTEXCEPT也同样按成对方式解析。不过,本节介绍的其他结构会在一次解析步骤中同时考虑它们的所有输入。 + + + + diff --git a/zh/9.6/unaccent.sgml b/zh/9.6/unaccent.sgml new file mode 100644 index 00000000..0bc0bfe7 --- /dev/null +++ b/zh/9.6/unaccent.sgml @@ -0,0 +1,153 @@ + + + + unaccent + + + unaccent + + + + unaccent是一个文本搜索词典,它会从词位中移除重音符号(变音符号)。它是一个过滤字典,也就是说,它的输出总会被传递给下一个词典(如果有),这不同于词典的通常行为。这使得全文搜索能够以不区分重音的方式处理文本。 + + + + unaccent当前的实现还不能作为thesaurus词典的正规化字典使用。 + + + + 配置 + + + unaccent词典接受下列选项: + + + + + RULES是包含转换规则列表的文件的基名。该文件必须存放在$SHAREDIR/tsearch_data/中(其中$SHAREDIR表示PostgreSQL安装的共享数据目录)。其名称必须以.rules结尾,但在RULES参数中不应包含这个后缀。 + + + + + 规则文件的格式如下: + + + + + 每一行表示一条转换规则,由一个带重音的字符和一个不带重音的字符组成。前者会被转换成后者。例如: + +À A +Á A +Â A +Ã A +Ä A +Å A +Æ AE + + 这两个字符必须以空白分隔,并且每行开头或结尾的空白都会被忽略。 + + + + + + 另一种情况是,如果某一行只给出一个字符,则该字符的各次出现都会被删除;这对那些用独立字符表示重音的语言很有用。 + + + + + + 实际上,这里的每个字符都可以是不包含空白的任意字符串,因此unaccent词典除了用于去除变音符号之外,也可用于其他类型的子串替换。 + + + + + 与其他PostgreSQL文本搜索配置文件一样,规则文件必须以 UTF-8 编码存储。加载时,数据会自动转换为当前数据库的编码。任何包含无法转换的字符的行都会被静默忽略,因此规则文件可以包含不适用于当前编码的规则。 + + + + + 一个更完整并且对大多数欧洲语言都可直接使用的示例可见于unaccent.rules。安装unaccent模块时,该文件会被安装到$SHAREDIR/tsearch_data/中。这个规则文件会把带重音的字符转换成对应的不带重音字符,同时还会把连字展开为等价的一串简单字符(例如,将 Æ 转换为 AE)。 + + + + + 用法 + + + 安装unaccent扩展会创建一个文本搜索模板unaccent以及一个基于该模板的词典unaccentunaccent词典的默认参数设置是RULES='unaccent',因此它可立即配合标准的unaccent.rules文件使用。如果愿意,也可以修改这个参数,例如 + + +mydb=# ALTER TEXT SEARCH DICTIONARY unaccent (RULES='my_rules'); + + + 或者基于该模板创建新的词典。 + + + + 要测试该词典,可以尝试: + +mydb=# select ts_lexize('unaccent','Hôtel'); + ts_lexize +----------- + {Hotel} +(1 row) + + + + + 下面的示例展示了如何将unaccent词典插入到文本搜索配置中: + +mydb=# CREATE TEXT SEARCH CONFIGURATION fr ( COPY = french ); +mydb=# ALTER TEXT SEARCH CONFIGURATION fr + ALTER MAPPING FOR hword, hword_part, word + WITH unaccent, french_stem; +mydb=# select to_tsvector('fr','Hôtels de la Mer'); + to_tsvector +------------------- + 'hotel':1 'mer':4 +(1 row) + +mydb=# select to_tsvector('fr','Hôtel de la Mer') @@ to_tsquery('fr','Hotels'); + ?column? +---------- + t +(1 row) + +mydb=# select ts_headline('fr','Hôtel de la Mer',to_tsquery('fr','Hotels')); + ts_headline +------------------------ + <b>Hôtel</b> de la Mer +(1 row) + + + + + + 函数 + + + unaccent()函数会从给定字符串中移除重音符号(变音符号)。从本质上说,它是对unaccent这一类型词典的一个包装器,但也可以在常规文本搜索环境之外使用。 + + + + unaccent + + + +unaccent(dictionary regdictionary, string text) returns text + + + + 如果省略dictionary参数,则会使用与unaccent()函数本身位于同一模式中、名为unaccent的文本搜索词典。 + + + + 例如: + +SELECT unaccent('unaccent', 'Hôtel'); +SELECT unaccent('Hôtel'); + + + + + diff --git a/zh/9.6/user-manag.sgml b/zh/9.6/user-manag.sgml new file mode 100644 index 00000000..524947e1 --- /dev/null +++ b/zh/9.6/user-manag.sgml @@ -0,0 +1,366 @@ + + + + 数据库角色 + + + PostgreSQL使用角色这一概念来管理数据库访问权限。角色可以被看作数据库用户,也可以被看作一组数据库用户,这取决于该角色的设置方式。角色可以拥有数据库对象(例如表和函数),并且可以把这些对象上的权限赋予其他角色,以控制谁能访问哪些对象。此外,还可以把一个角色中的成员资格授予另一个角色,从而允许成员角色使用授予该角色的权限。 + + + + 角色这一概念涵盖了用户这两个概念。在 + PostgreSQL 8.1 之前的版本中,用户和组是两类不同的实体,但现在只有角色。任意角色都可以充当用户、组,或者同时充当两者。 + + + + 本章描述如何创建和管理角色。有关角色权限对各种数据库对象影响的更多信息,见。 + + + + 数据库角色 + + + 角色 + + + + 用户 + + + + CREATE ROLE + + + + DROP ROLE + + + + 数据库角色在概念上与操作系统用户完全分离。在实践中,维持两者之间的对应关系可能比较方便,但这并非必需。数据库角色在整个数据库集簇安装中是全局的(而非每个数据库各自独立)。要创建角色,请使用 SQL 命令: + +CREATE ROLE name; + + name 遵循 SQL 标识符规则:要么不加修饰且不含特殊字符,要么用双引号括起。(在实践中,通常还需要为该命令添加其他选项,例如 LOGIN。下文会给出更多细节。)要移除现有角色,请使用类似的 命令: + +DROP ROLE name; + + + + + createuser + + + + dropuser + + + + 为了方便,提供了程序,作为这些 SQL 命令的包装器,可以从 shell 命令行调用: + +createuser name +dropuser name + + + + + 要确定现有角色的集合,请查看 pg_roles 系统目录,例如 + +SELECT rolname FROM pg_roles; + + 使用 程序的 \du 元命令也可以列出现有角色。 + + + + 为了完成数据库系统的初始引导,一个刚初始化的系统总会包含一个预定义角色。该角色总是一个超级用户,并且除非另行指定了不同名称,否则它的名称与用initdb初始化数据库集簇的操作系统用户相同。这个角色通常命名为postgres。要创建更多角色,必须先以这个初始角色建立连接。 + + + + 每个到数据库服务器的连接都是以某个特定角色名建立的,而这个角色决定在该连接中发出命令时的初始访问权限。某个数据库连接要使用的角色名,由发起连接请求的客户端以应用特定的方式指明。例如,psql程序使用命令行选项来指明要以哪个角色连接。很多应用默认假定使用当前操作系统用户的名字(包括createuserpsql)。因此,在角色名和操作系统用户名之间维护对应关系通常很方便。 + + + + 某个客户端连接能够以哪些数据库角色进行连接,由客户端认证设置决定,如中所述。(因此,客户端并不限于只能以与其操作系统用户匹配的角色连接,正如一个人的登录名不必与其真实姓名一致。)由于角色身份决定了已连接客户端可用的权限集合,所以在设置多用户环境时,仔细配置权限非常重要。 + + + + + 角色属性 + + + 数据库角色可以具有多个属性,这些属性定义其权限,并与客户端认证系统交互。 + + + + 登录权限登录权限 + + + 只有具有 LOGIN 属性的角色才能用作数据库连接的初始角色名。具有 LOGIN 属性的角色可以视为数据库用户。要创建具有登录权限的角色,可以使用以下任一命令: + +CREATE ROLE name LOGIN; +CREATE USER name; + + (CREATE USER 等价于 CREATE ROLE,区别在于 CREATE USER 默认假定具有 LOGIN,而 CREATE ROLE 不会如此。) + + + + + + 超级用户状态超级用户 + + + 数据库超级用户会绕过除登录权之外的所有权限检查。这是一种危险的权限,不应草率使用;最好以非超级用户角色完成大部分工作。要创建新的数据库超级用户,使用CREATE + ROLE name SUPERUSER。这必须由已经是超级用户的角色来执行。 + + + + + + 创建数据库数据库创建权限 + + + 角色必须被明确授予创建数据库的权限(超级用户除外,因为后者会绕过所有权限检查)。要创建这样的角色,使用CREATE ROLE + name CREATEDB。 + + + + + + 创建角色角色创建权限 + + + 必须显式授予角色创建更多角色的权限(超级用户除外,因为它们会绕过所有权限检查)。要创建这样的角色,请使用CREATE ROLE name CREATEROLE。具有CREATEROLE权限的角色还可以修改和删除其他角色,以及授予或撤销这些角色的成员资格。不过,要创建、修改、删除超级用户角色或更改其成员资格,必须具有超级用户身份;仅有CREATEROLE不足以执行这些操作。 + + + + + + 发起复制角色发起复制的权限 + + + 角色必须被明确授予发起流复制的权限(超级用户除外,因为后者会绕过所有权限检查)。用于流复制的角色还必须具有LOGIN权限。要创建这样的角色,使用CREATE ROLE name REPLICATION + LOGIN。 + + + + + + 密码密码 + + + 只有在客户端认证方法要求用户连接数据库时提供密码时,密码才有意义。认证方法都会使用密码。数据库密码与操作系统密码是分离的。可在创建角色时通过CREATE ROLE + name PASSWORD 'string'指定密码。 + + + + + + + + + 角色创建后,可以使用以下命令修改其属性: + ALTER ROLEALTER ROLE + 详情请参见 命令的参考页。 + + + + + 一种良好做法是创建具有CREATEDBCREATEROLE权限但不是超级用户的角色,然后使用此角色执行数据库和角色的所有日常管理工作。这种方法可以避免在实际上并不需要超级用户权限的任务中以超级用户身份操作所带来的风险。 + + + + + 角色还可以为中描述的许多运行时配置设置指定角色特定默认值。例如,如果出于某种原因你希望每次连接时都禁用索引扫描(提示:这不是个好主意),你可以使用: + +ALTER ROLE myname SET enable_indexscan TO off; + + 这会保存该设置(但不会立即生效)。在该角色后续建立的连接中,它看起来就像在会话开始之前执行了SET enable_indexscan TO off一样。你仍然可以在会话期间更改该设置;它只会作为默认值。要移除角色特定默认设置,使用ALTER ROLE rolename RESET varname。注意,附加到没有LOGIN权限的角色上的角色特定默认值几乎没有用,因为它们永远不会生效。 + + + + + 角色成员资格 + + + 角色成员资格 + + + + 为了简化权限管理,把用户分组通常很方便:这样,权限就可以整体授予给一个组,或者从整个组中撤销。在PostgreSQL中,这通过创建一个表示该组的角色,然后把该组角色中的成员资格授予各个用户角色来实现。 + + + + 要建立一个组角色,首先创建该角色: + +CREATE ROLE name; + + 通常,作为组使用的角色不会有LOGIN属性,不过如果你愿意,也可以设置它。 + + + + 组角色存在后,就可以使用 和 + 命令添加和移除成员: + +GRANT group_role TO role1, ... ; +REVOKE group_role FROM role1, ... ; + + 也可以把成员资格授予其他组角色(因为组角色与非组角色实际上没有任何区别)。数据库不允许建立循环成员关系。此外,也不允许将角色的成员资格授予 + PUBLIC。 + + + + 组角色的成员可以通过两种方式使用该角色的权限。首先,组的每个成员都可以显式执行 + ,以暂时成为该组角色。在这种状态下,数据库会话可以使用组角色的权限,而不是原始登录角色的权限;创建的任何数据库对象也被视为由组角色而非登录角色拥有。其次,具有 INHERIT 属性的成员角色可以自动使用其所属角色的权限,包括这些角色继承的所有权限。例如,假定我们已经执行了: + +CREATE ROLE joe LOGIN INHERIT; +CREATE ROLE admin NOINHERIT; +CREATE ROLE wheel NOINHERIT; +GRANT admin TO joe; +GRANT wheel TO admin; + + 以角色 joe 连接后,数据库会话立即可以使用直接授予 joe 的权限,以及授予 admin 的所有权限,因为 joe + 继承 admin 的权限。不过,授予 wheel 的权限不可用,因为即使 joe 间接属于 wheel,该成员资格也是通过 admin 获得的,而后者具有 NOINHERIT 属性。执行以下命令后: + +SET ROLE admin; + + 会话将只能使用授予 + admin 的权限,而不能使用授予 joe 的权限。执行以下命令后: + +SET ROLE wheel; + + 会话将只能使用授予 + wheel 的权限,而不能使用授予 joeadmin 的权限。使用以下任一命令可以恢复原始的权限状态: + +SET ROLE joe; +SET ROLE NONE; +RESET ROLE; + + + + + + SET ROLE命令总是允许切换到原始登录角色直接或间接所属的任何角色。因此,在上例中,没有必要先成为admin再成为wheel。 + + + + + + 在 SQL 标准中,用户与角色之间有明确区别,并且用户不会自动继承权限,而角色会。这种行为可以在PostgreSQL中通过让用作 SQL 角色的角色具有INHERIT属性,而让用作 SQL 用户的角色具有NOINHERIT属性来获得。不过,出于与 8.1 之前版本向后兼容的考虑,PostgreSQL默认给所有角色都赋予INHERIT属性;在那些早期版本中,用户总能使用授予其所属组的权限。 + + + + + 角色属性LOGINSUPERUSERCREATEDBCREATEROLE可以被视为特殊权限,但它们从不会像数据库对象上的普通权限那样被继承。要使用其中之一,必须实际执行SET ROLE切换到拥有该属性的特定角色。继续上面的例子,我们可以选择把CREATEDBCREATEROLE赋予admin角色。这样,一个以joe角色连接的会话并不会立即拥有这些权限,只有在执行SET ROLE admin之后才会拥有。 + + + + + + + 要删除组角色,请使用 : + +DROP ROLE name; + + 该组角色中的所有成员资格都会自动被撤销(但成员角色不会受到其他影响)。 + + + + + 删除角色 + + + 由于角色可以拥有数据库对象,并且可以拥有访问其他对象的权限,所以删除一个角色通常不只是简单执行一次。必须先删除或重新分配该角色拥有的任何对象;并且必须撤销授予该角色的任何权限。 + + + + 可以使用 ALTER 命令逐个转移对象的所有权,例如: + +ALTER TABLE bobs_table OWNER TO alice; + + 另一种办法是使用 命令,把待删除角色拥有的所有对象的所有权重新分配给另一个角色。由于 REASSIGN OWNED 无法访问其他数据库中的对象,因此必须在包含该角色所拥有对象的每个数据库中运行它。(请注意,首次执行这样的 REASSIGN OWNED 时,会更改待删除角色所拥有的所有跨数据库共享对象(即数据库或表空间)的所有权。) + + + + 一旦有价值的对象都已转移给新拥有者,待删除角色所拥有的其余对象即可使用命令删除。同样,由于该命令不能访问其他数据库中的对象,因此必须在包含该角色所拥有对象的每个数据库中运行它。另外,DROP + OWNED不会删除整个数据库或表空间,因此如果该角色拥有任何尚未转移给新拥有者的数据库或表空间,就必须手工删除它们。 + + + + DROP OWNED还会负责移除目标角色在其他角色所拥有的对象上获授的所有权限。由于REASSIGN OWNED不会处理这类对象,所以通常需要同时运行REASSIGN OWNEDDROP OWNED(按这个顺序!),才能完整移除待删除角色的依赖关系。 + + + + 总之,移除曾用来拥有对象的角色,最通用的做法是: + + +REASSIGN OWNED BY doomed_role TO successor_role; +DROP OWNED BY doomed_role; +-- repeat the above commands in each database of the cluster +DROP ROLE doomed_role; + + + + 如果并非所有拥有的对象都要转移给同一个后继拥有者,最好先手工处理这些例外情况,然后再执行上述步骤作最后清理。 + + + + 如果在仍有依赖对象存在时尝试执行DROP ROLE,它会发出消息指出哪些对象需要被重新分配或删除。 + + + + + 默认角色 + + + 角色 + + + + PostgreSQL提供一组默认角色,用于访问某些常用的特权功能和信息。管理员可以将这些角色 GRANT 给其环境中的用户和/或其他角色,使这些用户能够访问指定的功能和信息。 + + + + 默认角色在中介绍。请注意,随着新功能的加入,各默认角色的具体权限可能会在将来发生变化。管理员应关注发行说明中的变更。 + + + + 默认角色 + + + + 角色 + 允许的访问 + + + + + pg_signal_backend + 向其他后端发送信号(例如:取消查询、终止)。 + + + +
    + + + 管理员可以使用 GRANT 命令将这些角色的访问权限授予用户: + + +GRANT pg_signal_backend TO admin_user; + + + +
    + + + 函数安全性 + + + 函数、触发器和行级安全策略允许用户把代码插入后端服务器,而其他用户可能会无意中执行这些代码。因此,这些机制使用户能够相对容易地对其他用户实施特洛伊木马攻击。最强的保护方式是严格控制谁能定义对象。如果这不可行,则应编写只引用拥有者可信的对象的查询,并从search_path中移除 public 模式以及任何其他允许不受信任用户创建对象的模式。 + + + + 函数在后端服务器进程中运行,并具有数据库服务器守护进程的操作系统权限。如果函数所用的编程语言允许不受检查的内存访问,就有可能更改服务器的内部数据结构。因此,这类函数可以做很多别的事情,其中就包括绕过任何系统访问控制。允许这种访问的函数语言被认为是不受信任的,而PostgreSQL只允许超级用户创建用这类语言编写的函数。 + + + +
    diff --git a/zh/9.6/uuid-ossp.sgml b/zh/9.6/uuid-ossp.sgml new file mode 100644 index 00000000..787d0a9f --- /dev/null +++ b/zh/9.6/uuid-ossp.sgml @@ -0,0 +1,149 @@ + + + + uuid-ossp + + + uuid-ossp + + + uuid-ossp模块提供一些函数,可使用几种标准算法之一生成通用唯一标识符(UUID)。它还提供一些函数,用于生成某些特殊的 UUID 常量。 + + + <literal>uuid-ossp</literal> 函数 + + 显示了可用于生成 UUID 的函数。相关标准 ITU-T Rec. X.667、ISO/IEC 9834-8:2005 和 RFC 4122 规定了生成 UUID 的四种算法,由版本号 1、3、4 和 5 标识。(不存在版本 2 算法。)每种算法可能适用于不同的一组应用。 + + + 用于生成 UUID 的函数 + + + + 函数 + + 描述 + + + + + + uuid_generate_v1()uuid_generate_v1 + + 此函数生成第 1 版 UUID。这需要使用计算机的 MAC 地址和时间戳。请注意,这类 UUID 会泄露创建该标识符的计算机身份及创建时间,因此可能不适合某些对安全敏感的应用。 + + + + uuid_generate_v1mc()uuid_generate_v1mc + + 此函数生成第 1 版 UUID,但使用随机多播 MAC 地址,而不是计算机的真实 MAC 地址。 + + + + uuid_generate_v3(namespace uuid, name text)uuid_generate_v3 + + 此函数在给定命名空间中使用指定输入名称生成第 3 版 UUID。命名空间应是中所示uuid_ns_*()函数生成的特殊常量之一。(理论上它可以是任意 UUID。)名称是所选命名空间中的一个标识符。 + + + 例如: + + +SELECT uuid_generate_v3(uuid_ns_url(), 'http://www.postgresql.org'); + + + 名称参数会先做 MD5 哈希,因此无法从生成的 UUID 中反推出明文内容。 + 通过这种方法生成 UUID 不包含随机因素,也不依赖于环境,因此结果可重现。 + + + + + uuid_generate_v4() + + 此函数生成第 4 版 UUID,它完全由随机数派生。 + + + + uuid_generate_v5(namespace uuid, name text) + + 此函数生成第 5 版 UUID,其工作方式类似于第 3 版 UUID,但使用 SHA-1 作为哈希方法。由于 SHA-1 被认为比 MD5 更安全,应优先使用第 5 版而不是第 3 版。 + + + + +
    + + + 返回 UUID 常量的函数 + + + + uuid_nil() + + 一个nil UUID 常量,不会作为真实 UUID 出现。 + + + + uuid_ns_dns() + + 指定 UUID 的 DNS 命名空间的常量。 + + + + uuid_ns_url() + + 指定 UUID 的 URL 命名空间的常量。 + + + + uuid_ns_oid() + + 指定 UUID 的 ISO 对象标识符(OID)命名空间的常量。(这涉及 ASN.1 OID,与PostgreSQL使用的 OID 无关。) + + + + uuid_ns_x500() + + 指定 UUID 的 X.500 可辨识名称(DN)命名空间的常量。 + + + + +
    +
    + + + 构建<filename>uuid-ossp</filename> + + + 历史上,此模块依赖 OSSP UUID 库,这也是该模块名称的由来。 + 虽然在 + 仍然可以找到 OSSP UUID 库, + 但它维护得并不好,而且移植到较新的平台上正变得越来越困难。 + 如今,在某些平台上,uuid-ossp已可在不依赖 OSSP 库的情况下构建。 + 在 FreeBSD、NetBSD 和其他一些 BSD 派生平台上,合适的 UUID 生成功能包含在核心 + libc库中。在 Linux、OS X 和其他一些平台上, + 相应功能由libuuid库提供,该库最初来自 + e2fsprogs项目(不过在现代 Linux 上,它被视为 + util-linux-ng的一部分)。 + 调用configure时,可指定 + 以使用 BSD 函数, + 或指定以使用 + e2fsprogslibuuid, + 或指定以使用 OSSP UUID 库。 + 某台机器上可能同时具备多个这样的库,因此configure不会自动选择其一。 + + + + 如果只需要随机生成的(第 4 版)UUID,可以考虑改用模块中的gen_random_uuid()函数。 + + + + + 作者 + + + Peter Eisentraut peter_e@gmx.net + + + + +
    diff --git a/zh/9.6/vacuumlo.sgml b/zh/9.6/vacuumlo.sgml new file mode 100644 index 00000000..24979ea6 --- /dev/null +++ b/zh/9.6/vacuumlo.sgml @@ -0,0 +1,177 @@ + + + + + vacuumlo + + + + vacuumlo + 1 + 应用程序 + + + + vacuumlo + PostgreSQL 数据库中移除孤立的大对象 + + + + + vacuumlo + option + dbname + + + + + 描述 + + + vacuumlo是一个简单的实用程序,用于从 + PostgreSQL数据库中移除孤立的大对象。 + 孤立的大对象(LO)是指其 OID 没有出现在该数据库任何 + oidlo数据列中的 LO。 + + + + 如果你使用这个程序,也可能会对模块中的 + lo_manage触发器感兴趣。 + lo_manage有助于尽量避免一开始就创建出孤立 LO。 + + + + 命令行中给出的所有数据库都会被处理。 + + + + + 选项 + + + vacuumlo接受以下命令行参数: + + limit + + + 每个事务最多移除limit个大对象 + (默认值为 1000)。由于服务器会为每个被移除的 LO 获取一个锁, + 在单个事务中移除过多 LO 有可能超出 + 。 + 如果你希望在单个事务中完成全部移除,请把该限制设为 0。 + + + + + + + + 不实际移除任何内容,只显示将要执行的操作。 + + + + + + + 输出大量进度消息。 + + + + + + + + + 打印vacuumlo版本并退出。 + + + + + + + + + + 显示vacuumlo命令行参数的帮助并退出。 + + + + + + + + vacuumlo还接受以下用于连接参数的命令行参数: + + hostname + + 数据库服务器主机。 + + + + + port + + 数据库服务器端口。 + + + + + username + + 用于连接的用户名。 + + + + + + + + + 绝不发出密码提示。如果服务器要求密码认证,而又无法通过其他方式 + (例如.pgpass文件)获得密码,则连接尝试将失败。 + 这个选项在批处理作业和脚本中很有用,因为这些场景下没有用户可以输入密码。 + + + + + + + + + 强制vacuumlo在连接数据库之前提示输入密码。 + + + + 这个选项并非必不可少,因为如果服务器要求密码认证, + vacuumlo会自动提示输入密码。不过, + vacuumlo会浪费一次连接尝试,才知道服务器需要密码。 + 在某些情况下,为了避免这次额外的连接尝试,提前指定是值得的。 + + + + + + + + + 注解 + + + vacuumlo按以下方法工作:首先, + vacuumlo构建一个临时表,其中包含所选数据库中所有大对象的 OID。 + 然后,它会扫描数据库中所有类型为 + oidlo的列,并从临时表中移除匹配的项。 + (注意:只会考虑名称正好为这两种的类型;特别是,基于这些类型定义的域不会被考虑。) + 临时表中剩余的项标识出孤立 LO,并会被移除。 + + + + + 作者 + + + Peter Mount peter@retep.org.uk + + + + diff --git a/zh/9.6/wal.sgml b/zh/9.6/wal.sgml new file mode 100644 index 00000000..4b291829 --- /dev/null +++ b/zh/9.6/wal.sgml @@ -0,0 +1,608 @@ + + + + 可靠性与预写式日志 + + 本章说明如何使用预写式日志,实现高效、可靠的运行。 + + + 可靠性 + + + 可靠性是任何严肃数据库系统的重要属性,而 + PostgreSQL 会尽一切可能保证运行可靠。可靠运行的 + 一个方面是,已提交事务所记录的所有数据都应存储在不受断电、操作系统故障和 + 硬件故障影响的非易失性区域中(当然不包括该非易失性区域本身的故障)。通常,只要 + 能成功将数据写入计算机的永久存储设备(磁盘驱动器或等效设备),就满足这一 + 要求。实际上,即使一台计算机遭受致命损坏,只要磁盘驱动器得以幸存,就可以将 + 它们移到另一台硬件相近的计算机上,所有已提交事务仍将完好无损。 + + + + 虽然周期性地强制将数据写到磁盘盘片似乎是个简单操作,但事实并非如此。由于 + 磁盘驱动器远慢于主存和 CPU,在计算机主存与磁盘盘片之间存在多层缓存。首先是 + 操作系统的缓冲区缓存,它会缓存常用磁盘块并合并磁盘写入。幸运的是,所有 + 操作系统都为应用提供了强制将数据从缓冲区缓存写入磁盘的方法,而 + PostgreSQL 会使用这些机制。(关于如何调整这一 + 过程,见参数 。) + + + + 其次,磁盘驱动器控制器中可能还有缓存;这在 RAID 控制卡上 + 尤为常见。其中一些缓存是 直写式,也就是说写请求一到达 + 就立刻发送到驱动器。另一些是 回写式,也就是说数据会在 + 稍后的某个时间发送到驱动器。这类缓存可能构成可靠性隐患,因为磁盘控制器缓存 + 中的内存是易失性的,断电时会丢失内容。较好的控制卡配有 + 后备电池单元BBU),也就是卡上 + 带有电池,可在系统断电时维持缓存供电。电力恢复后,数据便会写入磁盘驱动器。 + + + + 最后,大多数磁盘驱动器自身也带有缓存。有些是直写式,有些是回写式;对于 + 回写式驱动器缓存,同样存在与磁盘控制器缓存相同的数据丢失隐患。消费级 IDE 和 + SATA 驱动器尤其可能带有在断电后无法保留内容的回写式缓存。很多固态驱动器 + (SSD)也带有易失性的回写式缓存。 + + + + 这些缓存通常都可以禁用;不过,具体方法会因操作系统和驱动器类型而异: + + + + + + 在 Linux 上,可以使用 + hdparm -I 查询 IDE 和 SATA 驱动器;如果 + Write cache 旁边有一个 *, + 就表示启用了写缓存。可以使用 hdparm -W 0 关闭 + 写缓存。SCSI 驱动器可以使用 + sdparm + 查询。使用 sdparm --get=WCE 检查是否启用了写缓存, + 使用 sdparm --clear=WCE 将其禁用。 + + + + + + 在 FreeBSD 上,可以使用 + atacontrol 查询 IDE 驱动器,并通过在 + /boot/loader.conf 中设置 + hw.ata.wc=0 来关闭写缓存;SCSI 驱动器也可以使用 + camcontrol identify 查询,并在可用时借助 + sdparm 查询和修改写缓存。 + + + + + + 在 Solaris 上,磁盘写缓存由 + format -e 控制。 + (Solaris 的 ZFS 文件系统在启用磁盘写缓存时仍然是 + 安全的,因为它会发出自己的磁盘缓存刷新命令。) + + + + + + 在 Windows 上,如果 + wal_sync_method 是 + open_datasync(默认值),可以通过取消勾选 + My Computer\Open\disk drive\Properties\Hardware\Properties\Policies\Enable write caching on the disk + 来禁用写缓存。另一种办法是将 wal_sync_method 设为 + fsyncfsync_writethrough, + 这两者都会阻止写缓存。 + + + + + + 在 OS X 上,可以通过将 + wal_sync_method 设为 + fsync_writethrough 来防止写缓存。 + + + + + + 较新的 SATA 驱动器(遵循 ATAPI-6 或更高版本标准的那些) + 提供了驱动器缓存刷新命令(FLUSH CACHE EXT),而 SCSI + 驱动器则早已支持类似的 SYNCHRONIZE CACHE 命令。这些 + 命令对 PostgreSQL 并不直接可见,但某些文件系统 + (如 ZFSext4)可以利用它们将 + 启用了回写缓存的驱动器上的数据刷新到盘片。遗憾的是,这类文件系统与带后备电池 + 单元(BBU)的磁盘控制器配合时表现并不理想。在这种配置中, + 同步命令会强制把控制器缓存中的全部数据写到磁盘,削弱 BBU 的大部分好处。 + 可以运行 程序查看自己是否受此影响。如果确实 + 受影响,而又能这样做,则可通过关闭文件系统中的写屏障或重新配置磁盘控制器, + 恢复 BBU 的性能优势。如果关闭了写屏障,一定要确保电池保持正常工作;电池故障 + 有可能导致数据丢失。希望文件系统和磁盘控制器的设计者最终能解决这种次优行为。 + + + + 当操作系统向存储硬件发出写请求后,它几乎做不了什么来确保数据已经到达真正的 + 非易失性存储区域。相反,管理员有责任确保所有存储组件都能同时保证数据和 + 文件系统元数据的完整性。应避免使用写缓存没有电池后备的磁盘控制器。在 + 驱动器层面,如果驱动器无法保证在关闭前把数据写出,就应禁用回写缓存。如果 + 使用 SSD,要注意其中很多默认并不遵守缓存刷新命令。可以借助 diskchecker.pl + 测试 I/O 子系统行为是否可靠。 + + + + 数据丢失的另一种风险来自磁盘盘片写操作本身。磁盘盘片被分为若干扇区,通常每个 + 512 字节。每一次物理读写都处理整个扇区。当写请求到达驱动器时,它可能是若干个 + 512 字节的倍数(PostgreSQL 通常一次写入 + 8192 字节,也就是 16 个扇区),而写入过程可能在任何时刻因断电失败,这就意味着 + 某些 512 字节扇区写成功了,另一些却没有。为防范这类故障, + PostgreSQL 会在修改磁盘上的实际页面 + 之前,定期将整页镜像写入持久 WAL 存储。这样一来,在 + 崩溃恢复时 PostgreSQL 就能从 WAL 恢复部分写入的 + 页面。如果你使用了能阻止部分页面写入的文件系统软件(如 ZFS),可以通过关闭 + 参数 来禁用这种页面镜像。带后备电池 + 单元(BBU)的磁盘控制器并不能阻止部分页面写入,除非它们保证写入 BBU 的数据 + 总是完整的整页(8kB)。 + + + PostgreSQL 还可防范存储设备上因硬件错误或随时间发生的介质故障而引起的某些数据损坏, + 例如读写垃圾数据。 + + + + WAL 文件中的每条单独记录都受 CRC-32(32 位)校验保护,因此我们可以判断 + 记录内容是否正确。CRC 值会在写入每条 WAL 记录时设置,并在崩溃恢复、归档 + 恢复和复制过程中进行检查。 + + + + + 数据页目前默认不带校验和,但记录在 WAL 记录中的整页镜像会受到保护; + 关于启用数据页校验和的详情,请参见 initdb。 + + + + + pg_clogpg_subtrans、 + pg_multixactpg_serial、 + pg_notifypg_stat、 + pg_snapshots 等内部数据结构并不直接带校验和,其页面 + 也不受整页写保护。不过,在这些数据结构是持久的场合,会写入 WAL 记录,使 + 最近的更改能够在崩溃恢复时被准确重建,而这些 WAL 记录本身会按前述方式受 + 到保护。 + + + + + pg_twophase 中各个单独的状态文件受 CRC-32 保护。 + + + + + 较大 SQL 查询用于排序、物化和存放中间结果的临时数据文件当前不带校验和, + 对这些文件的更改也不会写入 WAL 记录。 + + + + + + PostgreSQL 不防范可纠正的内存错误,因此这里假定 + 你使用的是带有业界标准纠错码(ECC)或更强保护机制的 RAM。 + + + + + 预写式日志(<acronym>WAL</acronym>) + + + WAL + + + + 事务日志 + WAL + + + + 预写式日志WAL)是确保数据完整性 + 的标准方法。大多数(即使不是全部)事务处理书籍中都能找到详细描述。简而言之, + WAL 的核心思想是,对数据文件(即表和索引所在之处)的更改, + 必须在这些更改被记入日志之后才能写出;也就是说,必须在描述这些更改的 WAL + 记录被刷入持久存储之后,再去写数据文件。如果遵循这个过程,就无需在每次事务 + 提交时都把数据页刷盘,因为我们知道,一旦发生崩溃,就可以利用日志恢复数据 + 库:任何尚未应用到数据页上的更改,都可以根据 WAL 记录重做。(这就是前滚恢 + 复,也称 REDO。) + + + + + 由于 WAL 会在崩溃后恢复数据库文件内容,因此要可靠地存储 + 数据文件或 WAL 文件,并不需要日志化文件系统。实际上,日志开销反而可能降低 + 性能,特别是在日志会导致文件系统数据刷盘时。幸运的是, + 通常可以通过文件系统挂载选项关闭日志期间的数据刷写,例如 + Linux ext3 文件系统上的 data=writeback。不过,日志化 + 文件系统确实能够提高崩溃后的启动速度。 + + + + + + 使用 WAL 会显著减少磁盘写入次数,因为要保证事务已提交, + 通常只需把 WAL 文件刷入磁盘,而不必把该事务修改过的每个数据文件都刷盘。 + WAL 文件按顺序写入,因此同步 WAL 的代价远低于刷写数据页。对于处理大量小事务、 + 且这些事务触及数据存储不同部分的服务器,这一点尤其明显。此外,当服务器正在 + 处理大量并发的小事务时,对 WAL 文件执行一次 fsync 就 + 可能足以提交多个事务。 + + + + WAL 还使在线备份和时间点恢复成为可能,如 + 所述。通过归档 WAL 数据,我们就能支 + 持回退到可用 WAL 数据覆盖范围内的任意时刻:只需先安装数据库的一个较早物理 + 备份,然后把 WAL 重放到目标时刻即可。更进一步说,这个物理备份不必是数据库 + 状态的瞬时快照 — 即使备份是在一段时间内完成的,重放这段时间内的 WAL + 也能修复任何内部不一致。 + + + + + 异步提交 + + + 同步提交 + + + + 异步提交 + + + + 异步提交是一项可选功能,它允许事务更快完成,但代价是 + 如果数据库发生崩溃,最近的事务可能会丢失。在许多应用中,这是可以接受的权衡。 + + + + 如前一节所述,事务提交通常是 同步的:服务器要等到该 + 事务的 WAL 记录被刷入持久存储后,才向客户端返回成功 + 指示。因此,客户端可以确信那些已被报告为提交成功的事务一定会被保留,即使 + 服务器紧接着立刻崩溃也是如此。然而,对于短事务而言,这种等待是总事务时间的 + 主要组成部分。选择异步提交模式意味着服务器会在事务从逻辑上完成后就立刻返回 + 成功,而不必等到它生成的 WAL 记录真正到达磁盘。这会显著 + 提升小事务的吞吐量。 + + + + 异步提交引入了数据丢失风险。在向客户端报告事务完成与事务真正被提交(也就是 + 说,即使服务器崩溃也保证不会丢失)之间,存在一个很短的时间窗口。因此,如果 + 客户端会基于“系统一定会记住这个事务”这一假设采取外部动作,就不应使用异步提 + 交。例如,银行显然不会对记录 ATM 吐钞的事务使用异步提交。但在很多场景中, + 例如事件日志记录,并不需要这种强保证。 + + + + 使用异步提交所承担的风险是数据丢失,而不是数据损坏。如果数据库发生崩溃,它会 + 通过重放 WAL 直到最后一条已刷写记录来恢复。因此,数据库 + 会被恢复到自洽状态,但任何尚未刷到磁盘的事务都不会反映在该状态中。其最终 + 效果就是丢失最后几个事务。由于事务按提交顺序重放,所以不会引入任何不一致性 + — 例如,如果事务 B 做出的更改依赖于更早的事务 A 的效果,就不可能出现 + A 的效果丢失而 B 的效果却被保留的情况。 + + + + 用户可以为每个事务选择提交模式,因此同步提交事务和异步提交事务可以并发运行。 + 这使得在性能与事务持久性的确定性之间能够灵活权衡。提交模式由用户可设置的 + 参数 控制,而该参数可以用任何 + 设置配置参数的方式来更改。任一事务实际使用的模式取决于事务提交开始时 + synchronous_commit 的值。 + + + + 某些实用命令,例如 DROP TABLE,无论 + synchronous_commit 如何设置,都会强制同步提交。这是为了 + 保证服务器文件系统与数据库逻辑状态之间的一致性。支持两阶段提交的命令,例如 + PREPARE TRANSACTION,也始终是同步的。 + + + + 如果数据库在异步提交与写出该事务 WAL 记录之间的风险窗口内 + 崩溃,那么该事务所做的更改丢失。该风险窗口的时长是 + 受限的,因为一个后台进程(WAL 写入器)会将尚未写出的 + WAL 记录每隔 + 毫秒刷到磁盘。风险窗口的实际最大时长是 + wal_writer_delay 的三倍,因为 WAL 写入器在系统繁忙时被 + 设计为倾向于一次写出整页。 + + + + + 立即模式关闭等同于服务器崩溃,因此也会造成任何未刷写的异步提交丢失。 + + + + + 异步提交的行为不同于将 设为 off。 + fsync 是影响所有事务的全服务器设置。它会禁用 + PostgreSQL 内部所有试图同步数据库不同部分写入的 + 逻辑,因此系统崩溃(即硬件或操作系统崩溃,而不是 + PostgreSQL 本身故障)可能导致数据库状态遭受任意 + 严重的损坏。在许多场景中,异步提交能带来关闭 fsync 所能 + 获得的大部分性能提升,却不承担数据损坏的风险。 + + + + 听起来也很像异步提交,但它实际上是一种同步 + 提交方法(事实上,在异步提交期间 commit_delay 会被忽略)。 + commit_delay 会在事务把 WAL 刷到磁盘 + 之前引入一段延迟,希望某个事务执行的一次刷盘也能服务于其他差不多同时提交的 + 事务。可以把这个设置看成是扩大一个时间窗口,让事务得以加入即将参与同一次刷盘 + 的组,从而在多个事务之间摊销这次刷盘的代价。 + + + + + + <acronym>WAL</acronym> 配置 + + + 有若干与 WAL 相关的配置参数会影响数据库性能。本节解释它们 + 的用法。有关设置服务器配置参数的一般信息,请参见 。 + + + + 检查点检查点 + 是事务序列中的一些点,在这些点上可以保证堆和索引数据文件已经用检查点之前写入 + 的全部信息进行了更新。执行检查点时,所有脏数据页都会刷盘,并向 WAL + 文件写入一条特殊的检查点记录。(更改记录此前已写入 WAL + 文件并刷盘。)如果发生崩溃,崩溃恢复过程会查看最新的检查点记录,以确定应该从 WAL + 的哪个位置(称为重做记录)开始执行 REDO。该点之前对数据文件所做的任何更改 + 都保证已经在磁盘上。因此,检查点之后,位于包含重做记录的段之前的 WAL 段不再需要, + 可以被回收或删除。(若正在执行 WAL 归档,则这些 WAL 段必 + 须先归档,随后才能回收或删除。) + + + + 检查点要求将所有脏数据页刷盘,这可能造成可观的 I/O 负载。因此,检查点活动会 + 被节流,使 I/O 从检查点开始时就启动,并在下一个检查点预计开始前完成;这样可 + 将检查点期间的性能下降降到最低。 + + + + 服务器的检查点进程会定期自动执行检查点。每经过 + 秒,或者当 + 即将超出时,就会开始一个检查点,以先到者为 + 准。默认设置分别是 5 分钟和 1 GB。如果自上一个检查点以来没有写入任何 WAL, + 那么即使 checkpoint_timeout 已经过去,也会跳过新的 + 检查点。(如果正在使用 WAL 归档,而你希望对文件归档频率设定下限,从而限制 + 潜在数据丢失,那么应当调整参数 + ,而不是检查点参数。) + 也可以使用 SQL 命令 CHECKPOINT 强制执行检查点。 + + + + 减小 checkpoint_timeout 和/或 + max_wal_size 会让检查点更频繁地发生。这样可以加快崩溃后 + 恢复,因为需要重做的工作更少。不过,这必须与更频繁地将脏数据页刷盘所增加的成本 + 权衡。如果设置了 (默认就是如此), + 还要考虑另一个因素。为了保证数据页一致性,每个检查点之后对某个数据页的首次 + 修改,都会导致把整页内容写入日志。在这种情况下,更短的检查点间隔会增加输出到 + WAL 的数据量,部分抵消缩短间隔的目的,并且无论如何都会带来更多磁盘 I/O。 + + + + 检查点的代价相当高,首先是因为它要求写出当前所有脏缓冲区,其次是因为它还会像 + 上文所述那样带来额外的后续 WAL 流量。因此,明智的做法是将检查点参数设得足够 + 高,避免检查点发生得过于频繁。作为对检查点参数的一种简单合理性检查,你可以 + 设置参数 。如果检查点之间的间隔 + 小于 checkpoint_warning 秒,服务器日志就会输出一条消息, + 建议增大 max_wal_size。偶尔出现这样的消息无需惊慌,但如果 + 它频繁出现,就应当增大检查点控制参数。大型 COPY 传输之类的 + 批量操作,如果 max_wal_size 设得不够高,也可能导致出现 + 多次此类警告。 + + + + 为了避免大量页面写出在短时间内淹没 I/O 系统,检查点期间对脏缓冲区的写出会被 + 分散到一段时间内。这段时间由 + 控制,它表示为检查点间隔的一个分数。系统会调节 + I/O 速率,使检查点在给定比例的 checkpoint_timeout 秒数 + 经过时完成,或者在 max_wal_size 即将超出之前完成,以较早 + 者为准。使用默认值 0.5 时,可以预期 PostgreSQL + 会在下一次检查点开始前的这段时间约过一半时完成每个检查点。 + 在正常运行时已非常接近最大 I/O 吞吐量的系统上,你可能希望增大 + checkpoint_completion_target,以降低检查点带来的 I/O 负载。 + 这样做的缺点是,延长检查点会影响恢复时间,因为需要保留更多 WAL 段以备恢复时 + 使用。虽然 checkpoint_completion_target 可以设到 1.0,但最好 + 保持在该值以下(或许最多为 0.9),因为检查点除了写出脏缓冲区之外还包含其他活动。 + 设置为 1.0 很可能导致检查点不能按时完成,从而因为所需 WAL 段数量的意外波动而 + 损失性能。 + + + + 在 Linux 和 POSIX 平台上, + 允许在检查点写出的操作系统页面达到可配置的字节数后,强制将它们刷盘。 + 否则,这些页面可能会停留在操作系统页缓存中,从而在检查点末尾发出 + fsync 时引发停顿。这个设置通常有助于降低事务延迟,但也可能 + 对性能产生不利影响,尤其是在工作负载大于 + 、但小于操作系统页缓存时。 + + + + pg_xlog 目录中 WAL 段文件的数量取决于 + min_wal_sizemax_wal_size,以及此前几 + 个检查点周期中产生的 WAL 数量。当旧的 WAL 段文件不再需要时,它们会被删除或 + 回收(也就是重命名为编号序列中的未来段文件)。如果由于 WAL 输出速率短期突增而 + 超过了 max_wal_size,那么不再需要的段文件就会被删除, + 直到系统重新回到该限制之下。在此限制以下,系统会回收足够多的 WAL 文件,以覆 + 盖到下一个检查点之前的预估需求,其余则删除。这个估计基于此前几个检查点周期中 + 所用 WAL 文件数量的移动平均值。如果实际使用量超过估计值,移动平均会立即增大, + 因而在一定程度上能够适应峰值使用量,而不仅仅是平均使用量。 + min_wal_size 规定了为将来复用而回收的 WAL 文件最小总量; + 即使系统空闲,且 WAL 使用量估计表明只需要很少 WAL,也始终会回收这么多文件以 + 供未来使用。 + + + + 无论 max_wal_size 如何设置,最近的 + + 1 个 WAL 文件 + 都会始终保留。此外,如果使用了 WAL 归档,那么旧段在完成归档之前不能被删除或 + 回收。如果 WAL 归档跟不上 WAL 产生的速度,或者 archive_command + 反复失败,那么旧 WAL 文件就会在 + pg_xlog 中不断累积,直到问题解决。使用复制槽的备库 + 如果速度很慢或者已经失败,也会产生同样的效果(见 + )。 + + + + 在归档恢复或备库模式下,服务器会周期性地执行 + 重启点重启点, + 其行为类似于正常运行时的检查点:服务器会强制将其所有状态刷盘,更新 + pg_control 文件,以表明已经处理过的 WAL 数据无需再次 + 扫描,然后回收 pg_xlog 目录中的旧 WAL 段文件。重启点的 + 执行频率不会高于主库上的检查点,因为重启点只能在检查点记录处执行。当到达某条检查点记录且距离上次重启点 + 至少已经过去 checkpoint_timeout 秒,或者 WAL 大小即将超过 + max_wal_size 时,就会触发重启点。不过,由于重启点的可执行时机受到 + 限制,恢复期间经常会超出 max_wal_size,最多可能超出一个 + 检查点周期对应的 WAL 量。(无论如何,max_wal_size 从来 + 都不是硬上限,因此你始终应留出充足余量,以免耗尽磁盘空间。) + + + + 有两个常用的内部 WAL 函数: + XLogInsertRecordXLogFlush。 + XLogInsertRecord 用于把新记录放入共享内存中的 + WAL 缓冲区。如果新记录没有可用空间, + XLogInsertRecord 就不得不把 + 若干已满的 WAL 缓冲区写出(移入内核缓存)。这并不理想,因为 + XLogInsertRecord 会在每一次数据库底层修改(例如插入行) + 时被调用,而在调用它时,受影响数据页上正持有排他锁,因此这一操作必须尽可能 + 快。更糟的是,写 WAL 缓冲区还可能迫使系统创建新的 WAL 段, + 这会花费更多时间。正常情况下,WAL 缓冲区应由 + XLogFlush 请求负责写出并刷盘; + 该请求大多发生在事务提交时,以确保事务记录已刷盘至持久存储。在 WAL 输出量很高 + 的系统上,XLogFlush 请求可能不够频繁,无法避免 + XLogInsertRecord 自己去执行写出。在这种系统上,应增加 + WAL 缓冲区的数量,具体做法是修改参数 + 。当设置了 + 且系统非常繁忙时,提高 + wal_buffers 还有助于平滑每次检查点后紧接着那段时期的响应时间。 + + + + 参数 定义了组提交的领导者进程在 + XLogFlush 内部获取锁之后会睡眠多少微秒,在此期间,组提 + 交的追随者会排队跟在领导者后面。这一延迟允许其他服务器进程把自己的提交记录添 + 加到 WAL 缓冲区中,以便它们都能由领导者最终执行的同步操作一并刷盘。如果没有 + 启用 ,或者当前处于活跃事务中的其他会话少于 + 个,就不会发生睡眠;这样可以避免在其他 + 会话不太可能很快提交时还去睡眠。请注意,在某些平台上,休眠请求的分辨率是 + 10 毫秒,因此介于 1 到 10000 微秒之间的任何非零 + commit_delay 设置,效果都相同。还要注意,在某些平台上, + 休眠操作持续的时间可能会比该参数所请求的略长。 + + + + 由于 commit_delay 的目的,是让每次刷盘操作的成本在并发 + 提交的事务之间分摊(代价是可能增加事务延迟),因此在选择该设置之前,有必要 + 先量化这一成本。该成本越高,commit_delay 在提高事务吞吐量 + 方面预计就越有效,当然这种效果也有上限。可以使用 + 程序来测量单次 WAL 刷盘操作平均需要多少微秒。通常,把该程序报告的“单次 + 8kB 写操作之后再刷盘”的平均耗时取一半,往往是 + commit_delay 最有效的设置,因此建议把这一值作为针对特定 + 工作负载进行优化时的起点。虽然在 WAL 位于高延迟旋转磁盘上时,调优 + commit_delay 尤其有用,但即便是在同步时间非常快的存储 + 介质上,例如固态驱动器或带有电池后备写缓存的 RAID 阵列,其收益也可能很可观;不过 + 这无疑应当在有代表性的工作负载上进行测试。在这种场景下,通常应使用更高的 + commit_siblings 值,而在高延迟介质上,较小的 + commit_siblings 值往往更有帮助。还要注意, + commit_delay 如果设得过高,也完全可能由于显著增加事务 + 延迟而导致总体事务吞吐量下降。 + + + + 当 commit_delay 设为 0(默认值)时,仍然可能出现一种组 + 提交,但每个组只会包含那些恰好在前一次刷盘操作(若有)进行期间,到达“需要将 + 其提交记录刷盘”这一点的会话。客户端数量更高时,往往会出现一种 + gangway effect,以至于即使 commit_delay + 为 0,组提交的效果也会变得很显著,因此显式设置 + commit_delay 的帮助反而会变小。只有在以下两种条件同时 + 满足时,设置 commit_delay 才可能有帮助:(1)存在一些 + 并发提交的事务;以及(2)吞吐量在某种程度上受限于提交速率。但在旋转延迟较高 + 的介质上,即使只有两个客户端(也就是一个正在提交的客户端,再加上一个并发的 + 兄弟事务),这个设置也能有效提高事务吞吐量。 + + + + 参数 决定 + PostgreSQL 将如何请求内核把 + WAL 更新强制刷盘。就可靠性而言,所有选项应当都是相同 + 的;例外是 fsync_writethrough,它有时能够在其他选项做不 + 到时强制将磁盘缓存中的数据刷盘。不过,具体哪个选项速度最快则高度依赖平台。可以使用 + 程序测试不同选项的速度。请注意,如果 + fsync 已被关闭,那么这个参数就没有意义。 + + + + 启用配置参数 (前提是 + PostgreSQL 编译时支持它)会导致每一次 + XLogInsertRecordXLogFlush + 的 WAL 调用都被记录到服务器日志中。将来,这个选项可能会 + 被更通用的机制所取代。 + + + + + WAL 内部机制 + + + WAL 会自动启用;管理员除了确保满足 + WAL 文件的磁盘空间需求,并完成任何必要调优之外,不需要再 + 做其他操作(见 )。 + + + + + WAL 文件保存在数据目录下的 pg_xlog 目录中,由一组段文件 + 组成,每个段通常为 16 MB(不过可在构建服务器时通过修改 + configure 选项来 + 改变大小)。每个段又分为若干页面,通常每页 8 kB(该大小可通过 configure 选 + 项 更改)。WAL 记录头定义在 + access/xlogrecord.h 中;记录内容则取决于被记录的事件类 + 型。段文件以不断递增的数字命名,从 + 000000010000000000000001 开始。这些编号不会回绕,但要 + 把可用的编号耗尽还需要非常、非常长的时间。 + + + + 如果能将 WAL 放在与主数据库文件不同的磁盘上,会更有利。这可以通过把 + pg_xlog 目录移动到其他位置(当然要在服务器关闭时进行), + 然后在主数据目录中的原位置创建一个指向新位置的符号链接来实现。 + + + + WAL 的目标是确保数据库记录在被修改之前,日志已经先被写 + 出;但如果磁盘驱动器磁盘驱动器 + 实际上只是缓存了数据、尚未将其存储到磁盘上,却向内核谎报 + 写入成功,这一目标就会被破坏。在这种情况下,断电可能导致不可恢复的数据损坏。 + 管理员应尽量确保保存 PostgreSQL + WAL 文件的磁盘不会做出这种虚假报告。(见 + 。) + + + + 在完成检查点并刷写 WAL 之后,该检查点的位置会保存在文件 + pg_control 中。因此,在恢复开始时,服务器会先读取 + pg_control,再读取检查点记录;然后从检查点记录给出的 + WAL 位置向前扫描并执行 REDO。由于检查点之后对数据页的第一次修改,会把整页内 + 容保存在 WAL 中(假设没有禁用 ), + 因而自该检查点以来被修改的所有页面都会恢复到一致状态。 + + + + 为处理 pg_control 损坏的情况,我们本应支持按相反顺序扫 + 描现有 WAL 段 — 也就是从新到旧 —,以便找到最新检查点。这一功能 + 尚未实现。 + pg_control 足够小(不足一个磁盘页),因此不会受部分写 + 问题影响;到目前为止,也没有仅仅因为无法读取 + pg_control 本身而导致数据库故障的报告。所以,尽管理论上 + 它是个薄弱环节,pg_control 在实践中似乎不是问题。 + + + diff --git a/zh/9.6/xaggr.sgml b/zh/9.6/xaggr.sgml new file mode 100644 index 00000000..10b82858 --- /dev/null +++ b/zh/9.6/xaggr.sgml @@ -0,0 +1,364 @@ + + + + 用户定义的聚合 + + + 聚合函数 + 用户定义的 + + + + PostgreSQL中的聚合函数是依据状态值状态转移函数来定义的。也就是说,聚合通过一个状态值来工作,并在处理每个后续输入行时更新该状态值。要定义一个新的聚合函数,需要为状态值选定一种数据类型、为状态选定一个初始值,并指定一个状态转移函数。状态转移函数接受先前的状态值以及当前行的聚合输入值,并返回一个新的状态值。如果聚合的期望结果不同于运行状态值中需要保存的数据,还可以指定一个最终函数。最终函数接受结束时的状态值,并返回所需的聚合结果。原则上,转移函数和最终函数都只是普通函数,因此也可以在聚合上下文之外使用。(实际上,出于性能原因,创建只能在作为聚合一部分被调用时工作的专用转移函数通常更有帮助。) + + + + 因此,除了聚合用户所看到的参数和结果数据类型之外,还存在一种内部状态值数据类型,它可能既不同于参数类型,也不同于结果类型。 + + + + 如果我们定义一个不使用最终函数的聚合,那么得到的就是一个对每一行的列值进行逐步计算的聚合。sum就是这种聚合的一个示例。sum从零开始,并始终把当前行的值加到运行总和中。例如,如果我们希望让sum聚合能用于复数数据类型,那么只需要该数据类型的加法函数。聚合定义如下: + + +CREATE AGGREGATE sum (complex) +( + sfunc = complex_add, + stype = complex, + initcond = '(0,0)' +); + + + 它可以这样使用: + + +SELECT sum(a) FROM test_complex; + + sum +----------- + (34,53.9) + + + (注意,这里依赖了函数重载:名为sum的聚合不止一个,但PostgreSQL能够判断对于complex类型的列该使用哪一种 sum。) + + + + 如果没有非空输入值,上述sum定义将返回零(即初始状态值)。也许我们更希望在这种情况下返回空值 — SQL 标准要求sum这样做。实现这一点只需省略initcond短语,使初始状态值为空值。通常这意味着sfunc需要检查输入的状态值是否为空值。但对于sum以及maxmin这类其他简单聚合,只需把第一个非空输入值放入状态变量,然后从第二个非空输入值开始应用转移函数即可。如果初始状态值为空值,并且转移函数被标记为strict(即不会对空值输入调用),PostgreSQL会自动这样做。 + + + + strict转移函数的另一项默认行为是,只要遇到空值输入,就会保留先前的状态值不变。因此,空值会被忽略。如果你需要对空值输入采用其他行为,就不要把转移函数声明为 strict,而应在函数代码中自行检测空值输入并完成所需处理。 + + + + avg(平均值)是一个更复杂的聚合示例。它需要两项运行状态:输入值之和以及输入值的个数。最终结果通过将这两者相除得到。平均值通常通过使用数组作为状态值来实现。例如,内置的avg(float8)实现如下: + + +CREATE AGGREGATE avg (float8) +( + sfunc = float8_accum, + stype = float8[], + finalfunc = float8_avg, + initcond = '{0,0,0}' +); + + + + + + + float8_accum需要一个三元素数组,而不只是两个元素,因为它累积的不仅是输入值的和与计数,还有平方和。这样它除了可用于avg之外,也可用于其他一些聚合。 + + + + + SQL 中的聚合函数调用允许使用DISTINCTORDER BY选项,以控制哪些行会传递给聚合的转移函数,以及传递顺序。这些选项由系统在内部实现,并不是聚合支持函数需要关心的事情。 + + + + 更多细节请参见命令。 + + + + + 移动聚合模式 + + + 移动聚合模式 + + + + 聚合函数 + 移动聚合 + + + + 聚合函数可以选择支持移动聚合模式,这样在帧起点会移动的窗口中执行聚合函数时,速度可以显著提高。(关于将聚合函数作为窗口函数使用的信息,见。)其基本思想是,除了常规的前向状态转移函数外,聚合还提供一个逆向状态转移函数,这样当某些行离开窗口帧时,就可以把它们从聚合的运行状态值中移除。例如,sum聚合若使用加法作为前向状态转移函数,就会使用减法作为逆向状态转移函数。如果没有逆向状态转移函数,每次帧起点移动时,窗口函数机制都必须从头重新计算聚合,运行时间就与输入行数乘以平均帧长度成正比。有了逆向状态转移函数,运行时间就只与输入行数成正比。 + + + + 逆向状态转移函数接收当前状态值,以及当前状态中所包含的最早那一行的聚合输入值。它必须重建这样一个状态值:就好像给定的输入行从未参与过聚合,只有其后的各行被聚合一样。这有时要求前向状态转移函数保存比普通聚合模式所需更多的状态。因此,移动聚合模式与普通模式采用完全独立的实现:它有自己的状态数据类型、自己的前向状态转移函数,以及在需要时自己的最终函数。如果不需要额外状态,这些也可以与普通模式的数据类型和函数相同。 + + + + 作为示例,我们可以把上面给出的sum聚合扩展为支持移动聚合模式: + + +CREATE AGGREGATE sum (complex) +( + sfunc = complex_add, + stype = complex, + initcond = '(0,0)', + msfunc = complex_add, + minvfunc = complex_sub, + mstype = complex, + minitcond = '(0,0)' +); + + + 名称以m开头的参数定义了移动聚合的实现。除了逆向状态转移函数minvfunc之外,它们分别对应于不带m的普通聚合参数。 + + + + 用于移动聚合模式的前向状态转移函数不允许返回空值作为新的状态值。如果逆向状态转移函数返回空值,就表示该逆向函数无法针对这个特定输入逆转状态计算,因此当前帧起始位置的聚合计算会从头重做。这一约定使得移动聚合模式可用于这样一些场景:在少数不常见的情况下,难以从运行状态值中逆向消除某个输入的影响。遇到这些情况时,逆向状态转移函数可以放弃处理,只要它在大多数情况下都能工作,总体上仍然划算。例如,一个处理浮点数的聚合,可能会在必须从运行状态值中移除NaN(非数字)输入时选择放弃处理。 + + + + 在编写移动聚合支持函数时,务必确保逆向状态转移函数能够精确地重建正确的状态值。否则,是否使用移动聚合模式就可能导致用户可见的结果差异。一个看似很容易添加逆向状态转移函数、但实际上无法满足这一要求的聚合示例,是对float4float8输入求和的sum。对sum(float8)的一种朴素的声明可能是 + + +CREATE AGGREGATE unsafe_sum (float8) +( + stype = float8, + sfunc = float8pl, + mstype = float8, + msfunc = float8pl, + minvfunc = float8mi +); + + + 然而,这个聚合给出的结果可能与没有逆向状态转移函数时截然不同。例如,考虑 + + +SELECT + unsafe_sum(x) OVER (ORDER BY n ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) +FROM (VALUES (1, 1.0e20::float8), + (2, 1.0::float8)) AS v (n,x); + + + 这个查询把0作为第二个结果返回,而预期答案是1。原因在于浮点值的精度有限:把1加到1e20上,结果仍然是1e20,因此再从中减去1e20得到的是0,而不是1。注意,这是浮点算术本身的一般局限,不是PostgreSQL的限制。 + + + + + + 多态和可变参数聚合 + + + 聚合函数 + 多态 + + + + 聚合函数 + 可变参数 + + + 聚合函数可以使用多态的状态转移函数或最终函数,这样同一组函数就能用于实现多个聚合。关于多态函数的解释,请参见。再进一步,聚合函数自身也可以指定为具有多态输入类型和状态类型,从而让同一个聚合定义服务于多种输入数据类型。下面是一个多态聚合的示例: +CREATE AGGREGATE array_accum (anyelement) +( + sfunc = array_append, + stype = anyarray, + initcond = '{}' +); +对于任何给定的聚合调用,其实际状态类型都是一种数组类型,该数组以实际输入类型为元素类型。这个聚合的行为是把所有输入串接成这种类型的一个数组。(注意:内置聚合array_agg提供了类似的功能,而且性能比这个定义更好。) + + + 下面是以两种不同的实际数据类型作为参数时的输出: + + +SELECT attrelid::regclass, array_accum(attname) + FROM pg_attribute + WHERE attnum > 0 AND attrelid = 'pg_tablespace'::regclass + GROUP BY attrelid; + + attrelid | array_accum +---------------+--------------------------------------- + pg_tablespace | {spcname,spcowner,spcacl,spcoptions} +(1 row) + +SELECT attrelid::regclass, array_accum(atttypid::regtype) + FROM pg_attribute + WHERE attnum > 0 AND attrelid = 'pg_tablespace'::regclass + GROUP BY attrelid; + + attrelid | array_accum +---------------+--------------------------- + pg_tablespace | {name,oid,aclitem[],text[]} +(1 row) + + + + + 通常,具有多态结果类型的聚合函数也会像上例一样具有多态状态类型。这是必要的,因为否则最终函数就无法以合理方式声明:它会需要多态结果类型,却没有多态参数类型,而CREATE FUNCTION会以无法从调用中推断结果类型为由拒绝这种声明。不过,有时使用多态状态类型并不方便。最常见的情况是聚合支持函数要用 C 编写,而状态类型应声明为internal,因为在 SQL 层面并没有与之对应的类型。为了解决这种情况,可以把最终函数声明为接受额外的dummy参数,这些参数与聚合的输入参数相匹配。由于调用最终函数时没有可用的具体值,这些假参数总是以 NULL 值传入。它们唯一的作用,是让多态最终函数的结果类型与聚合的输入类型关联起来。例如,内置聚合array_agg的定义等效于: + + +CREATE FUNCTION array_agg_transfn(internal, anynonarray) + RETURNS internal ...; +CREATE FUNCTION array_agg_finalfn(internal, anynonarray) + RETURNS anyarray ...; + +CREATE AGGREGATE array_agg (anynonarray) +( + sfunc = array_agg_transfn, + stype = internal, + finalfunc = array_agg_finalfn, + finalfunc_extra +); + + + 这里,finalfunc_extra选项指定最终函数除了状态值之外,还会接收与聚合输入参数对应的额外假参数。额外的anynonarray参数使array_agg_finalfn的声明合法。 + + + + 与普通函数很类似,可以通过把聚合函数的最后一个参数声明为VARIADIC数组,使其接受可变数量的参数;见。聚合的转移函数也必须将同样的数组类型作为其最后一个参数。这些转移函数通常也会被标记为VARIADIC,但这并非严格要求。 + + + + + + 可变参数聚合很容易与ORDER BY选项(见)一起被误用,因为在这种组合中,解析器无法判断是否给出了数量错误的实际参数。请记住,ORDER BY右侧的所有内容都是排序键,而不是聚合的参数。例如,在 + +SELECT myaggregate(a ORDER BY a, b, c) FROM ... + + 中,解析器会把它看作一个聚合函数参数和三个排序键。然而,用户本来可能是想写 + +SELECT myaggregate(a, b, c ORDER BY a) FROM ... + + 如果myaggregate是可变参数聚合,那么这两种调用都可能完全合法。 + + + + 因此,在创建名称相同但常规参数个数不同的聚合函数之前,最好三思。 + + + + + + + 有序集聚合 + + + 聚合函数 + 有序集 + + + + 到目前为止,我们所描述的都是普通聚合。PostgreSQL还支持有序集聚合,它与普通聚合有两个关键区别。第一,除了对每个输入行都求值一次的普通聚合参数之外,有序集聚合还可以拥有只在每次聚合操作中求值一次的直接参数。第二,普通聚合参数的语法会显式指定它们的排序顺序。有序集聚合通常用于实现依赖特定行顺序的计算,例如排名或百分位点,因此这种排序顺序是任何调用都必须具备的一部分。例如,内置的percentile_disc定义等效于: + + +CREATE FUNCTION ordered_set_transition(internal, anyelement) + RETURNS internal ...; +CREATE FUNCTION percentile_disc_final(internal, float8, anyelement) + RETURNS anyelement ...; + +CREATE AGGREGATE percentile_disc (float8 ORDER BY anyelement) +( + sfunc = ordered_set_transition, + stype = internal, + finalfunc = percentile_disc_final, + finalfunc_extra +); + + + 这个聚合接受一个float8直接参数(百分位分数)以及一个可以是任意可排序数据类型的聚合输入。它可以这样用来获取家庭收入的中位数: + + +SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households; + percentile_disc +----------------- + 50489 + + + 这里,0.5是一个直接参数;如果百分位分数在不同行之间变化,那就没有意义了。 + + + + 与普通聚合不同,有序集聚合的输入行排序不是由系统在内部完成的,而是由聚合的支持函数负责。典型的实现方法是在聚合的状态值中保存一个tuplesort对象的引用,把输入行送入该对象,然后在最终函数中完成排序并读出数据。这样的设计使最终函数可以执行特殊操作,例如向待排序数据中注入额外的假想行。普通聚合通常可以使用由PL/pgSQL或其他 PL 语言编写的支持函数来实现,但有序集聚合通常必须用 C 编写,因为它们的状态值无法定义为任何 SQL 数据类型。(在上面的示例中,注意状态值被声明为internal类型 — 这很典型。) + + + + 有序集聚合的状态转移函数接收当前状态值以及每一行的聚合输入值,并返回更新后的状态值。这与普通聚合的定义相同,但要注意,不会提供直接参数(如果有的话)。最终函数接收最后的状态值、直接参数的值(如果有),以及(如果指定了finalfunc_extra)与聚合输入对应的 NULL 值。与普通聚合一样,只有当聚合是多态的时,finalfunc_extra才真正有用;这时需要这些额外的假参数来把最终函数的结果类型与聚合的输入类型关联起来。 + + + + 目前,有序集聚合不能用作窗口函数,因此也就无须支持移动聚合模式。 + + + + + + + 部分聚合 + + + 聚合函数 + 部分聚合 + + + + 聚合函数还可以选择支持部分聚合。部分聚合的思想是,在输入数据的不同子集上分别独立运行聚合的状态转移函数,然后把这些子集产生的状态值合并起来,得到与一次性扫描全部输入时相同的状态值。这种模式可用于并行聚合,让不同的工作进程扫描表的不同部分。每个工作进程都会产生一个部分状态值,最后再把这些状态值合并起来,得到最终状态值。(未来,这种模式也可能用于合并本地表和远程表上的聚合结果等用途,但目前尚未实现。) + + + + 要支持部分聚合,聚合定义必须提供一个合并函数。它接收该聚合状态类型的两个值(表示分别在两组输入行子集上进行聚合的结果),并生成一个新的同类型值,表示对这两组行合并后进行聚合本应得到的状态。至于这两组输入行的相对顺序,则是未指定的。这意味着,对输入行顺序敏感的聚合通常无法定义出有用的合并函数。 + + + + 举个简单的例子,MAXMIN聚合可以通过把合并函数指定为与其转移函数相同的“两者取较大值”或“两者取较小值”比较函数,来支持部分聚合。SUM聚合则只需要把加法函数用作合并函数。(同样,除非状态值比输入数据类型更宽,否则合并函数与转移函数相同。) + + + + 合并函数很像一种转移函数,只不过它的第二个参数接受的是状态类型的值,而不是底层输入类型的值。特别是,处理空值和 strict 函数的规则与转移函数类似。另外,如果聚合定义指定了非空的initcond,要记住它不仅会用作每次部分聚合运行的初始状态,也会用作合并函数的初始状态;合并函数会被调用,把每个部分结果都合并到该状态中。 + + + + 如果聚合的状态类型声明为internal,那么合并函数必须负责确保其结果分配在聚合状态值所使用的正确内存上下文中。这尤其意味着,当第一个输入为NULL时,不能简单返回第二个输入,因为那个值位于错误的上下文中,生命周期也不够长。 + + + + 当聚合的状态类型声明为internal时,通常也应在聚合定义中提供序列化函数反序列化函数,以便能把这种状态值从一个进程复制到另一个进程。如果没有这些函数,就无法进行并行聚合,而未来诸如本地/远程聚合之类的应用也很可能无法工作。 + + + + 序列化函数必须接受一个internal类型参数,并返回一个bytea类型结果,用来表示打包成扁平字节块的状态值。反过来,反序列化函数负责逆转这种转换。它必须接受byteainternal类型的两个参数,并返回一个internal类型结果。(第二个参数不会被使用,且总是零,但出于类型安全考虑必须保留。)与合并函数的结果不同,反序列化函数的结果不需要长期存在,因此只需在当前内存上下文中分配即可。 + + + + 还要注意,若要让一个聚合能够并行执行,必须将聚合本身标记为PARALLEL SAFE。系统不会参考其支持函数上的并行安全性标记。 + + + + + + 聚合的支持函数 + + + 聚合函数 + 支持函数 + + + 用 C 编写的函数可以通过调用AggCheckCallContext来检测自己是否作为聚合支持函数被调用,例如: +if (AggCheckCallContext(fcinfo, NULL)) +检查这一点的一个原因是,如果对状态转移函数来说结果为真,那么第一个输入一定是临时状态值,因此可以安全地原地修改,而不必分配一个新副本。示例见int8inc()。(这是函数可以安全地修改按引用传递的输入的唯一情况。特别是,普通聚合的最终函数在任何情况下都不得修改其输入,因为在某些情况下,它们会在同一个最终状态值上被再次执行。) + + + AggCheckCallContext的第二个参数可用于取得保存聚合状态值的内存上下文。这对于希望把展开对象(见)用作状态值的转移函数很有用。第一次调用时,转移函数应返回一个展开对象,其内存上下文是聚合状态上下文的子上下文;之后的调用则持续返回同一个展开对象。示例见array_append()。(array_append()并不是任何内置聚合的转移函数,但它的写法保证了在用作自定义聚合的转移函数时也能高效工作。) + + + + 另一个可供用 C 编写的聚合函数使用的支持例程是AggGetAggref,它返回定义该聚合调用的Aggref解析节点。这主要对有序集聚合有用,因为它们可以检查Aggref节点的子结构,以找出自己应实现的排序顺序。示例可见PostgreSQL源代码中的orderedsetaggs.c。 + + + + + diff --git a/zh/9.6/xfunc.sgml b/zh/9.6/xfunc.sgml new file mode 100644 index 00000000..4092994e --- /dev/null +++ b/zh/9.6/xfunc.sgml @@ -0,0 +1,2710 @@ + + + + 用户定义的函数 + + + 函数 + 用户定义的 + + + + PostgreSQL提供四种函数: + + + + + 查询语言函数(用SQL编写的函数)() + + + + + 过程语言函数(例如,用PL/pgSQLPL/Tcl编写的函数)() + + + + + 内部函数() + + + + + C 语言函数() + + + + + + + 每一类函数可以采用基础类型、复合类型或者它们的组合作为参数。此外,每一类函数可以返回一个基础类型或一个复合类型。函数也能被定义成返回基础类型或复合类型值的集合。 + + + + 很多类函数可以接受或者返回特定的伪类型(例如,多态类型),但是可用的功能因函数类别而异。详情可以参考每一种函数的描述。 + + + 定义SQL函数最容易,因此我们将从讨论它们开始。大部分SQL函数的概念也能用到其他类型的函数上。 + + + 在这一章中,查看命令的参考页有助于更好地理解示例。 + 这章中的一些示例可以在PostgreSQL源代码发布的src/tutorial目录中的funcs.sqlfuncs.c中找到。 + + + + + 查询语言(<acronym>SQL</acronym>)函数 + + + 函数 + 用户定义的 + 在 SQL 中 + + + + SQL 函数执行由任意 SQL 语句组成的一个列表,并返回列表中最后一个查询的结果。在简单(非集合)情况下,将返回最后一个查询结果的第一行。(请记住,除非使用 ORDER BY,否则多行结果中的第一行并没有明确定义。)如果最后一个查询完全没有返回任何行,则返回空值。 + + + + 或者,一个 SQL 函数可以通过指定函数的返回类型为SETOF sometype被声明为返回一个集合(也就是多个行),或者等效地声明它为RETURNS TABLE(columns)。在这种情况下,最后一个查询的结果的所有行会被返回。下文将给出进一步的细节。 + + + SQL 函数的函数体必须是一个由分号分隔的 SQL 语句列表。最后一条语句后的分号是可选的。除非该函数被声明为返回 void,否则最后一条语句必须是 SELECT,或者是 INSERTUPDATEDELETE 并带有 RETURNING 子句。 + + 任意一组 SQL 语言命令都可以打包并定义成函数。除 SELECT 查询外,这些命令还可以包含数据修改查询(INSERT, + UPDATEDELETE),以及其他 SQL 命令。(不能使用事务控制命令,例如 COMMITSAVEPOINT,以及某些工具命令,例如 VACUUM,来编写 SQL 函数。)不过,最后一条命令必须是 SELECT,或者带有 RETURNING 子句,并返回与函数声明返回类型相符的结果。或者,如果你想定义一个执行动作但没有有用返回值的 SQL 函数,也可以把它定义为返回 void。例如,下面这个函数会删除 emp 表中薪资为负的行: +CREATE FUNCTION clean_emp() RETURNS void AS ' + DELETE FROM emp + WHERE salary < 0; +' LANGUAGE SQL; + +SELECT clean_emp(); + + clean_emp +----------- + +(1 row) + + + + + SQL 函数的整个函数体都会先经过解析,然后才开始执行。虽然 SQL 函数可以包含修改系统目录的命令(例如 CREATE TABLE),但在对函数中的后续命令进行解析分析时,这些命令的效果还不可见。因此,例如将 CREATE TABLE foo (...); INSERT INTO foo VALUES(...); 打包到同一个 SQL 函数中,就不会按预期工作,因为 foo 在解析 INSERT 命令时还不存在。在这类情况下,建议使用 PL/pgSQL 而不是 SQL 函数。 + + + + CREATE FUNCTION 命令的语法要求把函数体写成一个字符串常量。对于字符串常量,通常使用美元引用最方便(见)。如果你选择使用常规的单引号字符串常量语法,那么必须在函数体中将单引号(')和反斜线(\)写成双份(假定使用转义字符串语法,见)。 + + + + <acronym>SQL</acronym>函数的参数 + + + 函数 + 命名参数 + + + + 一个 SQL 函数的参数可以在函数体中用名称或编号引用。下面会有两种方法的示例。 + + + + 要使用一个名称,将函数参数声明为带有一个名称,然后在函数体中只写该名称。如果参数名称与函数内当前 SQL 命令中的任意列名相同,列名将优先。如果不想这样,可以用函数本身的名称来限定参数名,也就是function_name.argument_name(如果这会与一个被限定的列名冲突,照例还是列名赢得优先。你可以通过为 SQL 命令中的表选择一个不同的别名来避免这种混淆)。 + + + + 在更旧的数字方法中,参数可以用语法$n引用:$1指的是第一个输入参数,$2指的是第二个,以此类推。不管特定的参数是否使用名称声明,这种方法都有效。 + + + + 如果一个参数是一种复合类型,那么点号记法(如 + argname.fieldname + 或$1.fieldname)也可以被用来 + 访问该参数的属性。同样,你可能需要用函数的名称来限定参数的名称以避免歧义。 + + + + SQL 函数参数只能被用做数据值而不能作为标识符。例如这是合理的: + +INSERT INTO mytable VALUES ($1); + +但这样就不行: + +INSERT INTO $1 VALUES (42); + + + + + + 使用名称来引用 SQL 函数参数的能力是在PostgreSQL 9.2 中加入的。要在老的服务器中使用的函数必须使用$n记法。 + + + + + + 基础类型上的<acronym>SQL</acronym>函数 + + + 最简单的 SQL 函数没有参数,只是返回一个基础类型,例如 + integer: + + +CREATE FUNCTION one() RETURNS integer AS $$ + SELECT 1 AS result; +$$ LANGUAGE SQL; + +-- Alternative syntax for string literal: +CREATE FUNCTION one() RETURNS integer AS ' + SELECT 1 AS result; +' LANGUAGE SQL; + +SELECT one(); + + one +----- + 1 + + + + + 注意我们为该函数的结果在函数体内定义了一个列别名(名为result),但是这个列别名在函数以外是不可见的。因此,结果被标记为one而不是result。 + + + + 定义接受基础类型参数的 SQL 函数也几乎同样容易: + + +CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$ + SELECT x + y; +$$ LANGUAGE SQL; + +SELECT add_em(1, 2) AS answer; + + answer +-------- + 3 + + + + + 或者,我们也可以不用参数名,而改用数字: + + +CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$ + SELECT $1 + $2; +$$ LANGUAGE SQL; + +SELECT add_em(1, 2) AS answer; + + answer +-------- + 3 + + + + + 下面是一个更有用的函数,它可以用来从银行账户中扣款: + + +CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$ + UPDATE bank + SET balance = balance - debit + WHERE accountno = tf1.accountno; + SELECT 1; +$$ LANGUAGE SQL; + + + 用户可以这样执行该函数,从 17 号账户中扣除 $100.00: + + +SELECT tf1(17, 100.0); + + + + + 在这个示例中,我们为第一个参数选择了名称accountno,但是这和表bank中的一个列名相同。 + 在UPDATE命令中, + accountno引用列bank.accountno,因此 + tf1.accountno必须被用来引用该参数。 + 我们当然可以通过为该参数使用一个不同的名称来避免这样的问题。 + + + + 实际上我们可能喜欢从该函数得到一个更有用的结果而不是一个常数 1,因此一个更可能的定义是: + + +CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$ + UPDATE bank + SET balance = balance - debit + WHERE accountno = tf1.accountno; + SELECT balance FROM bank WHERE accountno = tf1.accountno; +$$ LANGUAGE SQL; + + + 它会调整余额并且返回新的余额。 + 同样的事情也可以用一个使用RETURNING的命令实现: + + +CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$ + UPDATE bank + SET balance = balance - debit + WHERE accountno = tf1.accountno + RETURNING balance; +$$ LANGUAGE SQL; + + + + + + 复合类型上的<acronym>SQL</acronym>函数 + + + 编写接受复合类型参数的函数时,我们不仅必须指定要用哪个参数,还必须指定该参数的哪个属性(字段)。例如,假设 + emp 是一个包含雇员数据的表,因此它也是该表每一行对应的复合类型名称。下面的函数 + double_salary 用来计算某人的工资翻倍后会是多少: + + +CREATE TABLE emp ( + name text, + salary numeric, + age integer, + cubicle point +); + +INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)'); + +CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$ + SELECT $1.salary * 2 AS salary; +$$ LANGUAGE SQL; + +SELECT name, double_salary(emp.*) AS dream + FROM emp + WHERE emp.cubicle ~= point '(2,1)'; + + name | dream +------+------- + Bill | 8400 + + + + + 注意这里用 $1.salary 语法来选取参数行值中的一个字段。 + 还要注意,调用时的 SELECT 命令使用 + table_name.* 将表的当前整行取作一个复合值。该表行也可以仅用表名来引用: + +SELECT name, double_salary(emp) AS dream + FROM emp + WHERE emp.cubicle ~= point '(2,1)'; + + 但这种用法已被弃用,因为它很容易让人混淆(关于表行复合值这两种记法的更多细节,见)。 + + + + 有时候即时构造一个复合参数值会很方便。这可以用ROW构造器完成。 + 例如,我们可以调整被传递给函数的数据: + +SELECT name, double_salary(ROW(name, salary*1.1, age, cubicle)) AS dream + FROM emp; + + + + + 也可以构建一个返回复合类型的函数。这是一个返回单一emp行的函数示例: + + +CREATE FUNCTION new_emp() RETURNS emp AS $$ + SELECT text 'None' AS name, + 1000.0 AS salary, + 25 AS age, + point '(2,2)' AS cubicle; +$$ LANGUAGE SQL; + + + 在这个示例中,我们为每一个属性指定了一个常量值,但是可以用任何计算来替换这些常量。 + + + + 定义该函数时有两点重要注意事项: + + + + 查询中的选择列表顺序必须与列在该复合类型所关联的表中出现的顺序完全相同。(系统不考虑像上面这样指定的列名。) + + + 你必须对表达式进行类型转换,使之与复合类型的定义匹配,否则会得到这样的错误: + +ERROR: function declared to return emp returns varchar instead of text at column 1 + + + + + + + + 定义同一个函数的另一种方法是: +CREATE FUNCTION new_emp() RETURNS emp AS $$ + SELECT ROW('None', 1000.0, 25, '(2,2)')::emp; +$$ LANGUAGE SQL; +这里我们编写了一个 SELECT,它只返回具有正确复合类型的单列。在这种情况下,这样做并没有更好,但在某些情况下它是一种方便的替代方法 — 例如,如果我们需要通过调用另一个返回所需复合值的函数来计算结果。 + + + 我们既可以把这个函数直接当作值表达式调用: + + +SELECT new_emp(); + + new_emp +-------------------------- + (None,1000.0,25,"(2,2)") + + + 也可以把它当作表函数来调用: + + +SELECT * FROM new_emp(); + + name | salary | age | cubicle +------+--------+-----+--------- + None | 1000.0 | 25 | (2,2) + + + 第二种方式会在中进一步说明。 + + + + 当你使用返回复合类型的函数时,可能只需要取其结果中的一个字段(属性)。可以使用下面这样的语法: + + +SELECT (new_emp()).name; + + name +------ + None + + + 这里需要额外的括号,以免解析器产生歧义。如果不加括号,结果会是这样: + + +SELECT new_emp().name; +ERROR: syntax error at or near "." +LINE 1: SELECT new_emp().name; + ^ + + + + + 另一种选择是使用函数记法来提取属性: + + +SELECT name(new_emp()); + + name +------ + None + + + 如所述,字段记法和函数记法是等价的。 + + + + 另一种使用函数返回复合类型的方法是将结果传递给另一个接受正确行类型作为输入的函数: + + +CREATE FUNCTION getname(emp) RETURNS text AS $$ + SELECT $1.name; +$$ LANGUAGE SQL; + +SELECT getname(new_emp()); + getname +--------- + None +(1 row) + + + + + + 带有输出参数的<acronym>SQL</acronym>函数 + + + 函数 + 输出参数 + + + + 描述函数结果的另一种方法是使用输出参数,例如: + + +CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int) +AS 'SELECT x + y' +LANGUAGE SQL; + +SELECT add_em(3,7); + add_em +-------- + 10 +(1 row) + + + 这与中展示的 + add_em 版本本质上并无不同。输出参数的真正价值在于, + 它们为定义返回多列的函数提供了一种方便的方法。例如: + + +CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int) +AS 'SELECT x + y, x * y' +LANGUAGE SQL; + + SELECT * FROM sum_n_product(11,42); + sum | product +-----+--------- + 53 | 462 +(1 row) + + + 这里本质上发生的事情是:我们为该函数的结果创建了一个匿名复合类型。上面的示例与下面这种写法的最终效果相同: + + +CREATE TYPE sum_prod AS (sum int, product int); + +CREATE FUNCTION sum_n_product (int, int) RETURNS sum_prod +AS 'SELECT $1 + $2, $1 * $2' +LANGUAGE SQL; + + + 但通常不必额外定义一个独立的复合类型会更方便。注意,附在输出参数上的名称并非只是装饰,它们决定了这个匿名复合类型的列名。(如果省略输出参数名称,系统会自行选择一个名称。) + + + + 在从 SQL 调用这样一个函数时,输出参数不会被包括在调用参数列表中。这是因为PostgreSQL只考虑输入参数来定义函数的调用签名。这也意味着在为诸如删除函数等目的引用该函数时只有输入参数有关系。我们可以用下面的命令之一删除上述函数 + + +DROP FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int); +DROP FUNCTION sum_n_product (int, int); + + + + + 参数可以被标记为IN(默认)、OUTINOUT或者VARIADIC。 + 一个INOUT参数既作为一个输入参数(调用参数列表的一部分)又作为一个输出参数(结果记录类型的一部分)。 + VARIADIC参数是输入参数,但被按照下文所述特殊对待。 + + + + + 带有可变数量参数的<acronym>SQL</acronym>函数 + + + 函数 + 可变参数 + + + + 可变参数函数 + + + + SQL 函数可以被声明为接受可变数量的参数,只要所有 + 可选参数都属于同一种数据类型。可选参数会以数组形式传递给函数。定义这类函数时,需要把最后一个参数标记为 VARIADIC;该参数必须被声明为数组类型。例如: + + +CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$ + SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i); +$$ LANGUAGE SQL; + +SELECT mleast(10, -1, 5, 4.4); + mleast +-------- + -1 +(1 row) + + + 实际上,位于 VARIADIC 位置及之后的所有实参都会被收集成一个一维数组,就像你写成了: + + +SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work + + + 不过你实际上不能这样写,至少它不会匹配这个函数定义。被标记为 + VARIADIC 的参数匹配的是其元素类型出现一次或多次, + 而不是它自身的数组类型。 + + + + 有时候,能够把一个已经构造好的数组传给可变参数函数会很有用,尤其是当一个可变参数函数想把它的数组参数再传给另一个函数时。此外,在允许不受信任用户创建对象的模式中调用可变参数函数时,这是唯一安全的方式,见。你可以在调用中指定VARIADIC来做到这一点: + + +SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]); + + + 这样会阻止函数的可变参数按其元素类型展开,从而让数组实参能够按常规方式匹配。VARIADIC 只能附加在函数调用的最后一个实参上。 + + + + 在调用中指定VARIADIC也是向可变参数函数传递空数组的唯一方式,例如: + + +SELECT mleast(VARIADIC ARRAY[]::numeric[]); + + + 仅仅写成SELECT mleast()是行不通的,因为可变参数必须匹配至少一个实参。(如果你希望允许这种调用,可以再定义一个同名且不带参数的函数mleast。) + + + + 从可变参数派生出的数组元素参数会被视为没有自己的名字。这意味着除非你指定了 VARIADIC,否则不能使用命名参数来调用可变参数函数()。例如,下面的调用是可行的: + + +SELECT mleast(VARIADIC arr => ARRAY[10, -1, 5, 4.4]); + + + 但这些就不行: + + +SELECT mleast(arr => 10); +SELECT mleast(arr => ARRAY[10, -1, 5, 4.4]); + + + + + + 带有参数默认值的<acronym>SQL</acronym>函数 + + + 函数 + 参数的默认值 + + + + 函数可以被声明为对一些或者所有输入参数具有默认值。只要调用函数时 + 没有给出足够多的实参,就会插入默认值来弥补缺失的实参。由于参数只 + 能从实参列表的尾部开始被省略,在一个有默认值的参数之后的所有参数 + 都不得不也具有默认值(尽管使用命名参数记法可以允许放松这种限制, + 这种限制仍然会被强制以便位置参数记法能工作)。不管你是否使用它,这种能力都要求在某些用户不信任其他用户的数据库中调用函数时做一些预防措施,见。 + + + + 例如: + +CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3) +RETURNS int +LANGUAGE SQL +AS $$ + SELECT $1 + $2 + $3; +$$; + +SELECT foo(10, 20, 30); + foo +----- + 60 +(1 row) + +SELECT foo(10, 20); + foo +----- + 33 +(1 row) + +SELECT foo(10); + foo +----- + 15 +(1 row) + +SELECT foo(); -- fails since there is no default for the first argument +ERROR: function foo() does not exist + + 也可以用 = 符号代替关键字 DEFAULT。 + + + + + <acronym>SQL</acronym> 函数作为表来源 + + + 所有的 SQL 函数都可以被用在查询的FROM子句中,但是 + 对于返回复合类型的函数特别有用。如果函数被定义为返回一种基础类型, + 该表函数会产生一个单列表。如果该函数被定义为返回一种复合类型,该 + 表函数会为该复合类型的每一个属性产生一列。 + + + + 以下是一个示例: + + +CREATE TABLE foo (fooid int, foosubid int, fooname text); +INSERT INTO foo VALUES (1, 1, 'Joe'); +INSERT INTO foo VALUES (1, 2, 'Ed'); +INSERT INTO foo VALUES (2, 1, 'Mary'); + +CREATE FUNCTION getfoo(int) RETURNS foo AS $$ + SELECT * FROM foo WHERE fooid = $1; +$$ LANGUAGE SQL; + +SELECT *, upper(fooname) FROM getfoo(1) AS t1; + + fooid | foosubid | fooname | upper +-------+----------+---------+------- + 1 | 1 | Joe | JOE +(1 row) + + + 如示例所示,我们可以像操作普通表的列一样操作函数结果中的列。 + + + + 注意我们只从函数得到了一行。这是因为我们没有使用SETOF。 + 这会在下一节中介绍。 + + + + + 返回集合的<acronym>SQL</acronym>函数 + + + 函数 + 使用 SETOF + + + + 当一个 SQL 函数被声明为返回SETOF + sometype时,该函数的 + 最后一个查询会被执行完,并且它输出的每一行都会被 + 作为结果集的一个元素返回。 + + + + 这一特性通常是在查询的 FROM 子句中调用函数时使用的。在这种情况下,函数返回的每一行都会成为查询所见表中的一行。例如,假设表 foo 与上文相同,我们写: + + +CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ + SELECT * FROM foo WHERE fooid = $1; +$$ LANGUAGE SQL; + +SELECT * FROM getfoo(1) AS t1; + + + 那么会得到: + + fooid | foosubid | fooname +-------+----------+--------- + 1 | 1 | Joe + 1 | 2 | Ed +(2 rows) + + + + + 也可以通过输出参数定义的列来返回多行,例如: + + +CREATE TABLE tab (y int, z int); +INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8); + +CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) +RETURNS SETOF record +AS $$ + SELECT $1 + tab.y, $1 * tab.y FROM tab; +$$ LANGUAGE SQL; + +SELECT * FROM sum_n_product_with_tab(10); + sum | product +-----+--------- + 11 | 10 + 13 | 30 + 15 | 50 + 17 | 70 +(4 rows) + + + 这里的关键点是:你必须写成 RETURNS SETOF record,以表明该函数返回的是多行而不是单行。如果只有一个输出参数,则写该参数的类型,而不是 record。 + + + + 通过多次调用集合返回函数来构造查询结果通常很有用,其中每次调用的参数都来自表或子查询的连续行。首选方式是使用 LATERAL 关键字,详见。下面是一个使用集合返回函数枚举树结构元素的示例: + + +SELECT * FROM nodes; + name | parent +-----------+-------- + Top | + Child1 | Top + Child2 | Top + Child3 | Top + SubChild1 | Child1 + SubChild2 | Child1 +(6 rows) + +CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$ + SELECT name FROM nodes WHERE parent = $1 +$$ LANGUAGE SQL STABLE; + +SELECT * FROM listchildren('Top'); + listchildren +-------------- + Child1 + Child2 + Child3 +(3 rows) + +SELECT name, child FROM nodes, LATERAL listchildren(name) AS child; + name | child +--------+----------- + Top | Child1 + Top | Child2 + Top | Child3 + Child1 | SubChild1 + Child1 | SubChild2 +(5 rows) + + + 这个例子并没有做出任何无法用简单连接实现的事,但在更复杂的计算中,把一部分工作放进函数里通常会非常方便。 + + + + 集合返回函数也可以出现在查询的选择列表中。对于查询本身生成的每一行,集合返回函数都会被调用,并为其结果集中的每一个元素生成一行输出。前面的例子也可以写成这样: + + +SELECT listchildren('Top'); + listchildren +-------------- + Child1 + Child2 + Child3 +(3 rows) + +SELECT name, listchildren(name) FROM nodes; + name | listchildren +--------+-------------- + Top | Child1 + Top | Child2 + Top | Child3 + Child1 | SubChild1 + Child1 | SubChild2 +(5 rows) + + + 在最后一个 SELECT 中,注意没有为 Child2Child3 等显示任何输出行。这是因为 listchildren 对这些参数返回的是空集,因此不会生成结果行。这与使用 LATERAL 语法把函数结果做内连接时得到的行为相同。 + + + + + 如果一个函数的最后一条命令是 INSERTUPDATEDELETE 并带有 RETURNING,那么即使该函数没有声明为 SETOF,或者调用它的查询并未取走全部结果行,该命令也总会执行到完成。RETURNING 子句产生的额外结果行会被静默丢弃,但相应的表修改仍然会发生,并且会在函数返回前全部完成。 + + + + + 在选择列表中而不是 FROM 子句中使用集合返回函数的关键问题在于, + 把多个集合返回函数放在同一个选择列表中时,行为并不十分合理。 + (如果这样做,实际得到的输出行数是每个集合返回函数各自产生行数的最小公倍数。) + 在调用多个集合返回函数时,LATERAL 语法产生的结果更不容易令人意外,通常应改用它。 + + + + + + 返回<literal>TABLE</literal>的<acronym>SQL</acronym>函数 + + + 函数 + RETURNS TABLE + + + + 还有另一种方法可以把函数声明为返回一个集合,即使用 + RETURNS TABLE(columns)语法。 + 这等效于使用一个或者多个OUT参数外加把函数标记为返回 + SETOF record(或者是SETOF单个输出参数的 + 类型)。这种写法是在最近的 SQL 标准中指定的,因此可能比使用 + SETOF的移植性更好。 + + + + 例如,前面的求和并且相乘的示例也可以这样来做: + + +CREATE FUNCTION sum_n_product_with_tab (x int) +RETURNS TABLE(sum int, product int) AS $$ + SELECT $1 + tab.y, $1 * tab.y FROM tab; +$$ LANGUAGE SQL; + + + 不允许把显式的OUT或者INOUT参数用于 + RETURNS TABLE记法 — 必须把所有输出列放在 + TABLE列表中。 + + + + + 多态<acronym>SQL</acronym>函数 + + + SQL 函数可以被声明为接受并返回这些多态类型:anyelement, + anyarrayanynonarray, + anyenumanyrange。关于多态函数的更详细解释,见 。下面这个多态函数 make_array 会根据两个任意数据类型的元素构造一个数组: +CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$ + SELECT ARRAY[$1, $2]; +$$ LANGUAGE SQL; + +SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; + intarray | textarray +----------+----------- + {1,2} | {a,b} +(1 row) + + + + 注意类型转换 'a'::text 的使用是为了指定该参数的类型是 text。如果该参数只是一个字符串字面量,就必须这样做,否则它会被当作 unknown 类型,并且 unknown 的数组也不是一种合法的类型。如果没有该类型转换,将得到这样的错误: + +ERROR: could not determine polymorphic type because input has type "unknown" + + + + + 允许存在具有固定返回类型的多态参数,但反过来则不允许。例如: +CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$ + SELECT $1 > $2; +$$ LANGUAGE SQL; + +SELECT is_greater(1, 2); + is_greater +------------ + f +(1 row) + +CREATE FUNCTION invalid_func() RETURNS anyelement AS $$ + SELECT 1; +$$ LANGUAGE SQL; +ERROR: cannot determine result data type +DETAIL: A function returning a polymorphic type must have at least one polymorphic argument. + + + + + 多态也可以和带输出参数的函数一起使用。例如: + +CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray) +AS 'select $1, array[$1,$1]' LANGUAGE SQL; + +SELECT * FROM dup(22); + f2 | f3 +----+--------- + 22 | {22,22} +(1 row) + + + + + 多态也可以用于可变参数函数。例如: + +CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$ + SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i); +$$ LANGUAGE SQL; + +SELECT anyleast(10, -1, 5, 4); + anyleast +---------- + -1 +(1 row) + +SELECT anyleast('abc'::text, 'def'); + anyleast +---------- + abc +(1 row) + +CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$ + SELECT array_to_string($2, $1); +$$ LANGUAGE SQL; + +SELECT concat_values('|', 1, 4, 2); + concat_values +--------------- + 1|4|2 +(1 row) + + + + + + 带有排序规则的<acronym>SQL</acronym>函数 + + + 排序规则 + 在 SQL 函数中 + + + 当一个 SQL 函数有一个或多个支持排序规则的数据类型参数时,会根据实参所带的排序规则,为每次函数调用确定一个排序规则,详见 。如果能够成功确定(也就是说,参数之间不存在隐式排序规则冲突),那么所有支持排序规则的参数都会被视为隐式带有该排序规则。这会影响函数中对排序规则敏感的操作的行为。例如,使用上文的 anyleast 函数时, +SELECT anyleast('abc'::text, 'ABC'); +的结果将取决于数据库的默认排序规则。在 C 区域设置下,结果会是 ABC,但在许多其他区域设置下则会是 abc。可以在任意参数上添加 COLLATE 子句来强制指定要使用的排序规则,例如: +SELECT anyleast('abc'::text, 'ABC' COLLATE "C"); +另外,如果你希望某个函数无论以什么排序规则调用,都始终按特定排序规则工作,可以在函数定义中按需插入 COLLATE 子句。下面这个版本的 anyleast 将始终使用 en_US 区域设置来比较字符串: +CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$ + SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i); +$$ LANGUAGE SQL; +但请注意,如果把它用于不支持排序规则的数据类型,就会抛出错误。 + + + 如果无法在实参之间确定共同的排序规则,那么 SQL 函数会把参数视为带有其数据类型的默认排序规则 + (通常是数据库的默认排序规则,但对域类型参数来说也可能不同)。 + + + + 这种支持排序规则的参数的行为,可以看作是仅适用于文本数据类型的一种受限多态形式。 + + + + + + 函数重载 + + + 重载 + 函数 + + + + 可以用同样的 SQL 名称定义多于一个函数,只要它们的参数不同即可。 + 换句话说,函数名可以被重载。不管你是否使用它,这种能力都要求在某些用户不信任其他用户的数据库中调用函数时采取安全预防措施,见。当一个查询 + 被执行时,服务器将从数据类型和所提供的参数个数来决定要调用哪个 + 函数。重载也可用来模拟具有可变参数个数(最大个数有限)的函数。 + + + + 在创建一个重载函数家族时,应该小心不要创建歧义。例如,给定函数: + +CREATE FUNCTION test(int, real) RETURNS ... +CREATE FUNCTION test(smallint, double precision) RETURNS ... + + 对于test(1, 1.5)这样的输入就无法立刻清楚地知道 + 应该调用哪个函数。当前实现的解决规则在 + 中有描述,但是设计一个依赖于这种行为的系统是不明智的。 + + + + 具有单个复合类型参数的函数,通常不应与该类型的任何属性(字段)同名。 + 回想一下,attribute(table)被视为等价于 + table.attribute。 + 如果在作用于复合类型的函数复合类型的属性之间出现歧义,系统总会优先选择属性。 + 可以通过给函数名加上模式限定(即 + schema.func(table)) + 来覆盖这一选择,但更好的做法仍然是避免使用会冲突的名字。 + + + + 另一类可能的冲突发生在可变参数函数和非可变参数函数之间。例如,可以同时创建 + foo(numeric)foo(VARIADIC numeric[])。 + 这时,对于只提供一个 numeric 参数的调用(例如 foo(10.1)),就不清楚该匹配哪个函数。 + 规则是:优先使用在搜索路径中出现较早的那个函数;如果两者位于同一模式中,则优先使用非可变参数函数。 + + + + 在重载 C 语言函数时有一个额外的约束:重载函数家族中的每一个 + 函数的 C 名称必须与其他所有函数的 C 名称不同,不管是内部的 + 还是动态载入的。如果这条规则被违背,该行为将不可移植。你可能 + 会得到一个运行时链接器错误,或者这些函数之一将被调用(通常 + 是内部的那一个)。SQL CREATE + FUNCTION命令的AS子句的另一种形式 + 可以把 SQL 函数名和 C 源代码中的函数名分离。例如: + +CREATE FUNCTION test(int) RETURNS int + AS 'filename', 'test_1arg' + LANGUAGE C; +CREATE FUNCTION test(int, int) RETURNS int + AS 'filename', 'test_2arg' + LANGUAGE C; + + 这里的 C 函数名称反映了很多种可能的习惯之一。 + + + + + 函数易变性分类 + + + 易变性 + 函数 + + + VOLATILE + + + STABLE + + + IMMUTABLE + + + 每一个函数都有一个易变性分类,可能的类别是 VOLATILESTABLEIMMUTABLEVOLATILE 会作为默认类别,条件是 命令没有指定类别。易变性分类是给优化器的关于该函数行为的一种承诺: + + + 一个VOLATILE函数可以做任何事情,包括修改数据库。在 + 使用相同的参数连续调用时,它能返回不同的结果。优化器不会对这类函 + 数的行为做任何假定。使用易变函数的查询会在需要该函数值的每一行上重新对它求值。 + + + + + 一个STABLE函数不能修改数据库并且被确保对一个语句中 + 的所有行用给定的相同参数返回相同的结果。这种分类允许优化器把该函 + 数的多个调用优化成一个调用。特别是,在一个索引扫描条件中使用包含 + 这样一个函数的表达式是安全的(因为一次索引扫描只会计算一次比较值, + 而不是为每一行都计算一次,在一个索引扫描条件中不能使用 + VOLATILE函数)。 + + + + + 一个IMMUTABLE函数不能修改数据库并且被确保用相同的参数 + 永远返回相同的结果。这种分类允许优化器在一个查询用常量参数调用该函数 + 时提前计算该函数。例如,一个 + SELECT ... WHERE x = 2 + 2这样的查询可以被简化为 + SELECT ... WHERE x = 4,因为整数加法操作符底层的函数被 + 标记为IMMUTABLE。 + + + + + + + 为了最好的优化结果,你应该把函数标记为对它们合法的易变性分类中最严格 + 的那种。 + + + + 任何带有副作用的函数必须被标记为VOLATILE, + 这样对它的调用就不能被优化掉。甚至如果一个函数的值在一个查询中会 + 变化,即使它没有副作用也需要被标记为VOLATILE。这样的 + 示例有random()currval()、 + timeofday()等。 + + + + 另一种重要的示例是current_timestamp家族的函数有资格 + 被标记为STABLE,因为它们的值在一个事务中不会改变。 + + + + 在考虑先规划然后立即执行的简单交互式查询时,在STABLE和 + IMMUTABLE分类间的区别相对较小:一个函数是在规划时只 + 执行一次还是在查询执行开始期间只执行一次没有太大关系。但是如果计划 + 被保存下来然后在后面被重用,区别就大了。如果把一个实际上并非不可变的函数标记为IMMUTABLE,就可能在规划期间过早将它折叠成常量,导致 + 在后续重用该计划时继续使用陈旧的值。在使用预备语句,或使用会缓存执行计划的函数语言(如 PL/pgSQL)时,这会带来严重问题。 + + + + 对于用 SQL 或者其他任何标准过程语言编写的函数,还有第二种由易变性分类 + 决定的特性,即由调用该函数的 SQL 命令所作的数据修改的可见性。 + VOLATILE函数将看到这些更改,STABLE + 或者IMMUTABLE函数则看不到。这种行为使用 MVCC 的快照 + 行为(见)实现:STABLE和 + IMMUTABLE函数使用一个在调用查询开始时建立的快照,而 + VOLATILE函数在它们执行的每一个查询的开始都获得一个新鲜 + 的快照。 + + + + + 用 C 编写的函数按照它们自己需要的方式管理快照,但是通常最好 + 让 C 函数也按照上面的方式来。 + + + + + 由于这种快照行为,只包含 SELECT 命令的函数可以安全地标记为 STABLE, + 即使它查询的表可能正被并发查询修改。PostgreSQL 会使用为调用查询建立的快照来执行 STABLE 函数中的所有命令, + 因而该函数在整个查询期间看到的都是数据库的固定视图。 + + + + 对 IMMUTABLE 函数中的 SELECT 也采用同样的快照行为。 + 一般来说,在 IMMUTABLE 函数里查询数据库表并不明智,因为一旦表内容发生变化,就会破坏其不变性。 + 不过,PostgreSQL 并不会强制禁止这样做。 + + + + 一种常见的错误是当一个函数的结果依赖于一个配置参数时把它标记为 + IMMUTABLE。例如,一个操纵时间戳的函数有可能结果 + 依赖于设置。为了安全起见,这类 + 函数应该被标记为STABLE。 + + + + + PostgreSQL要求STABLE + 和IMMUTABLE函数中不包含非SELECT + 的 SQL 命令以阻止数据修改(这也不是完全万无一失,因为这类函数还可以 + 调用修改数据库的VOLATILE函数。如果那样做,你将发现 + 该STABLEIMMUTABLE函数不会发现由被调 + 用函数所作的数据库改变,因为它们对它的快照不可见)。 + + + + + + 过程语言函数 + + + PostgreSQL允许用除 SQL 和 C 之外 + 的语言编写用户定义的函数。这些语言通常被称为过程语言PL)。 + 过程语言并不内置在PostgreSQL服务器中, + 它们通过可装载模块提供。更多信息请见以及接下来的 + 章节。 + + + + + 内部函数 + + 函数内部 + + + 内部函数由 C 编写并且已经被静态链接到PostgreSQL + 服务器中。该函数定义的主体指定该函数的 C 语言名称, + 它不必与为 SQL 使用而声明的名称相同。(出于向后兼容的原因,也接受空 + 主体,此时会认为 C 语言函数名与 SQL 函数名相同。) + + + + 通常,所有存在于服务器中的内部函数都在数据库集簇的初始化(见 + )期间被声明,但是用户可以使用 + CREATE FUNCTION为一个内部函数创建 + 额外的别名。在CREATE FUNCTION中用 + 语言名internal来声明内部函数。例如,要为 + sqrt函数创建一个别名: + +CREATE FUNCTION square_root(double precision) RETURNS double precision + AS 'dsqrt' + LANGUAGE internal + STRICT; + + (大部分内部函数应该被声明为严格)。 + + + + + 上述场景中并非所有预定义的函数都是 + 内部函数。有些预定义的函数由 SQL + 编写。 + + + + + + C 语言函数 + + + 函数 + 用户定义的 + 在 C 中 + + + + 用户定义的函数可以用 C 编写(或者可以与 C 兼容的语言,例如 C++)。 + 这类函数被编译成动态载入对象(也被称为共享库)并且由服务器在 + 需要时载入。动态载入是把C语言函数和 + 内部函数区分开的特性 — 两者真正的编码习惯 + 实际上是一样的(因此,标准的内部函数库是用户定义的 C 函数很好 + 的源代码实例)。 + + + + 目前 C 函数使用两种不同的调用约定。较新的版本 1调用约定通过为函数编写一个PG_FUNCTION_INFO_V1()宏来指示,如下文所示。没有这样的宏则表示这是一个旧式(版本 0)函数。无论哪种情况,CREATE FUNCTION中指定的语言名称都是C。旧式函数由于可移植性问题和功能缺乏,现在已被弃用,但出于兼容性原因仍然支持。 + + + + 动态载入 + + + 动态载入 + + + + 在一个会话中第一次调用一个特定可载入目标文件中的用户定义函数时, + 动态载入器会把那个目标文件载入到内存以便该函数被调用。因此用户 + 定义的 C 函数的CREATE FUNCTION必须 + 为该函数指定两块信息:可载入目标文件的名称,以及要在该目标文件中 + 调用的特定函数的 C 名称(链接符号)。如果没有显式指定 C 名称,则 + 它被假定为和 SQL 函数名相同。 + + + + 下面的算法被用来基于CREATE FUNCTION + 命令中给定的名称来定位共享目标文件: + + + + + 如果名称是一个绝对路径,则载入给定的文件。 + + + + + + 如果该名称以字符串$libdir开始,那么这一部分会被 + PostgreSQL包的库目录名(在编译时确定)替换。 + $libdir + + + + + + 如果该名称不包含目录部分,会在配置变量 + 指定的路径中搜索该 + 文件。dynamic_library_path + + + + + + 否则(在该路径中没找到该文件,或者它包含一个非绝对目录), + 动态载入器将尝试接受给定的名称,这大部分会导致失败(依赖 + 当前工作目录是不可靠的)。 + + + + + 如果这个序列不起作用,会把平台相关的共享库文件名扩展(通常是 + .so)追加到给定的名称并且再次尝试上述 + 的过程。如果还是失败,则载入失败。 + + + + 建议通过相对于 $libdir 的路径,或者通过动态库路径来定位共享库。这样一来,如果新安装位于不同的位置,版本升级会更简单。$libdir 实际代表的目录可以通过命令 pg_config --pkglibdir 查出。 + + + + 用于运行PostgreSQL服务器的 + 用户 ID 必须能够通过要载入文件的路径。常见的错误是把文件或 + 更高层的目录变得对postgres用户 + 不可读或者不可执行。 + + + + 在任何情况下,CREATE FUNCTION命令 + 中给定的文件名会被原封不动地记录在系统目录中,这样如果需要再次 + 载入该文件则会应用同样的过程。 + + + + + PostgreSQL不会自动编译 C 函数。在 + 从CREATE FUNCTION命令中引用目标文件 + 之前,它必须先被编译好。更多信息请见。 + + + + + 魔数块 + + + 为确保动态装载的目标文件不会被加载到不兼容的服务器中,PostgreSQL会检查该文件是否包含内容适当的魔数块。这样服务器就能检测出明显的不兼容情况,例如代码所针对的大版本不同于当前的 PostgreSQL。魔数块自 PostgreSQL 8.2 起就是必需的。要加入魔数块,请在模块某一个(且只能一个)源文件中写入下列内容,并事先包含头文件 fmgr.h: + + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif +可以省略 #ifdef 检查,如果代码不需要针对 8.2 之前的 PostgreSQL 版本编译的话。 + + + 在第一次使用之后,动态载入目标文件会保留在内存中。在同一个会话中, + 后续对该文件中函数的调用只需付出一次很小的符号表查找开销。如果需要 + 强制重新载入一个目标文件(例如在重新编译之后),就需要开启一个新的会话。 + + + + _PG_init + + _PG_fini + + 库初始化函数 + + + 库终结函数 + + + 动态装载的文件可以选择包含初始化和终结函数。如果文件包含一个名为 _PG_init 的函数,该函数会在文件装载后立即被调用。该函数不接收参数,并且应返回 void。如果文件包含一个名为 _PG_fini 的函数,该函数会在文件卸载前立即被调用。同样,该函数不接收参数,并且应返回 void。注意,_PG_fini 只会在卸载文件时被调用,而不会在进程终止时被调用。(目前,卸载已被禁用,永远不会发生,但将来可能会改变。) + + + + + C 语言函数中的基础类型 + + + 数据类型 + 内部组织 + + + + 要了解如何编写 C 语言函数,你需要了解 + PostgreSQL如何在内部表达基础类型 + 以及如何与函数传递它们。在内部, + PostgreSQL把基础类型视为一块内存数据块。 + 你为该类型定义的用户自定义函数,决定了 PostgreSQL 如何操作它。 + 也就是说,PostgreSQL 只负责把数据存到磁盘、再从磁盘取回, + 而数据的输入、处理和输出则依赖你定义的这些函数。 + + + + 基础类型可以有三种内部格式之一: + + + + + 传值,定长 + + + + + 传引用,定长 + + + + + 传引用,变长 + + + + + + + 传值类型在长度上只能是 1、2 或 4 字节(如果你的机器上 + sizeof(Datum)是 8,则还有 8 字节)。你应当小心地 + 定义你的类型以便它们在所有的架构上都是相同的尺寸(字节)。例如, + long类型很危险,因为它在某些机器上是 4 字节但在 + 另外一些机器上是 8 字节,而int类型在大部分 Unix 机器 + 上都是 4 字节。在 Unix 机器上int4类型一种合理的实现 + 可能是: + + +/* 4 字节整数,传值 */ +typedef int int4; + + + (实际的 PostgreSQL C 代码会把这种类型称为int32,因为 + C 中的习惯是intXX + 表示XX 。注意 + 因此还有尺寸为 1 字节的 C 类型int8。SQL 类型 + int8在 C 中被称为int64。另见 + )。 + + + + 另一方面,任意尺寸的定长类型都可以通过引用传递。例如,这里有一种 + PostgreSQL类型的实现示例: + + +/* 16 字节结构,传引用 */ +typedef struct +{ + double x, y; +} Point; + + + 在PostgreSQL函数中传进或传出这种 + 类型时,只能使用指向这种类型的指针。要返回这样一种类型的值,用 + palloc分配正确的内存量,然后填充分配好的内存, + 并且返回一个指向该内存的指针(还有,如果只想返回与具有相同数据类型的 + 一个输入参数相同的值,可以跳过额外的palloc并且返回 + 指向该输入值的指针)。 + + + + 最后,所有变长类型必须也以引用的方式传递。所有变长类型必须用一个 + 正好 4 字节的不透明长度字段开始,该字段会由SET_VARSIZE + 设置,绝不要直接设置该字段!所有要被存储在该类型中的数据必须在内存 + 中接着该长度字段的后面存储。长度字段包含该结构体的总长度,也就是包括长度字段本身的尺寸。 + + + + 另一个重点是要避免在数据类型值中留下未被初始化的位。例如,要注意 + 把可能存在于结构体中的任何对齐填充字节置零。如果不这样做,你的数据 + 类型的逻辑等价常量可能会被规划器认为是不等的,进而导致低效的(不过 + 还是正确的)计划。 + + + + + 绝不要修改通过引用传递的输入值的内容。如果这样做 + 很可能会破坏磁盘上的数据,因为给出的指针可能直接指向一个磁盘缓冲 + 区。这条规则唯一的例外在中有解释。 + + + + + 例如,我们可以这样定义类型text: + + +typedef struct { + int32 length; + char data[FLEXIBLE_ARRAY_MEMBER]; +} text; + + + [FLEXIBLE_ARRAY_MEMBER]记号表示数据部分的实际 + 长度不由该声明指定。 + + + + 在操作变长类型时,我们必须小心分配正确数量的内存,并正确设置长度字段。 + 例如,如果我们想在一个text结构体 + 中存储 40 字节,我们可以使用这样的代码片段: + +data, buffer, 40); +... +]]> + + + VARHDRSZsizeof(int32)一样, + 但是用宏VARHDRSZ来引用变长类型的额外开销的 + 尺寸被认为是比较好的风格。还有,必须 + 使用SET_VARSIZE宏来设置长度字段,而不是用 + 简单的赋值来设置。 + + + + 说明了在编写使用 + PostgreSQL 内置类型的 C 语言函数时,哪种 C 类型对应哪种 SQL 类型。 + 定义文件列给出了需要包含的头文件,以获取类型定义。 + (实际的定义可能位于所列文件包含的其他文件中。建议用户坚持使用已定义的接口。) + 请注意,在任何源文件中都应该始终首先包含postgres.h, + 因为它声明了很多你反正都会用到的内容。 + + + + 内置 SQL 类型等效的 C 类型 + + + + + SQL 类型 + + + C 类型 + + + 定义文件 + + + + + + abstime + AbsoluteTime + utils/nabstime.h + + + bigint (int8) + int64 + postgres.h + + + boolean + bool + postgres.h(可能是编译器内置) + + + box + BOX* + utils/geo_decls.h + + + bytea + bytea* + postgres.h + + + "char" + char + (编译器内置) + + + character + BpChar* + postgres.h + + + cid + CommandId + postgres.h + + + date + DateADT + utils/date.h + + + smallint (int2) + int16 + postgres.h + + + int2vector + int2vector* + postgres.h + + + integer (int4) + int32 + postgres.h + + + real (float4) + float4* + postgres.h + + + double precision (float8) + float8* + postgres.h + + + interval + Interval* + datatype/timestamp.h + + + lseg + LSEG* + utils/geo_decls.h + + + name + Name + postgres.h + + + oid + Oid + postgres.h + + + oidvector + oidvector* + postgres.h + + + path + PATH* + utils/geo_decls.h + + + point + POINT* + utils/geo_decls.h + + + regproc + regproc + postgres.h + + + reltime + RelativeTime + utils/nabstime.h + + + text + text* + postgres.h + + + tid + ItemPointer + storage/itemptr.h + + + time + TimeADT + utils/date.h + + + time with time zone + TimeTzADT + utils/date.h + + + timestamp + Timestamp + datatype/timestamp.h + + + tinterval + TimeInterval + utils/nabstime.h + + + varchar + VarChar* + postgres.h + + + xid + TransactionId + postgres.h + + + +
    + + + 了解了基础类型所有可能的结构后,就可以看一些实际函数示例。 + +
    + + + 版本 0 调用约定 + + + 我们先介绍旧式调用约定——尽管这种方法现在已被弃用,但入门时更容易上手。在版本 0 方法中,C 函数的参数和结果就按普通的 C 风格声明,只是要注意使用上文所示的每种 SQL 数据类型的 C 表示。 + + + + 下面是一些示例: + + +#include "utils/geo_decls.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +/* by value */ + +int +add_one(int arg) +{ + return arg + 1; +} + +/* by reference, fixed length */ + +float8 * +add_one_float8(float8 *arg) +{ + float8 *result = (float8 *) palloc(sizeof(float8)); + + *result = *arg + 1.0; + + return result; +} + +Point * +makepoint(Point *pointx, Point *pointy) +{ + Point *new_point = (Point *) palloc(sizeof(Point)); + + new_point->x = pointx->x; + new_point->y = pointy->y; + + return new_point; +} + +/* by reference, variable length */ + +text * +copytext(text *t) +{ + /* + * VARSIZE is the total size of the struct in bytes. + */ + text *new_t = (text *) palloc(VARSIZE(t)); + SET_VARSIZE(new_t, VARSIZE(t)); + /* + * VARDATA is a pointer to the data region of the struct. + */ + memcpy((void *) VARDATA(new_t), /* destination */ + (void *) VARDATA(t), /* source */ + VARSIZE(t) - VARHDRSZ); /* how many bytes */ + return new_t; +} + +text * +concat_text(text *arg1, text *arg2) +{ + int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ; + text *new_text = (text *) palloc(new_text_size); + + SET_VARSIZE(new_text, new_text_size); + memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ); + memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ), + VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ); + return new_text; +} +]]> + + + + + 假定上述代码已经写在文件funcs.c中并编译为共享对象,我们可以用类似下面的命令向 + PostgreSQL定义这些函数: + + +CREATE FUNCTION add_one(integer) RETURNS integer + AS 'DIRECTORY/funcs', 'add_one' + LANGUAGE C STRICT; + +-- note overloading of SQL function name "add_one" +CREATE FUNCTION add_one(double precision) RETURNS double precision + AS 'DIRECTORY/funcs', 'add_one_float8' + LANGUAGE C STRICT; + +CREATE FUNCTION makepoint(point, point) RETURNS point + AS 'DIRECTORY/funcs', 'makepoint' + LANGUAGE C STRICT; + +CREATE FUNCTION copytext(text) RETURNS text + AS 'DIRECTORY/funcs', 'copytext' + LANGUAGE C STRICT; + +CREATE FUNCTION concat_text(text, text) RETURNS text + AS 'DIRECTORY/funcs', 'concat_text' + LANGUAGE C STRICT; + + + + + 这里DIRECTORY代表共享库文件所在的目录(例如 + PostgreSQL的教程目录,其中包含本节示例所用的代码)。 + (更好的风格是在AS子句中只写'funcs',前提是已把 + DIRECTORY加入搜索路径。无论哪种情况,都可以省略共享库的系统特定扩展名,通常是 + .so.sl。) + + + + 注意我们把函数声明为strict(严格),意思是如果任何输入值为空,系统会自动假定结果为空。这样做可以避免在函数代码中检查空输入。否则,我们就必须显式检查空值,即检查每个传引用参数是否为空指针。(对于传值参数,我们甚至没有办法检查!) + + + + 尽管这种调用约定使用简单,但可移植性不好;在某些体系结构上,以这种方式传递小于int的数据类型会有问题。另外,也没有返回空结果的简单方法,除了把函数声明为严格函数之外,也没有其他处理空参数的简单方法。下面介绍的版本 1 约定克服了这些缺点。 + + + + + 版本 1 的调用约定 + + + 版本-1 的调用约定依赖于宏来降低传参数和结果的复杂度。版本-1 函数的 + C 声明总是: + +Datum funcname(PG_FUNCTION_ARGS) + + 此外,宏调用: + +PG_FUNCTION_INFO_V1(funcname); + + 必须出现在同一个源文件中(按惯例会正好写在该函数本身之前)。 + 这种宏调用不是internal语言函数所需要的,因为 + PostgreSQL会假定所有内部函数都使用 + 版本-1 调用约定。不过,对于动态载入函数是必需的。 + + + + 在版本-1 函数中,每一个实参都使用对应于该参数数据类型的PG_GETARG_xxx()宏取得,结果要用对应于返回类型的PG_RETURN_xxx()宏返回。PG_GETARG_xxx()的参数是要取得的函数参数的编号,从零开始计。PG_RETURN_xxx()的参数是实际要返回的值。 + + + + 这里我们展示与上面相同的函数,以版本-1 风格编码: + + + +#include "fmgr.h" +#include "utils/geo_decls.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +/* by value */ + +PG_FUNCTION_INFO_V1(add_one); + +Datum +add_one(PG_FUNCTION_ARGS) +{ + int32 arg = PG_GETARG_INT32(0); + + PG_RETURN_INT32(arg + 1); +} + +/* by reference, fixed length */ + +PG_FUNCTION_INFO_V1(add_one_float8); + +Datum +add_one_float8(PG_FUNCTION_ARGS) +{ + /* The macros for FLOAT8 hide its pass-by-reference nature. */ + float8 arg = PG_GETARG_FLOAT8(0); + + PG_RETURN_FLOAT8(arg + 1.0); +} + +PG_FUNCTION_INFO_V1(makepoint); + +Datum +makepoint(PG_FUNCTION_ARGS) +{ + /* Here, the pass-by-reference nature of Point is not hidden. */ + Point *pointx = PG_GETARG_POINT_P(0); + Point *pointy = PG_GETARG_POINT_P(1); + Point *new_point = (Point *) palloc(sizeof(Point)); + + new_point->x = pointx->x; + new_point->y = pointy->y; + + PG_RETURN_POINT_P(new_point); +} + +/* by reference, variable length */ + +PG_FUNCTION_INFO_V1(copytext); + +Datum +copytext(PG_FUNCTION_ARGS) +{ + text *t = PG_GETARG_TEXT_P(0); + /* + * VARSIZE is the total size of the struct in bytes. + */ + text *new_t = (text *) palloc(VARSIZE(t)); + SET_VARSIZE(new_t, VARSIZE(t)); + /* + * VARDATA is a pointer to the data region of the struct. + */ + memcpy((void *) VARDATA(new_t), /* destination */ + (void *) VARDATA(t), /* source */ + VARSIZE(t) - VARHDRSZ); /* how many bytes */ + PG_RETURN_TEXT_P(new_t); +} + +PG_FUNCTION_INFO_V1(concat_text); + +Datum +concat_text(PG_FUNCTION_ARGS) +{ + text *arg1 = PG_GETARG_TEXT_P(0); + text *arg2 = PG_GETARG_TEXT_P(1); + int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ; + text *new_text = (text *) palloc(new_text_size); + + SET_VARSIZE(new_text, new_text_size); + memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ); + memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ), + VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ); + PG_RETURN_TEXT_P(new_text); +} +]]> + + + + 这些函数的CREATE FUNCTION命令与版本 0 的等价形式相同。 + + + + 乍看之下,版本 1 的编码约定似乎只是无谓的故弄玄虚。不过,它们确实提供了许多改进,因为宏可以隐藏不必要的细节。例如,在编码add_one_float8时,我们不再需要知道float8是传引用类型。另一个例子是,变长类型的GETARG宏允许更有效地取得toasted(压缩或外置)值。 + + + + 版本-1 函数的一大改进是对空输入和结果的更好处理。宏PG_ARGISNULL(n)允许一个函数测试是否每一个输入为空值(当然,只需要在没有声明为strict的函数中这样做)。和PG_GETARG_xxx()宏一样,输入参数也是从零开始计数。注意应该在验证了一个参数不是空值之后才执行PG_GETARG_xxx()。要返回空值结果,应执行PG_RETURN_NULL(),它对严格的以及非严格的函数都有用。 + + + + 新式接口中提供的其他选项是PG_GETARG_xxx()宏的两个变种。其中的第一种是PG_GETARG_xxx_COPY(),它确保返回的指定参数的拷贝可以被安全地写入(通常的宏有时会返回一个指向表中物理存储值的指针,不能写入该值。使用PG_GETARG_xxx_COPY()宏可以保证得到一个可写的结果)。第二种变种PG_GETARG_xxx_SLICE()宏有三个参数。第一个是函数参数的编号(如上文)。第二个和第三个是要被返回的段的偏移量和长度。偏移量从零开始计算,而负值的长度则表示要求返回该值的剩余部分。当大型值的存储类型为external时,这些宏提供了访问这些大型值的部分内容的更有效方法(列的存储类型可以使用ALTER TABLE tablename ALTER COLUMN colname SET STORAGE storagetype来指定。storagetypeplainexternalextended或者main)。 + + + + 最后,版本-1 的函数调用约定可以返回集合结果()、实现触发器函数()和过程语言调用处理器()。版本-1 代码也比版本-0 更可移植,因为它不违反 C 标准中关于函数调用协议的限制。更多细节 + 可见源代码发布中的src/backend/utils/fmgr/README。 + + + + + 编写代码 + + + 在开始更高级的话题之前,我们应该讨论一下用于 + PostgreSQL C 语言函数的编码规则。 + 虽然有可能把不是 C 编写的函数载入到 + PostgreSQL中,但即使能够做到,通常也很困难, + 因为其他语言(例如 C++、FORTRAN 或者 Pascal)通常不会遵循和 C + 相同的调用约定。也就是说,其他语言不会以同样的方式在函数之间传递 + 参数以及返回值。由于这个原因,我们会假定你的 C 语言函数确实是用 C + 编写的。 + + + + 编写和构建 C 语言函数的基本规则如下: + + + + + 使用 pg_config + --includedir-serverpg_config用于用户定义的 C 函数 + 查出 PostgreSQL 服务器头文件在你的系统上(或你的用户将要运行的系统上)安装于何处。 + + + + + + 为了让你的代码能够被 PostgreSQL 动态装入,编译和链接时总是需要特殊的选项。关于如何在特定操作系统上完成这件事,详见。 + + + + + + 记得按照中的说明,为你的共享库定义一个魔数块。 + + + + + + 分配内存时,使用 PostgreSQL 提供的 + pallocpallocpfreepfree, + 而不是相应的 C 库函数 mallocfree。用 palloc 分配的内存会在每个事务结束时自动释放,从而避免内存泄漏。 + + + + + + 总是使用 memset 将结构体的所有字节清零(或者一开始就用 palloc0 来分配它们)。即使你给结构体的每个字段都赋了值,结构体中仍可能存在包含垃圾值的对齐填充字节(也就是结构体中的空洞)。如果不这么做,就很难支持哈希索引或哈希连接,因为那时你必须只挑出数据结构中真正有意义的位来计算哈希值。规划器有时也依赖按位相等来比较常量,因此如果逻辑上等价的值在按位上不相等,就可能得到不理想的规划结果。 + + + + + + PostgreSQL 的大多数内部类型都在 postgres.h 中声明,而函数管理器接口(PG_FUNCTION_ARGS 等)位于 fmgr.h 中,因此至少需要包含这两个文件。出于可移植性考虑,最好把 postgres.h 放在 最前面,先于任何其他系统或用户头文件。包含 postgres.h 时,也会顺带为你包含 elog.hpalloc.h。 + + + + + + 目标文件中定义的符号名不能彼此冲突,也不能与 + PostgreSQL 服务器可执行文件中定义的符号冲突。如果你收到这类错误消息,就必须重命名相关函数或变量。 + + + + + &dfunc; + 复合类型参数 + + + 复合类型没有像 C 结构体那样的固定布局。复合类型的实例可能包含 + 空值字段。此外,继承层次中的复合类型可能具有和同一继承层次中 + 其他成员不同的字段。因此, + PostgreSQL提供了函数接口 + 以便从 C 访问复合类型的字段。 + + + 假设我们想编写一个函数来回答如下查询: +SELECT name, c_overpaid(emp, 1500) AS overpaid + FROM emp + WHERE name = 'Bill' OR name = 'Sam'; +使用版本 0 调用约定,我们可以将 c_overpaid 定义为: limit; +} +]]> + + + 按照版本-1 编码,上面的函数写成这样: + + limit); +} +]]> + + + + + GetAttributeByName 是 + PostgreSQL 的一个系统函数,用于从指定行中取出属性。它有三个参数:传入函数的 HeapTupleHeader 类型参数、所需属性的名称,以及一个用于指示该属性是否为 null 的返回参数。GetAttributeByName 返回一个 Datum 值,你可以用适当的 DatumGetXXX() 宏把它转换为正确的数据类型。注意,如果 null 标志被设置,那么返回值本身没有意义;在尝试对结果做任何处理之前,务必先检查这个 null 标志。 + + + + 也有GetAttributeByNum函数,它可以用目标属性 + 的列号而不是属性名来选择目标属性。 + + + + 下面的命令声明 SQL 中的c_overpaid: + + +CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean + AS 'DIRECTORY/funcs', 'c_overpaid' + LANGUAGE C STRICT; + + + 注意我们用了STRICT,这样我们不需要检查输入参数是否 + 为 NULL。 + + + + + 返回行(复合类型) + + + 要从 C 语言函数中返回一行或一个复合类型值,可以使用一套特殊的 API, + 它通过一组宏和函数隐藏了构造复合数据类型时的大部分复杂性。要使用这套 API,源文件中必须包含: + +#include "funcapi.h" + + + + + 构造复合数据值(下文简称元组)有两种方式: + 一种是从 Datum 值数组构造,另一种是从 C 字符串数组构造,这些字符串会传给该元组各列数据类型的输入转换函数。 + 无论采用哪种方式,首先都需要获取或构造描述该元组结构的 TupleDesc。 + 处理 Datum 时,需要把 TupleDesc 传给 BlessTupleDesc,然后为每一行调用 heap_form_tuple。 + 处理 C 字符串时,则要把 TupleDesc 传给 TupleDescGetAttInMetadata,然后为每一行调用 BuildTupleFromCStrings。 + 对于返回元组集合的函数,这些准备步骤可以在第一次调用函数时一次性完成。 + + + + 有一些辅助函数可以用来设置所需的 TupleDesc。在大多数返回复合值的函数中,推荐的做法是调用: + +TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, + Oid *resultTypeId, + TupleDesc *resultTupleDesc) + + 传入调用函数本身收到的同一个 fcinfo 结构体(这当然要求使用版本 1 调用约定)。 + resultTypeId 可以指定为 NULL,也可以指定为一个本地变量的地址,用于接收函数结果类型的 OID。 + resultTupleDesc 应当是一个本地 TupleDesc 变量的地址。 + 检查返回结果是否为 TYPEFUNC_COMPOSITE;如果是,resultTupleDesc 就会被填入所需的 TupleDesc。 + (如果不是,可以报告一个类似function returning record called in context that cannot accept type record的错误。) + + + + + get_call_result_type能够解析一个多态函数结果的实际类型, + 因此不仅在返回复合类型的函数中,在返回标量多态结果的函数中它也是非常 + 有用的。resultTypeId输出主要用于返回多态标量的函数。 + + + + + + get_call_result_type有一个兄弟 + get_expr_result_type,它被用来解析被表示为一棵表达式 + 树的函数调用的输出类型。在尝试从函数自身外部确定结果类型时可以用它。 + 也有一个get_func_result_type,当只有函数的 OID 可用时 + 可以用它。不过这些函数无法处理被声明为返回record的 + 函数,并且get_func_result_type无法解析多态类型,因此你 + 应该优先使用get_call_result_type。 + + + + + 更早、现在已被弃用的获取TupleDesc的函数有: + +TupleDesc RelationNameGetTupleDesc(const char *relname) + + 它可以为一个指定名称的关系的行类型得到TupleDesc, + 还有: + +TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) + + 可以基于一个类型 OID 得到TupleDesc。这可以被用来 + 为一种基本或者复合类型获得TupleDesc。不过,对于 + 返回record的函数它不起作用,并且它无法解析多态类型。 + + + + 一旦有了一个TupleDesc,如果计划处理 Datum,可以调用: + +TupleDesc BlessTupleDesc(TupleDesc tupdesc) + + 如果计划处理 C 字符串,可调用: + +AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) + + 如果正在编写一个返回集合的函数,你可以把这些函数的结果保存在 + FuncCallContext结构体中 — 分别使用 + tuple_desc或者attinmeta字段。 + + + + 在处理 Datum 时,使用 + +HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull) + + 来用 Datum 形式的用户数据构建一个HeapTuple。 + + + + 在处理 C 字符串时,使用 + +HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) + + 来用 C 字符串形式的用户数据构建一个HeapTuple。 + values是一个 C 字符串数组,每一个元素是返回行 + 的一个属性。每一个 C 字符串应该是该属性数据类型的输入函数所期望 + 的格式。为了对一个属性返回空值,values数组中对 + 应的指针应该被设置为NULL。对于你返回的每一行都需要再次调用这个函数。 + + + + 一旦已经构建了一个要从函数中返回的元组,它必须被转换成一个 + Datum。使用 + +HeapTupleGetDatum(HeapTuple tuple) + + 可把一个HeapTuple转换成合法的 Datum。如果你 + 只想返回一行,那么这个Datum可以被直接返回,在一个 + 集合返回函数中它也可以被当做当前的返回值。 + + + + 下一节中会有一个示例。 + + + + + + 返回集合 + + + C 语言函数有两个返回集合(多行)的选项。在一种称为ValuePerCall + 模式的方法中,一个集合返回函数被重复调用(每次传递相同的参数),并在每次调用时返回一个新行, + 直到没有更多行要返回并且 通过返回 NULL 来表示这一点。因此,集合返回函数 (SRF) + 必须在调用之间保存足够的状态以记住它在做什么并在每次调用时返回正确的下一项。 + 在另一种称为Materialize模式的方法中,SRF 填充并返回一个包含其整个结果的 tuplestore 对象; + 那么整个结果只发生一次调用,不需要调用间状态。 + + + + 使用 ValuePerCall 模式时,重要的是要记住查询不能保证运行完成; + 也就是说,由于诸如LIMIT之类的选项, + 执行器可能会在获取所有行之前停止调用集合返回函数。 + 这意味着在最后一次调用中执行清理活动是不安全的,因为这可能永远不会发生。 + 对于需要访问外部资源(例如文件描述符)的函数,建议使用 Materialize 模式。 + + + + 本节的其余部分记录了一组使用 ValuePerCall 模式的 SRF 常用(尽管不是必须使用)的辅助宏。 + 有关 Materialize 模式的其他详细信息可以在src/backend/utils/fmgr/README中找到。 + 此外,PostgreSQL源代码分发中的contrib + 模块包含许多使用 ValuePerCall 和 Materialize 模式的 SRF 示例。 + + + + 要使用此处描述的 ValuePerCall 支持宏,请包含funcapi.h。 + 这些宏与结构体FuncCallContext一起使用,该结构体包含需要跨调用保存的状态。 + 在使用这些宏的 SRF 中,fcinfo->flinfo->fn_extra用于在调用之间保存 + 指向FuncCallContext的指针。 + 宏在第一次使用时自动填充该字段,并期望在后续使用中找到相同的指针。 + +typedef struct FuncCallContext +{ + /* + * 本次调用以前已经被调用过多少次 + * + * SRF_FIRSTCALL_INIT() 会为你把 call_cntr 初始化为 0, + * 并且在每次调用 SRF_RETURN_NEXT() 时增加。 + */ + uint64 call_cntr; + + /* + * 可选:最大调用次数 + * + * 这里的 max_calls 只是为了方便,设置它是可选的。 + * 如果没有设置,你必须提供替代的方法来了解函数什么时候做完。 + */ + uint64 max_calls; + + /* + * 可选:指向结果槽的指针 + * + * 此字段已过时,仅为向后兼容而保留,即兼容使用已弃用的 + * TupleDescGetSlot() 的用户定义 SRF。 + */ + TupleTableSlot *slot; + + /* + * 可选:指向用户提供的上下文信息的指针 + * + * user_fctx 是一个指向你自己的数据的指针,它可用来在函数的多次 + * 调用之间保存任意的上下文信息。 + */ + void *user_fctx; + + /* + * 可选:指向包含属性类型输入元数据的结构的指针 + * + * attinmeta 被用在返回元组(即复合数据类型)时,在返回基础类型 + * 时不会使用。只有想用BuildTupleFromCStrings()创建返回元组时才需要它。 + */ + AttInMetadata *attinmeta; + + /* + * 用于保存必须在多次调用间都存在的结构的内存上下文 + * + * SRF_FIRSTCALL_INIT() 会为你设置 multi_call_memory_ctx,并且由 + * SRF_RETURN_DONE() 来清理。对于任何需要在 SRF 的多次调用间都 + * 存在的内存来说,它是最合适的内存上下文。 + */ + MemoryContext multi_call_memory_ctx; + + /* + * 可选:指向包含元组描述的结构的指针 + * + * tuple_desc 被用在返回元组(即复合数据类型)时,并且只有在用 + * heap_form_tuple() 而不是 BuildTupleFromCStrings() 构建元组时才需要它。 + * 注意这里存储的 TupleDesc 指针通常已经被先运行过 BlessTupleDesc()。 + */ + TupleDesc tuple_desc; + +} FuncCallContext; + + + + + 使用此基础结构的SRF将使用的宏是: + +SRF_IS_FIRSTCALL() + + 来判断你的函数是否是第一次被调用。在第一次调用时(只能在第一次调用时)使用: + +SRF_FIRSTCALL_INIT() + + 初始化FuncCallContext。在每次函数调用时,包含第一次,调用: + +SRF_PERCALL_SETUP() + + 设置使用FuncCallContext。 + + + + 如果你的函数在当前调用中有数据要返回,请使用: + +SRF_RETURN_NEXT(funcctx, result) + + 把它返回给调用者(result必须是类型Datum, + 可以是一个单一值或者按上文所述准备好的元组)。最后,当函数完成了 + 数据返回后,可使用: + +SRF_RETURN_DONE(funcctx) + + 来清理并且结束SRF。 + + + + 调用 SRF 时当前所处的内存上下文是一个瞬时上下文, + 它会在两次调用之间被清空。这意味着,你不必对用 palloc 分配的所有东西都调用 pfree,因为它们反正会自动释放。 + 不过,如果你需要分配在多次调用之间持续存在的数据结构,就必须把它们放到别处。 + 对于任何需要一直存活到 SRF 运行结束的数据,multi_call_memory_ctx 所指向的内存上下文就是合适的位置。 + 在大多数情况下,这意味着你应当在做首次调用初始化时切换到 multi_call_memory_ctx。 + 可以使用 funcctx->user_fctx 保存指向这类跨调用数据结构的指针。 + (在 multi_call_memory_ctx 中分配的数据会在查询结束时自动消失,因此同样无需手工释放。) + + + + + 虽然函数的实参在多次调用之间保持不变,但如果在瞬时上下文中 + 反 TOAST 了参数(通常由 + PG_GETARG_xxx + 宏完成),那么被反 TOAST 的拷贝将在每次循环中被释放。相应地, + 如果你把这些值的引用保存在user_fctx中,你也必 + 须在反 TOAST 之后把它们拷贝到 + multi_call_memory_ctx中,或者确保你只在那个 + 上下文中反 TOAST 这些值。 + + + + + 一个完整的伪代码示例: + +Datum +my_set_returning_function(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + Datum result; + further declarations as needed + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + /* 这里是一次性设置代码: */ + user code + if returning composite + build TupleDesc, and perhaps AttInMetadata + endif returning composite + user code + MemoryContextSwitchTo(oldcontext); + } + + /* 这里是每一次都要做的设置代码: */ + user code + funcctx = SRF_PERCALL_SETUP(); + user code + + /* 这里只是一种测试是否执行完的方法: */ + if (funcctx->call_cntr < funcctx->max_calls) + { + /* 这里返回另一个项: */ + user code + obtain result Datum + SRF_RETURN_NEXT(funcctx, result); + } + else + { + /* 这里已经完成了项的返回,所以只报告事实。 */ + /* (不要将清理代码放在这里。) */ + SRF_RETURN_DONE(funcctx); + } +} + + + + + 一个返回复合类型的简单SRF的完整示例如下: +multi_call_memory_ctx); + + /* 要返回的元组总数 */ + funcctx->max_calls = PG_GETARG_UINT32(0); + + /* 为我们的结果类型构建一个元组描述符 */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + + /* + * 生成后续从原始C字符串生成元组所需的属性元数据 + */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + MemoryContextSwitchTo(oldcontext); + } + + /* 每次函数调用时执行的操作 */ + funcctx = SRF_PERCALL_SETUP(); + + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + attinmeta = funcctx->attinmeta; + + if (call_cntr < max_calls) /* 当还有更多要发送时执行 */ + { + char **values; + HeapTuple tuple; + Datum result; + + /* + * 为构建返回的元组准备一个值数组。 + * 这应该是一个由后续类型输入函数处理的C字符串数组。 + */ + values = (char **) palloc(3 * sizeof(char *)); + values[0] = (char *) palloc(16 * sizeof(char)); + values[1] = (char *) palloc(16 * sizeof(char)); + values[2] = (char *) palloc(16 * sizeof(char)); + + snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1)); + snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1)); + snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1)); + + /* 构建一个元组 */ + tuple = BuildTupleFromCStrings(attinmeta, values); + + /* 将元组转换为 datum */ + result = HeapTupleGetDatum(tuple); + + /* 清理(这实际上并不是必要的) */ + pfree(values[0]); + pfree(values[1]); + pfree(values[2]); + pfree(values); + + SRF_RETURN_NEXT(funcctx, result); + } + else /* 当没有更多要发送时执行 */ + { + SRF_RETURN_DONE(funcctx); + } +} +]]> + + + 在SQL中声明此函数的一种方法是: + +CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer); + +CREATE OR REPLACE FUNCTION retcomposite(integer, integer) + RETURNS SETOF __retcomposite + AS 'filename', 'retcomposite' + LANGUAGE C IMMUTABLE STRICT; + + 另一种方法是使用OUT参数: + +CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer, + OUT f1 integer, OUT f2 integer, OUT f3 integer) + RETURNS SETOF record + AS 'filename', 'retcomposite' + LANGUAGE C IMMUTABLE STRICT; + + 请注意,采用这种方法时,函数的输出类型在形式上是一个匿名的 record 类型。 + + + + + 多态参数和返回类型 + + + 可以声明 C 语言函数来接受和返回多态类型 anyelementanyarrayanynonarrayanyenumanyrange。关于多态函数的更详细解释,见 。当函数参数或者返回 + 类型被定义为多态类型时,函数的编写者无法提前知道会用什么数据类型 + 调用该函数或者该函数需要返回什么数据类型。在fmgr.h + 中提供了两种例程来允许版本-1 的 C 函数发现其参数的实际数据类型以及 + 它要返回的类型。这些例程被称为 + get_fn_expr_rettype(FmgrInfo *flinfo)和 + get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)。它们 + 返回结果或者参数的类型的 OID,或者当该信息不可用时返回 + InvalidOid。结构体flinfo通常被当做 + fcinfo->flinfo访问。参数argnum则是从零 + 开始计。get_call_result_type也可被用作 + get_fn_expr_rettype的一种替代品。还有 + get_fn_expr_variadic,它可以被用来找出可变参数 + 是否已经被合并到了一个数组中。这主要用于 + VARIADIC "any"函数,因为对于接收普通数组类型的 + 可变参数函数来说总是会发生这类合并。 + + + + 例如,假设我们想要写一个接收一个任意类型元素并且返回一个该类型的一维 + 数组的函数: + + +PG_FUNCTION_INFO_V1(make_array); +Datum +make_array(PG_FUNCTION_ARGS) +{ + ArrayType *result; + Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + Datum element; + bool isnull; + int16 typlen; + bool typbyval; + char typalign; + int ndims; + int dims[MAXDIM]; + int lbs[MAXDIM]; + + if (!OidIsValid(element_type)) + elog(ERROR, "could not determine data type of input"); + + /* 得到提供的元素,小心它为 NULL 的情况 */ + isnull = PG_ARGISNULL(0); + if (isnull) + element = (Datum) 0; + else + element = PG_GETARG_DATUM(0); + + /* 只有一个维度 */ + ndims = 1; + /* 和一个元素 */ + dims[0] = 1; + /* 且下界是 1 */ + lbs[0] = 1; + + /* 得到该元素类型所需的信息 */ + get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); + + /* 现在构建数组 */ + result = construct_md_array(&element, &isnull, ndims, dims, lbs, + element_type, typlen, typbyval, typalign); + + PG_RETURN_ARRAYTYPE_P(result); +} + + + + + 下面的命令在 SQL 中声明了 make_array 函数: + + +CREATE FUNCTION make_array(anyelement) RETURNS anyarray + AS 'DIRECTORY/funcs', 'make_array' + LANGUAGE C IMMUTABLE; + + + + + 还有一种只对 C 语言函数可用的多态变体:它们可以被声明为接受 + "any" 类型的参数。(注意,这个类型名必须用双引号括起来,因为它同时也是 SQL 保留字。) + 它与 anyelement 类似,但不会要求不同的 "any" 参数必须是同一种类型,也不会帮助确定函数的结果类型。 + C 语言函数还可以把最后一个参数声明为 VARIADIC "any"。 + 这可以匹配一个或多个任意类型的实参(不必是同一种类型)。 + 这些参数不会像普通可变参数函数那样被收集成一个数组,而是会单独传给函数。 + 使用这种特性时,必须结合 PG_NARGS() 宏以及前面介绍的方法来确定实参的个数和类型。 + 此外,这种函数的用户也可能希望在函数调用中使用 VARIADIC 关键字,以便让函数把数组元素当作独立参数处理。 + 如果希望支持这种行为,函数本身就必须在使用 get_fn_expr_variadic 检测到实参被标记为 VARIADIC 后自行实现它。 + + + + + 转换函数 + + 某些函数调用可以在规划期间根据函数特有的属性进行简化。例如,int4mul(n, 1) 可以直接简化为 n。要定义这类针对特定函数的优化,请编写一个转换函数,并将其 OID 放入主函数的 protransform 字段中,该字段位于其 pg_proc 项中。转换函数必须具有 SQL 签名 protransform(internal) RETURNS internal。参数实际上是 FuncExpr *,它是一个表示主函数调用的占位节点。如果转换函数对表达式树的分析证明,可以用简化的表达式树替代它所表示的所有可能的具体调用,就构建并返回该简化表达式。否则,返回一个 NULL 指针(不是 SQL 空值)。 + + 我们不保证 PostgreSQL 在转换函数可以简化的情况下绝不会调用主函数。请确保简化后的表达式与实际调用主函数严格等价。 + + 目前,出于安全考虑,这项功能没有在 SQL 层面开放给用户,因此实际只能用于优化内置函数。 + + + + 共享内存与 LWLocks + + 附加模块可以在服务器启动时预留 LWLock 和一块共享内存。附加模块的共享库必须通过在以下参数中指定它来预装载:shared_preload_libraries。共享内存可以通过下面的调用来预留: +void RequestAddinShmemSpace(int size) +该调用应放在你的 _PG_init 函数中。 + LWLock 可以通过下面的调用来预留: +void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) +该调用应放在 _PG_init 中。这样可以确保一个包含 num_lwlocks 个 LWLock 的数组可通过以下名称访问:tranche_name。使用 GetNamedLWLockTranche 可以取得指向该数组的指针。 + 为避免可能的竞争条件,每个后端都应使用 LWLock AddinShmemInitLock 来连接和初始化分配给它的共享内存,如下所示: +static mystruct *ptr = NULL; + +if (!ptr) +{ + bool found; + + LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + ptr = ShmemInitStruct("my struct name", size, &found); + if (!found) + { + initialize contents of shmem area; + acquire any requested LWLocks using: + ptr->locks = GetNamedLWLockTranche("my tranche name"); + } + LWLockRelease(AddinShmemInitLock); +} + + + + + + 把 C++ 用于可扩展性 + + + C++ + + + + 尽管PostgreSQL后端是用 C 编写的, + 只要遵循下面的指导方针也可以用 C++ 编写扩展: + + + + + 所有被后端访问的函数必须对后端呈现一种 C 接口,然后这些 C 函数 + 调用 C++ 函数。例如,对后端访问的函数要求extern C + 链接。对需要在后端和 C++ 代码之间作为指针传递的任何函数也要 + 这样做。 + + + + + 使用合适的释放方法释放内存。例如,大部分后端内存是通过 + palloc()分配的,所以应使用pfree() + 来释放。在这种情况中使用 C++ 的delete会失败。 + + + + + 防止异常传播到 C 代码中(在所有extern C函数的顶层 + 使用一个捕捉全部异常的块)。即使 C++ 代码不会显式地抛出任何 + 异常也需要这样做,因为类似内存不足等事件仍会抛出异常。任何异常 + 都必须被捕捉并且用适当的错误传回给 C 接口。如果可能,用 + 来编译 C++ 以完全消灭异常。在这种 + 情况下,你必须在 C++ 代码中检查失败,例如检查new() + 返回的 NULL。 + + + + + 如果从 C++ 代码调用后端函数,确定 C++ 调用栈只包含传统 C 风格 + 的数据结构(POD)。这是必要的,因为后端错误会 + 产生远距离的longjmp(),它无法正确地展开具有非 + POD 对象的 C++ 调用栈。 + + + + + + + 总之,最好把 C++ 代码放在与后端交互的extern C函数之后, + 并且避免异常、内存和调用栈泄露。 + + + +
    diff --git a/zh/9.6/xindex.sgml b/zh/9.6/xindex.sgml new file mode 100644 index 00000000..2f926700 --- /dev/null +++ b/zh/9.6/xindex.sgml @@ -0,0 +1,894 @@ + + + + 索引扩展接口 + + + 索引 + 用于用户定义的数据类型 + + + + 到目前为止所描述的过程使我们能够定义新类型、新函数以及新操作符。然而,我们还不能在一种新数据类型的列上定义索引。为此,必须为该新数据类型定义一个操作符类。本节稍后将用一个示例说明这一概念:为 B-树索引方法定义一个新的操作符类,以便按绝对值升序存储和排序复数。 + + + + 操作符类可以分组成操作符族,以展示语义兼容的类之间的关系。只涉及单一数据类型时,一个操作符类就已足够,因此我们先关注这种情况,然后再回到操作符族。 + + + + 索引方法和操作符类 + + pg_am 表为每个索引方法(内部称为访问方法)保存一行。对表进行常规访问的支持内置于 PostgreSQL 中,但所有索引方法都在 pg_am 中描述。可以编写必要的代码,然后在 pg_am 中创建一行,从而添加新的索引访问方法 — 但这超出了本章的范围(参见)。 + + + 索引方法的例程并不直接知道它将处理哪些数据类型。相反,一个操作符类操作符类标识了索引方法在处理特定数据类型时需要使用的那组操作。之所以称为操作符类,是因为它指定的一项内容就是可与索引一起使用的 WHERE 子句操作符集合(也就是能被转换成索引扫描条件的操作符)。操作符类还可以指定索引方法内部操作所需的一些支持函数,但这些函数并不直接对应任何可与索引一起使用的 WHERE 子句操作符。 + + + + 可以为同一种数据类型和索引方法定义多个操作符类。这样就能为一种数据类型定义多套索引语义。例如,一个 B-树索引要求为其处理的每一种数据类型定义一种排序顺序。对于复数数据类型,也许既需要一个按复数绝对值排序的 B-树操作符类,也需要另一个按实部排序的操作符类,等等。通常,其中一个操作符类会被视为最常用,并标记为该数据类型在该索引方法上的默认操作符类。 + + + + 同一个操作符类名可以用于多个不同的索引方法(例如,B-树和哈希索引方法都有名为 int4_ops 的操作符类),但每一个这样的类都是独立实体,必须分别定义。 + + + + + 索引方法策略 + + + 与操作符类关联的操作符通过策略号来标识,用以表示每个操作符在其操作符类上下文中的语义。例如,B-树对键施加了严格的从小到大的顺序,因此像小于大于等于这样的操作符,对 B-树来说就很重要。由于 PostgreSQL 允许用户定义操作符,PostgreSQL 不能仅凭操作符名称(例如 <>=)就判断它是哪一类比较。取而代之的是,索引方法定义了一组策略,可以把它们看成是广义的操作符。每个操作符类都会说明,对于某种特定数据类型和某种索引语义解释,每一种策略分别对应哪个实际操作符。 + + + + B-树索引方法定义了五种策略,如所示。 + + + + B-树策略 + + + + 操作 + 策略号 + + + + + 小于 + 1 + + + 小于等于 + 2 + + + 等于 + 3 + + + 大于等于 + 4 + + + 大于 + 5 + + + +
    + + + 哈希索引只支持等值比较,因此它们只使用一种策略,如所示。 + + + + 哈希策略 + + + + 操作 + 策略号 + + + + + 等于 + 1 + + + +
    + + + GiST 索引更加灵活:它们根本没有固定的策略集合。相反,每个特定 GiST 操作符类中负责一致性检查的支持例程会按自己的方式解释策略号。举例来说,一些内置的 GiST 索引操作符类会为二维几何对象建立索引,并提供R 树策略,如所示。其中四个是真正的二维测试(重叠、相同、包含、被包含),四个只考虑 X 方向,另外四个则在 Y 方向上提供相同测试。 + + + + GiST 二维<quote>R 树</quote>策略 + + + + 操作 + 策略号 + + + + + 严格位于左侧 + 1 + + + 不延伸到右侧 + 2 + + + 重叠 + 3 + + + 不延伸到左侧 + 4 + + + 严格位于右侧 + 5 + + + 相同 + 6 + + + 包含 + 7 + + + 被包含 + 8 + + + 不延伸到上方 + 9 + + + 严格位于下方 + 10 + + + 严格位于上方 + 11 + + + 不延伸到下方 + 12 + + + +
    + + + SP-GiST 索引在灵活性方面与 GiST 索引类似:它们也没有固定的策略集合。相反,每个操作符类的支持例程会根据该操作符类的定义解释策略号。举例来说,内置点操作符类所使用的策略号如所示。 + + + + SP-GiST 点策略 + + + + 操作 + 策略号 + + + + + 严格位于左侧 + 1 + + + 严格位于右侧 + 5 + + + 相同 + 6 + + + 被包含 + 8 + + + 严格位于下方 + 10 + + + 严格位于上方 + 11 + + + +
    + + + GIN 索引与 GiST 和 SP-GiST 索引类似,也没有固定的策略集合。相反,每个操作符类的支持例程会根据该操作符类的定义解释策略号。举例来说,内置数组操作符类所使用的策略号如所示。 + + + + GIN 数组策略 + + + + 操作 + 策略号 + + + + + 重叠 + 1 + + + 包含 + 2 + + + 被包含 + 3 + + + 等于 + 4 + + + +
    + + + BRIN 索引也与 GiST、SP-GiST 和 GIN 索引一样,没有固定的策略集合。每个操作符类的支持函数都会根据该操作符类的定义解释策略号。举例来说,内置 Minmax 操作符类所使用的策略号如所示。 + + + + BRIN Minmax 策略 + + + + 操作 + 策略号 + + + + + 小于 + 1 + + + 小于等于 + 2 + + + 等于 + 3 + + + 大于等于 + 4 + + + 大于 + 5 + + + +
    + + + 注意,上面列出的所有操作符都返回布尔值。实际上,所有被定义为索引方法搜索操作符的操作符都必须返回 boolean,因为要与索引配合使用,它们必须出现在 WHERE 子句的顶层。(某些索引访问方法还支持排序操作符,这类操作符通常不返回布尔值;该特性见。) + +
    + + + 索引方法支持例程 + + + 仅靠策略信息通常不足以让系统知道如何使用索引。实际上,索引方法还需要额外的支持例程才能工作。例如,B-树索引方法必须能够比较两个键,并判断其中一个是大于、等于还是小于另一个。类似地,哈希索引方法必须能够为键值计算哈希码。这些操作并不对应 SQL 命令条件中使用的操作符;它们是索引方法内部使用的管理例程。 + + + + 与策略一样,操作符类会标识对于给定的数据类型和语义解释,应由哪些具体函数承担这些角色。索引方法定义它需要的函数集合,而操作符类则会通过为函数分配由索引方法规定的支持函数号来标识正确的函数。 + + + 所示,B-树要求一个支持函数,并允许操作符类作者按需再提供一个支持函数。 + + + B-树支持函数 + + + + 函数 + 支持号 + + + + + + 比较两个键,并返回一个小于零、等于零或大于零的整数,用以表示第一个键是小于、等于还是大于第二个键 + + 1 + + + 返回可从 C 调用的排序支持函数的地址,详见 utils/sortsupport.h(可选) + 2 + + + +
    + + 哈希索引要求一个支持函数,如所示。 + + + 哈希支持函数 + + + + 函数 + 支持号 + + + + + 计算一个键的哈希值 + 1 + + + +
    + + + GiST 索引有九个支持函数,其中两个是可选的,如所示。 + (详见。) + + + + GiST 支持函数 + + + + 函数 + 描述 + 支持号 + + + + + consistent + 确定键是否满足查询条件 + 1 + + + union + 计算一组键的并集 + 2 + + + compress + 计算将被索引的键或值的压缩表示 + 3 + + + decompress + 计算压缩键的解压表示 + 4 + + + penalty + 计算把新键插入具有给定子树键的子树时的罚值 + 5 + + + picksplit + 确定页面中的哪些项要移到新页面,并计算结果页面的并集键 + 6 + + + equal + 比较两个键,并在它们相等时返回真 + 7 + + + distance + 确定键到查询值的距离(可选) + 8 + + + fetch + 为仅索引扫描计算压缩键的原始表示(可选) + 9 + + + +
    + + 所示,SP-GiST 索引要求五个支持函数。(详见。) + + + SP-GiST 支持函数 + + + + 函数 + 描述 + 支持号 + + + + + config + 提供有关该操作符类的基本信息 + 1 + + + choose + 确定如何把一个新值插入内部元组 + 2 + + + picksplit + 确定如何划分一组值 + 3 + + + inner_consistent + 确定对某个查询需要搜索哪些子分区 + 4 + + + leaf_consistent + 确定键是否满足查询条件 + 5 + + + +
    + + + 如所示,GIN 索引有六个支持函数,其中三个是可选的(详见)。 + + + + GIN 支持函数 + + + + 函数 + 描述 + 支持号 + + + + + compare + + 比较两个键,并返回一个小于零、等于零或大于零的整数,用以表示第一个键是小于、等于还是大于第二个键 + + 1 + + + extractValue + 从待索引值中提取键 + 2 + + + extractQuery + 从查询条件中提取键 + 3 + + + consistent + + 确定值是否匹配查询条件(布尔变体;如果支持函数 6 存在则可选) + + 4 + + + comparePartial + + 比较查询中的部分键与索引中的键,并返回一个小于零、等于零或大于零的整数,用以指示 GIN 应忽略该索引项、将该项视为匹配,还是停止索引扫描(可选) + + 5 + + + triConsistent + + 确定值是否匹配查询条件(三值变体;如果支持函数 4 存在则可选) + + 6 + + + +
    + + BRIN 索引有四个基本支持函数,如所示;这些基本函数可能要求提供额外的支持函数。(详见。) + + + BRIN 支持函数 + + + + 函数 + 描述 + 支持号 + + + + + opcInfo + + 返回描述被索引列摘要数据的内部信息 + + 1 + + + add_value + 向一个现有的摘要索引元组增加一个新值 + 2 + + + consistent + 确定值是否匹配查询条件 + 3 + + + union + + 计算两个摘要元组的并集 + + 4 + + + +
    + + + 与搜索操作符不同,支持函数返回的是特定索引方法所期望的数据类型;例如,对 B-树的比较函数来说,就是一个有符号整数。每个支持函数的参数个数和类型也同样取决于索引方法。对于 B-树和哈希,比较支持函数和哈希支持函数接受的输入数据类型,与操作符类中包含的操作符相同;但对大多数 GiST、SP-GiST、GIN 和 BRIN 支持函数来说并非如此。 + +
    + + + 示例 + + + 现在我们已经了解了这些基本思想,下面给出先前承诺的创建新操作符类示例。(这个可运行示例位于源码发布包中的src/tutorial/complex.csrc/tutorial/complex.sql。)该操作符类封装了一组按绝对值顺序对复数排序的操作符,因此我们把它命名为complex_abs_ops。首先,我们需要一组操作符。定义操作符的过程已经在中讨论过。对于 B-树上的操作符类,我们需要如下操作符: + + + 绝对值小于(策略 1) + 绝对值小于等于(策略 2) + 绝对值等于(策略 3) + 绝对值大于等于(策略 4) + 绝对值大于(策略 5) + + + + + 定义一组相关比较操作符时,最不容易出错的方式是先编写 B-树比较支持函数,再把其他函数写成围绕该支持函数的一行包装器函数。这样可以降低在边界情况下得到不一致结果的概率。按照这种方法,我们首先编写: + +x*(c)->x + (c)->y*(c)->y) + +static int +complex_abs_cmp_internal(Complex *a, Complex *b) +{ + double amag = Mag(a), + bmag = Mag(b); + + if (amag < bmag) + return -1; + if (amag > bmag) + return 1; + return 0; +} +]]> + + + 现在,小于函数如下所示: + + + + + 其他四个函数的区别只在于它们如何比较内部函数的结果与 0。 + + + 接下来,在 SQL 中声明这些函数,以及基于这些函数的操作符: +CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool + AS 'filename', 'complex_abs_lt' + LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR < ( + leftarg = complex, rightarg = complex, procedure = complex_abs_lt, + commutator = > , negator = >= , + restrict = scalarltsel, join = scalarltjoinsel +); +必须指定正确的交换子和求反器操作符,以及合适的限制选择率与连接选择率函数,否则优化器无法有效使用索引。注意,小于、等于和大于这几种情况应使用不同的选择率函数。 + + + 这里还有几点值得注意: + + + + + 只能有一个名为 = 且两个操作数都为 complex 类型的操作符。在这个例子里,我们并没有任何其他 = 操作符可用于 complex;但如果我们是在构造一种实际使用的数据类型,可能会希望 = 表示复数的普通相等,而不是绝对值相等。在那种情况下,我们就需要为 complex_abs_eq 选用其他操作符名。 + + + + + + 尽管 PostgreSQL 能处理 SQL 名称相同但参数数据类型不同的函数,C 却只能处理给定名称的一个全局函数。因此,我们不应该把 C 函数简单命名成 abs_eq 之类。通常,在 C 函数名中包含数据类型名称是个好习惯,这样就不会与其他数据类型的函数发生冲突。 + + + + + + 我们原本也可以把该函数的 SQL 名称取为 abs_eq,并依靠 PostgreSQL 通过参数数据类型把它与其他同名 SQL 函数区分开。为了让示例保持简单,这里我们让 C 层和 SQL 层的函数使用相同的名称。 + + + + + + + 下一步是注册 B-树要求的支持例程。实现该例程的 C 示例代码与操作符函数位于同一个文件中。该函数的声明如下: + + +CREATE FUNCTION complex_abs_cmp(complex, complex) + RETURNS integer + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + + + + + 现在我们已经有了所需的操作符和支持例程,就可以最终创建操作符类: + += , + OPERATOR 5 > , + FUNCTION 1 complex_abs_cmp(complex, complex); +]]> + + + + + 这样就完成了!现在应该可以在 complex 列上创建并使用 B-树索引。 + + + + 我们本来也可以把操作符项写得更详细一些,例如: + + OPERATOR 1 < (complex, complex) , + + 但是当操作符接受的数据类型与该操作符类所服务的数据类型相同时,就没有必要这样写。 + + + + 上述示例假定你希望把这个新操作符类设为 complex 数据类型的默认 B-树操作符类。如果不是这样,只需省去 DEFAULT 这个词。 + + + + + 操作符类和操作符族 + + + 到目前为止,我们一直隐含地假定一个操作符类只处理一种数据类型。虽然某个特定的索引列当然只能有一种数据类型,但对把被索引列与另一种数据类型的值进行比较的操作建立索引往往也很有用。此外,如果某个与操作符类相关的跨数据类型操作符有用,通常另一种数据类型本身也会有一个相关的操作符类。把相关类之间的联系显式表示出来会很有帮助,因为这有助于规划器优化 SQL 查询(尤其是对 B-树操作符类而言,因为规划器中包含大量有关如何使用它们的知识)。 + + + + 为了满足这些需求,PostgreSQL使用操作符族操作符族这一概念。一个操作符族包含一个或多个操作符类,还可以包含属于整个族、但不属于族中任何单一类的可索引操作符及其相应的支持函数。我们称这样的操作符和函数在该族中是松散的,而不是绑定在某个特定类中。通常,每个操作符类只包含单一数据类型的操作符,而跨数据类型操作符则作为操作符族中的松散成员存在。 + + + + 一个操作符族中的所有操作符和函数都必须具有兼容的语义,而兼容性的要求由索引方法设定。因此,你也许会疑惑,为什么还要把该族的某些子集单独划成操作符类;事实上,对很多用途而言,类的划分并不重要,真正有意义的分组只有操作符族。之所以定义操作符类,是因为它们规定了支持特定索引所需的操作符族内容。如果某个索引使用了某个操作符类,那么在不删除该索引的情况下就不能删除该操作符类 — 但操作符族中的其他部分,也就是其他操作符类和松散操作符,则可以被删除。因此,一个操作符类应当只包含在特定数据类型上支持索引所合理需要的最小操作符和函数集合,而那些相关但非必需的操作符,则可以作为操作符族的松散成员加入。 + + + 例如,PostgreSQL内置了 B-树操作符族integer_ops,其中包含操作符类int8_opsint4_opsint2_ops,它们分别用于以下列类型上的索引:bigintint8), + integerint4)和smallintint2)。该操作符族还包含跨数据类型的比较操作符,允许对这些类型中的任意两个进行比较,因此可以使用一种类型的比较值搜索另一种类型上的索引。可以用以下定义复制这个操作符族:= , + OPERATOR 5 > , + FUNCTION 1 btint8cmp(int8, int8) , + FUNCTION 2 btint8sortsupport(internal) ; + +CREATE OPERATOR CLASS int4_ops +DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS + -- standard int4 comparisons + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 btint4cmp(int4, int4) , + FUNCTION 2 btint4sortsupport(internal) ; + +CREATE OPERATOR CLASS int2_ops +DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS + -- standard int2 comparisons + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 btint2cmp(int2, int2) , + FUNCTION 2 btint2sortsupport(internal) ; + +ALTER OPERATOR FAMILY integer_ops USING btree ADD + -- cross-type comparisons int8 vs int2 + OPERATOR 1 < (int8, int2) , + OPERATOR 2 <= (int8, int2) , + OPERATOR 3 = (int8, int2) , + OPERATOR 4 >= (int8, int2) , + OPERATOR 5 > (int8, int2) , + FUNCTION 1 btint82cmp(int8, int2) , + + -- cross-type comparisons int8 vs int4 + OPERATOR 1 < (int8, int4) , + OPERATOR 2 <= (int8, int4) , + OPERATOR 3 = (int8, int4) , + OPERATOR 4 >= (int8, int4) , + OPERATOR 5 > (int8, int4) , + FUNCTION 1 btint84cmp(int8, int4) , + + -- cross-type comparisons int4 vs int2 + OPERATOR 1 < (int4, int2) , + OPERATOR 2 <= (int4, int2) , + OPERATOR 3 = (int4, int2) , + OPERATOR 4 >= (int4, int2) , + OPERATOR 5 > (int4, int2) , + FUNCTION 1 btint42cmp(int4, int2) , + + -- cross-type comparisons int4 vs int8 + OPERATOR 1 < (int4, int8) , + OPERATOR 2 <= (int4, int8) , + OPERATOR 3 = (int4, int8) , + OPERATOR 4 >= (int4, int8) , + OPERATOR 5 > (int4, int8) , + FUNCTION 1 btint48cmp(int4, int8) , + + -- cross-type comparisons int2 vs int8 + OPERATOR 1 < (int2, int8) , + OPERATOR 2 <= (int2, int8) , + OPERATOR 3 = (int2, int8) , + OPERATOR 4 >= (int2, int8) , + OPERATOR 5 > (int2, int8) , + FUNCTION 1 btint28cmp(int2, int8) , + + -- cross-type comparisons int2 vs int4 + OPERATOR 1 < (int2, int4) , + OPERATOR 2 <= (int2, int4) , + OPERATOR 3 = (int2, int4) , + OPERATOR 4 >= (int2, int4) , + OPERATOR 5 > (int2, int4) , + FUNCTION 1 btint24cmp(int2, int4) ; +]]> +注意,此定义重载了操作符策略编号和支持函数编号:每个编号在操作符族中出现多次。只要同一编号的每个实例具有不同的输入数据类型,这就是允许的。两个输入类型都等于某个操作符类输入类型的实例,是该操作符类的主要操作符和支持函数,通常应声明为操作符类的一部分,而不是操作符族的松散成员。 + + 在一个 B-树操作符族中,该族中的所有操作符都必须以兼容的方式排序,也就是说,传递律必须适用于该族支持的所有数据类型:如果 A = B 且 B = C,则 A = C,以及如果 A < B 且 B < C,则 A < C。此外,操作符族中所表示的类型之间的隐式或二进制强制类型转换不得改变相关的排序顺序。对族中的每一个操作符,都必须有一个具有相同两个输入数据类型的支持函数。建议让操作符族保持完整,也就是说,对每一种数据类型组合都应包含全部操作符。每个操作符类只应包含其数据类型对应的非跨数据类型操作符和支持函数。 + + + 要构建一个多数据类型的哈希操作符族,必须为该族支持的每一种数据类型创建相互兼容的哈希支持函数。这里的兼容性是指:对任意两个被该族中的等值操作符视为相等的值,这些函数都保证返回相同的哈希码,即使这两个值属于不同类型也是如此。当这些类型具有不同的物理表示时,这通常难以实现,但在某些情况下可以做到。此外,将该操作符族中一种数据类型的值通过隐式或二进制强制转换转为该族中另一种数据类型时,不得改变所计算出的哈希值。注意,每种数据类型只有一个支持函数,而不是每个等值操作符一个。建议让操作符族保持完整,也就是说,对每一种数据类型组合都提供一个等值操作符。每个操作符类只应包含其数据类型对应的非跨数据类型等值操作符和支持函数。 + + + + GiST、SP-GiST 和 GIN 索引没有任何显式的跨数据类型操作概念。它们所支持的操作符集合,就是给定操作符类的主要支持函数所能处理的那些操作符。 + + + + 在 BRIN 中,要求取决于提供操作符类的框架。对于基于 minmax 的操作符类,所要求的行为与 B-树操作符族相同:族中的所有操作符都必须以兼容的方式排序,并且类型转换不能改变相关的排序顺序。 + + + + + 在 PostgreSQL 8.3 之前,并没有操作符族这一概念,因此任何打算与索引一起使用的跨数据类型操作符都必须直接绑定到该索引的操作符类中。虽然这种做法仍然有效,但已被弃用,因为它会使索引的依赖关系过于宽泛,而且当两种数据类型都在同一操作符族中拥有操作符时,规划器能更有效地处理跨数据类型比较。 + + + + + + 系统对操作符类的依赖 + + + 排序操作符 + + + + PostgreSQL利用操作符类来从多方面推断操作符的属性,而不仅仅是判断它们能否用于索引。因此,即便你并不打算为自己的数据类型列建立索引,也可能会想创建操作符类。 + + + + 特别地,ORDER BYDISTINCT等 SQL 特性要求对值的比较和排序。为了在用户定义的数据类型上实现这些特性,PostgreSQL会为数据类型查找默认 B-树操作符类。这个操作符类的相等成员定义了用于GROUP BYDISTINCT的值的等值概念,而该操作符类施加的排序顺序定义了默认的ORDER BY顺序。 + + + 用户定义类型的数组比较也依赖于该类型默认 B-树操作符类所定义的语义。 + + 如果一种数据类型没有默认的 B-树操作符类,系统就会查找默认的哈希操作符类。但由于这类操作符类只提供等值语义,因此在实践中它只足以支持数组相等比较。 + + + 如果某种数据类型没有默认操作符类,而你又试图将这些 SQL 特性用于该数据类型,就会得到类似无法识别排序操作符这样的错误。 + + + + + 在版本 7.4 以前的PostgreSQL中,排序和分组操作将隐式地使用名为=<以及>的操作符。新的依赖于默认操作符类的行为避免了对具有特定名字的操作符行为作出任何假设。 + + + + 另一个重要点在于,出现在哈希操作符族中的操作符,都是哈希连接、哈希聚合以及相关优化的候选对象。这里哈希操作符族至关重要,因为它标识了应当使用的哈希函数。 + + + + 排序操作符 + + 有些索引访问方法(目前只有 GiST)支持排序操作符的概念。我们到目前为止讨论的是搜索操作符。对于搜索操作符,可以搜索索引以找出满足以下条件的所有行:WHERE + indexed_column + operator + constant。注意,不保证匹配行的返回顺序。排序操作符则不限制可以返回的行集合,而是确定这些行的顺序。对于排序操作符,可以扫描索引,按以下表达式表示的顺序返回行:ORDER BY + indexed_column + operator + constant。这样定义排序操作符,是因为当操作符用于度量距离时,它可以支持最近邻搜索。例如,如下查询: point '(101,456)' LIMIT 10; +]]> +可以找到距离指定目标点最近的十个地点。location 列上的 GiST 索引能够高效完成此操作,因为<->是排序操作符。 + + + 搜索操作符必须返回布尔结果,而排序操作符通常返回其他类型的结果,例如用于表示距离的 float 或 numeric。这种类型通常不同于被索引的数据类型。为了避免对不同数据类型行为作硬编码假设,在定义排序操作符时,必须指定一个 B-树操作符族,用来说明结果数据类型的排序顺序。正如上一节所述,B-树操作符族定义了 PostgreSQL 的顺序概念,因此这是一种自然的表示方式。由于点的 <-> 操作符返回 float8,因此可以在创建操作符类时这样指定它: + (point, point) FOR ORDER BY float_ops +]]> + + 其中 float_ops 是包含针对 float8 的操作的内置操作符族。这个声明表明,该索引能够按 <-> 操作符值递增的顺序返回行。 + + + + + 操作符类的特殊特性 + + + 还有两个操作符类的特殊特性我们尚未讨论,主要是因为它们对最常用的索引方法没有用处。 + + + + 通常,把一个操作符声明为某个操作符类(或操作符族)的成员,意味着该索引方法能够利用该操作符准确检索出满足 WHERE 条件的那组行。例如: + +SELECT * FROM table WHERE integer_column < 4; + + 这个查询可以由整数列上的 B-树索引精确满足。但也有一些情况下,索引只能作为匹配行的不精确指引。例如,如果某个 GiST 索引只存储几何对象的边界框,那么它就无法精确满足测试非矩形对象(如多边形)之间重叠的 WHERE 条件。不过,我们仍然可以利用该索引找出边界框与目标对象边界框重叠的对象,然后只对索引找到的那些对象执行精确的重叠测试。如果适用于这种场景,就称该索引对这个操作符是有损的。有损索引搜索的实现方式是:当某一行可能满足、也可能不满足查询条件时,由索引方法返回一个recheck标志。随后,核心系统会在取回的行上重新测试原始查询条件,以判断它是否应当作为合法匹配返回。只要索引能保证返回所有必需的行,外加可能存在的一些额外行,这种方法就是有效的,因为这些额外行可以通过执行原始操作符调用来剔除。支持有损搜索的索引方法(目前有 GiST、SP-GiST 和 GIN)允许个别操作符类的支持函数设置 recheck 标志,因此这本质上也是一种操作符类特性。 + + + 再次考虑只在索引中存储多边形等复杂对象的包围盒的情况。此时,在索引条目中存储整个多边形没有多少价值,不如只存储一个更简单的对象,其类型为box。这种情况由STORAGE选项表达,该选项位于CREATE OPERATOR CLASS:可以写成如下形式: +CREATE OPERATOR CLASS polygon_ops + DEFAULT FOR TYPE polygon USING gist AS + ... + STORAGE box; +目前,只有 GiST、GIN 和 BRIN 索引方法支持与列数据类型不同的STORAGE类型。GiST 的compressdecompress支持函数在使用STORAGE时必须处理数据类型转换。在 GIN 中,STORAGE类型标识值的类型,通常与被索引列的类型不同。例如,整数数组列的操作符类可以只使用整数作为键。GIN 的extractValueextractQuery支持函数负责从被索引值中提取键。BRIN 与 GIN 类似:STORAGE类型标识所存储摘要值的类型,而操作符类的支持函数负责正确解释这些摘要值。 + + +
    diff --git a/zh/9.6/xml2.sgml b/zh/9.6/xml2.sgml new file mode 100644 index 00000000..eca96a0f --- /dev/null +++ b/zh/9.6/xml2.sgml @@ -0,0 +1,358 @@ + + + + xml2 + + + xml2 + + + + xml2 模块提供 XPath 查询与 XSLT 功能。 + + + + 弃用说明 + + + 自 PostgreSQL 8.3 起,核心服务器就提供了基于 SQL/XML 标准的 XML 相关功能。该功能覆盖了 XML 语法检查和 XPath 查询,也就是本模块所做的事情,而且能力更多;不过,两者的 API 完全不兼容。计划在未来的 PostgreSQL 版本中移除此模块,转而采用较新的标准 API,因此建议你尝试迁移应用程序。如果你发现本模块的某些功能在较新的 API 中还没有以足够完善的形式提供,请向 pgsql-hackers@lists.postgresql.org 说明你的问题,以便补齐这一不足。 + + + + + 函数说明 + + 显示了该模块提供的函数。这些函数提供简单直接的 XML 解析和 XPath 查询功能。所有参数的类型都是 text,为简洁起见,此处不再显示。 + + + 函数 + + + + 函数 + 返回值 + 描述 + + + + + + + xml_is_well_formed(document) + + + bool + + 此函数解析其参数中的文档文本,如果文档是良构 XML,则返回 true。(注意:在 PostgreSQL 8.2 之前,此函数名为 xml_valid()。由于 XML 中有效性和良构性具有不同含义,那是个错误的名称。旧名称仍然可用,但已被弃用。) + + + + + + xpath_string(document, query) + + + text + + 这些函数计算所提供文档上的 XPath 查询,并将结果转换为指定类型。 + + + + + + xpath_number(document, query) + + + float4 + + + + + xpath_bool(document, query) + + + bool + + + + + xpath_nodeset(document, query, toptag, itemtag) + + + text + + 该函数计算文档上的查询,并将结果封装在 XML 标签中。如果结果有多个值,输出将如下所示: +<toptag> +<itemtag>Value 1(可以是 XML 片段)</itemtag> +<itemtag>Value 2....</itemtag> +</toptag> +如果toptagitemtag为空字符串,就会省略相应的标签。 + + + + + + xpath_nodeset(document, query) + + + text + + + 与 xpath_nodeset(document, query, toptag, itemtag) 相同,但结果省略这两个标签。 + + + + + + + xpath_nodeset(document, query, itemtag) + + + text + + xpath_nodeset(document, query, toptag, itemtag)类似,但结果省略toptag + + + + + + xpath_list(document, query, separator) + + + text + + 此函数返回由指定分隔符分隔的多个值,例如,如果分隔符为,,则返回 Value 1,Value 2,Value 3 + + + + + + xpath_list(document, query) + + + text + + 这是上一个函数的包装器,使用 , 作为分隔符。 + + + + +
    +
    + + + <literal>xpath_table</literal> + + + xpath_table + + + +xpath_table(text key, text document, text relation, text xpaths, text criteria) returns setof record + + + + xpath_table 是一个表函数,它对一组文档中的每个文档执行一组 XPath 查询,并将结果作为表返回。原始文档表中的主键字段会作为结果的第一列返回,因此结果集可以方便地用于连接。参数说明见 。 + + + + <function>xpath_table</function> 参数 + + + + 参数 + 描述 + + + + + key + + + 字段的名称 — 这只是一个作为输出表第一列使用的字段,也就是说,它用于标识每个输出行来自哪条记录(关于多个值,见下文注释) + + + + + document + + + 包含 XML 文档的字段名 + + + + + relation + + + 包含这些文档的表或视图名 + + + + + xpaths + + + 一个或多个 XPath 表达式,用 | 分隔 + + + + + criteria + + + WHERE 子句的内容。这一项不能省略,因此如果你想处理该表或视图中的所有行,请使用 true1=1 + + + + + +
    + + 这些参数(XPath 字符串除外)只是直接替换进一个普通的 SQL SELECT 语句中,因此你有一定的灵活性 — 该语句为 + + + + SELECT <key>, <document> FROM <relation> WHERE <criteria> + + + + 因此,在这些特定位置上,任何合法内容都可以使用。该 SELECT 的结果必须恰好返回两列(除非你试图为 key 或 document 列出多个字段,否则都会如此)。请注意,这种简化做法要求你验证任何用户提供的值,以避免 SQL 注入攻击。 + + + 该函数必须用在 FROM 表达式中,并带有一个 AS 子句来指定输出列,例如 + +SELECT * FROM +xpath_table('article_id', + 'article_xml', + 'articles', + '/article/author|/article/pages|/article/title', + 'date_entered > ''2003-01-01'' ') +AS t(article_id integer, author text, page_count integer, title text); + + AS 子句定义了输出表中各列的名称和类型。第一列是 字段,其余各列对应 XPath 查询。如果 XPath 查询多于结果列,多出的查询将被忽略。如果结果列多于 XPath 查询,多出的列将为 NULL。 + + + + 注意,这个示例把 page_count 结果列定义为整数。该函数内部只处理字符串表示,因此当你在输出中声明整数时,它会取 XPath 结果的字符串表示,并使用 PostgreSQL 输入函数将其转换为整数(或者 AS 子句要求的任何其他类型)。如果做不到这一点 — 例如结果为空 — 就会报错,因此如果你认为数据可能有问题,最好还是把列类型设为 text。 + + + + 调用该函数的 SELECT 语句不一定非得只是 SELECT * — 它可以按名称引用输出列,或者将它们与其他表连接。该函数会生成一个虚拟表,你可以对它执行任何想要的操作(例如聚合、连接、排序等)。因此,更复杂一点的例子还可以是: + +SELECT t.title, p.fullname, p.email +FROM xpath_table('article_id', 'article_xml', 'articles', + '/article/title|/article/author/@id', + 'xpath_string(article_xml,''/article/@date'') > ''2003-03-20'' ') + AS t(article_id integer, title text, author_id integer), + tblPeopleInfo AS p +WHERE t.author_id = p.person_id; + + 当然,出于方便,你也可以把这一切封装到一个视图中。 + + + + 多值结果 + + + xpath_table 函数假定每个 XPath 查询的结果都可能有多个值,因此该函数返回的行数可能不同于输入文档的数量。返回的第一行包含每个查询的第一个结果,第二行包含每个查询的第二个结果。如果某个查询的值少于其他查询,则会在相应位置返回空值。 + + + + 在某些情况下,用户会知道某个 XPath 查询只会返回单个结果(例如唯一文档标识符)— 如果它与返回多个结果的 XPath 查询一起使用,这个单值结果只会出现在结果的第一行。解决办法是把键字段作为与一个更简单的 XPath 查询结果进行连接的条件之一。例如: + + +CREATE TABLE test ( + id int PRIMARY KEY, + xml text +); + +INSERT INTO test VALUES (1, '<doc num="C1"> +<line num="L1"><a>1</a><b>2</b><c>3</c></line> +<line num="L2"><a>11</a><b>22</b><c>33</c></line> +</doc>'); + +INSERT INTO test VALUES (2, '<doc num="C2"> +<line num="L1"><a>111</a><b>222</b><c>333</c></line> +<line num="L2"><a>111</a><b>222</b><c>333</c></line> +</doc>'); + +SELECT * FROM + xpath_table('id','xml','test', + '/doc/@num|/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c', + 'true') + AS t(id int, doc_num varchar(10), line_num varchar(10), val1 int, val2 int, val3 int) +WHERE id = 1 ORDER BY doc_num, line_num + + id | doc_num | line_num | val1 | val2 | val3 +----+---------+----------+------+------+------ + 1 | C1 | L1 | 1 | 2 | 3 + 1 | | L2 | 11 | 22 | 33 + + + + + 要让 doc_num 在每一行中都出现,解决办法是调用两次 xpath_table 并连接结果: + + +SELECT t.*,i.doc_num FROM + xpath_table('id', 'xml', 'test', + '/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c', + 'true') + AS t(id int, line_num varchar(10), val1 int, val2 int, val3 int), + xpath_table('id', 'xml', 'test', '/doc/@num', 'true') + AS i(id int, doc_num varchar(10)) +WHERE i.id=t.id AND i.id=1 +ORDER BY doc_num, line_num; + + id | line_num | val1 | val2 | val3 | doc_num +----+----------+------+------+------+--------- + 1 | L1 | 1 | 2 | 3 | C1 + 1 | L2 | 11 | 22 | 33 | C1 +(2 rows) + + + +
    + + + XSLT 函数 + + + 如果安装了 libxslt,则可使用下列函数: + + + + <literal>xslt_process</literal> + + + xslt_process + + + +xslt_process(text document, text stylesheet, text paramlist) returns text + + + + 这个函数将 XSL 样式表应用到文档上,并返回转换后的结果。paramlist 是转换时要使用的参数赋值列表,以 a=1,b=2 的形式指定。注意,参数解析非常简单:参数值中不能包含逗号! + + + + 此外还有 xslt_process 的双参数版本,它不会向转换传递任何参数。 + + + + + + 作者 + + + John Gray jgray@azuli.co.uk + + + + 本模块由 Torchbox Ltd. (www.torchbox.com) 赞助开发。它采用与 PostgreSQL 相同的 BSD 许可证。 + + + +
    diff --git a/zh/9.6/xoper.sgml b/zh/9.6/xoper.sgml new file mode 100644 index 00000000..d9c42388 --- /dev/null +++ b/zh/9.6/xoper.sgml @@ -0,0 +1,361 @@ + + + + 用户定义的操作符 + + + 操作符 + 用户定义的 + + + + 每个操作符都是对执行实际工作的底层函数调用的一种语法糖;因此, + 你必须先创建底层函数,才能创建操作符。不过,操作符并不仅仅是 + 语法糖,因为它还携带一些额外信息,可帮助查询规划器优化使用该操作符的查 + 询。下一节将专门解释这些附加信息。 + + + + PostgreSQL支持左一元、右一元和二元操作符。操作 + 符可以被重载;重载操作符 + 也就是说,同一个操作符名可以用于不同的操作符,而这些操作符具有不同数量 + 和类型的操作数。执行查询时,系统会根据提供的操作数数量和类型确定应调用 + 哪个操作符。 + + + 下面是创建一个用于将两个复数相加的操作符的示例。假定我们已经创建了类型complex的定义(见)。首先需要一个完成实际工作的函数,然后就可以定义操作符: +CREATE FUNCTION complex_add(complex, complex) + RETURNS complex + AS 'filename', 'complex_add' + LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR + ( + leftarg = complex, + rightarg = complex, + procedure = complex_add, + commutator = + +); + + + + + 现在我们就可以执行下面这样的查询: + + +SELECT (a + b) AS c FROM test_complex; + + c +----------------- + (5.2,6.05) + (133.42,144.95) + + + + + 上面展示了如何创建一个二元操作符。要创建一元操作符,只需省略 + leftarg(左一元操作符)或 rightarg(右一元操作符)之一。procedure 子句和参数子句是 + CREATE OPERATOR 中唯一必需的项。示例中出现的 + commutator 子句,则是给查询优化器的一个可选提示。关于 + commutator 以及其他优化器提示的更多细节,会在下一节说 + 明。 + + + + + 操作符优化信息 + + + PostgreSQL 的操作符定义可以包含若干可选子句, + 用来告诉系统该操作符行为的一些有用信息。只要适用,就应当提供这些子句, + 因为它们可以显著加快使用该操作符的查询执行速度。但如果你提供了这些信息, + 就必须确保它们是正确的!错误地使用优化子句可能导致查询变慢、输出出现隐 + 蔽错误,或者引发其他糟糕后果。如果你拿不准,完全可以省略某个优化子句; + 唯一的后果,只是查询可能会比本来需要的更慢。 + + + + 未来版本的 PostgreSQL 可能会加入更多优化子 + 句。这里描述的是 &version; 版本所理解的全部子句。 + + + + <literal>COMMUTATOR</literal> + + + 如果给出 COMMUTATOR 子句,它指定一个与正在定义的操 + 作符互为交换子的操作符。若对所有可能的输入值 x、y,都有 (x A y) 等于 + (y B x),则称操作符 A 是操作符 B 的交换子。注意,B 也同样是 A 的交换 + 子。例如,对某种特定数据类型而言,< 和 + > 通常互为交换子,而操作符 + 通 + 常与其自身可交换。但操作符 - 通常并不与任何操作符可 + 交换。 + + + + 一个可交换操作符的左操作数类型,与其交换子的右操作数类型相同,反之亦 + 然。因此,PostgreSQL 只需知道交换子操作符 + 的名称,就可以查找出该交换子,而这也正是 + COMMUTATOR 子句中所需提供的全部内容。 + + + + 对于将用于索引和连接子句的操作符,提供交换子信息至关重要,因为这样查询 + 优化器才能把这类子句翻转成不同计划类型所需的形式。例 + 如,考虑这样一个查询,其 WHERE 子句形如 + tab1.x = tab2.y,其中 tab1.x 和 + tab2.y 都是某种用户定义类型,并假设 + tab2.y 上建有索引。除非优化器能够确定如何把该子句翻 + 转成 tab2.y = tab1.x,否则它就无法生成索引扫描,因为 + 索引扫描机制要求传给它的操作符左侧必须是已建立索引的列。 + PostgreSQL 不会仅凭假 + 设就认为这种转换有效;= 操作符的创建者必须通过为该操 + 作符标记交换子信息,明确声明这种转换是有效的。 + + + 定义一个与自身可交换的操作符时,直接定义即可。但定义一对可交换操作符时,情况就稍微复杂一些:第一个要定义的操作符如何引用另一个尚未定义的操作符呢?这个问题有两种解决办法: + + 一种办法是在定义第一个操作符时省略 COMMUTATOR 子句,然后在第二个操作符的定义中提供该子句。由于 PostgreSQL 知道可交换操作符是成对出现的,因此它在看到第二个定义时,会自动回头补全第一个定义中缺少的 COMMUTATOR 子句。 + + + + 另一种更直接的办法是在两个定义中都包含 COMMUTATOR 子句。当 PostgreSQL 处理第一个定义并发现 COMMUTATOR 引用了一个不存在的操作符时,系统会在系统目录中为该操作符建立一个占位项。这个占位项只有操作符名称、左右操作数类型和结果类型包含有效数据,因为这就是 PostgreSQL 此时能够推断出的全部信息。第一个操作符的目录项将链接到这个占位项。之后,当你定义第二个操作符时,系统会用第二个定义中的附加信息更新该占位项。如果在占位操作符补全之前尝试使用它,就只会得到一条错误消息。 + + + + + + + <literal>NEGATOR</literal> + + + 如果给出 NEGATOR 子句,它指定一个与正在定义的操作符 + 互为求反器的操作符。若操作符 A 和 B 都返回布尔结果,并且对所有可能的输 + 入 x、y,都有 (x A y) 等于 NOT (x B y),则称 A 是 B 的求反器。注意,B + 也同样是 A 的求反器。例如,对大多数数据类型而言, + <>= 构成一对求反器。一个 + 操作符永远不可能合法地成为其自身的求反器。 + + + + 与交换子不同,一对一元操作符完全可能合法地被标记为彼此的求反器;这意味 + 着对所有 x,都有 (A x) 等于 NOT (B x),或者对右一元操作符有等价的关系。 + + + + 一个操作符的求反器必须与待定义操作符具有相同的左操作数类型和/或右操作数 + 类型,因此与 COMMUTATOR 一样,在 + NEGATOR 子句中只需给出操作符名称即可。 + + + + 提供求反器对查询优化器很有帮助,因为它允许把 + NOT (x = y) 这样的表达式简化为 + x <> y。这种情况比你想象得更常见,因为 + NOT 操作可能会作为其他重排的结果被插入进来。 + + + 可以使用上面解释的定义交换子对的相同方法,来定义成对的求反器操作符。 + + + + + <literal>RESTRICT</literal> + + + 如果给出 RESTRICT 子句,它指定该操作符的限制选择率估 + 算函数。(注意,这里是函数名,而不是操作符名。) + RESTRICT 子句只对返回 boolean 的二元操 + 作符有意义。限制选择率估算器的作用,是针对当前操作符和某个特定常量值, + 猜测一张表中有多少比例的行会满足如下形式的 + WHERE 子句条件: + +column OP constant + + 这会帮助优化器大致了解具有这种形式的 WHERE 子句将淘 + 汰多少行。(你可能会问:如果常量在左边会怎样?嗯,这正是 + COMMUTATOR 的作用之一……) + + + + 编写新的限制选择率估算函数远远超出了本章的范围,不过幸运的是,对于你自 + 己的很多操作符,通常都可以直接使用系统提供的某个标准估算器。标准的限制 + 选择率估算器如下: + + eqsel 用于 = + neqsel 用于 <> + scalarltsel 用于 <<= + scalargtsel 用于 >>= + + 这些分类可能看起来有点奇怪,但仔细想想就会发现它们是有道理的。= 通常只会接受表中很小一部分行;<> 通常只会拒绝很小一部分行。< 接受的比例取决于给定常量落在该表列的值范围中的什么位置(恰好,这正是 ANALYZE 收集并提供给选择率估算器的信息)。对于相同的比较常量,<= 接受的比例会比 < 略大,但二者足够接近,不值得区分,尤其是无论如何我们很可能也只能做出粗略猜测。类似的说法也适用于 >>= + + + 对于选择率非常高或非常低的操作符,即使它们实际上并不是真正的相等或不等 + 比较,你也常常可以勉强使用 eqsel 或 + neqsel。例如,几何类型中的近似相等操作符就使用 + eqsel,其依据是它们通常只会匹配表中很小一部分项。 + + + + 对于那些能够以某种合理方式转换为数值标量、从而可进行范围比较的数据类 + 型,你可以使用 scalarltselscalargtsel。如果可能的话,请把该数据类型加入函数 + convert_to_scalar()(位于 + src/backend/utils/adt/selfuncs.c)所理解的范围 + 中。(最终,这个函数应被通过 pg_type 系统目录某一 + 列标识的、按数据类型划分的函数所取代;但这件事目前还没有发生。)如果不 + 这样做,系统仍然可以工作,但优化器的估算效果就不会像本可达到的那样好。 + + + + 在 src/backend/utils/adt/geo_selfuncs.c 中,还为几 + 何操作符提供了其他选择率估算函数:areasel、 + positionselcontsel。截至 + 目前,这些函数都还只是桩实现,但你也许仍会想使用它们(或者更好的是,改 + 进它们)。 + + + + + <literal>JOIN</literal> + + + 如果给出 JOIN 子句,它指定该操作符的连接选择率估算函 + 数。(注意,这里是函数名,而不是操作符名。)JOIN 子 + 句只对返回 boolean 的二元操作符有意义。连接选择率估算器的 + 作用,是针对当前操作符,猜测两张表中有多少比例的行对会满足如下形式的 + WHERE 子句条件: + +table1.column1 OP table2.column2 + + 与 RESTRICT 子句一样,这会极大地帮助优化器判断在若 + 干可能的连接顺序中,哪一种预计工作量最小。 + + + + 与前面一样,本章不会尝试解释如何编写连接选择率估算函数,而只是建议你在 + 适用时使用某个标准估算器: + + eqjoinsel 用于 = + neqjoinsel 用于 <> + scalarltjoinsel 用于 <<= + scalargtjoinsel 用于 >>= + areajoinsel 用于基于二维面积的比较 + positionjoinsel 用于基于二维位置的比较 + contjoinsel 用于基于二维包含关系的比较 + + + + + + <literal>HASHES</literal> + + + 如果存在 HASHES 子句,它会告知系统:在基于该操作符 + 的连接中,可以使用哈希连接方法。HASHES 只对返回 + boolean 的二元操作符有意义;在实践中,该操作符还必须 + 表示某种数据类型或某对数据类型上的相等关系。 + + + + 哈希连接背后的假设是:只有当左值和右值被哈希到同一个哈希码时,连接操作 + 符才有可能返回 true。如果两个值被放进不同的哈希桶,连接过程就根本不会 + 去比较它们,这实际上隐含假定连接操作符的结果必定为 false。因此,对于那 + 些并不表示某种相等关系的操作符,指定 HASHES 永远没有 + 意义。在大多数情况下,只有对两侧都接受同一数据类型的操作符支持哈希才是 + 现实可行的。不过,有时也可以为两种或更多数据类型设计兼容的哈希函数;也 + 就是说,尽管这些值的表示不同,但对于相等的值,函数仍会 + 生成相同的哈希码。例如,对不同位宽的整数做到这一点就相当简单。 + + + + 若要被标记为 HASHES,该连接操作符必须出现在某个哈希 + 索引操作符族中。创建操作符时并不会强制这一点,因为那时引用它的操作符族 + 当然还不存在。但如果没有这样的操作符族存在,运行时尝试在哈希连接中使用 + 该操作符就会失败。系统需要通过该操作符族来查找与该操作符输入数据类型相 + 对应的哈希函数。当然,在创建该操作符族之前,你还必须先创建合适的哈希函 + 数。 + + + + 准备哈希函数时应格外小心,因为它有一些依赖机器的方式可能会导致结果不正 + 确。例如,如果你的数据类型是某种结构体,其中可能包含无意义的填充位,那 + 就不能简单地把整个结构体传给 hash_any。(除非你编写 + 了其他操作符和函数,以确保这些未使用位始终为零,而这正是推荐的策略。) + 再比如,在符合 IEEE 浮点标准的机器上,负零和正零是 + 不同的值(位模式不同),但它们被定义为比较相等。如果某个浮点值可能包含 + 负零,就需要采取额外步骤,确保它生成与正零相同的哈希值。 + + + + 一个可参与哈希连接的操作符,必须拥有一个交换子(如果两侧操作数数据类型 + 相同,则为它自身;如果不同,则应是相关的相等操作符),并且该交换子 + 也必须出现在同一个操作符族中。否则,在使用该操作符时可能会发生规划器错 + 误。另外,对于支持多种数据类型的哈希操作符族,最好(虽然并非严格必需) + 为每一种数据类型组合都提供相等操作符;这样可以获得更好的优化效果。 + + + + + 作为可哈希连接操作符底层实现的函数,必须标记为 immutable 或 stable。 + 如果它是 volatile,系统就永远不会尝试把该操作符用于哈希连接。 + + + + + + 如果某个可哈希连接操作符的底层函数被标记为 strict,那么该函数还必须是 + 完备的:也就是说,对于任意两个非空输入,它都应返回 true 或 false,而绝 + 不能返回 null。如果不遵循这条规则,对 IN 操作进行哈 + 希优化时可能会产生错误结果。(具体来说,IN 可能会在 + 按标准本应返回 null 的地方返回 false;或者抛出一条错误,抱怨它没有为 + null 结果做好准备。) + + + + + + + <literal>MERGES</literal> + + + 如果存在 MERGES 子句,它会告知系统:在基于该操作符 + 的连接中,可以使用归并连接方法。MERGES 只对返回 + boolean 的二元操作符有意义;在实践中,该操作符还必须 + 表示某种数据类型或某对数据类型上的相等关系。 + + + + 归并连接的基本思想,是先把左表和右表分别排序,然后同步扫描它们。因此, + 两种数据类型都必须能够被完全排序,而连接操作符必须只能在那对值位于排序 + 次序中同一位置时才成功。实际效果上,这意味着连接操作符 + 必须表现得像相等比较一样。不过,只要两种不同的数据类型在逻辑上兼容,也 + 完全可以对它们进行归并连接。例如,smallint 与 + integer 之间的相等操作符就是可归并连接的。我们只需要能把 + 两种数据类型都带入逻辑兼容排序序列的排序操作符即可。 + + + + 若要被标记为 MERGES,该连接操作符必须作为相等成员出 + 现在某个 btree 索引操作符族中。创建操作符时并不会强 + 制这一点,因为那时引用它的操作符族当然还不存在。但除非能找到匹配的操作 + 符族,否则该操作符实际上不会被用于归并连接。因此, + MERGES 标记的作用,是向规划器提供一个提示,告诉它值 + 得去查找匹配的操作符族。 + + + + 一个可参与归并连接的操作符,必须拥有一个交换子(如果两侧操作数数据类型 + 相同,则为它自身;如果不同,则应是相关的相等操作符),并且该交换子 + 也必须出现在同一个操作符族中。否则,在使用该操作符时可能会发生规划器错 + 误。另外,对于支持多种数据类型的 btree 操作符族,最 + 好(虽然并非严格必需)为每一种数据类型组合都提供相等操作符;这样可以获 + 得更好的优化效果。 + + + + + 作为可归并连接操作符底层实现的函数,必须标记为 immutable 或 stable。如 + 果它是 volatile,系统就永远不会尝试把该操作符用于归并连接。 + + + + diff --git a/zh/9.6/xplang.sgml b/zh/9.6/xplang.sgml new file mode 100644 index 00000000..70de55d5 --- /dev/null +++ b/zh/9.6/xplang.sgml @@ -0,0 +1,181 @@ + + + + 过程语言 + + + 过程语言 + + + + PostgreSQL允许使用 SQL 和 C 之外的其他语言 + 编写用户定义的函数。这些语言统称为过程语言 + (PL)。对于用过程语言编写的函数,数据库服务器 + 并不内置关于如何解释函数源文本的知识。相反,这项任务会交给一个了解该 + 语言细节的专门的调用处理器。该调用处理器既可以自行完成解析、语法分析、 + 执行等全部工作,也可以在PostgreSQL与某种 + 现有编程语言实现之间充当粘合剂。与其他任何 C 函数一样, + 调用处理器本身也是一个被编译进共享对象并按需装载的 C 语言函数。 + + + + 标准PostgreSQL发行版当前提供四种过程语言: + PL/pgSQL)、 + PL/Tcl)、 + PL/Perl)以及 + PL/Python)。 + 另有一些可用的过程语言并未包含在核心发行版中。 + 提供了查找它们的信息。此外,用户还可 + 以自行定义其他语言;开发新过程语言的基础知识见 + 。 + + + + 安装过程语言 + + + 过程语言必须在每个要使用它的数据库中安装。不过,安装在 + 数据库template1中的过程语言会自动在随后创建的所有 + 数据库中可用,因为它们在template1中的条目会由 + CREATE DATABASE复制。因此,数据库管理员可以决定哪 + 些数据库提供哪些语言,并且如果需要,还可以让某些语言默认可用。 + + + + 对于标准发行版附带的语言,只需执行 + CREATE EXTENSION + language_name,即可将该语言安装到当前数据库 + 中。此外,也可以从 shell 命令行使用程序来完成这项工作。例如,要把语言 + PL/Perl 安装到数据库 + template1,可以使用: + +createlang plperl template1 +下文所述的手工过程只建议用于安装那些尚未打包为扩展的语言。 + + + + 手工安装过程语言 + + + 在数据库中安装过程语言需要五个步骤,且必须由数据库超级用户执行。多数 + 情况下,所需的 SQL 命令都应打包成某个扩展的安装脚本, + 这样就可以用CREATE EXTENSION来执行它们。 + + + + + 该语言调用处理器的共享对象必须先编译好并安装到合适的库目录中。这与 + 构建和安装含有普通用户定义 C 函数的模块的方法相同;见 + 。该语言调用处理器往往还会依赖一个提供实际编 + 程语言引擎的外部库;如果是这样,该库也必须安装。 + + + + + + 必须用如下命令声明该调用处理器: + +CREATE FUNCTION handler_function_name() + RETURNS language_handler + AS 'path-to-shared-object' + LANGUAGE C; + + 特殊返回类型language_handler会告诉数据库系统,该函数返 + 回的不是某种已定义的SQL数据类型,并且不能在 + SQL语句中直接使用。 + + + + + 可选地,语言调用处理器可以提供一个内联处理器函数,用于执行用该语言编写的匿名代码块(命令)。如果该语言提供了内联处理器函数,可用类似下面的命令声明它: +CREATE FUNCTION inline_function_name(internal) + RETURNS void + AS 'path-to-shared-object' + LANGUAGE C; + + + + + + + 可选地,语言调用处理器可以提供一个验证器函数,用于 + 在不实际执行的情况下检查函数定义是否正确。如果存在验证器函数, + CREATE FUNCTION就会调用它。如果该语言提供了验证 + 器函数,可用类似下面的命令声明它: + +CREATE FUNCTION validator_function_name(oid) + RETURNS void + AS 'path-to-shared-object' + LANGUAGE C STRICT; + + + + + + 最后,必须用如下命令声明该 PL: +CREATE TRUSTED PROCEDURAL LANGUAGE language_name + HANDLER handler_function_name + INLINE inline_function_name + VALIDATOR validator_function_name ; +可选关键字TRUSTED表示,该语言不会授予用户原本不具备的数据访问能力。受信任的语言是为普通数据库用户(即没有超级用户权限的用户)设计的,并允许他们安全地创建函数和触发器过程。由于 PL 函数是在数据库服务器内部执行的,因此TRUSTED标记只应赋予那些不允许访问数据库服务器内部或文件系统的语言。语言PL/pgSQL, + PL/Tcl以及PL/Perl被认为是受信任的;而语言PL/TclU, + PL/PerlU以及PL/PythonU旨在提供无限制的功能,因此应被标记为受信任的。 + + + + + 展示了以 + PL/Perl为例时,手工安装过程是如何进行的。 + + + + 手工安装<application>PL/Perl</application> + + + 下面的命令告诉数据库服务器到哪里查找 + PL/Perl语言调用处理器函数的共享对象: + + +CREATE FUNCTION plperl_call_handler() RETURNS language_handler AS + '$libdir/plperl' LANGUAGE C; + + + + + PL/Perl具有内联处理器函数和验证器函数,因此我们也声明它们: +CREATE FUNCTION plperl_inline_handler(internal) RETURNS void AS + '$libdir/plperl' LANGUAGE C; + +CREATE FUNCTION plperl_validator(oid) RETURNS void AS + '$libdir/plperl' LANGUAGE C STRICT; + + + + 下面的命令: +CREATE TRUSTED PROCEDURAL LANGUAGE plperl + HANDLER plperl_call_handler + INLINE plperl_inline_handler + VALIDATOR plperl_validator; +随后指定应为以下函数和触发器过程调用前面声明的函数:其语言属性为plperl。 + + + + + 在默认的PostgreSQL安装中, + PL/pgSQL语言的调用处理器会被构建并安装到 + 目录中;此外, + PL/pgSQL语言本身也安装在所有数据库中。如 + 果在构建时配置了Tcl支持,那么 + PL/TclPL/TclU + 的调用处理器会被构建并安装到该库目录中,但这两种语言本身默认并不会安 + 装到任何数据库中。同样,如果在构建时配置了 Perl 支持,就会构建并安装 + PL/PerlPL/PerlU + 的调用处理器;如果在构建时配置了 Python 支持,则会安装 + PL/PythonU的调用处理器,但这些语言默认也 + 不会安装到任何数据库中。 + + + + + diff --git a/zh/9.6/xtypes.sgml b/zh/9.6/xtypes.sgml new file mode 100644 index 00000000..b96b1d41 --- /dev/null +++ b/zh/9.6/xtypes.sgml @@ -0,0 +1,329 @@ + + + + 用户定义的类型 + + + 数据类型 + 用户定义的 + + + + 如中所述, + PostgreSQL可以扩展以支持新的数据类型。本节描述 + 如何定义新的基础类型,也就是在SQL语言层之下定义的 + 数据类型。创建新的基础类型需要用底层语言(通常是 C)实现操作该类型的 + 函数。 + + + + 本节中的示例位于源码分发包的src/tutorial目录中的 + complex.sqlcomplex.c。关于 + 如何运行这些示例,请参见该目录中的README文件。 + + + + + 输入函数 + + + 输出函数 + + 用户定义类型必须始终具有输入函数和输出函数。这些函数决定该类型如何 + 以字符串形式出现(供用户输入和向用户输出),以及该类型在内存中如何 + 组织。输入函数接受一个以空字符结尾的字符串作为参数,并返回该类型的 + 内部(内存中)表示。输出函数接受该类型的内部表示作为参数,并返回一 + 个以空字符结尾的字符串。如果我们希望该类型除了存储之外还能做别的事 + 情,就必须提供额外的函数来实现我们希望该类型支持的各种操作。 + + + + 假设我们要定义一种表示复数的类型complex。在内存中表示复 + 数的一种自然方式是下面这个 C 结构体: + + +typedef struct Complex { + double x; + double y; +} Complex; + + + 我们需要把它做成按引用传递的类型,因为它太大了,无法放进单个 + Datum值中。 + + + + 作为该类型的外部字符串表示,我们选择形如(x,y)的 + 字符串。 + + + + 输入函数和输出函数通常都不难编写,尤其是输出函数。但是在定义该类型的 + 外部字符串表示时,要记住,最终你必须为这种表示编写一个完整而健壮的解 + 析器,作为输入函数。例如: + +x = x; + result->y = y; + PG_RETURN_POINTER(result); +} +]]> +输出函数可以简单地写成:x, complex->y); + PG_RETURN_CSTRING(result); +} +]]> + + + + + 应当注意让输入函数和输出函数互为逆运算。如果不是这样,当你需要把数据 + 转储到文件中再读回时,就会遇到严重问题。这在涉及浮点数时尤为常见。 + + + + 可选地,用户定义类型还可以提供二进制输入和输出例程。二进制 I/O 通常 + 比文本 I/O 更快,但可移植性较差。与文本 I/O 一样,外部二进制表示的精 + 确定义完全由你决定。大多数内置数据类型都尽量提供与机器无关的二进制表 + 示。对于complex,我们将借助类型float8的二 + 进制 I/O 转换器: + +x = pq_getmsgfloat8(buf); + result->y = pq_getmsgfloat8(buf); + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(complex_send); + +Datum +complex_send(PG_FUNCTION_ARGS) +{ + Complex *complex = (Complex *) PG_GETARG_POINTER(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendfloat8(&buf, complex->x); + pq_sendfloat8(&buf, complex->y); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} +]]> + + + + + 一旦我们写好了 I/O 函数并将它们编译进共享库,就可以在 SQL 中定义 + complex类型。首先将其声明为一种 shell 类型: + + +CREATE TYPE complex; + + + 这会建立一个占位符,使我们可以在定义其 I/O 函数时引用该类型。现在我们 + 可以定义这些 I/O 函数: + + +CREATE FUNCTION complex_in(cstring) + RETURNS complex + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION complex_out(complex) + RETURNS cstring + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION complex_recv(internal) + RETURNS complex + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION complex_send(complex) + RETURNS bytea + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + + + + + 最后,我们可以给出该数据类型的完整定义: + +CREATE TYPE complex ( + internallength = 16, + input = complex_in, + output = complex_out, + receive = complex_recv, + send = complex_send, + alignment = double +); + + + + + + 数组 + 用户定义类型的 + + 当你定义一种新的基础类型时,PostgreSQL会 + 自动提供对该类型数组的支持。该数组类型通常与基础类型同名,只是在前面 + 加一个下划线字符(_)。 + + + + 一旦该数据类型存在,我们就可以声明额外的函数,为该数据类型提供有用的 + 操作。随后可以在这些函数之上定义操作符;如果需要,还可以创建操作符类 + 以支持该数据类型的索引。这些附加层会在后续各节中讨论。 + + + + 如果数据类型的内部表示是可变长度的,则这种内部表示必须遵循可变长度数 + 据的标准布局:前四个字节必须是一个从不直接访问的char[4] + 字段(惯例上命名为vl_len_)。必须使用 + SET_VARSIZE()宏在该字段中存储该 datum 的总大小(包括 + 长度字段本身),并使用VARSIZE()取回它。(这些宏 + 之所以存在,是因为长度字段可能会随平台不同而采用编码形式。) + + + + 更多细节见命令的说明。 + + + + + TOAST 注意事项 + + + TOAST + 与用户定义类型 + + + + 如果你的数据类型的值在内部形式上的大小可变,通常最好让该数据类型支持 + TOAST(见)。即使这些 + 值总是小到不需要压缩或外部存储,也应该这样做,因为 + TOAST还可以通过减少头部开销来为小数据节省空间。 + + + + 为了支持TOAST存储,操作该数据类型的 C 级函数必须始 + 终使用PG_DETOAST_DATUM对传给它们的任何 TOAST 化 + 值执行去 TOAST 化处理。(这一细节通常通过定义特定于该类型的 + GETARG_DATATYPE_P宏来隐藏。)然后,在执行 + CREATE TYPE命令时,把内部长度指定为 + variable,并选择某个不同于plain + 的合适存储选项。 + + + + 如果数据对齐并不重要(无论只是对某个特定函数而言,还是因为该数据类型 + 本来就指定了字节对齐),那么就有可能避免 + PG_DETOAST_DATUM的一部分开销。你可以改用 + PG_DETOAST_DATUM_PACKED(通常通过定义 + GETARG_DATATYPE_PP宏来隐藏),并使用 + VARSIZE_ANY_EXHDRVARDATA_ANY + 宏访问一个可能采用打包形式的 datum。再次注意,即使数据类型定义指定了 + 对齐方式,这些宏返回的数据也不是对齐的。如果对齐很重要,就必须使用常 + 规的PG_DETOAST_DATUM接口。 + + + + + + 较旧的代码常把vl_len_声明为 + int32字段,而不是char[4]字段。只要结构体定 + 义中还有其他至少按int32对齐的字段,这样做是可以的。但 + 在处理可能未对齐的 datum 时使用这种结构体定义就很危险;编译器可能据此假定该 datum 实际上是对齐的,从而在对齐要求严格的体系结构上导致核心转储。 + + + + + 支持TOAST带来的另一个特性,是可以拥有一种比磁盘上 + 存储的格式更便于处理的展开内存数据表示。常规 + 或扁平的 varlena 存储格式归根结底只是一块字节数据;例 + 如,它不能包含指针,因为它可能被复制到内存中的其他位置。对于复杂数据 + 类型,处理扁平格式的代价可能相当高,因此PostgreSQL + 提供了一种办法,把扁平格式展开成更适合计算的表示形式, + 然后在该数据类型的各个函数之间以这种格式在内存中传递。 + + + + 要使用展开存储,数据类型必须定义一种遵循 + src/include/utils/expandeddatum.h中规则的展开 + 格式,并提供函数把扁平 varlena 值展开为展开格式,再把 + 展开格式压平回常规 varlena 表示。然后要确保该数据类型的 + 所有 C 函数都能接受这两种表示,必要时可在收到参数后立即把一种转换成另 + 一种。 + 这并不要求一次性修正该数据类型的全部现有函数,因为标准 + PG_DETOAST_DATUM宏被定义为会把展开输入转换成常规 + 扁平格式。因此,现有那些处理扁平 varlena 格式的函数,即使效率略低一 + 些,也仍能处理展开输入;除非更好的性能很重要,否则不必转换它们。 + + + + 能够处理展开表示的 C 函数通常分为两类:只能处理展开格式的,以及既能处 + 理展开输入也能处理扁平 varlena 输入的。前者更容易编写,但总体上可能效 + 率较低,因为为了让单个函数使用展开格式而把扁平输入转换成展开形式,其 + 代价可能比在展开格式上操作所节省的还要多。只需处理展开格式时,可以把 + 将扁平输入转换为展开形式的过程隐藏在参数提取宏内部,这样函数看起来不 + 会比处理传统 varlena 输入的函数更复杂。要同时处理这两类输入,可以编写 + 一个参数提取函数,对外部、短头部和压缩的 varlena 输入执行去 TOAST 化, + 但对展开输入则不做处理。这样的函数可以定义为返回一个指向联合体的指 + 针,该联合体包含扁平 varlena 格式与展开格式。调用者可以使用 + VARATT_IS_EXPANDED_HEADER()宏判断收到的是哪种 + 格式。 + + + + TOAST基础设施不仅允许区分常规 varlena 值和展开 + 值,还能区分指向展开值的可读写(read-write)和 + 只读(read-only)指针。只需要查看展开值,或者只会以安 + 全且在语义上不可见的方式修改它的 C 函数,不必关心收到的是哪种指针。 + 那些会生成输入值修改版本的 C 函数,如果收到可读写指针,可以原地修改展 + 开输入值;但如果收到只读指针,则不得修改输入,此时必须先复制该值,生 + 成一个新的可修改值。构造了新展开值的 C 函数应始终返回指向它的可读写指 + 针。另外,原地修改可读写展开值的 C 函数如果在中途失败,应注意让该值保 + 持在合理状态。 + + + + 有关如何处理展开值的示例,请参见标准数组基础设施,尤其是 + src/backend/utils/adt/array_expanded.c。 + + + + +