postgres的initdb解析——从一次插件升级失败说起

我们公司基于postgres开发了一款数据库产品,不用说我们对OSS的源码做了改动,并且也集成和自己编写了一些插件。因此,当postgresql和相关插件升级时,我们也需要将升级反应到自己的产品中去,这是背景。

这次的问题是在我们升级postgresql的插件orafce(3.2.0-->3.6.0)时发生的。按照往常惯例我们将该插件升级后,组织源码进行编译,OK编译通过。

然而,在我们进行测试时,程序在initdb时就报错停止了。报错如下:

[postgres@localhost data]$ initdb -D `pwd`
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english". (15541)

Data page checksums are disabled. (18153)

fixing permissions on existing directory /data ... ok (15516)
creating subdirectories ... ok (15516)
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok (15516)
creating template1 database in /data/base/1 ... ok (15516)
initializing pg_authid ... ok (15516)
initializing dependencies ... ok (15516)
creating information schema ... FATAL: relation "information_schema.columns" does not exist (10414)
......
(以下省略)

众人费解。只是升级了插件,怎么会导致无法创建initdb呢?

我注意到报错中的这行:

creating information schema ... FATAL: relation "information_schema.columns" does not exist (10414)

似乎挺熟悉?我看看了orafce最新的升级文件。发现在文件中有以下SQL文:

-- Oracle system views
create view oracle.user_tab_columns as
    select table_name,
           column_name,
           data_type,
           coalesce(character_maximum_length, numeric_precision) AS data_length,
           numeric_precision AS data_precision,
           numeric_scale AS data_scale,
           is_nullable AS nullable,
           ordinal_position AS column_id,
           is_updatable AS data_upgraded,
           table_schema
    from information_schema.columns;

似乎对上了,真的是升级惹的祸?不应该啊,社区应该没这么蠢啊,再说只是个插件,怎么会影响postgres呢?这个时候还没导入插件呢,怎么会存在这个错误?

困惑。

因为我们产品中的postgresql的代码也有我们自己添加的代码,为了确定错误来源,我先取了一份postgresql的source,在contrib目录下放入最新的orafce代码。然后make world。

在运行initdb命令:

(以上省略)
copying template1 to postgres ... ok (15516)
syncing data to disk ... ok (15516)

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    pg_ctl -D /data -l logfile start

成功了!说明不是OSS的问题。

很好,那么就是我们的修正代码的问题了。然而initdb这一块代码不是我们team维护的,我不知道我们的产品修改了什么。好的,老老实实看看差分代码吧。

结果差分代码一看,initdb.c文件我们就修改了两行代码,看了看,也只是改善性代码,和本次的问题毫无关系。费解。。。。

好吧,既然是initdb出了问题,我看看initdb的代码,理理思路吧。跑过去看看抛出出错信息的代码。再加上自己的整理。大概搞清楚了原因。

具体是什么原因先卖个关子。我们先看看postgresql在initdb时会执行哪些操作(缩进代表调用关系):

main
	确保stdout和stderr的缓冲行为与交互式使用中的行为相匹配
	设置特定于应用程序的区域设置和服务目录
	process command-line options
	sync_only模式
	密码与服务器认证相关
	setup_pgdata()设置pgdata环境
	setup_bin_paths()获取postgres命令的路径,判断是否与initdb版本匹配
	set_info_version()提取信息模式所需的版本号
	setup_data_file_paths() check初始化脚本和配置文件模板
	setup_locale_encoding()设置编码方式
	setup_text_search()根据encoding设置text search方式
	do_sync模式
	initialize_data_directory
		create_data_directory()创建数据库目录pgdata
		create_xlog_or_symlink()创建pgdata下的pg_xlog目录
		创建其他子目录
		创建并配置postgresql.conf文件
		bootstrap_template1()调用postgres在Bootstrap模式下创建模板数据库template1
		(以下的配置均针对模板数据库template1)
		setup_auth()设置template1的密码表
		setup_depend()建立系统表pg_depend,该表描述了数据库对象之间的依赖关系
		setup_sysviews()运行脚本system_views.sql创建系统视图★
		setup_description()创建系统表pg_description和pg_shdescription
		setup_collation()创建系统表pg_collation,该表描述了可用的排序规则,其本质是从一个SQL名字到操作系统locale分类的映射
		setup_conversion()运行脚本conversion_create.sql创建系统表pg_conversion,该表描述编码转换过程
		setup_dictionary()运行脚本snowball_create.sql创建一些额外的目录
		setup_privileges()为postgres内置的一些object设置默认权限
		setup_schema()运行脚本information_schema.sql创建模式information_schema★
		load_plpgsql()加载plpgsql服务端编程语言
		vacuum_db()清理template1
		make_template0()根据做好的template1,拷贝复制一份作为template0
		make_postgres()根据做好的template1,拷贝复制一份作为postgres
	告诉用户initdb执行结束

以上就是initdb的执行过程。很清晰也很简单。其中最重要的是initialize_data_directory()函数。正是该函数一步步地建立起了数据库的基本元素。因此,出问题的地方就在这个函数"内部"。

聪明的朋友可能看出来了,我在上面打了两处"★"。是的,没有错,正是这两块代码的问题,哦不,确切的说是sql的问题。

下面来说。

我们可爱的开发人员在system_views.sql中加入了以下两行(询问发现是客户希望使用orafce功能,但又不想手动创建,想"透明"地使用):

CREATE EXTENSION orafce;
SET search_path TO DEFAULT;

我们知道orafce的初始化引用了 information_schema.columns这个视图,而这个视图在information_schema.sql中被创建。

而由上面的initdb执行过程可以看到:system_views.sql的执行顺序在information_schema.sql之前,也是就说:
我们在初始化 orafce的时候,information_schema.columns还不存在,因此,会在执行system_views.sql时,报以上错误。

知道了原因就很好改了,只要保证加入的那两行sql代码在创建information_schema之后即可~

以上

posted @ 2018-01-30 23:01  非我在  阅读(1102)  评论(0编辑  收藏  举报