PG_DUMP 源码简析
pg_dump是一个用于备份PostgreSQL 数据库的工具。它甚至可以在数据库正在使用的时候进行完整一致的备份。pg_dump并不阻塞其它用户对数据库的访问(读或者写)。
转储格式可以是一个脚本或者归档文件。脚本转储的格式是纯文本,它包含许多 SQL 命令,这些 SQL 命令可以用于重建该数据库并将之恢复到保存成脚本的时候的状态。使用psql从这样的脚本中恢复。甚至可以用于在其它机器甚至是其它硬件体系的机器上重建该数据库,通过对脚本进行一些修改,甚至可以在其它 SQL 数据库产品上重建该数据库。
归档文件格式必须和pg_restore一起使用重建数据库。它们允许pg_restore对恢复什么东西进行选择,或者甚至是在恢复之前对需要恢复的条目进行重新排序。归档文件也是设计成可以跨平台移植的。
如果一种候选文件格式和pg_restore结合,那么pg_dump就能提供一种灵活的归档和传输机制。 pg_dump可以用于备份整个数据库,然后就可以使用 pg_restore检查这个归档和/或选择要恢复的数据库部分。最灵活的输出文件格式是"custom"(自定义)格式(-Fc)和 "directory"(目录)格式(-Fd)。它们允许对归档元素进行选取和重新排列,支持并行恢复并且缺省时是压缩的。 "directory"格式是唯一支持并行转储的格式。
1、 PG_DUMP 使用
pg_dump [connection-option...][option...] [dbname]
选项说明
dbname
将要转储的数据库名。如果没有声明这个参数,那么使用环境变量PGDATABASE。如果那个环境变量也没声明,那么使用发起连接的用户名。
-a/--data-only
只输出数据,不输出模式(数据定义)。转储表数据、大对象和序列值。
-b/--blobs
在转储中包含大对象。除非指定了--schema, --table, --schema-only开关,否则这是默认行为。因此-b 开关仅用于在选择性转储的时候添加大对象。
-c/--clean
输出命令在输出创建数据库命令之前先清理(drop)该数据库对象。(如果任何对象在目标数据库中不存在,则转储可能生成一些无害的错误消息。)
-C/--create
以一条创建该数据库本身并且与这个数据库连接命令开头进行输出。如果是这种形式的脚本,那么你在运行脚本之前和目的安装中的哪个数据库连接就不重要了。如果也声明了 --clean,那么脚本在重新连接到数据库之前删除并重新创建目标数据库。
-E encoding /--encoding=encoding
以指定的字符集编码创建转储。缺省时,转储是按照数据库编码创建的。另外一个获取同样结果的方法是将PGCLIENTENCODING环境变量设置为期望的转储编码。
-f file /--file=file
输出文件名。文件基础输出格式时可以省略这个参数,这种情况下使用标准输出。但是,在声明目标目录而不是文件时必须给出目录输出格式。在这种情况下,目录通过pg_dump创建并且必须之前不存在。
-F format/--format=format
选择输出的格式。format可以是下列之一:
p
plain
纯文本SQL脚本文件(缺省)。
c
custom
适合输入到pg_restore里的自定义格式归档。加上目录输出格式,这是最灵活的格式,它允许在转储期间对已归档的条目进行手动选择和重新排列。这个格式缺省的时候是压缩的。
d
directory
适合输入到pg_restore里的目录格式归档。这将创建一个目录,该目录包含一个为每个被转储的表和二进制大对象的文件,加上一个号称目录的文件,该文件以pg_restore可读的机器可读格式描述转储的对象。目录格式归档可以用标准Unix工具操作;例如,在非压缩归档中的文件可以用 gzip工具压缩。这个格式缺省的时候是压缩的,并且也支持并行转储。
t
tar
适合输入到pg_restore里的tar归档文件。 tar格式兼容目录格式;提取tar格式归档产生一个有效的目录格式归档。不过, tar格式不支持压缩并且限制单独的表为8 GB。还有,表数据条目的相关顺序在转储期间不能更改。
-j njobs /--jobs=njobs
通过同时转储njobs表并行运行转储。该选项减少了转储的时间,但是也增加了数据库服务器的负载。可以只将这个选项用于目录输出格式,因为这是多进程可以同时写它们的数据的唯一的输出格式。
pg_dump将打开njobs + 1个到数据库的连接,所以使用时需要根据max_connections值进行设置,不能大于该值。
-n schema / --schema=schema
只转储匹配schema的模式内容,包括模式本身以及其中包含的对象。如果没有声明这个选项,所有目标数据库中的非系统模式都会被转储出来。可以使用多个-n选项指定多个模式。同样,schema参数将按照psql 的\d命令的规则被解释为匹配模式,因此可以使用通配符匹配多个模式。在使用通配符的时候,最好用引号进行界定,以防止 shell 将通配符进行扩展。
-N schema /--exclude-schema=schema
不转储任何匹配schema的模式内容。模式匹配规则与-n完全相同。可以指定多个-N以排除多种匹配的模式。
如果同时指定了-n和-N,那么将只转储匹配-n 但不匹配-N的模式。如果出现-N但是不出现-n,那么匹配-N的模式将不会被转储。
-o /--oids
作为数据的一部分,为每个表都输出对象标识(OIDs)。如果你的应用需要OID字段的话(比如在外键约束中用到),那么使用这个选项。否则,不应该使用这个选项。
-O /--no-owner
不把对象的所有权设置为对应源数据库。pg_dump默认发出 ALTER OWNER或SET SESSION AUTHORIZATION 语句以设置创建的数据库对象的所有权。如果这些脚本将来没有被超级用户 (或者拥有脚本中全部对象的用户)运行的话将会失败。-O 选项就是为了让该脚本可以被任何用户恢复并且将脚本中对象的所有权赋予该选项指定的用户。
-s/--schema-only
只输出对象定义(模式),不输出数据。
-S username/--superuser=username
指定关闭触发器时需要用到的超级用户名。它只有使用了--disable-triggers 的时候才有影响。一般情况下最好不要输入这个参数,而是用超级用户启动生成的脚本。
-t table/--table=table
只转储出匹配table的表(或视图、序列、外表)。可以使用多个-t选项匹配多个表。同样,table 参数将按照psql的\d命令的规则被解释为匹配模式,因此可以使用通配符匹配多个模式。在使用通配符的时候,最好用引号进行界定,以防止 shell 将通配符进行扩展。
使用了-t之后,-n和-N选项就失效了。因为被-t 选中的表将无视-n和-N选项而被转储,同时除了表之外的其他对象不会被转储。
-T table/--exclude-table=table
不要转储任何匹配table模式的表。模式匹配规则与-t完全相同。可以指定多个-T以排除多种匹配的表。
如果同时指定了-t和-T,那么将只转储匹配-t 但不匹配-T的表。如果出现-T但是不出现-t,那么匹配 -T的表将不会被转储。
-v/--verbose
指定冗余模式。这样将令pg_dump 输出详细的对象评注以及转储文件的启停时间和进度信息到标准错误上。
-V/--version
显示pg_dump版本
-x/--no-privileges --no-acl
禁止转储访问权限(grant/revoke 命令)。
-Z 0..9
--compress=0..9
指定要使用的压缩级别。0表示不压缩。对于自定义归档格式,指定单个表数据段的压缩,并且缺省是中等水平的压缩。对于纯文本输出,设置非零压缩级别会压缩整个输出文件,就像通过gzip反馈回来一样;但是缺省是不压缩的。 tar归档模式当前不支持压缩。
--binary-upgrade
此选项用于在线升级工具。不建议也不支持用于其他目的。该选项的行为可能会在将来的版本中改变。
--column-inserts
--attribute-inserts
把数据转储为带有明确字段名的INSERT命令 (INSERTINTO table (column, ...) VALUES ...)。这样会导致恢复非常缓慢,它主要用于制作那种可以用于其它非 PostgreSQL数据库的转储。由于这个选项为每条记录都生成一条命令,因此如果其中某一行命令出错,那么将仅有该行数据丢失,而不是整个表的数据丢失。
--disable-dollar-quoting
这个选项关闭使用美元符界定函数体。强制它们用 SQL 标准的字符串语法的引号包围。
--disable-triggers
这个选项只是和创建仅有数据的转储相关。它告诉pg_dump 包含在恢复数据时临时关闭目标表上触发器的命令。如果在表上有参照完整性检查或者其它触发器,而恢复数据的时候不想重载他们,那么就应该使用这个选项。
--exclude-table-data=table
不要转储任何匹配table模式的表。模式匹配规则与-t完全相同。可以给出多个--exclude-table-data 以排除多个匹配的表。当你需要指定表的定义时该选项是有用的,即使你不需要表里面的数据。
--inserts
将数据输出为的INSERT命令(而不是COPY)。这样会导致恢复非常缓慢。这个选项主要用于制作那种可以用于其它非PostgreSQL数据库的转储。
--lock-wait-timeout=timeout
在转储开始的时候不要等待请求一个共享表锁。相反,如果无法在指定的 timeout内锁住表则失败。 timeout可以用任意SET statement_timeout接受的格式声明
--no-security-labels
不转储安全标签。
--no-synchronized-snapshots
该选项允许在9.2以前的服务器上运行pg_dump -j,参阅-j 参数的文档获取更多信息。
--no-tablespaces
忽略表空间
--no-unlogged-table-data
不要转储未记录表的内容。该选项对于表定义(模式)是否转储没有影响;它只阻止转储表的数据。当从备用服务器转储时,未记录表中的数据总是排除。
--quote-all-identifiers
强制给所有标识符加上引号。这在转储一个数据库到一个可能引入了额外关键字的新版本中时可能是有用的。
--section=sectionname
只转储命名的章节。该章节名可以是pre-data, data, 或post-data。可以多次声明这个选项以选择多个章节。缺省是转储所有章节。
数据章节包含实际的表数据、大对象内容和序列值。原始数据项包含索引、触发器、规则和约束(除了验证检查约束)的定义。之前的数据项包含所有其他数据定义项。
--serializable-deferrable
为转储使用一个可串行化的事务,以保证使用的快照和稍后的数据库状态一致;做这些是通过等待事务流中的一个点,该点没有异常会出现,所以不会有转储失败或导致其他事务 serialization_failure而回滚的风险。获取关于事务隔离和并发控制的更多信息。
--use-set-session-authorization
输出符合 SQL 标准的SET SESSIONAUTHORIZATION命令而不是ALTER OWNER 命令来确定对象所有权。这样令转储更加符合标准,但是如果转储文件中的对象的历史有些问题,那么可能不能正确恢复。并且,使用SET SESSION AUTHORIZATION 的转储需要数据库超级用户的权限才能转储成功,而ALTER OWNER需要的权限则低得多。
-?
--help
显示关于pg_dump命令行参数的帮助。
控制数据库的连接参数:
-d dbname
--dbname=dbname
声明要连接的数据库名称。相当于在命令行中声明dbname 作为第一个非选项参数。
-h host
--host=host
指定运行服务器的主机名。如果数值以斜杠开头,则被用作到 Unix 域套接字的路径。缺省从PGHOST环境变量中获取(如果设置了的话),否则,尝试一个 Unix 域套接字连接。
-p port
--port=port
指定服务器正在侦听的 TCP 端口或本地 Unix 域套接字文件的扩展(描述符)。缺省使用PGPORT环境变量(如果设置了的话),否则,编译时的缺省值。
-U username
--username=username
连接的用户名。
-w
--no-password
从不发出密码提示问题。如果服务器要求密码认证并且密码不可用于其他意思如 .pgpass文件,则连接尝试将会失败。该选项在批量工作和不存在用户输入密码的脚本中很有帮助。
-W
--password
数据库连接时需要密码
--role=rolename
指定创建转储的角色名。这个选项导致连接到数据库之后 pg_dump 发出一个SET ROLE rolename命令。当认证的用户(通过-U指定)缺乏 pg_dump所需的权限时是很有用的,可以转变成有所需权限的角色。一些安装有反对作为超级用户直接登录的政策,使用这个选项允许转储不违反该政策。环境变量
在参数为设置时,获取环境变量值
PGDATABASE
PGHOST
PGOPTIONS
PGPORT
PGUSER
用例
将mydb数据库转储到一个 SQL 脚本文件:
$ pg_dump mydb > db.sql |
将上述脚本导入一个(新建的)数据库newdb:
$ psql -d newdb -f db.sql |
将数据库转储为自定义格式的归档文件
$ pg_dump -Fc mydb > db.dump |
将数据库转储为目录格式归档:
$ pg_dump -Fd mydb -f dumpdir |
将数据库转储为目录格式归档,并行5个worker工作:
$ 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 |
转储所有以east或west开头并以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 |
在-t等选项中指定大写字母或大小写混合的名字必须用双引号界定,否则将被自动转换为小写。但是因为双引号在 shell 中有特殊含义,所以必须将双引号再放进单引号中。这样一来,要转储一个大小写混合的表名,你就需要像下面这样:
$ pg_dump -t "\"MixedCaseName\"" mydb > mytab.sql |
2、 PG_DUMP流程图
3、 PG_DUMP 逻辑说明
3.1 、参数解析 & 参数冲突校验
1 –a(只备份数据) 与 –s (只备份模式) 冲突
2 -a(只备份数据) 与 –c (删除对象) 冲突
3 –inserts(insert 模式)与 –o(备份oid)冲突
4 --if-exists 与 –c 需同时存在
5 -j大于1时–F 为不能为d
3.2 、初始化备份文件格式和文件读取模式
文件格式定义:
typedefenum _archiveFormat
{
archUnknown = 0,
archCustom = 1,
archTar = 3,
archNull = 4,
archDirectory = 5
}ArchiveFormat;
通过枚举定义文件格式类型,分别对应4中不同的文件格式,分别对应4种不同的处理类型,处理过程参考pg_backup_custom.c,pg_backup_directory.c,pg_backup_null.c,pg_backup_tar.c。
文件读取模式定义:
typedefenum _archiveMode
{
archModeAppend,
archModeWrite,
archModeRead
}ArchiveMode;
通过枚举定义文件读写方式 分别为追加,写,读 三中模式。
根据参数-F、--format 初始化输出文件格式和模式
1 –F ,--format = a/append
文件格式:archNull 读取模式:archModeAppend
2 –F ,--format = c/custom
文件格式:archCustom 读取模式:archModeWrite
3 –F ,--format = d/directory
文件格式:archDirectory 读取模式:archModeWrite
4 –F ,--format = p/plain
文件格式:archNull 读取模式:archModeWrite
5 –F ,--format = t/tar
文件格式:archTar 读取模式:archModeWrite
3.3 、创建全局变量结构Archive
Archive结构
typedefstruct Archive
{
DumpOptions *dopt; /* 备份参数 */
RestoreOptions *ropt; /* 恢复参数*/
int verbose;
char *remoteVersionStr; /* 备份的数据库版本 str类型*/
int remoteVersion; /* 备份的数据库版本 int类型 */
bool isStandby; /*是否在进行热备份 */
int minRemoteVersion; /* 兼容最低的数据库版本 */
int maxRemoteVersion;
int numWorkers; /* 并发个数 */
char *sync_snapshot_id; /*异步快照id
* operation */
/* info needed for string escaping */
int encoding; /* libpq code for client_encoding */
bool std_strings; /* standard_conforming_strings */
char *use_role; /* Issue SET ROLE to this */
/* error handling */
bool exit_on_error; /* whether to exit on SQL errors... */
int n_errors; /* number of errors (if no die) */
/* The rest is private */
}Archive;
ArchiveHandle结构:
struct_archiveHandle
{
Archive public; /* Public part of archive */
int version; /* Version of file */
char *archiveRemoteVersion; /* When reading an archive, the
* version of the dumped DB */
char *archiveDumpVersion; /* When reading an archive, the
* version of the dumper */
int debugLevel; /* Used for logging (currently onlyby
* --verbose) */
size_t intSize; /* Size of an integer in the archive*/
size_t offSize; /* Size of a file offset in thearchive -
* Added V1.7 */
ArchiveFormat format; /* Archive format */
sqlparseInfo sqlparse; /* state for parsing INSERT data */
time_t createDate; /* Date archive created */
/*
*Fields used when discovering header. A format can always get the
*previous read bytes from here...
*/
int readHeader; /* Used if file header has been readalready */
char *lookahead; /*Buffer used when reading header to discover
* format */
size_t lookaheadSize; /* Size of allocated buffer */
size_t lookaheadLen; /* Length of data in lookahead */
pgoff_t lookaheadPos; /* Current read position in lookahead buffer */
ArchiveEntryPtr ArchiveEntryPtr; /* Called for each metadata object */
StartDataPtr StartDataPtr; /* Called when table data is about to be
* dumped */
WriteDataPtr WriteDataPtr; /* Called to send some table data to the
* archive */
EndDataPtr EndDataPtr; /* Called when table data dump isfinished */
WriteBytePtr WriteBytePtr; /* Write a byte to output */
ReadBytePtr ReadBytePtr; /* Read a byte from an archive */
WriteBufPtr WriteBufPtr; /* Write a buffer of output to the archive*/
ReadBufPtr ReadBufPtr; /* Read a buffer of input from thearchive */
ClosePtr ClosePtr; /* Close the archive */
ReopenPtr ReopenPtr; /* Reopen the archive */
WriteExtraTocPtr WriteExtraTocPtr; /* Write extra TOC entry data
* associated with the current archive
* format */
ReadExtraTocPtr ReadExtraTocPtr; /* Read extr info associated with
* archie format */
PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */
PrintTocDataPtr PrintTocDataPtr;
StartBlobsPtr StartBlobsPtr;
EndBlobsPtr EndBlobsPtr;
StartBlobPtr StartBlobPtr;
EndBlobPtr EndBlobPtr;
SetupWorkerPtr SetupWorkerPtr;
WorkerJobDumpPtr WorkerJobDumpPtr;
WorkerJobRestorePtr WorkerJobRestorePtr;
ClonePtr ClonePtr; /* Clone format-specific fields */
DeClonePtr DeClonePtr; /* Clean up cloned fields */
CustomOutPtr CustomOutPtr; /* Alternative script output routine */
/* Stuff for direct DB connection */
char *archdbname; /*DB name *read* from archive */
trivalue promptPassword;
char *savedPassword; /* password for ropt->username, if known */
char *use_role;
PGconn *connection;
/* If connCancel isn't NULL, SIGINThandler will send a cancel */
PGcancel *volatile connCancel;
int connectToDB; /* Flag to indicate if direct DB connection is
* required */
ArchiverOutput outputKind; /* Flag for what we're currently writing */
bool pgCopyIn; /* Currently in libpq 'COPY IN'mode. */
int loFd; /* BLOB fd */
int writingBlob; /* Flag */
int blobCount; /* # of blobs restored */
char *fSpec; /*Archive File Spec */
FILE *FH; /*General purpose file handle */
void *OF;
int gzOut; /* Output file */
struct _tocEntry *toc; /* Header of circular list of TOCentries */
int tocCount; /* Number of TOC entries */
DumpId maxDumpId; /* largest DumpId among all TOC entries*/
/* arrays created after the TOC list iscomplete: */
struct _tocEntry **tocsByDumpId; /* TOCs indexed by dumpId */
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
int compression; /* Compression requested on open Possible
* values for compression: -1
* Z_DEFAULT_COMPRESSION 0 COMPRESSION_NONE
* 1-9 levels for gzip compression */
ArchiveMode mode; /* File mode - r or w */
void *formatData; /*Header data specific to file format */
/* these vars track state to avoidsending redundant SET commands */
char *currUser; /*current username, or NULL if unknown */
char *currSchema; /*current schema, or NULL */
char *currTablespace; /* current tablespace, orNULL */
bool currWithOids; /* current default_with_oids setting */
void *lo_buf;
size_t lo_buf_used;
size_t lo_buf_size;
int noTocComments;
ArchiverStage stage;
ArchiverStage lastErrorStage;
struct _tocEntry *currentTE;
struct _tocEntry *lastErrorTE;
};
ArchiveHandle结构第一个元素为Archive,可以看做是Archive结构的继承,在保存有Archive成员结构指针的情况下,可以通过指针互转来实现结构的转换,此使用方式类似与c++的多态。
初始化 Archive结构变量
Archive*
CreateArchive(constchar *FileSpec, const ArchiveFormat fmt,
const int compression, ArchiveMode mode,SetupWorkerPtr setupDumpWorker)
函数CreateArchive 通过创建并初始化ArchiveHandle 变量,返回一个Archive 变量。
FileSpec需要输出的文件名。
Fmt 文件格式
Compression压缩级别
Mode 文件读取方式
setupDumpWorker多任务启动函数指针。
设置参数指针到archive.dopt
3.4 、初始化数据库连接
3.5 、获取schema oid(ex,in)
如果参数中-n, --schema,-N, --exclude-schema ,不空,则通过nspname查询pg_namespace 获取其oid,在后面获取模式对象时进行过滤。
3.6 、获取表 oid(ex,in)
如果参数中 -t,--table ,-T, --exclude-table 不空,则通过relname 查询pg_class表,获取其oid
SELECT c.oid FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind in ('r', 'S', 'v', 'm', 'f','P')
3.7 、获取需要备份表数据的表oid
如果参数中 --exclude-table-data不空 ,则通过relname 查询需要忽略器数据的表对象oid。
3.8 、获取系统所有对象
函数原型: TableInfo *getSchemaData(Archive *fout, int *numTablesPtr)
返回TableInfo数组对象 以及 numTablesPtr表对象个数。
每一种数据对象都定义一种对应的数据结构类型,且都继承于DumpableObject结构,获取到的对象结构通过AssignDumpId函数将结构地址寄存于dumpIdMap数组中,通过objtype指定对象类型,以及通过dump成员变量设置是否需要进行备份。
Objtype类型:
typedefenum
{
/* When modifying this enum, updatepriority tables in pg_dump_sort.c! */
DO_NAMESPACE,
DO_EXTENSION,
DO_TYPE,
DO_SHELL_TYPE,
DO_FUNC,
DO_AGG,
DO_OPERATOR,
DO_ACCESS_METHOD,
DO_OPCLASS,
DO_OPFAMILY,
DO_COLLATION,
DO_CONVERSION,
DO_TABLE,
DO_ATTRDEF,
DO_INDEX,
DO_RULE,
DO_TRIGGER,
DO_CONSTRAINT,
DO_FK_CONSTRAINT, /* see note for ConstraintInfo */
DO_PROCLANG,
DO_CAST,
DO_TABLE_DATA,
DO_SEQUENCE_SET,
DO_DUMMY_TYPE,
DO_TSPARSER,
DO_TSDICT,
DO_TSTEMPLATE,
DO_TSCONFIG,
DO_FDW,
DO_FOREIGN_SERVER,
DO_DEFAULT_ACL,
DO_TRANSFORM,
DO_BLOB,
DO_BLOB_DATA,
DO_PRE_DATA_BOUNDARY,
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
DO_POLICY
}DumpableObjectType;
Dump备份级别:
#defineDUMP_COMPONENT_NONE (0)
#defineDUMP_COMPONENT_DEFINITION (1 <<0)
#defineDUMP_COMPONENT_DATA (1<< 1)
#defineDUMP_COMPONENT_COMMENT (1<< 2)
#defineDUMP_COMPONENT_SECLABEL (1<< 3)
#defineDUMP_COMPONENT_ACL (1<< 4)
#defineDUMP_COMPONENT_POLICY (1<< 5)
#defineDUMP_COMPONENT_USERMAP (1<< 6)
#defineDUMP_COMPONENT_ALL (0xFFFF)
1 获取扩展对象:pg_extensions
ExtensionInfo*getExtensions(Archive *fout, int *numExtensions)
2 获取扩展成员pg_depend
Void getExtensionMembership(Archive *fout,ExtensionInfo extinfo[],
int numExtensions)
3 获取名空间pg_namespace
NamespaceInfo *getNamespaces(Archive *fout,int *numNamespaces)
4 获取表对象pg_class relkind in ('S','r','v','c','m','f','P')
TableInfo*getTables(Archive *fout, int *numTables)
5 获取队列
Void getOwnedSeqs(Archive *fout, TableInfotblinfo[], int numTables)
6 获取函数对象
FuncInfo *getFuncs(Archive *fout, int *numFuncs)
7 获取数据类型
TypeInfo *getTypes(Archive *fout, int*numTypes)
8 获取语言类型
ProcLangInfo *getProcLangs(Archive *fout, int*numProcLangs)
9 获取聚合函数
AggInfo*getAggregates(Archive *fout, int *numAggs)
10 获取操作符重载对象
OprInfo*getOperators(Archive *fout, int *numOprs)
11 获取索引对象
AccessMethodInfo*getAccessMethods(Archive *fout, int *numAccessMethods)
12 获取索引操作符类
OpclassInfo*getOpclasses(Archive *fout, int *numOpclasses)
13 操作符族
OpfamilyInfo*getOpfamilies(Archive *fout, int *numOpfamilies)
14 文本解析器
TSParserInfo*getTSParsers(Archive *fout, int *numTSParsers)
15 文本搜索模板
TSTemplateInfo*getTSTemplates(Archive *fout, int *numTSTemplates)
16 文本搜索字典
TSDictInfo*getTSDictionaries(Archive *fout, int *numTSDicts)
17 文本搜索配置
TSConfigInfo*getTSConfigurations(Archive *fout, int *numTSConfigs)
18 存储外部数据封装器
FdwInfo*getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
19 储外部服务器
ForeignServerInfo*getForeignServers(Archive *fout, int *numForeignServers)
20 新创建对象的初始化权限
DefaultACLInfo*getDefaultACLs(Archive *fout, int *numDefaultACLs)
21 排序规则
CollInfo*getCollations(Archive *fout, int *numCollations)
22 编码转换信息
ConvInfo*getConversions(Archive *fout, int *numConversions)
23 存储数据类型转换路径
CastInfo*getCasts(Archive *fout, int *numCasts)
24 pg_transform
TransformInfo*getTransforms(Archive *fout, int *numTransforms)
25 继承对象
InhInfo*getInherits(Archive *fout, int *numInherits)
26 分区表
PartInfo*getPartitions(Archive *fout, int *numPartitions)
27 时间触发器
EventTriggerInfo*getEventTriggers(Archive *fout, int *numEventTriggers)
28 处理扩展表
Void processExtensionTables(Archive*fout, ExtensionInfo extinfo[],
int numExtensions)
29 在tbinfo中扩展继承信息
staticvoid
flagInhTables(TableInfo*tblinfo, int numTables,
InhInfo *inhinfo, int numInherits)
30 为分区表指定分区范围,以及父表信息
staticvoid
flagPartitions(TableInfo*tblinfo, int numTables,
PartInfo *partinfo, int numPartitions)
31 获取表对象附加属性
void
getTableAttrs(Archive*fout, TableInfo *tblinfo, int numTables)
32 组织属性到tblinfo信息中
staticvoid
flagInhAttrs(DumpOptions*dopt, TableInfo *tblinfo, int numTables)
34 获取索引
void
getIndexes(Archive*fout, TableInfo tblinfo[], int numTables)
35 获取约束
void
getConstraints(Archive*fout, TableInfo tblinfo[], int numTables)
36 获取触发器
void
getTriggers(Archive*fout, TableInfo tblinfo[], int numTables)
37 获取规则
RuleInfo*
getRules(Archive*fout, int *numRules)
38 pg_policy策略
void
getPolicies(Archive*fout, TableInfo tblinfo[], int numTables)
40 获取分区主表定义信息
void
getTablePartitionKeyInfo(Archive*fout, TableInfo *tblinfo, int numTables)
表对象信息结构:
typedefstruct _tableInfo
{
/*
*These fields are collected for every table in the database.
*/
DumpableObject dobj;
char *rolname; /*name of owner, or empty string */
char *relacl;
char *rrelacl;
char *initrelacl;
char *initrrelacl;
char relkind;
char relpersistence;/* relation persistence */
bool relispopulated;/* relation is populated */
char relreplident; /* replica identifier */
char *reltablespace; /* relation tablespace */
char *reloptions; /*options specified by WITH (...) */
char *checkoption; /* WITH CHECK OPTION, if any */
char *toast_reloptions; /* WITH options for the TOAST table */
bool hasindex; /* does it have any indexes? */
bool hasrules; /* does it have any rules? */
bool hastriggers; /* does it have any triggers? */
bool rowsec; /* is row security enabled? */
bool forcerowsec; /* is row security forced? */
bool hasoids; /* does it have OIDs? */
uint32 frozenxid; /* table's relfrozenxid */
uint32 minmxid; /* table's relminmxid */
Oid toast_oid; /* toast table's OID, or 0 if none */
uint32 toast_frozenxid; /* toast table's relfrozenxid, if any */
uint32 toast_minmxid; /* toast table's relminmxid */
int ncheck; /* # of CHECK expressions */
char *reloftype; /* underlying type for typed table */
/* these two are set only if table is asequence owned by a column: */
Oid owning_tab; /* OID of table owning sequence */
int owning_col; /* attr # of column owning sequence*/
int relpages; /* table's size in pages (frompg_class) */
bool interesting; /* true if need to collect more data */
bool dummy_view; /* view's real definition must bepostponed */
bool postponed_def; /* matview must be postponed intopost-data */
/*
*These fields are computed only if we decide the table is interesting
*(it's either a table to dump, or a direct parent of a dumpable table).
*/
int numatts; /* number of attributes */
char **attnames; /*the attribute names */
char **atttypnames; /* attribute type names */
int *atttypmod; /*type-specific type modifiers */
int *attstattarget; /* attribute statistics targets */
char *attstorage; /*attribute storage scheme */
char *typstorage; /*type storage scheme */
bool *attisdropped; /* true if attr is dropped; don't dump it */
int *attlen; /*attribute length, used by binary_upgrade */
char *attalign; /*attribute align, used by binary_upgrade */
bool *attislocal; /*true if attr has local definition */
char **attoptions; /*per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
bool *notnull; /*NOT NULL constraints on attributes */
bool *inhNotNull; /*true if NOT NULL is inherited */
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
struct _constraintInfo *checkexprs; /*CHECK constraints */
char *partkeydef; /*partition key definition */
/*
*Stuff computed only for dumpable tables.
*/
int numParents; /* number of (immediate) parenttables */
struct _tableInfo **parents; /* TableInfos of immediate parents */
struct _tableDataInfo *dataObj; /* TableDataInfo, if dumping itsdata */
int numTriggers; /* number of triggers for table */
struct _triggerInfo *triggers; /* array of TriggerInfo structs */
struct _tableInfo *partitionOf; /* TableInfo for the partition parent */
char *partitiondef; /* partition key definition */
}TableInfo;
3.9 、查找需要备份数据的对象
当前节点完成需要备份数据的对象整理,主要数据对象通过遍历tblinfo数组信息获取,使用TableDataInfo结构保存需要备份数据的对象,不同的对象类型其objtype不一致。
TableDataInfo结构定义:
typedefstruct _tableDataInfo
{
DumpableObject dobj;
TableInfo *tdtable; /* link totable to dump */
bool oids; /* include OIDs in data? */
char *filtercond; /*WHERE condition to limit rows dumped */
}TableDataInfo;
不需要备份数据的对象:
1 --exclude-table-data 参数指定的表对象不需要备份数据
2 视图对象不需要备份数据
3 外部表对象不需要备份数据
4 分区主表不需要备份数据
5 unlogged permanent table 表对象并设置了 --no-unlogged-table-data不需要备份数据。
Objtype 类型处理:
1 雾化视图 dobj.objType =DO_REFRESH_MATVIEW
2 序列dobj.objType =DO_SEQUENCE_SET
3 其它dobj.objType =DO_TABLE_DATA
入口函数:
static void
getTableData(DumpOptions *dopt,TableInfo *tblinfo,int numTables, bool oids, charrelkind)
3.10 、排序对象数组
获取系统所有对象后,所有对象存放在DumpableObject **dobjs数组当中(保存对象地址),在每个对象创建前可能会依赖于其它对象,因此在处理对象时需要按照一定的顺序。
备份时排序通过两种方式完成:
1 对象类型->模式名->对象名->对象oid
2 对于依赖其它对象的对象,被依赖的对象将优先处理。
通过对象类型排序:
1 对象类型排序顺序
在对象数据生成时,每种对象定义对应的objtype,然后通过objtype作为下标定义其排序的优先顺序,objtype定义参考DumpableObjectType。
优先顺序定义:
staticconst int dbObjectTypePriority[] =
{
1, /*DO_NAMESPACE */
4, /*DO_EXTENSION */
5, /*DO_TYPE */
5, /*DO_SHELL_TYPE */
6, /*DO_FUNC */
7, /*DO_AGG */
8, /*DO_OPERATOR */
8, /* DO_ACCESS_METHOD*/
9, /*DO_OPCLASS */
9, /*DO_OPFAMILY */
3, /*DO_COLLATION */
11, /*DO_CONVERSION */
18, /*DO_TABLE */
20, /*DO_ATTRDEF */
28, /*DO_INDEX */
29, /*DO_RULE */
30, /* DO_TRIGGER */
27, /*DO_CONSTRAINT */
31, /*DO_FK_CONSTRAINT */
2, /*DO_PROCLANG */
10, /*DO_CAST */
23, /*DO_TABLE_DATA */
24, /*DO_SEQUENCE_SET */
19, /*DO_DUMMY_TYPE */
12, /*DO_TSPARSER */
14, /*DO_TSDICT */
13, /*DO_TSTEMPLATE */
15, /*DO_TSCONFIG */
16, /*DO_FDW */
17, /*DO_FOREIGN_SERVER */
32, /*DO_DEFAULT_ACL */
3, /*DO_TRANSFORM */
21, /*DO_BLOB */
25, /*DO_BLOB_DATA */
22, /*DO_PRE_DATA_BOUNDARY */
26, /*DO_POST_DATA_BOUNDARY */
33, /*DO_EVENT_TRIGGER */
34, /*DO_REFRESH_MATVIEW */
35 /*DO_POLICY */
};
2 objtype 一致时,优先根据模式名进行排序
cmpval = strcmp(obj1->namespace->dobj.name,
obj2->namespace->dobj.name)
3 模式名一致时 ,优先排序对象名
cmpval = strcmp(obj1->name,obj2->name);
4 模式名一致时,针对函数,操作运算符,对象属性等 分别根据参数类型,操作符类型,属性对象的顺序(num)进行排序
5 其它情况优先参考oid大小顺序。
排序入口函数:
staticint
DOTypeNameCompare(constvoid *p1, const void *p2)
通过依赖进行排序:
排序入口函数
staticbool
TopoSort(DumpableObject**objs,
int numObjs,
DumpableObject **ordering, /* output argument */
int *nOrdering) /* output argument */
3.11 、备份encoding,stdstring
3.12 、备份数据库
如果参数-n ,-t参数没有设置并且-a参数没有设置,生成创建数据库语法加入到输出链表中(输出链表在3.13中介绍)。
static voiddumpDatabase(Archive *fout)
3.13 、备份对象以及数据
对象备份根据前面获取到的对象信息,按照对象本身的性质生成对象创建语法,对象数据备份有copy 方式和insert语法方式,当设定参数--inserts 时,备份的数据以insert 语法的形式输出,否则以copy的形式输出。
对象备份和数据备份同样以DumpableObject的方式保存在对象数组当中,依次遍历排好序的对象,然后根据定义的objType 类型,按照类型分别处理对象的输出形式。
从上文中知道NamespaceInfo,ExtensionInfo等结构都继承于DumpableObject结构,以下处理时能按照需要的结构类型进行转换。处理逻辑如下:
staticvoid
dumpDumpableObject(Archive*fout, DumpableObject *dobj)
switch (dobj->objType)
{
case DO_NAMESPACE:
dumpNamespace(fout,(NamespaceInfo *) dobj);
break;
case DO_EXTENSION:
dumpExtension(fout,(ExtensionInfo *) dobj);
break;
case DO_TYPE:
dumpType(fout, (TypeInfo *)dobj);
break;
case DO_SHELL_TYPE:
dumpShellType(fout,(ShellTypeInfo *) dobj);
break;
case DO_FUNC:
dumpFunc(fout, (FuncInfo *)dobj);
break;
case DO_AGG:
dumpAgg(fout, (AggInfo *)dobj);
break;
case DO_OPERATOR:
dumpOpr(fout, (OprInfo *)dobj);
break;
case DO_ACCESS_METHOD:
dumpAccessMethod(fout,(AccessMethodInfo *) dobj);
break;
case DO_OPCLASS:
dumpOpclass(fout,(OpclassInfo *) dobj);
break;
case DO_OPFAMILY:
dumpOpfamily(fout,(OpfamilyInfo *) dobj);
break;
case DO_COLLATION:
dumpCollation(fout,(CollInfo *) dobj);
break;
case DO_CONVERSION:
dumpConversion(fout,(ConvInfo *) dobj);
break;
case DO_TABLE:
dumpTable(fout, (TableInfo*) dobj);
break;
case DO_ATTRDEF:
dumpAttrDef(fout,(AttrDefInfo *) dobj);
break;
case DO_INDEX:
dumpIndex(fout, (IndxInfo*) dobj);
break;
case DO_REFRESH_MATVIEW:
refreshMatViewData(fout,(TableDataInfo *) dobj);
break;
case DO_RULE:
dumpRule(fout, (RuleInfo *)dobj);
break;
case DO_TRIGGER:
dumpTrigger(fout,(TriggerInfo *) dobj);
break;
case DO_EVENT_TRIGGER:
dumpEventTrigger(fout,(EventTriggerInfo *) dobj);
break;
case DO_CONSTRAINT:
dumpConstraint(fout,(ConstraintInfo *) dobj);
break;
case DO_FK_CONSTRAINT:
dumpConstraint(fout,(ConstraintInfo *) dobj);
break;
case DO_PROCLANG:
dumpProcLang(fout,(ProcLangInfo *) dobj);
break;
case DO_CAST:
dumpCast(fout, (CastInfo *)dobj);
break;
case DO_TRANSFORM:
dumpTransform(fout,(TransformInfo *) dobj);
break;
case DO_SEQUENCE_SET:
dumpSequenceData(fout,(TableDataInfo *) dobj);
break;
case DO_TABLE_DATA:
dumpTableData(fout,(TableDataInfo *) dobj);
break;
case DO_DUMMY_TYPE:
/* table rowtypes and arraytypes are never dumped separately */
break;
case DO_TSPARSER:
dumpTSParser(fout,(TSParserInfo *) dobj);
break;
case DO_TSDICT:
dumpTSDictionary(fout,(TSDictInfo *) dobj);
break;
case DO_TSTEMPLATE:
dumpTSTemplate(fout,(TSTemplateInfo *) dobj);
break;
case DO_TSCONFIG:
dumpTSConfig(fout,(TSConfigInfo *) dobj);
break;
caseDO_FDW:
dumpForeignDataWrapper(fout,(FdwInfo *) dobj);
break;
case DO_FOREIGN_SERVER:
dumpForeignServer(fout,(ForeignServerInfo *) dobj);
break;
case DO_DEFAULT_ACL:
dumpDefaultACL(fout,(DefaultACLInfo *) dobj);
break;
case DO_BLOB:
dumpBlob(fout, (BlobInfo *)dobj);
break;
case DO_BLOB_DATA:
if (dobj->dump &DUMP_COMPONENT_DATA)
ArchiveEntry(fout,dobj->catId, dobj->dumpId,
dobj->name, NULL, NULL, "",
false, "BLOBS", SECTION_DATA,
"", "", NULL,
NULL, 0,
dumpBlobs, NULL);
break;
case DO_POLICY:
dumpPolicy(fout,(PolicyInfo *) dobj);
break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing todo */
break;
}
}
组织好的语法使用双向链表存储,链表头在ArchiveHandle结构中进行声明,每处理一个对象,判别dobj.dump值,处理是否需要备份逻辑,然后将需要备份的对象添加到链表当中,输出时从链表中获取。
链表结构:
struct_tocEntry
{
struct _tocEntry *prev;
struct _tocEntry *next;
CatalogId catalogId;
DumpId dumpId;
teSection section;
bool hadDumper; /* Archiver was passed a dumperroutine (used
* in restore) */
char *tag; /*index tag */
char *namespace; /*null or empty string if not in a schema */
char *tablespace; /*null if not in a tablespace; empty string
* means use database default */
char *owner;
bool withOids; /* Used only by "TABLE" tags */
char *desc;
char *defn;
char *dropStmt;
char *copyStmt;
DumpId *dependencies; /* dumpIds of objects this one depends on */
int nDeps; /* number of dependencies */
DataDumperPtr dataDumper; /* Routine to dump data for object */
void *dataDumperArg; /* Arg for above routine */
void *formatData; /*TOC Entry data specific to file format */
/* working state while dumping/restoring*/
teReqs reqs; /* do we need schema and/or dataof object */
bool created; /* set for DATA member if TABLE wascreated */
/* working state (needed only forparallel restore) */
struct _tocEntry *par_prev; /* list linksfor pending/ready items; */
struct _tocEntry *par_next; /* these areNULL if not in either list */
int depCount; /* number of dependencies not yetrestored */
DumpId *revDeps; /*dumpIds of objects depending on this one */
int nRevDeps; /* number of such dependencies */
DumpId *lockDeps; /*dumpIds of objects this one needs lock on */
int nLockDeps; /* number of such dependencies */
};
链表输出入库函数:
void
ArchiveEntry(Archive*AHX, //ArchiveHandle 备份输出总结构地址
CatalogId catalogId,// 对象id,
DumpId dumpId, //对象数组下表
const char *tag,//对象名
const char *namespace,//对象所属模式
const char *tablespace,//表空间
const char *owner, //用户名
boolwithOids,//是否备份oid
const char *desc,//备份对象的类型名
teSection section,// teSection
const char *defn,//对象创建语法
const char *dropStmt, //对象清除语法
constchar *copyStmt,//对象数据备份语法
const DumpId *deps, int nDeps,
DataDumperPtr dumpFn, void *dumpArg)
3.14 、输出备份文件
备份文件的输出根据备份参数设置的不同,使用函数指针,实现在统一过程当中,实现不同文件类型的操作,指针的初始化在3.3 节中实现:
switch (AH->format)
{
case archCustom:
InitArchiveFmt_Custom(AH);
break;
case archNull:
InitArchiveFmt_Null(AH);
break;
case archDirectory:
InitArchiveFmt_Directory(AH);
break;
case archTar:
InitArchiveFmt_Tar(AH);
break;
default:
exit_horribly(modulename,"unrecognized file format \"%d\"\n", fmt);
}
其中InitArchiveFmt_Custom 在pg_backup_custom.c文件中实现, InitArchiveFmt_Null 在pg_backup_null.c中实现,InitArchiveFmt_Directory在 pg_backup_directory.c中实现 ,InitArchiveFmt_Tar 在pg_backup_tar.c中实现。
1 普通文本格式文件生成在
Void RestoreArchive(Archive *AHX) 函数中完成,依次输出注释,环境设置,对象生成语法到文件当中。
2 其它格式输出按照占位的方式输出 ,除了输出文件头和文件内容外,其它不同操作实现各不相同 参考如下:
Void CloseArchive(Archive *AHX) 函数中完成,实际通过函数指针调用各自格式的函数。除archNull格式,其它文件输出都包含文件头和文件内容两部分。具体参考各自文件的staticvoid
_CloseArchive(ArchiveHandle *AH) 函数。
文件头输出格式一致,包含PGDMP,版本信息,备份时间等信息,具体如下:
void
WriteHead(ArchiveHandle*AH)
{
struct tm crtm;
(*AH->WriteBufPtr) (AH,"PGDMP", 5); /* Magiccode */
(*AH->WriteBytePtr) (AH,ARCHIVE_MAJOR(AH->version));
(*AH->WriteBytePtr) (AH,ARCHIVE_MINOR(AH->version));
(*AH->WriteBytePtr) (AH,ARCHIVE_REV(AH->version));
(*AH->WriteBytePtr) (AH,AH->intSize);
(*AH->WriteBytePtr) (AH,AH->offSize);
(*AH->WriteBytePtr) (AH,AH->format);
WriteInt(AH, AH->compression);
crtm =*localtime(&AH->createDate);
WriteInt(AH, crtm.tm_sec);
WriteInt(AH, crtm.tm_min);
WriteInt(AH, crtm.tm_hour);
WriteInt(AH, crtm.tm_mday);
WriteInt(AH, crtm.tm_mon);
WriteInt(AH, crtm.tm_year);
WriteInt(AH, crtm.tm_isdst);
WriteStr(AH, PQdb(AH->connection));
WriteStr(AH, AH->public.remoteVersionStr);
WriteStr(AH, PG_VERSION);
}
文件内容输出:
for (te =AH->toc->next;te != AH->toc;te = te->next)
{
if ((te->reqs & (REQ_SCHEMA |REQ_DATA | REQ_SPECIAL)) == 0)
continue;
WriteInt(AH,te->dumpId);
WriteInt(AH,te->dataDumper ? 1 :0);
/* OIDis recorded as a string for historical reasons */
sprintf(workbuf,"%u", te->catalogId.tableoid);
WriteStr(AH,workbuf);
sprintf(workbuf,"%u", te->catalogId.oid);
WriteStr(AH,workbuf);
WriteStr(AH,te->tag);
WriteStr(AH,te->desc);
WriteInt(AH,te->section);
WriteStr(AH,te->defn);
WriteStr(AH,te->dropStmt);
WriteStr(AH,te->copyStmt);
WriteStr(AH,te->namespace);
WriteStr(AH,te->tablespace);
WriteStr(AH,te->owner);
WriteStr(AH,te->withOids ? "true" : "false");
/* Dumplist of dependencies */
for (i = 0;i < te->nDeps;i++)
{
sprintf(workbuf,"%d", te->dependencies[i]);
WriteStr(AH,workbuf);
}
WriteStr(AH,NULL); /*Terminate List */
if (AH->WriteExtraTocPtr)
(*AH->WriteExtraTocPtr) (AH,te);
}