开发者常见问题解答/es

来自 PostgreSQL Wiki
跳转到导航跳转到搜索

如何参与项目

如何参与 PostgreSQL 的开发?

您可以下载代码并查看。请查看下一个问题。

订阅并阅读 pgsql-hackers 邮件列表(通常称为“黑客”)。这是项目的主要贡献者和核心成员讨论开发的地方。

如何下载/更新当前源代码?

有多种方法可以获取源代码。偶尔的开发者可以直接从 ftp://ftp.postgresql.org/pub/snapshot/ 获取最新的源代码。

更积极的开发者可以利用我们匿名访问的源代码管理系统。源代码目前存储在 CVS 中。有关如何从 CVS 存储库获取源代码的更多信息,请参见 http://developer.postgresql.org/docs/postgres/cvs.html

我们还有一个 CVS 存储库的 Git 镜像;请参见 http://git.postgresql.org/?p=postgresql.git

开发代码需要什么开发环境?

PostgreSQL 主要使用 C 编程语言开发。源代码面向 Unix 和 Windows 环境(Windows 2000、XP 及更高版本)中最流行的平台。

大多数开发者运行 Unix 类操作系统,并使用开源工具,如 GCCGNU MakeGDBAutoconf 等。如果您以前参与过开源代码贡献,您可能已经熟悉这些工具。使用 Windows 工具的开发者可以使用 MinGW。一些开发者使用其他发行版的编译器,结果不尽相同。

安装说明 中可以找到生成 PostgreSQL 所需的完整软件列表。

不断重建源代码的开发者在配置时传递 --enable-depend 标志。结果是,如果您对 C 头文件进行了修改,所有依赖于该文件的源文件也会被重建。

src/Makefile.custom 文件可用于设置环境变量,如 CUSTOM_COPT,这些变量用于每次编译。

在哪里可以了解更多关于代码的信息?

除了源代码本身的文档外,您还可以找到一些在 https://postgresql.ac.cn/developer 上讨论代码的论文/演示文稿。一个优秀的演示文稿可以在 http://neilconway.org/talks/hacking/ 上找到。

哪些领域需要做工作?

主要功能在 待办事项列表 中详细说明。

您可以通过查看文件、SQL 标准和推荐的书籍(参见 开发者书籍)来了解这些功能的更多信息。

如何参与 PostgreSQL 网站的开发?

PostgreSQL 网站的开发在 pgsql-www 邮件列表 中进行讨论。项目页面位于 http://pgweb.postgresql.org/,其中包含可用的源代码。

开发工具和帮助

哪些工具可供开发者使用?

首先,src/tools 目录中的所有文件都是为开发者设计的。

   RELEASE_CHANGES son cambios que se han hecho para cada versión liberada (release)
   backend         descripción/Diagrama de Flujo de los directorios de backend.
   ccsym           encuentre defines estandar hechos por su compilador.
   copyright       arreglos a los anuncios de copyright.
   
   entab           convierte espacios en tabulaciones, utilizado por pgindent
   find_static     encuentra funciones que pueden hechas estáticas
   find_typedef    localiza typedefs en el código fuente
   find_badmacros  encuentra macros que utilizan llaves incorrectamente
   fsync           un escript que provee información sobre el costo de las
                   llamadas del systema de sincronización del caché
   make_ctags      genera un archivo 'tags' para vi en cada directorio
   make_diff       genera archivos *.orig y *.diff del código fuente
   make_etags      genera archivos 'etags' para emacs
   make_keywords   genera una comparación entre nuestras palabras clave y el estándar SQL'92
   make_mkid       genera archivos ID para mkid
   pgcvslog        se utiliza para generar una lista de cambios para cada versión
   pginclude       scripts para añadir/eliminar archivos include
   pgindent        genera la indentación de los archivos de código fuente
   pgtest          un sistema semi-automático de generación
   thread          un script para pruebas de hilos (threads)

在 src/include/catalg

   unused_oids     un script que busca OIDs sin utilizar para utilizarlos en los catálogos del sistema
   duplicate_oids  localiza OIDs duplicados en las definiciones de los catálogos del sistema

