一.GUC
在初始化内存环境之后,需要配置Postmaster运行时所需的各种参数。GUC(Grand Unified Configuration)模块实现了多种数据类型(目前有boolean、int、float、string四种)的变量配置。这些参数可能会由不同的进程在不同的实际进行配置,系统会根据既定的优先权来确定什么情况下的配置可以生效。
参数共有六种类型(通过枚举类型CucContext定义),并且只能在合适的环境下进行配置:
- PGC_INTERNAL:参数只能通过内部进程设定,用户不能设定。
- PGC_POSTMASTER:参数只能在Postmaster启动时通过读配置文件或处理命令行参数来配置。
- PGC_SIGHUP:参数只能在Postmaster启动时配置,或当我们改变了配置文件并发送信号SIGUP通知Postmaster或Postgres的时候进行配置。
- PGC_BACKEND:参数只能在Postmaster启动时读配置文件设置,或由客户端在进行连接请求时设置。已经启动的后台进程会忽略此类参数的改变。
- PGC_USERSET:可以在任何时候进行配置。
- PGC_SUSET:参数只能在Postmaster启动时或由超级用户通过SQL语言(SET命令)进行设置。
下面这个枚举类型数据结构用于描述参数的来源,按照优先级从低到高的顺序排列,一个设置起作用当且仅当先前的设置优先级比当前的设置的优先级低或者相等。
typedef enum
{
PGC_S_DEFAULT, //参数设为默认值
PGC_S_ENV_VAR, //参数通过环境变量得到
PGC_S_FILE, //读配置文件得到
PGC_S_ARGV, //从Postmaster命令行得到
PGC_S_DATABASE, //数据库安装时指定
PGC_S_USER, //用户指定
PGC_S_CLIENT, //通过客户连接请求传送过来的数据包指定
PGC_S_OVERRIDE, //在特定情况下用来强制设定为默认值
PGC_S_INTERACTIVE, //仅仅是作为交互式来源和非交互式来源之间的分隔
PGC_S_TEST, //仅用于测试
PGC_S_SESSION //通过SET命令设置
}GucSource; //参数来源,按优先级递增的顺序排列
每一种数据类型的GUC参数都由两部分组成:共性部分和特性部分。共性部分的数据结构如下:
struct config_generic
{
const char *name; //参数名
GucContext context; //参数类型
enum config_group group; //用于根据功能对参数进行分组
const char *short_desc; //参数简短描述
const char *long_desc; //参数详细描述
int flags; //参数标志
enum config_type vartype; //参数值的数据类型
int status; //参数状态
CucSource reset_source; //参数值为reset_value时参数的来源
GucSource source; //当前参数来源
GucStack *stack; //当修改发生时,用来保存旧值,以支持回滚
char *sourcefile; //配置所在的源文件
Int sourceline; //在源文件中的行号
};
其中参数的数据类型config_type有五种:PGC_BOOL(布尔型)、PGC_INT(整型)、PGC_REAL(实数)、PGC_STRING(字符串)和PGC_ENUM(枚举)。
每一种具体的数据类型的参数都有其特性的数据结构,特性数据结构中的第一个项都是一个指向其共性数据结构的指针。整数型参数的特性部分如下:
struct config_int
{
struct config_generic gen; //参数的共性数据结构
int *variable; //参数当前被设置的值(所有特性结构都有此项)
int boot_val; //参数的初始值
int reset_val; //重新设置时,如果用户未指定,则取该参数值
int min; //参数的下界
int max; //参数的上界
GucIntAssignHook assign_hook; //函数指针,用来设置reset_val
GucShowHook show_hook; //目前没有用到
};
二.Postmaster配置参数的过程
Postmaster配置参数的基本过程包括三个步骤,如下图:
1.初始化GUC参数
Postmaster将首先调用InitializeGUCOptions函数将参数设置为默认值:
1)首先调用build_guc_variables函数来统计参数个数并分配相应的config_generic类型的全局指针数组guc_variables以保存每个参数结构体的地址,并且对该数组进行排序。由于参数是通过全局静态数组ConfigureNamesBool、ConfigureNamesInt、ConfigureNamesReal、ConfigureNamesString、ConfigureNamesEnum存储的,因此在build_guc_variables函数中只需要遍历相应的数组,统计参数的个数并将参数结构体中config_generic域的参数vartype设置为相应的参数类型。当遍历完所有参数后,根据总的参数个数分配config_generic指针数组guc_vars,然后再次遍历静态参数数组,将每个参数结构的首地址保存到guc_vars数组中(这里分配的数组个数为当前参数总数的1.25倍,主要是为了方便以后参数的扩充)。接着将全局变量guc_variables也指向guc_vars数组。最后通过快速排序法把guc_variables按照参数名进行排序。
2)接下来将每个参数设置为默认值。对于guc_variables中的每个参数,initializeGUCOptions函数先将其config_generic域中的status设置为0,将reset_source、tentative_source、source设置为PGC_S_DEFAULT表示默认;stack、sourcefile设置为NULL;然后根据参数值vartype的不同类型分别调用相应的assign_hook函数(如果该参数设置了该函数),assign_hook函数用来设置boot_val,最后将boot_val赋值给reset_val和variable指向的变量,通过这样一系列的步骤就将参数设置为了默认值。
3)通过系统调用getenv来获得环境变量PGPORT、PGDATESTYLE、PGCLIENTENCODING的值,不为空则调用SetConfigOption函数来设置这三个变量对应的参数的值。
4)最后,检测系统的最大安全栈深度,如果这个深度值大于100KB且不超过2MB,则用它设置max_stack_depth参数。
2.配置GUC参数
如果用户启动Postmaster进程时通过命令行参数指定了一些GUC的参数值,那么Postmaster需要从命令行参数中将这些GUC参数的值解析出来并且设置到相应的GUC参数中。根据命令行设置参数主要是通过getopt和SetConfigOption这两个函数来完成的。
对于getopt返回的每一个参数选项及其参数值,通过一个switch语句根据参数选项的不同分别调用SetConfigOption函数设置相应的参数。
SetConfigOption函数的第一个参数为参数名;第二个参数为参数值,其值存放在getopt函数返回的optarg字符串中;第三个参数为参数类型最后一个参数为参数来源。由于在这里Postmaster只在处理,命令行参数,所以这里的参数类型和参数来源分别设置为PGC_POSTMASTER和PGC_S_ARGV。
SetConfigOption函数是通过调用set_config