解读PolarDB PostgreSQL 15 bootstrap.c
解读PolarDB PostgreSQL 15 bootstrap.c
1 bootstrap 过程概括:
“bootstrap”
指的是 PostgreSQL 的引导过程,也就是创建初始数据库的过程。
“Creates initial template database via initdb”
Creates initial template database:创建初始的模板数据库。模板数据库是新数据库的基础,所有新数据库都是从模板数据库复制而来。via initdb:通过initdb工具来创建。initdb是 PostgreSQL 提供的一个命令行工具,用于初始化一个新的 PostgreSQL 数据库集群。
“Because PostgreSQL requires access to system tables for almost every operation, getting those system tables in place is a problem.”
Because PostgreSQL requires access to system tables for almost every operation:因为 PostgreSQL 几乎所有的操作都需要访问系统表。系统表存储了数据库的元数据,例如表结构、用户权限等等。getting those system tables in place is a problem:如何创建这些系统表就成了一个问题。
“You can’t just create the tables and insert data into them in the normal way, because table creation and insertion requires the tables to already exist.”
You can't just create the tables and insert data into them in the normal way:你不能像创建普通表那样创建系统表,也不能像插入普通数据那样插入系统表的数据。because table creation and insertion requires the tables to already exist:因为创建表和插入数据本身就需要访问系统表,而此时系统表还不存在,这就形成了一个“先有鸡还是先有蛋”的问题。
“This code jams the data directly into tables using a special syntax used only by the bootstrap procedure.”
This code jams the data directly into tables:这段代码直接将数据“塞”到表中。using a special syntax used only by the bootstrap procedure:使用了一种特殊的语法,这种语法只在引导过程中使用。这种特殊语法绕过了通常的数据库操作,允许直接操作底层数据结构。
这段文字解释了 PostgreSQL 引导过程的必要性和特殊性。由于系统表是 PostgreSQL 运行的基础,但创建系统表又需要访问系统表,因此需要一种特殊的引导过程来创建初始的系统表。bootstrap.c 文件中的代码就是实现这个特殊引导过程的关键,它使用一种特殊的语法直接操作底层数据结构,从而创建初始的模板数据库。
简单来说,bootstrap 过程就像是在没有工具的情况下,手工搭建一个最简陋的房子,然后才能用这个房子里的工具来建造更完善的房子。
2 initdb 和 bootstrap.c 的关系
在 PostgreSQL 的源码中,bootstrap.c 并不是直接通过 initdb 调用的,但它们之间存在密切的关系。以下是详细的解释:
1. initdb 的作用
initdb 是 PostgreSQL 提供的一个工具,用于初始化一个新的数据库集群。它的主要任务包括:
创建数据库目录结构。 生成必要的系统表(如 pg_class、pg_attribute等)。初始化全局配置文件和默认数据。
initdb 是一个独立的可执行程序,通常由用户在安装 PostgreSQL 后手动运行。
2. bootstrap.c 的作用
bootstrap.c 是 PostgreSQL 源码中的一个核心模块,负责在数据库集群初始化时引导创建系统表。它的主要功能包括:
定义系统表的结构(如列名、数据类型等)。 生成系统表的初始数据。 调用底层的存储管理模块(如 storage.c)来实际创建这些表。
bootstrap.c 是 PostgreSQL 内部实现的一部分,而不是一个独立的可执行程序。
3. initdb 与 bootstrap.c 的关系
initdb 在初始化数据库集群时,会调用 PostgreSQL 的 postgres 可执行程序,并传递一个特殊的 --boot 参数。这个参数会触发 postgres 进入“引导模式”(bootstrap mode),在引导模式下,postgres 会调用 bootstrap.c 中的逻辑来创建系统表。
以下是调用关系的简化流程:
4. 关键代码片段
在 PostgreSQL 源码中,initdb 调用 postgres 的代码通常位于 initdb.c 中,类似于以下逻辑:
exec_prog = "postgres";
exec_args[0] = "--boot";
exec_args[1] = "-x";
exec_args[2] = "-X";
exec_args[3] = "-c";
exec_args[4] = "template1";
exec_args[5] = NULL;
exec_program(exec_prog, exec_args);
在 postgres 的 main.c 中,会检查 --boot 参数并进入引导模式:
if (strcmp(argv[1], "--boot") == 0) {
BootstrapModeMain();
}
BootstrapModeMain() 函数定义在 bootstrap.c 中,负责创建系统表。
5. 总结
** bootstrap.c**:是 PostgreSQL 内部用于创建系统表的模块。** initdb**:是一个独立的工具,用于初始化数据库集群,它会调用postgres --boot来触发bootstrap.c的逻辑。关系: initdb通过调用postgres --boot间接调用bootstrap.c,完成系统表的创建和数据库集群的初始化。
希望这个解释能帮助你理解 bootstrap.c 和 initdb 之间的关系!如果还有其他问题,欢迎继续提问。
3 bootstrap.c 代码解读
好的,我们来逐行解读 bootstrap.c 文件,我会尽量用通俗易懂的语言解释,并针对 C 语言初学者可能会遇到的问题进行说明。
文件头注释
/*-------------------------------------------------------------------------
*
* bootstrap.c
* routines to support running postgres in 'bootstrap' mode
* bootstrap mode is used to create the initial template database
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/bootstrap/bootstrap.c
*
*-------------------------------------------------------------------------
*/
这是一个多行注释,描述了文件的作用: bootstrap.c包含支持 PostgreSQL 在 "bootstrap" 模式下运行的例程。Bootstrap 模式用于创建初始的模板数据库。 版权信息,表明代码的版权归属。 IDENTIFICATION:标识文件的路径,src/backend/bootstrap/bootstrap.c。
头文件包含
#include"postgres.h"
#include<unistd.h>
#include<signal.h>
#include"access/genam.h"
#include"access/heapam.h"
#include"access/htup_details.h"
#include"access/tableam.h"
#include"access/toast_compression.h"
#include"access/xact.h"
#include"access/xlog_internal.h"
#include"bootstrap/bootstrap.h"
#include"catalog/index.h"
#include"catalog/pg_collation.h"
#include"catalog/pg_type.h"
#include"common/link-canary.h"
#include"libpq/pqsignal.h"
#include"miscadmin.h"
#include"nodes/makefuncs.h"
#include"pg_getopt.h"
#include"storage/bufmgr.h"
#include"storage/bufpage.h"
#include"storage/condition_variable.h"
#include"storage/ipc.h"
#include"storage/proc.h"
#include"tcop/tcopprot.h"
#include"utils/builtins.h"
#include"utils/fmgroids.h"
#include"utils/memutils.h"
#include"utils/rel.h"
#include"utils/relmapper.h"
#include是 C 语言的预处理指令,用于包含头文件。头文件中通常包含函数声明、结构体定义、宏定义等。postgres.h:PostgreSQL 的主头文件,包含 PostgreSQL 核心数据结构和函数的声明。<unistd.h>:包含unistd.h是为了使用 POSIX 操作系统 API,例如unistd.h中声明的getopt函数,用于解析命令行参数。<signal.h>:包含signal.h是为了使用信号处理机制,例如signal函数,用于注册信号处理函数。access/...:包含访问方法相关的头文件,如堆表访问、索引访问等。catalog/...:包含系统目录相关的头文件,如pg_type(数据类型)、pg_collation(排序规则)等。storage/...:包含存储管理相关的头文件,如缓冲区管理、页面管理等。utils/...:包含实用工具相关的头文件,如内存管理、关系管理等。bootstrap/bootstrap.h: 包含本模块的头文件,通常包含本模块对外提供的函数声明。
全局变量
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
staticvoidCheckerModeMain(void);
staticvoidbootstrap_signals(void);
static Form_pg_attribute AllocateAttribute(void);
staticvoidpopulate_typ_list(void);
static Oid gettype(char *type);
staticvoidcleanup(void);
/* ----------------
* global variables
* ----------------
*/
Relation boot_reldesc; /* current relation descriptor */
Form_pg_attribute attrtypes[MAXATTR]; /* points to attribute info */
int numattr; /* number of attributes for cur. rel */
uint32 bootstrap_data_checksum_version = 0;:一个无符号 32 位整数,用于指定 bootstrap 数据的校验和版本。0表示不使用校验和。static void CheckerModeMain(void);:声明了一个静态函数CheckerModeMain,该函数没有参数,返回void(即不返回任何值)。static关键字表示该函数只能在本文件中访问。static void bootstrap_signals(void);:声明了一个静态函数bootstrap_signals,用于设置 bootstrap 过程中的信号处理。static Form_pg_attribute AllocateAttribute(void);:声明了一个静态函数AllocateAttribute,用于分配Form_pg_attribute结构体的内存。static void populate_typ_list(void);:声明了一个静态函数populate_typ_list,用于填充类型列表。static Oid gettype(char *type);:声明了一个静态函数gettype,用于根据类型名称获取类型 OID。static void cleanup(void);:声明了一个静态函数cleanup,用于清理 bootstrap 过程中的资源。Relation boot_reldesc;:一个Relation类型的全局变量,表示当前打开的关系(表)的描述符。Form_pg_attribute attrtypes[MAXATTR];:一个Form_pg_attribute类型的数组,用于存储当前关系的属性(列)信息。MAXATTR是一个宏,定义了最大属性数量。int numattr;:一个整数,表示当前关系的属性数量。
类型信息结构体
/*
* Basic information associated with each type. This is used before
* pg_type is filled, so it has to cover the datatypes used as column types
* in the core "bootstrapped" catalogs.
*
* XXX several of these input/output functions do catalog scans
* (e.g., F_REGPROCIN scans pg_proc). this obviously creates some
* order dependencies in the catalog creation process.
*/
structtypinfo
{
char name[NAMEDATALEN];
Oid oid;
Oid elem;
int16 len;
bool byval;
char align;
char storage;
Oid collation;
Oid inproc;
Oid outproc;
};
staticconststructtypinfoTypInfo[] = {
{"bool", BOOLOID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid,
F_BOOLIN, F_BOOLOUT},
{"bytea", BYTEAOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
F_BYTEAIN, F_BYTEAOUT},
{"char", CHAROID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid,
F_CHARIN, F_CHAROUT},
{"int2", INT2OID, 0, 2, true, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
F_INT2IN, F_INT2OUT},
{"int4", INT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_INT4IN, F_INT4OUT},
{"float4", FLOAT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_FLOAT4IN, F_FLOAT4OUT},
{"name", NAMEOID, CHAROID, NAMEDATALEN, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, C_COLLATION_OID,
F_NAMEIN, F_NAMEOUT},
{"regclass", REGCLASSOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_REGCLASSIN, F_REGCLASSOUT},
{"regproc", REGPROCOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_REGPROCIN, F_REGPROCOUT},
{"regtype", REGTYPEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_REGTYPEIN, F_REGTYPEOUT},
{"regrole", REGROLEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_REGROLEIN, F_REGROLEOUT},
{"regnamespace", REGNAMESPACEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_REGNAMESPACEIN, F_REGNAMESPACEOUT},
{"text", TEXTOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
F_TEXTIN, F_TEXTOUT},
{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
F_TIDIN, F_TIDOUT},
{"xid", XIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_XIDIN, F_XIDOUT},
{"cid", CIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_CIDIN, F_CIDOUT},
{"pg_node_tree", PG_NODE_TREEOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
F_PG_NODE_TREE_IN, F_PG_NODE_TREE_OUT},
{"int2vector", INT2VECTOROID, INT2OID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_INT2VECTORIN, F_INT2VECTOROUT},
{"oidvector", OIDVECTOROID, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_OIDVECTORIN, F_OIDVECTOROUT},
{"_int4", INT4ARRAYOID, INT4OID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
F_ARRAY_IN, F_ARRAY_OUT},
{"_text", 1009, TEXTOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
F_ARRAY_IN, F_ARRAY_OUT},
{"_oid", 1028, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
F_ARRAY_IN, F_ARRAY_OUT},
{"_char", 1002, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
F_ARRAY_IN, F_ARRAY_OUT},
{"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
F_ARRAY_IN, F_ARRAY_OUT}
};
staticconstint n_types = sizeof(TypInfo) / sizeof(struct typinfo);
struct typinfo:定义了一个结构体,用于存储数据类型的基本信息。name:类型名称。oid:类型 OID(对象 ID)。elem:数组元素的类型 OID。len:类型长度。-1表示变长类型。byval:类型是否按值传递。align:类型对齐方式。storage:类型存储方式。collation: 排序规则 OID。inproc:类型输入函数 OID。outproc:类型输出函数 OID。TypInfo[]:一个typinfo结构体数组,存储了 PostgreSQL 内置数据类型的基本信息。这个数组在pg_type系统表被填充之前使用。n_types:一个整数,表示TypInfo数组中元素的数量。
类型映射结构体和全局变量
structtypmap
{/* a hack */
Oid am_oid;
FormData_pg_type am_typ;
};
static List *Typ = NIL; /* List of struct typmap* */
staticstructtypmap *Ap = NULL;
struct typmap:定义了一个结构体,用于存储类型 OID 和FormData_pg_type结构体。Typ:一个List类型的指针,用于存储typmap结构体的链表。这个链表用于缓存从pg_type系统表中读取的类型信息。NIL表示空链表。Ap:一个typmap结构体指针,指向当前正在使用的类型映射。
行数据和空值数组
static Datum values[MAXATTR]; /* current row's attribute values */
staticbool Nulls[MAXATTR];
values:一个Datum类型的数组,用于存储当前行的属性值。Datum是 PostgreSQL 中用于表示各种数据类型的通用类型。Nulls:一个bool类型的数组,用于标记当前行的属性值是否为空。true表示为空,false表示不为空。
无垃圾回收内存上下文
static MemoryContext nogc = NULL; /* special no-gc mem context */
nogc:一个MemoryContext类型的指针,指向一个特殊的内存上下文,用于存储不需要进行垃圾回收的数据。
索引列表结构体
/*
* At bootstrap time, we first declare all the indices to be built, and
* then build them. The IndexList structure stores enough information
* to allow us to build the indices after they've been declared.
*/
typedefstruct _IndexList
{
Oid il_heap;
Oid il_ind;
IndexInfo *il_info;
struct _IndexList *il_next;
} IndexList;
static IndexList *ILHead = NULL;
IndexList:定义了一个结构体,用于存储索引的信息。il_heap:堆表(即被索引的表)的 OID。il_ind:索引表的 OID。il_info:索引信息,包含索引的各种属性。il_next:指向下一个IndexList结构体的指针,用于形成链表。ILHead:一个IndexList类型的指针,指向索引列表的头部。
CheckerModeMain 函数
/*
* In shared memory checker mode, all we really want to do is create shared
* memory and semaphores (just to prove we can do it with the current GUC
* settings). Since, in fact, that was already done by
* CreateSharedMemoryAndSemaphores(), we have nothing more to do here.
*/
staticvoid
CheckerModeMain(void)
{
proc_exit(0);
}
CheckerModeMain:在共享内存检查模式下运行的函数。该函数的主要目的是创建共享内存和信号量,以验证当前的 GUC(Grand Unified Configuration)设置是否有效。 由于 CreateSharedMemoryAndSemaphores()函数已经完成了共享内存和信号量的创建,因此CheckerModeMain函数直接调用proc_exit(0)退出。
BootstrapModeMain 函数
/*
* The main entry point for running the backend in bootstrap mode
*
* The bootstrap mode is used to initialize the template database.
* The bootstrap backend doesn't speak SQL, but instead expects
* commands in a special bootstrap language.
*
* When check_only is true, startup is done only far enough to verify that
* the current configuration, particularly the passed in options pertaining
* to shared memory sizing, options work (or at least do not cause an error
* up to shared memory creation).
*/
void
BootstrapModeMain(int argc, char *argv[], bool check_only)
{
int i;
char *progname = argv[0];
int flag;
char *userDoption = NULL;
Assert(!IsUnderPostmaster);
InitStandaloneProcess(argv[0]);
/* Set defaults, to be overridden by explicit options below */
InitializeGUCOptions();
/* an initial --boot or --check should be present */
Assert(argc > 1
&& (strcmp(argv[1], "--boot") == 0
|| strcmp(argv[1], "--check") == 0));
argv++;
argc--;
while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1)
{
switch (flag)
{
case'B':
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case'D':
userDoption = pstrdup(optarg);
break;
case'd':
{
/* Turn on debugging for the bootstrap process. */
char *debugstr;
debugstr = psprintf("debug%s", optarg);
SetConfigOption("log_min_messages", debugstr,
PGC_POSTMASTER, PGC_S_ARGV);
SetConfigOption("client_min_messages", debugstr,
PGC_POSTMASTER, PGC_S_ARGV);
pfree(debugstr);
}
break;
case'F':
SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
break;
case'k':
bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION;
break;
case'r':
strlcpy(OutputFileName, optarg, MAXPGPATH);
break;
case'X':
{
int WalSegSz = strtoul(optarg, NULL, 0);
if (!IsValidWalSegSize(WalSegSz))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("-X requires a power of two value between 1 MB and 1 GB")));
SetConfigOption("wal_segment_size", optarg, PGC_INTERNAL,
PGC_S_DYNAMIC_DEFAULT);
}
break;
case'c':
case'-':
{
char *name,
*value;
ParseLongOption(optarg, &name, &value);
if (!value)
{
if (flag == '-')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("--%s requires a value",
optarg)));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("-c %s requires a value",
optarg)));
}
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
free(name);
if (value)
free(value);
break;
}
default:
write_stderr("Try \"%s --help\" for more information.\n",
progname);
proc_exit(1);
break;
}
}
if (argc != optind)
{
write_stderr("%s: invalid command-line arguments\n", progname);
proc_exit(1);
}
/* Acquire configuration parameters */
if (!SelectConfigFiles(userDoption, progname, false))
proc_exit(1);
/*
* Validate we have been given a reasonable-looking DataDir and change
* into it
*/
checkDataDir();
ChangeToDataDir();
CreateDataDirLockFile(false);
SetProcessingMode(BootstrapProcessing);
IgnoreSystemIndexes = true;
InitializeMaxBackends();
CreateSharedMemoryAndSemaphores();
/*
* XXX: It might make sense to move this into its own function at some
* point. Right now it seems like it'd cause more code duplication than
* it's worth.
*/
if (check_only)
{
SetProcessingMode(NormalProcessing);
CheckerModeMain();
abort();
}
/*
* Do backend-like initialization for bootstrap mode
*/
InitProcess();
BaseInit();
bootstrap_signals();
BootStrapXLOG();
/*
* To ensure that src/common/link-canary.c is linked into the backend, we
* must call it from somewhere. Here is as good as anywhere.
*/
if (pg_link_canary_is_frontend())
elog(ERROR, "backend is incorrectly linked to frontend functions");
InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, NULL);
/* Initialize stuff for bootstrap-file processing */
for (i = 0; i < MAXATTR; i++)
{
attrtypes[i] = NULL;
Nulls[i] = false;
}
/*
* Process bootstrap input.
*/
StartTransactionCommand();
boot_yyparse();
CommitTransactionCommand();
/*
* We should now know about all mapped relations, so it's okay to write
* out the initial relation mapping files.
*/
RelationMapFinishBootstrap();
/* Clean up and exit */
cleanup();
proc_exit(0);
}
BootstrapModeMain:是 bootstrap 模式的入口函数。-B:设置shared_buffers配置选项。-D:设置用户指定的配置目录。-d:设置调试级别。-F:禁用fsync。-k:启用数据校验和。-r:设置输出文件名。-X:设置 WAL 段大小。-c或--:设置其他配置选项。argc:命令行参数的数量。argv:命令行参数的字符串数组。check_only:一个布尔值,表示是否只进行检查而不执行实际的 bootstrap 过程。Assert(!IsUnderPostmaster);:断言,确保当前进程不是在 postmaster 进程下运行。InitStandaloneProcess(argv[0]);:初始化一个独立的进程。InitializeGUCOptions();:初始化 GUC 选项。Assert(argc > 1 && (strcmp(argv[1], "--boot") == 0 || strcmp(argv[1], "--check") == 0));:断言,确保命令行参数中包含--boot或--check选项。getopt循环:解析命令行参数。SelectConfigFiles(userDoption, progname, false):加载配置文件。checkDataDir();:检查数据目录。ChangeToDataDir();:切换到数据目录。CreateDataDirLockFile(false);:创建数据目录锁文件。SetProcessingMode(BootstrapProcessing);:设置处理模式为 bootstrap 模式。IgnoreSystemIndexes = true;:忽略系统索引。InitializeMaxBackends();:初始化最大后端数量。CreateSharedMemoryAndSemaphores();:创建共享内存和信号量。if (check_only):如果只进行检查,则调用CheckerModeMain()函数并退出。InitProcess();:初始化进程。BaseInit();:进行基本初始化。bootstrap_signals();:设置信号处理。BootStrapXLOG();:初始化 XLOG。if (pg_link_canary_is_frontend()) elog(ERROR, "backend is incorrectly linked to frontend functions");:检查后端是否错误地链接到前端函数。InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, NULL);:初始化 PostgreSQL。for (i = 0; i < MAXATTR; i++) { attrtypes[i] = NULL; Nulls[i] = false; }:初始化属性类型数组和空值数组。StartTransactionCommand();:开始事务。boot_yyparse();:解析 bootstrap 输入。CommitTransactionCommand();:提交事务。RelationMapFinishBootstrap();:完成关系映射。cleanup();:清理资源。proc_exit(0);:退出进程。
bootstrap_signals 函数
/*
* Set up signal handling for a bootstrap process
*/
staticvoid
bootstrap_signals(void)
{
Assert(!IsUnderPostmaster);
/*
* We don't actually need any non-default signal handling in bootstrap
* mode; "curl up and die" is a sufficient response for all these cases.
* Let's set that handling explicitly, as documentation if nothing else.
*/
pqsignal(SIGHUP, SIG_DFL);
pqsignal(SIGINT, SIG_DFL);
pqsignal(SIGTERM, SIG_DFL);
pqsignal(SIGQUIT, SIG_DFL);
}
bootstrap_signals:设置 bootstrap 进程的信号处理。Assert(!IsUnderPostmaster);:断言,确保当前进程不是在 postmaster 进程下运行。pqsignal(SIGHUP, SIG_DFL);:将SIGHUP信号的处理方式设置为默认。pqsignal(SIGINT, SIG_DFL);:将SIGINT信号的处理方式设置为默认。pqsignal(SIGTERM, SIG_DFL);:将SIGTERM信号的处理方式设置为默认。pqsignal(SIGQUIT, SIG_DFL);:将SIGQUIT信号的处理方式设置为默认。
boot_openrel 函数
/* ----------------
* boot_openrel
*
* Execute BKI OPEN command.
* ----------------
*/
void
boot_openrel(char *relname)
{
int i;
if (strlen(relname) >= NAMEDATALEN)
relname[NAMEDATALEN - 1] = '\0';
/*
* pg_type must be filled before any OPEN command is executed, hence we
* can now populate Typ if we haven't yet.
*/
if (Typ == NIL)
populate_typ_list();
if (boot_reldesc != NULL)
closerel(NULL);
elog(DEBUG4, "open relation %s, attrsize %d",
relname, (int) ATTRIBUTE_FIXED_PART_SIZE);
boot_reldesc = table_openrv(makeRangeVar(NULL, relname, -1), NoLock);
numattr = RelationGetNumberOfAttributes(boot_reldesc);
for (i = 0; i < numattr; i++)
{
if (attrtypes[i] == NULL)
attrtypes[i] = AllocateAttribute();
memmove((char *) attrtypes[i],
(char *) TupleDescAttr(boot_reldesc->rd_att, i),
ATTRIBUTE_FIXED_PART_SIZE);
{
Form_pg_attribute at = attrtypes[i];
elog(DEBUG4, "create attribute %d name %s len %d num %d type %u",
i, NameStr(at->attname), at->attlen, at->attnum,
at->atttypid);
}
}
}
boot_openrel:用于打开一个关系(表)。if (attrtypes[i] == NULL) attrtypes[i] = AllocateAttribute();:如果属性类型数组中对应的元素为空,则分配内存。memmove((char *) attrtypes[i], (char *) TupleDescAttr(boot_reldesc->rd_att, i), ATTRIBUTE_FIXED_PART_SIZE);:将属性信息从关系描述符复制到属性类型数组中。relname:要打开的关系的名称。if (strlen(relname) >= NAMEDATALEN) relname[NAMEDATALEN - 1] = '\0';:如果关系名称的长度超过NAMEDATALEN,则截断关系名称。if (Typ == NIL) populate_typ_list();:如果类型列表Typ为空,则填充类型列表。if (boot_reldesc != NULL) closerel(NULL);:如果当前已经打开了一个关系,则关闭该关系。boot_reldesc = table_openrv(makeRangeVar(NULL, relname, -1), NoLock);:打开关系,并将关系描述符存储在boot_reldesc中。numattr = RelationGetNumberOfAttributes(boot_reldesc);:获取关系的属性数量。for (i = 0; i < numattr; i++)循环:遍历关系的每个属性。
closerel 函数
/* ----------------
* closerel
* ----------------
*/
void
closerel(char *name)
{
if (name)
{
if (boot_reldesc)
{
if (strcmp(RelationGetRelationName(boot_reldesc), name) != 0)
elog(ERROR, "close of %s when %s was expected",
name, RelationGetRelationName(boot_reldesc));
}
else
elog(ERROR, "close of %s before any relation was opened",
name);
}
if (boot_reldesc == NULL)
elog(ERROR, "no open relation to close");
else
{
elog(DEBUG4, "close relation %s",
RelationGetRelationName(boot_reldesc));
table_close(boot_reldesc, NoLock);
boot_reldesc = NULL;
}
}
closerel:用于关闭一个关系(表)。name:要关闭的关系的名称。如果为NULL,则关闭当前打开的关系。如果指定了关系名称,则检查要关闭的关系是否是当前打开的关系。 table_close(boot_reldesc, NoLock);:关闭关系。boot_reldesc = NULL;:将boot_reldesc设置为NULL,表示当前没有打开任何关系。
DefineAttr 函数
/* ----------------
* DEFINEATTR()
*
* define a <field,type> pair
* if there are n fields in a relation to be created, this routine
* will be called n times
* ----------------
*/
void
DefineAttr(char *name, char *type, int attnum, int nullness)
{
Oid typeoid;
if (boot_reldesc != NULL)
{
elog(WARNING, "no open relations allowed with CREATE command");
closerel(NULL);
}
if (attrtypes[attnum] == NULL)
attrtypes[attnum] = AllocateAttribute();
MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE);
namestrcpy(&attrtypes[attnum]->attname, name);
elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type);
attrtypes[attnum]->attnum = attnum + 1;
typeoid = gettype(type);
if (Typ != NIL)
{
attrtypes[attnum]->atttypid = Ap->am_oid;
attrtypes[attnum]->attlen = Ap->am_typ.typlen;
attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
attrtypes[attnum]->attalign = Ap->am_typ.typalign;
attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
attrtypes[attnum]->attcompression = InvalidCompressionMethod;
attrtypes[attnum]->attcollation = Ap->am_typ.typcollation;
/* if an array type, assume 1-dimensional attribute */
if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0)
attrtypes[attnum]->attndims = 1;
else
attrtypes[attnum]->attndims = 0;
}
else
{
attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
attrtypes[attnum]->attlen = TypInfo[typeoid].len;
attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
attrtypes[attnum]->attalign = TypInfo[typeoid].align;
attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
attrtypes[attnum]->attcompression = InvalidCompressionMethod;
attrtypes[attnum]->attcollation = TypInfo[typeoid].collation;
/* if an array type, assume 1-dimensional attribute */
if (TypInfo[typeoid].elem != InvalidOid &&
attrtypes[attnum]->attlen < 0)
attrtypes[attnum]->attndims = 1;
else
attrtypes[attnum]->attndims = 0;
}
/*
* If a system catalog column is collation-aware, force it to use C
* collation, so that its behavior is independent of the database's
* collation. This is essential to allow template0 to be cloned with a
* different database collation.
*/
if (OidIsValid(attrtypes[attnum]->attcollation))
attrtypes[attnum]->attcollation = C_COLLATION_OID;
attrtypes[attnum]->attstattarget = -1;
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
{
attrtypes[attnum]->attnotnull = true;
}
elseif (nullness == BOOTCOL_NULL_FORCE_NULL)
{
attrtypes[attnum]->attnotnull = false;
}
else
{
Assert(nullness == BOOTCOL_NULL_AUTO);
/*
* Mark as "not null" if type is fixed-width and prior columns are
* likewise fixed-width and not-null. This corresponds to case where
* column can be accessed directly via C struct declaration.
*/
if (attrtypes[attnum]->attlen > 0)
{
int i;
/* check earlier attributes */
for (i = 0; i < attnum; i++)
{
if (attrtypes[i]->attlen <= 0 ||
!attrtypes[i]->attnotnull)
break;
}
if (i == attnum)
attrtypes[attnum]->attnotnull = true;
}
}
}
DefineAttr函数用于定义一个关系的属性(列)。BOOTCOL_NULL_FORCE_NOT_NULL:强制不允许为空。BOOTCOL_NULL_FORCE_NULL:强制允许为空。BOOTCOL_NULL_AUTO:自动确定是否允许为空。name:属性名称。type:属性类型。attnum:属性编号(从 0 开始)。nullness:指定属性是否允许为空。if (boot_reldesc != NULL):如果当前已经打开了一个关系,则报错,因为在创建关系时不允许打开其他关系。if (attrtypes[attnum] == NULL) attrtypes[attnum] = AllocateAttribute();:如果属性类型数组中对应的元素为空,则分配内存。MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE);:将属性类型数组中对应的元素清零。namestrcpy(&attrtypes[attnum]->attname, name);:设置属性名称。attrtypes[attnum]->attnum = attnum + 1;:设置属性编号。typeoid = gettype(type);:获取属性类型的 OID。if (Typ != NIL):如果类型列表Typ不为空,则从Typ中获取属性类型的信息。else:如果类型列表Typ为空,则从TypInfo数组中获取属性类型的信息。if (OidIsValid(attrtypes[attnum]->attcollation)):如果属性是排序规则相关的,则强制使用 C 排序规则。attrtypes[attnum]->attstattarget = -1;:设置统计目标。attrtypes[attnum]->attcacheoff = -1;:设置缓存偏移量。attrtypes[attnum]->atttypmod = -1;:设置类型修饰符。attrtypes[attnum]->attislocal = true;:设置属性为本地属性。根据 nullness的值,设置属性是否允许为空。如果 nullness为BOOTCOL_NULL_AUTO,则根据属性的类型和之前的属性是否允许为空,自动确定属性是否允许为空。
InsertOneTuple 函数
/* ----------------
* InsertOneTuple
*
* If objectid is not zero, it is a specific OID to assign to the tuple.
* Otherwise, an OID will be assigned (if necessary) by heap_insert.
* ----------------
*/
void
InsertOneTuple(void)
{
HeapTuple tuple;
TupleDesc tupDesc;
int i;
elog(DEBUG4, "inserting row with %d columns", numattr);
tupDesc = CreateTupleDesc(numattr, attrtypes);
tuple = heap_form_tuple(tupDesc, values, Nulls);
pfree(tupDesc); /* just free's tupDesc, not the attrtypes */
simple_heap_insert(boot_reldesc, tuple);
heap_freetuple(tuple);
elog(DEBUG4, "row inserted");
/*
* Reset null markers for next tuple
*/
for (i = 0; i < numattr; i++)
Nulls[i] = false;
}
InsertOneTuple函数用于插入一行数据。HeapTuple tuple;:声明一个HeapTuple类型的变量tuple,用于存储要插入的元组。TupleDesc tupDesc;:声明一个TupleDesc类型的变量tupDesc,用于存储元组描述符。int i;:声明一个整数变量i,用于循环。tupDesc = CreateTupleDesc(numattr, attrtypes);:创建一个元组描述符,描述元组的结构。tuple = heap_form_tuple(tupDesc, values, Nulls);:根据元组描述符、属性值数组和空值数组,创建一个元组。pfree(tupDesc);:释放元组描述符的内存。simple_heap_insert(boot_reldesc, tuple);:将元组插入到关系中。heap_freetuple(tuple);:释放元组的内存。for (i = 0; i < numattr; i++) Nulls[i] = false;:重置空值数组,为下一次插入做准备。
InsertOneValue 函数
/* ----------------
* InsertOneValue
* ----------------
*/
void
InsertOneValue(char *value, int i)
{
Oid typoid;
int16 typlen;
bool typbyval;
char typalign;
char typdelim;
Oid typioparam;
Oid typinput;
Oid typoutput;
AssertArg(i >= 0 && i < MAXATTR);
elog(DEBUG4, "inserting column %d value \"%s\"", i, value);
typoid = TupleDescAttr(boot_reldesc->rd_att, i)->atttypid;
boot_get_type_io_data(typoid,
&typlen, &typbyval, &typalign,
&typdelim, &typioparam,
&typinput, &typoutput);
values[i] = OidInputFunctionCall(typinput, value, typioparam, -1);
/*
* We use ereport not elog here so that parameters aren't evaluated unless
* the message is going to be printed, which generally it isn't
*/
ereport(DEBUG4,
(errmsg_internal("inserted -> %s",
OidOutputFunctionCall(typoutput, values[i]))));
}
InsertOneValue函数用于插入一个属性值。value:要插入的属性值的字符串表示。i:属性编号。AssertArg(i >= 0 && i < MAXATTR);:断言,确保属性编号在有效范围内。typoid = TupleDescAttr(boot_reldesc->rd_att, i)->atttypid;:获取属性类型的 OID。boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput);:获取属性类型的 I/O 信息。values[i] = OidInputFunctionCall(typinput, value, typioparam, -1);:调用输入函数将字符串表示的属性值转换为内部表示。ereport(DEBUG4, (errmsg_internal("inserted -> %s", OidOutputFunctionCall(typoutput, values[i]))));:输出调试信息,显示插入的属性值。
InsertOneNull 函数
/* ----------------
* InsertOneNull
* ----------------
*/
void
InsertOneNull(int i)
{
elog(DEBUG4, "inserting column %d NULL", i);
Assert(i >= 0 && i < MAXATTR);
if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull)
elog(ERROR,
"NULL value specified for not-null column \"%s\" of relation \"%s\"",
NameStr(TupleDescAttr(boot_reldesc->rd_att, i)->attname),
RelationGetRelationName(boot_reldesc));
values[i] = PointerGetDatum(NULL);
Nulls[i] = true;
}
InsertOneNull函数用于插入一个空值。i:属性编号。Assert(i >= 0 && i < MAXATTR);:断言,确保属性编号在有效范围内。if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull):如果属性不允许为空,则报错。values[i] = PointerGetDatum(NULL);:将属性值设置为NULL。Nulls[i] = true;:将空值数组中对应的元素设置为true,表示该属性值为空。
cleanup 函数
/* ----------------
* cleanup
* ----------------
*/
staticvoid
cleanup(void)
{
if (boot_reldesc != NULL)
closerel(NULL);
}
cleanup函数用于清理资源。if (boot_reldesc != NULL) closerel(NULL);:如果当前打开了一个关系,则关闭该关系。
populate_typ_list 函数
/* ----------------
* populate_typ_list
*
* Load the Typ list by reading pg_type.
* ----------------
*/
staticvoid
populate_typ_list(void)
{
Relation rel;
TableScanDesc scan;
HeapTuple tup;
MemoryContext old;
Assert(Typ == NIL);
rel = table_open(TypeRelationId, NoLock);
scan = table_beginscan_catalog(rel, 0, NULL);
old = MemoryContextSwitchTo(TopMemoryContext);
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_type typForm = (Form_pg_type) GETSTRUCT(tup);
structtypmap *newtyp;
newtyp = (struct typmap *) palloc(sizeof(struct typmap));
Typ = lappend(Typ, newtyp);
newtyp->am_oid = typForm->oid;
memcpy(&newtyp->am_typ, typForm, sizeof(newtyp->am_typ));
}
MemoryContextSwitchTo(old);
table_endscan(scan);
table_close(rel, NoLock);
}
populate_typ_list函数用于从pg_type系统表中加载类型信息,并将其存储在Typ列表中。Form_pg_type typForm = (Form_pg_type) GETSTRUCT(tup);:获取当前行的Form_pg_type结构体。newtyp = (struct typmap *) palloc(sizeof(struct typmap));:分配typmap结构体的内存。Typ = lappend(Typ, newtyp);:将typmap结构体添加到Typ列表中。newtyp->am_oid = typForm->oid;:设置typmap结构体的 OID。memcpy(&newtyp->am_typ, typForm, sizeof(newtyp->am_typ));:复制Form_pg_type结构体到typmap结构体中。Assert(Typ == NIL);:断言,确保Typ列表为空。rel = table_open(TypeRelationId, NoLock);:打开pg_type系统表。scan = table_beginscan_catalog(rel, 0, NULL);:开始扫描pg_type系统表。old = MemoryContextSwitchTo(TopMemoryContext);:切换到顶层内存上下文。while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)循环:遍历pg_type系统表中的每一行。MemoryContextSwitchTo(old);:切换回之前的内存上下文。table_endscan(scan);:结束扫描pg_type系统表。table_close(rel, NoLock);:关闭pg_type系统表。
gettype 函数
/* ----------------
* gettype
*
* NB: this is really ugly; it will return an integer index into TypInfo[],
* and not an OID at all, until the first reference to a type not known in
* TypInfo[]. At that point it will read and cache pg_type in Typ,
* and subsequently return a real OID (and set the global pointer Ap to
* point at the found row in Typ). So caller must check whether Typ is
* still NIL to determine what the return value is!
* ----------------
*/
static Oid
gettype(char *type)
{
if (Typ != NIL)
{
ListCell *lc;
foreach(lc, Typ)
{
structtypmap *app = lfirst(lc);
if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0)
{
Ap = app;
return app->am_oid;
}
}
/*
* The type wasn't known; reload the pg_type contents and check again
* to handle composite types, added since last populating the list.
*/
list_free_deep(Typ);
Typ = NIL;
populate_typ_list();
/*
* Calling gettype would result in infinite recursion for types
* missing in pg_type, so just repeat the lookup.
*/
foreach(lc, Typ)
{
structtypmap *app = lfirst(lc);
if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0)
{
Ap = app;
return app->am_oid;
}
}
}
else
{
int i;
for (i = 0; i < n_types; i++)
{
if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0)
return i;
}
/* Not in TypInfo, so we'd better be able to read pg_type now */
elog(DEBUG4, "external type: %s", type);
populate_typ_list();
return gettype(type);
}
elog(ERROR, "unrecognized type \"%s\"", type);
/* not reached, here to make compiler happy */
return0;
}
gettype函数用于根据类型名称获取类型 OID。如果找到,则返回该元素的索引。 如果没有找到,则加载 pg_type系统表,并再次调用gettype函数。如果找到,则将 Ap指针指向该typmap结构体,并返回该结构体的 OID。如果没有找到,则清空 Typ列表,重新加载pg_type系统表,并再次查找。type:类型名称。如果 Typ列表不为空,则在Typ列表中查找类型名称对应的typmap结构体。如果 Typ列表为空,则在TypInfo数组中查找类型名称对应的元素。如果仍然没有找到,则报错。
boot_get_type_io_data 函数
/* ----------------
* boot_get_type_io_data
*
* Obtain type I/O information at bootstrap time. This intentionally has
* almost the same API as lsyscache.c's get_type_io_data, except that
* we only support obtaining the typinput and typoutput routines, not
* the binary I/O routines. It is exported so that array_in and array_out
* can be made to work during early bootstrap.
* ----------------
*/
void
boot_get_type_io_data(Oid typid,
int16 *typlen,
bool *typbyval,
char *typalign,
char *typdelim,
Oid *typioparam,
Oid *typinput,
Oid *typoutput)
{
if (Typ != NIL)
{
/* We have the boot-time contents of pg_type, so use it */
structtypmap *ap = NULL;
ListCell *lc;
foreach(lc, Typ)
{
ap = lfirst(lc);
if (ap->am_oid == typid)
break;
}
if (!ap || ap->am_oid != typid)
elog(ERROR, "type OID %u not found in Typ list", typid);
*typlen = ap->am_typ.typlen;
*typbyval = ap->am_typ.typbyval;
*typalign = ap->am_typ.typalign;
*typdelim = ap->am_typ.typdelim;
/* XXX this logic must match getTypeIOParam() */
if (OidIsValid(ap->am_typ.typelem))
*typioparam = ap->am_typ.typelem;
else
*typioparam = typid;
*typinput = ap->am_typ.typinput;
*typoutput = ap->am_typ.typoutput;
}
else
{
/* We don't have pg_type yet, so use the hard-wired TypInfo array */
int typeindex;
for (typeindex = 0; typeindex < n_types; typeindex++)
{
if (TypInfo[typeindex].oid == typid)
break;
}
if (typeindex >= n_types)
elog(ERROR, "type OID %u not found in TypInfo", typid);
*typlen = TypInfo[typeindex].len;
*typbyval = TypInfo[typeindex].byval;
*typalign = TypInfo[typeindex].align;
/* We assume typdelim is ',' for all boot-time types */
*typdelim = ',';
/* XXX this logic must match getTypeIOParam() */
if (OidIsValid(TypInfo[typeindex].elem))
*typioparam = TypInfo[typeindex].elem;
else
*typioparam = typid;
*typinput = TypInfo[typeindex].inproc;
*typoutput = TypInfo[typeindex].outproc;
}
}
boot_get_type_io_data函数用于获取类型 I/O 信息。typid:类型 OID。typlen:类型长度。typbyval:类型是否按值传递。typalign:类型对齐方式。typdelim:类型分隔符。typioparam:类型 I/O 参数。typinput:类型输入函数 OID。typoutput:类型输出函数 OID。如果 Typ列表不为空,则在Typ列表中查找类型 OID 对应的typmap结构体,并从该结构体中获取类型 I/O 信息。如果 Typ列表为空,则在TypInfo数组中查找类型 OID 对应的元素,并从该元素中获取类型 I/O 信息。
AllocateAttribute 函数
/* ----------------
* AllocateAttribute
*
* Note: bootstrap never sets any per-column ACLs, so we only need
* ATTRIBUTE_FIXED_PART_SIZE space per attribute.
* ----------------
*/
static Form_pg_attribute
AllocateAttribute(void)
{
return (Form_pg_attribute)
MemoryContextAllocZero(TopMemoryContext, ATTRIBUTE_FIXED_PART_SIZE);
}
AllocateAttribute函数用于分配Form_pg_attribute结构体的内存。return (Form_pg_attribute) MemoryContextAllocZero(TopMemoryContext, ATTRIBUTE_FIXED_PART_SIZE);:在顶层内存上下文中分配ATTRIBUTE_FIXED_PART_SIZE大小的内存,并将其转换为Form_pg_attribute类型的指针。
index_register 函数
/*
* index_register() -- record an index that has been set up for building
* later.
*
* At bootstrap time, we define a bunch of indexes on system catalogs.
* We postpone actually building the indexes until just before we're
* finished with initialization, however. This is because the indexes
* themselves have catalog entries, and those have to be included in the
* indexes on those catalogs. Doing it in two phases is the simplest
* way of making sure the indexes have the right contents at the end.
*/
void
index_register(Oid heap,
Oid ind,
IndexInfo *indexInfo)
{
IndexList *newind;
MemoryContext oldcxt;
/*
* XXX mao 10/31/92 -- don't gc index reldescs, associated info at
* bootstrap time. we'll declare the indexes now, but want to create them
* later.
*/
if (nogc == NULL)
nogc = AllocSetContextCreate(NULL,
"BootstrapNoGC",
ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(nogc);
newind = (IndexList *) palloc(sizeof(IndexList));
newind->il_heap = heap;
newind->il_ind = ind;
newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo));
memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
/* expressions will likely be null, but may as well copy it */
newind->il_info->ii_Expressions =
copyObject(indexInfo->ii_Expressions);
newind->il_info->ii_ExpressionsState = NIL;
/* predicate will likely be null, but may as well copy it */
newind->il_info->ii_Predicate =
copyObject(indexInfo->ii_Predicate);
newind->il_info->ii_PredicateState = NULL;
/* no exclusion constraints at bootstrap time, so no need to copy */
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(indexInfo->ii_ExclusionProcs == NULL);
Assert(indexInfo->ii_ExclusionStrats == NULL);
newind->il_next = ILHead;
ILHead = newind;
MemoryContextSwitchTo(oldcxt);
}
index_register函数用于注册一个索引,以便稍后构建。heap:堆表(即被索引的表)的 OID。ind:索引表的 OID。indexInfo:索引信息。if (nogc == NULL) nogc = AllocSetContextCreate(NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_SIZES);:如果nogc内存上下文为空,则创建一个新的内存上下文,用于存储不需要进行垃圾回收的数据。oldcxt = MemoryContextSwitchTo(nogc);:切换到nogc内存上下文。newind = (IndexList *) palloc(sizeof(IndexList));:分配IndexList结构体的内存。newind->il_heap = heap;:设置IndexList结构体的堆表 OID。newind->il_ind = ind;:设置IndexList结构体的索引表 OID。newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo));:分配IndexInfo结构体的内存。memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));:复制IndexInfo结构体到IndexList结构体中。newind->il_info->ii_Expressions = copyObject(indexInfo->ii_Expressions);:复制索引表达式。newind->il_info->ii_Predicate = copyObject(indexInfo->ii_Predicate);:复制索引谓词。newind->il_next = ILHead;:将IndexList结构体添加到索引列表的头部。ILHead = newind;:更新索引列表的头部。MemoryContextSwitchTo(oldcxt);:切换回之前的内存上下文。
build_indices 函数
/*
* build_indices -- fill in all the indexes registered earlier
*/
void
build_indices(void)
{
for (; ILHead != NULL; ILHead = ILHead->il_next)
{
Relation heap;
Relation ind;
/* need not bother with locks during bootstrap */
heap = table_open(ILHead->il_heap, NoLock);
ind = index_open(ILHead->il_ind, NoLock);
index_build(heap, ind, ILHead->il_info, false, false);
index_close(ind, NoLock);
table_close(heap, NoLock);
}
}
build_indices函数用于构建所有已注册的索引。heap = table_open(ILHead->il_heap, NoLock);:打开堆表。ind = index_open(ILHead->il_ind, NoLock);:打开索引表。index_build(heap, ind, ILHead->il_info, false, false);:构建索引。index_close(ind, NoLock);:关闭索引表。table_close(heap, NoLock);:关闭堆表。for (; ILHead != NULL; ILHead = ILHead->il_next)循环:遍历索引列表。
总结
bootstrap.c 文件是 PostgreSQL bootstrap 过程的核心文件,它包含了创建初始模板数据库的各种函数。该文件使用一种特殊的 bootstrap 语言来描述数据库的结构和数据,并使用一系列函数来解析这些描述,创建表、定义属性、插入数据和构建索引。
希望这个详细的解释能够帮助你理解 bootstrap.c 文件的代码。如果你有任何其他问题,请随时提出。
以上内容基于DeepSeek及诸多AI生成, 轻微人工调整, 感谢杭州深度求索人工智能等公司
AI 生成的内容请自行辨别正确性, 当然也多了些许踩坑的乐趣, 毕竟冒险是每个男人的天性.