如果您在网络浏览器中打开 tools/backend/index.html 文件,您将看到一些程序,它们描述了数据流、后端组件的流程图以及共享内存区域的描述。您可以点击流程图中的任何地方以查看其描述。如果您点击目录名称,您将被带到源代码中的该目录,以浏览当前包含在其中的源代码。我们还在源代码的某些目录中包含了多个 README 文件来描述模块的功能。当您访问每个目录时,浏览器会显示这些信息。tools/backend 目录还包含在我们的网站上,标题为 __PostgreSQL 如何处理查询__。

其次,您需要一个能够处理标记的编辑器,以便您可以标记任何函数调用并查看函数的定义。大多数编辑器都通过标记文件(tags 或 etags)支持这一点。

第三,您需要从 ftp://ftp.gnu.org/gnu/id-utils/ 获取 id-utils。

运行 tools/make_mkid 会为源代码创建一个符号文件,可以快速查找。

一些开发者使用 cscope,它可以在 http://cscope.sf.net/ 上找到。另一些人使用 glimpse,它可以在 http://webglimpse.net/ 上找到。

tools/make_diff 文件包含用于创建 diff 补丁文件的工具,这些补丁文件可以应用到发行版中。这会生成上下文 diff 文件,这是我们首选的格式。

我们的标准是 BSD 格式,每层代码缩进一个制表符,每个制表符等于四个空格。您需要将编辑器或文件查看器配置为将制表符显示为四个空格。

   vi in ~/.exrv:
           set tabstop=4
           set shiftwidth=4
           set noexpandtab
   more:
           more -x4
   less:
           less -x4

最新源代码的 tools/editors 目录包含示例参数,这些参数可以与 emacs、xemacs 和 vim 编辑器一起使用,以帮助维护 PostgreSQL 的编码标准。

pgindent 将根据为您的操作系统上的缩进工具指定的标志对代码进行格式化。本文描述了保持一致的编码风格的价值。

