
pg_ctl提供了很多选项,比如stop,restart ,reload,status,我们上图给出的是start选项调用的流程。可以看到pg_ctl最后通过system函数,调用了另一可执行程序 postgres。稍等,PostgreSQL是如何找到可执行文件postgres的路径的?do_start函数中
- exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR)
postgres可执行文件的main函数的代码路径是在src/backend/main下面。postgres支持很多的参数,比如我们可以通过
- postgres --describe-config
我们看下postgres代码流程如何进入解析postgres.conf的

这一部分逻辑在PostMasterMain比较早的地方进行的,初始化完内存内存上下文,初始化玩GUCoption,解析完入参,就开始解析配置文件了。
ParseConfigFile这个函数位于src/backend/utils/misc/guc-file.c中,这个guc-file.c很有意思,代码看的莫名其妙。刚开始看GUC_yylex看的我一头雾水,不知所云,代码跟踪到yylex跟踪不下去了。后来发现目录下还有一个guc-file.l的文件。我就差不多想到了这肯定与传说中的yacc and lex有关系。 我首先翻看了Makefile,找到了如下code:
-
guc-file.c: guc-file.l
-
ifdef FLEX
-
$(FLEX) $(FLEXFLAGS) -o'$@' $<
-
else
-
@$(missing) flex $< $@
- endif
flex,全称是Fast Lexical Analyzer Generator。前身是lex。历史就比较有趣了,Eric Schmidt在1975年写了一个工具lex,这个工具被看作是yacc的兄弟,很有名气,尽管有点慢,而且bug有点多。1987年Vern Paxson写了flex,这个flex就比较好了,比较快,又可靠。值得一提的是Eric Schmidt,这个名字很熟悉吧,这是Google的执行主席。一起膜拜下大牛:

guc-file.l里面的解析稍微有点复杂,不利于我们入门。我找了Flex and bison这本书,练习了第一章的例子,算是基本入了门。
这个例子就如同Linux下的wc工具。我们看下代码:
-
root@manu:~/code/flex_bison# cat wc.l
-
/* just like Unix wc */
-
%{
-
int chars = 0;
-
int words = 0;
-
int lines = 0;
-
%}
-
-
%%
-
[a-zA-Z]+ { words++; chars += strlen(yytext); }
-
\n { chars++; lines++; }
-
. { chars++; }
-
%%
-
-
int main(int argc ,char* argv[])
-
{
-
yylex();
-
printf("%8d%8d%8d\n", lines, words, chars);
-
return 0;
- }
要想将wc.l编译成C文件,需要安装flex,这我就不说了,apt-get install就好了。
-
root@manu:~/code/flex_bison# flex -o wc.c wc.l
-
root@manu:~/code/flex_bison# ll
-
总用量 60
-
drwxr-xr-x 3 root root 4096 4月 5 13:22 ./
-
drwxr-xr-x 9 manu root 4096 4月 4 22:10 ../
-
-rw-r--r-- 1 root root 44406 4月 5 13:22 wc.c
- -rw-r--r-- 1 root root 313 4月 5 13:21 wc.l
-
root@manu:~/code/flex_bison# ./wc
-
hello world!
-
this is a new line
-
2 7 32
- root@manu:~/code/flex_bison#
我们看下PostgreSQL中guc-file.l中干的事情:
-
SIGN ("-"|"+")
-
DIGIT [0-9]
-
HEXDIGIT [0-9a-fA-F]
-
-
UNIT_LETTER [a-zA-Z]
-
-
INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
-
-
EXPONENT [Ee]{SIGN}?{DIGIT}+
-
REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
-
-
LETTER [A-Za-z_\200-\377]
-
LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
-
-
ID {LETTER}{LETTER_OR_DIGIT}*
-
QUALIFIED_ID {ID}"."{ID}
-
-
UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
- STRING \'([^'\\\n]|\\.|\'\')*\
怎么切分,就是模式匹配。定义好一些模式,匹配上的部分,被切出来,做相应的动作,如我们的wc程序,直接统计计数++,或者贴上相应的标签,如PostgreSQL中guc-file.l中那样,给第三部分进行处理。
-
\n ConfigFileLineno++; return GUC_EOL;
-
[ \t\r]+ /* eat whitespace */
-
#.* /* eat comment (.* matches anything until newline) */
-
-
{ID} return GUC_ID;
-
{QUALIFIED_ID} return GUC_QUALIFIED_ID;
-
{STRING} return GUC_STRING;
-
{UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
-
{INTEGER} return GUC_INTEGER;
-
{REAL} return GUC_REAL;
-
= return GUC_EQUALS;
-
-
. return GUC_ERROR;
-
- %%
-
datestyle = 'iso, ymd'
-
#intervalstyle = 'postgres'
-
timezone = 'PRC'
-
-
autovacuum_vacuum_threshold = 50
- cpu_index_tuple_cost = 0.005
- #.* /* eat comment (.* matches anything until newline) */
配置项的值类型有不同,有些值是string类型,有些是INT型的整数,有些是REAL型的实数。理解到此处,第三部分处理分词部分,整体就比较简单了。基本奉行一下几点:
1 #开头的 comment行不处理
2 每行开头的token必须是GUC_ID or GUC_QUALIFIED_ID类型,可以想下,如果配置项的某一行是 0.4=abc,明显是不合法的,因为我们期待的格式是option_name=option_value
-
if (token != GUC_ID && token != GUC_QUALIFIED_ID)
-
goto parse_error;
- opt_name = pstrdup(yytext)
-
token = yylex();
-
if (token == GUC_EQUALS)
-
token = yylex();
-
-
/* now we must have the option value */
-
if (token != GUC_ID &&
-
token != GUC_STRING &&
-
token != GUC_INTEGER &&
-
token != GUC_REAL &&
-
token != GUC_UNQUOTED_STRING)
-
goto parse_error;
-
if (token == GUC_STRING) /* strip quotes and escapes */
-
opt_value = GUC_scanstr(yytext);
-
else
- opt_value = pstrdup(yytext)
-
if (guc_name_compare(opt_name, "include_if_exists") == 0)
-
{
-
/*
-
* An include_if_exists directive isn't a variable and should be
-
* processed immediately.
-
*/
-
if (!ParseConfigFile(opt_value, config_file, false,
-
depth + 1, elevel,
-
head_p, tail_p))
-
OK = false;
-
yy_switch_to_buffer(lex_buffer);
-
pfree(opt_name);
-
pfree(opt_value);
-
}
-
else if (guc_name_compare(opt_name, "include") == 0)
-
{
-
/*
-
* An include directive isn't a variable and should be processed
-
* immediately.
-
*/
-
if (!ParseConfigFile(opt_value, config_file, true,
-
depth + 1, elevel,
-
head_p, tail_p))
-
OK = false;
-
yy_switch_to_buffer(lex_buffer);
-
pfree(opt_name);
-
pfree(opt_value);
- }
参考文献:
1 PostgreSQL 9.2.3 source code
2 flex and bison