pgindent 在每次测试版周期开始之前都会在所有源代码文件上运行。它会自动格式化所有源代码文件以使它们保持一致。需要特定换行符的注释块应格式化为注释块,其中注释以 /*------ 开头。这些注释不会以任何方式重新格式化。

pginclude 包含用于添加所有必要的 #include 文件的脚本,以及用于删除不必要的 #inlcude 的脚本。

当添加诸如类型 (types) 或函数 (functions) 之类的内部构造对象时,您需要为它们分配 OID。我们的约定是,所有手动分配的 OID 在 1-9999 的范围内具有不同的值。(从机制上讲,这些值在单个系统目录中是唯一的,但为了清晰起见,我们要求它们在整个系统中都是唯一的。)有一个名为 unused_oids 的脚本,位于 src/include/catalog 中,它显示了当前未使用的 OID。要分配一个新的 OID,请根据 unused_oids 选择一个空闲的 OID,为了获得额外的分数,请选择一个靠近当前相关对象的 OID。另请参阅 duplciate_oids 脚本,该脚本会在您犯错时提醒您。

哪些书籍适合开发者?

有五本好书

  • 数据库系统导论,作者 C.J. Date,Addison,Wesley
  • SQL 标准指南,作者 C.J. Date 等人,Addison,Wesley
  • 数据库系统基础,作者 Elmasri 和 Navathe
  • 事务处理,作者 Jim Gray,Morgan,Kaufmann
  • 事务信息系统,作者 Gerhard Weikum,Kaufmann

¿ configure 是怎么回事呢 ?

configure 和 configure.in 文件是 GNU 的 autoconf 包的一部分。configure 工具允许我们测试操作系统(操作系统)的各种功能,并设置可以在 C 程序和 Makefile 中测试的变量。Autoconf 安装在 PostgreSQL 主服务器上。要向 configure 添加选项,请编辑 configure.in 文件,然后运行 autoconf 生成 configure 文件。

当 configure 工具由用户执行时,它会测试操作系统的各种功能,将它们存储在 config.status 和 config.cache 文件中,并修改一系列 *.in 文件。例如,如果存在 Makefile.in 文件,configure 工具会生成一个 Makefile 文件,其中包含 configure 工具找到的所有 @var@ 参数的替换。

当您需要编辑文件时,请确保您不会浪费时间修改 configure 工具生成的文件。编辑 *.in 文件,然后重新运行 configure 工具以重新创建所需文件。如果您从源代码的上一级目录运行 distclean,configure 工具生成的所有文件都将被删除,因此您只会看到源代码分发中包含的文件。

¿ 如何添加一个新的端口 ?

有很多地方需要修改才能添加一个新的端口。首先,从 src/template 目录开始。添加您的操作系统(操作系统)的适当条目。此外,使用 src/config.guess 文件将您的操作系统(操作系统)添加到 src/template/.similar 中。您不需要让您的操作系统(操作系统)版本完全匹配。configure 工具的测试将按您的操作系统(操作系统)的精确版本号进行搜索,如果没有找到,则会搜索与没有版本号的版本号匹配的版本号。编辑 src/configure.in 以添加您的新操作系统(操作系统)。(参见上面描述的 configure 工具。)

然后查看 src/include/port 并为新的操作系统(操作系统)添加您的文件,以及适当的值。希望 src/include/storage/s_lock.h 中已经存在针对您的 CPU 的锁定代码。src/makefiles 目录还用于特定于 Makefiles 的处理。那里有一个 backend/port 目录,如果您需要针对您的操作系统(操作系统)的特殊文件,就可以使用它。

¿ 为什么不使用线程 (threads)、原始设备、异步 I/O <在这里插入您最喜欢的特性> ?

人们总是忍不住想要在操作系统(操作系统)出现新特性后立即使用它们。我们抵制这种诱惑。

首先,我们支持 15 种以上的操作系统(操作系统),因此任何新特性在被我们考虑之前都必须经过充分的验证。其次,最新特性并不能带来显著的改进。第三,它们通常有一些缺点,例如稳定性降低或需要额外的代码。因此,我们不会急于使用新特性,而是会等待特性稳定下来,然后要求提供证据证明可以实现可衡量的改进。

例如,目前在后端中没有使用线程 (threads),因为

  • 历史上,线程 (threads) 没有得到支持,并且包含许多错误。
  • 后端中的错误可能会损坏其他后端。
  • 使用线程 (threads) 带来的速度提升微不足道,与后端启动的剩余时间相比,速度提升微不足道。
  • 后端代码将变得更加复杂。

因此,我们并非不了解新特性。只是在采用新特性时,我们持谨慎态度。待办事项列表 (TODO list) 包含指向讨论的链接,这些讨论展示了我们在这些领域的理由。

¿ CVS 分支是如何管理的 ?

参见 分支管理

¿ 在哪里可以获取 SQL 标准的副本 ?

您应该从 ISOANSI 处购买。搜索 ISO/ANSI 9075。ANSI 的报价更便宜,但两个组织的文档内容相同。

由于购买官方标准指南的副本非常昂贵,大多数开发人员依赖于网上提供的各种草案。

其中一些是

PostgreSQL 文档包含有关 PostgreSQL 和 SQL 标准合规性 的信息。

有关该标准的一些网页是

请注意,拥有 SQL 标准副本并不意味着您就能成为 PostgreSQL 开发的宝贵贡献者。解释标准非常困难,需要多年的经验。无论如何,PostgreSQL 中的大多数特性都没有在标准中指定。

¿ 在哪里可以获得技术支持 ?

pgsql-hackers 邮件列表可以解答所有初学者关于源代码提出的许多技术问题 - 该列表的存档可以在 http://archives.postgresql.org/pgsql-hackers/ 中找到。

如果您找不到与您的问题相关的具体讨论,请随时在列表中发布您的问题。

大多数贡献者也会回答技术问题,包括有关新特性开发的问题,例如,在 irc.freenode.net 的 #postgresql 频道上的 IRC 中。

开发流程

¿ 在选择了一项工作内容后,我应该怎么办 ?

向 pgsql-hackers 发送电子邮件,提出您想做的事情的建议(假设您的贡献不是微不足道的)。独立工作不可取,因为其他人可能正在做同样的事情,或者您可能对工作内容理解错误。在电子邮件中,讨论您计划使用的内部实现方法,以及对用户可见的任何更改(新的语法等)。对于复杂的补丁,在开始工作之前,从社区那里获得有关您建议的反馈非常重要。如果没有这样做,您的补丁可能会被拒绝。如果您的工作由某家公司赞助,请阅读 这篇文章,以获取一些关于如何提高效率的技巧。

有一个网站专门用于维护等待审查的补丁,http://momjian.postgresql.org/cgi-bin/pgpatches,以及为下个版本保留的补丁,http://momjian.postgresql.org/cgi-bin/pgpatches_hold

我开发了一个补丁,下一步是什么 ?

您需要将补丁发送到 [email protected]。它将由项目的其他贡献者审查,然后被接受或退回以进行进一步的处理。为了帮助您确保您的补丁得到审查并及时发送,请尝试确保您的提交满足以下几点

  1. 确保您的补丁是在最新的源代码版本(对于开发人员来说是 CVS HEAD)的基础上生成的。有关 PostgreSQL 中分支的更多信息,请参见 分支管理
  2. 尝试生成一个尽可能易读的补丁,遵循格式约定。这使得审查人员更容易理解,而且没有理由不使用 pgindent。另外,避免不必要的空格,因为它们只会分散审查人员的注意力,格式更改将在下一次运行 pgindent 时被删除。
  3. 补丁应该以上下文 diff 格式生成(diff -c,并且应该可以从根目录应用。如果您不熟悉这种情况,您可能会发现 src/tools/make_diff/difforig 中的脚本很有帮助。统一 diff 仅在文件更改为单行并且不依赖于周围的行时才更可取。)
  4. PostgreSQL 采用 BSD 许可证。通过在 PostgreSQL 邮件列表中发布补丁,您将不可撤销地将您的补丁的权利授予 PostgreSQL 全球开发组,以便根据 BSD 许可证进行分发。
  5. 确认您的补丁通过了回归测试。如果您的更改针对特定端口,请列出您已对其进行测试的端口。
  6. 如果您添加了新特性,请确认该特性已得到全面测试。尝试在所有可能的场景中测试该特性。
  7. 具有新特性的补丁应附带文档补丁。如果您需要帮助查看 SQL 标准,请参见 这个问题
  8. 提供对实现的概述,最好在代码中添加注释。遵循代码注释风格通常是一个好方法。另外,请参见 developerWorks 上的这篇文章
  9. 如果这是一个与性能相关的补丁,请提供确认补丁优势的结果。即使没有这些信息,发布补丁也是可以的,但直到有人测试并发现性能显著提升之前,才会应用该补丁。

即使满足了以上所有要点,补丁也可能由于其他原因而被拒绝。请做好接收评论和进行修改的准备。

补丁被应用后,您将通过电子邮件收到通知,您的姓名可能会出现在下一个版本的发布说明中。

¿ 补丁是如何审查的 ?

审查补丁的人在应用补丁之前需要检查几件事。

  • 补丁遵循 SQL 标准,或者社区同意其行为。
  • 其风格与周围代码的风格相一致。
  • 正确使用所有 PostgreSQL 子系统。
  • 包含足够的注释。
  • 包含在所有受支持的操作系统上都能正常工作的代码。
  • 拥有适当的文档。
  • 通过回归测试,并在必要时添加新的测试。
  • 即使在异常情况下也能按预期运行。
  • 不包含任何对系统稳定性构成风险的因素。
  • 不会使源代码更加复杂。
  • 如果与性能相关,则包含可衡量的改进。
  • 对于普通 PostgreSQL 用户来说足够有用。
  • 遵循 PostgreSQL 的标准编码规范。

如何测试我的更改?

基本测试系统

测试代码最简单的方法是确保它基于最新版本的代码构建,并且没有生成编译器警告。

建议在配置中添加 --enable-cassert。这将启用源代码中的断言检查,有时会发现错误(bug),因为这些错误会导致数据损坏或段错误。这通常会使调试更容易。

然后,您可以通过 psql 进行测试。

回归测试套件

下一步是对现有回归测试套件进行测试。为此,在源代码根目录中执行 "make check"。如果任何测试失败,请进行调查。

如果您有意改变了某些现有行为,这种改变可能会导致回归测试失败,但不会导致实际回归。如果是这样,您还需要修补回归测试套件。

其他运行时测试

一些开发人员使用诸如 valgrind (http://valgrind.kde.org) 之类的工具进行内存测试,使用 gprof(随 GNU binutils 套件提供)和 oprofile (http://oprofile.sourceforge.net/) 进行性能分析,以及一些其他相关工具。

单元测试、静态分析、模型审查……怎么样?

关于其他测试框架已经有一些讨论,一些开发人员正在探索这些想法。

请注意,Makefiles 没有包含文件所需的适当依赖项。您需要执行 make clean,然后再次执行 make。如果您使用 GCC,则可以在 configure 中使用 --enable-depend 选项,使编译器自动计算依赖项。

技术问题

如何从后端代码中有效地访问系统目录中的信息?

首先,您需要找到您感兴趣的元组(记录)。有两种方法可以做到这一点。第一种方法是使用 SearchSysCache() 及其相关函数,允许您使用系统目录中的预定义索引查询系统目录。这是访问系统表的首选方法,因为第一次缓存调用会加载必要的记录,并且未来的请求可以在不访问表的情况下返回结果。缓存可用列表位于 src/backend/utils/cache/syscache.c 中。usr/backend/utils/cache/lsyscache.c 包含用于缓存列搜索的特定函数。

返回的记录是缓存的记录堆栈的私有副本。因此,您不应该修改或删除由 SearchSysCache() 返回的元组。您应该在不再需要时使用 ReleaseSysCache() 释放它;这会告诉缓存,如果需要,它可以丢弃该元组。如果您拒绝调用 ReleaseSysCache(),那么缓存条目将一直阻塞在缓存中,直到事务结束,这在开发期间是可以容忍的,但在版本中包含的代码中是不可接受的。

如果您无法使用系统缓存,您需要直接从表中提取数据,使用由所有后端共享的缓存缓冲区。后端会自动处理将记录加载到缓存缓冲区中。为此,请使用 heap_open() 打开表。您可以使用 heap_beginscan() 开始扫描表,然后使用 heap_getnext() 并继续,直到 HeapTupleIsValid() 返回 true。然后,您应该执行 heap_endscan()。可以将键分配给扫描。不使用索引,因此所有记录都将与键进行比较,并且只有有效的记录才会被返回。

您还可以使用 heap_fetch() 通过块号/偏移量获取所有记录。虽然扫描会自动锁定和解锁缓存缓冲区中的记录,但对于 heap_fetch(),您需要提供一个缓冲区指针,并在完成后执行 ReleaseBuffer()。

一旦拥有记录,您就可以通过简单地访问 HeapTuple 结构中的条目来获取所有元组共有的数据,例如 t_self 和 t_oid。如果您需要某个特定表的特定列,您需要获取 HeapTuple 指针,并使用 GETSTRUCT() 宏来访问元组的特定表开头。您可以转换指针,例如转换为 Form_pg_proc 如果您正在访问 pg_proc 表,或转换为 Form_pg_type 如果您正在访问 pg_type 表。然后,您可以使用结构指针访问元组字段

((Form_pg_class) GETSTRUCT(tupla))->relnatts

但是,请注意,这仅适用于固定宽度列,绝不适用于 NULL 列,并且仅适用于所有前列都是固定宽度且绝不为空的情况。否则,列位置是可变的,您需要使用 heap_getattr() 或其相关函数来从元组中提取它。

同样,请避免对结构字段进行赋值以更改活动元组。最好的方法是使用 heap_modifytuple() 并提供原始元组以及要更改的值。这将返回一个 palloc 元组,该元组将发送到 heap_update()。您可以通过将元组的 t_self 发送到 heap_delete() 来删除元组。您还可以使用 t_self 进行 heap_update()。请记住,元组也可能来自系统缓存的副本,这些副本可能会在调用 ReleaseSysCache() 后消失,或者直接从磁盘缓冲区读取,这些缓冲区会在您执行 heap_getnext()、heap_endscan 或 ReleaseBuffer() 时消失(对于 heap_fetch())。或者,它也可能是一个 palloc 元组,对于该元组,您需要在完成后执行 pfree()。

为什么有时表名、列名、类型名、函数名或视图名被称为 Name 或 NameData,有时被称为 char *?

表名、列名、类型名、函数名和视图名存储在系统表中,在 Name 类型的列中,Name 是一个固定宽度数据类型,在 NAMEDATALEN 字节处以 NULL 结尾。(NAMEDATALEN 的默认值为 64 字节。)

  typedef struct nameData
   {
       char        data[NAMEDATALEN];
   } NameData;
   typedef NameData *Name;

通过用户查询进入后端的表名、列名、类型名、函数名和视图名存储为以 NULL 结尾的可变宽度字符串。

许多函数都被调用,并使用这两种类型的名称,例如 heap_open()。由于 Name 是以 NULL 结尾的类型,因此将其发送到期望 char * 类型的函数是安全的。由于有很多情况需要将磁盘上的名称(Name)与用户提供的名称(char *)进行比较,因此在很多情况下,Name 和 char * 可以互换使用。

为什么我们使用 Node 和 List 来构建数据结构?

我们这样做是因为它允许我们以灵活的方式一致地将数据发送到后端。每个节点都包含一个 NodeTag 属性,该属性指定节点内包含的数据类型。列表是作为传递列表链接在一起的节点的集合。列表中元素的排序可能与否具有意义,具体取决于列表的特定用途。

以下是用于操作列表的一些命令

lfirst(i)
lfirst_int(i)
lfirst_oid(i)
返回列表中单元格 i 的数据(一个指针、整数或 OID)。
lnext(i)
返回列表中 i 之后的下一个单元格。
foreach(i, list)
遍历列表,将列表中的每个单元格分配给 i。

重要的是要注意,i 是一个 ListCell *,而不是列表单元格中的数据。您需要使用 lfirst 的变体之一来获取单元格中的数据。

以下是一些典型的代码片段,它们遍历包含 Var * 元素和进程的列表

           List        *list;
           ListCell    *i;
           ...
           foreach(i, list)
           {
               Var *var = (Var *) lfirst(i);
               ...
               /* procesar var aquí */
           }
lcons(node, list)
将一个节点添加到列表的开头,或者如果列表是 NIL,则使用节点创建一个新列表。
lappend(list, node)
将一个节点添加到列表的末尾。
list_concat(list1, list2)
将 list2 连接到 list1 的末尾。
list_length(list)
返回列表的长度。
list_nth(list, i)
返回列表中从零开始的第 i 个元素。
lcons_int, ...
有这些命令的整数版本:lcons_int、lappend_int 等。对于 OID 列表,也有版本:lcons_oid、lappend_oid 等。

您可以在 gdb 中轻松地打印这些节点。首先,要禁用使用 print 命令在 gdb 中输出时截断输出

(gdb) set print elements 0

而不是以 gdb 格式打印值,您可以使用以下两个命令以详细格式打印列表、节点和结构内容,该格式更容易理解。列表未在节点内展开,节点以详细方式打印。第一个语句以缩写格式打印,第二个语句以长格式打印

(gdb) call print(any_pointer)
(gdb) call pprint(any_pointer)

输出将出现在服务器日志文件中,或者如果您直接运行后端而没有 postmaster,则会出现在屏幕上。

我在结构中添加了一个字段。我还应该做些什么?

结构体会被发送到分析器、重写器、优化器和执行器,这需要一些支持。大多数结构体在 src/backend/nodes 中有用于创建、复制、读取和输出这些结构体的支持例程 -- 特别是,大多数节点类型需要在 copyfuncs.c 和 equalfuncs.c 文件中进行支持,而某些节点类型还需要在 outfuncs.c 和可能 readfuncs.c 中进行支持。确保在这些文件中添加对新字段的支持。找到结构体中任何其他可能需要为新字段添加代码的地方 -- 搜索结构体的现有字段引用是一个很好的方法。mkid 对此很有用。(参见 可用的工具)。