本篇内容主要讲解“PostgreSQL中PostgresMain函数的实现逻辑是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中PostgresMain函数的实现逻辑是什么”吧!
一、源码解读
PostgresMain函数
/* ---------------------------------------------------------------- * PostgresMain * postgres main loop -- all backends, interactive or otherwise start here * * argc/argv are the command line arguments to be used. (When being forked * by the postmaster, these are not the original argv array of the process.) * dbname is the name of the database to connect to, or NULL if the database * name should be extracted from the command line arguments or defaulted. * username is the PostgreSQL user name to be used for the session. * ---------------------------------------------------------------- */ /* 输入: argc/argv-Main函数的输入参数 dbname-数据库名称 username-用户名 输出: 无 */ void PostgresMain(int argc, char *argv[], const char *dbname, const char *username) { int firstchar;//临时变量,读取输入的Command StringInfoData input_message;//字符串增强结构体 sigjmp_buf local_sigjmp_buf;//系统变量 volatile bool send_ready_for_query = true;// bool disable_idle_in_transaction_timeout = false; /* Initialize startup process environment if necessary. */ if (!IsUnderPostmaster//未初始化?initialized for the bootstrap/standalone case InitStandaloneProcess(argv[0]);//初始化进程 SetProcessingMode(InitProcessing);//设置进程状态为InitProcessing /* * Set default values for command-line options. */ if (!IsUnderPostmaster) InitializeGUCOptions();//初始化GUC参数,GUC=Grand Unified Configuration /* * Parse command-line options. */ process_postgres_switches(argc, argv, PGC_POSTMASTER, &dbname);//解析输入参数 /* Must have gotten a database name, or have a default (the username) */ if (dbname == NULL)//输入的dbname为空 { dbname = username;//设置为用户名 if (dbname == NULL)//如仍为空,报错 ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("%s: no database nor user name specified", progname))); } /* Acquire configuration parameters, unless inherited from postmaster */ if (!IsUnderPostmaster) { if (!SelectConfigFiles(userDoption, progname))//读取配置文件conf/hba文件&定位数据目录 proc_exit(1); } /* * Set up signal handlers and masks. * * Note that postmaster blocked all signals before forking child process, * so there is no race condition whereby we might receive a signal before * we have set up the handler. * * Also note: it's best not to use any signals that are SIG_IGNored in the * postmaster. If such a signal arrives before we are able to change the * handler to non-SIG_IGN, it'll get dropped. Instead, make a dummy * handler in the postmaster to reserve the signal. (Of course, this isn't * an issue for signals that are locally generated, such as SIGALRM and * SIGPIPE.) */ if (am_walsender)//wal sender进程? WalSndSignals();//如果是,则调用WalSndSignals else//不是wal sender进程 { pqsignal(SIGHUP, PostgresSigHupHandler); /* set flag to read config * file */ pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */ pqsignal(SIGTERM, die); /* cancel current query and exit */ /* * In a standalone backend, SIGQUIT can be generated from the keyboard * easily, while SIGTERM cannot, so we make both signals do die() * rather than quickdie(). */ if (IsUnderPostmaster) pqsignal(SIGQUIT, quickdie); /* hard crash time */ else pqsignal(SIGQUIT, die); /* cancel current query and exit */ InitializeTimeouts(); /* establishes SIGALRM handler */ /* * Ignore failure to write to frontend. Note: if frontend closes * connection, we will notice it and exit cleanly when control next * returns to outer loop. This seems safer than forcing exit in the * midst of output during who-knows-what operation... */ pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, procsignal_sigusr1_handler); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); /* * Reset some signals that are accepted by postmaster but not by * backend */ pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some * platforms */ } pqinitmask();//Initialize BlockSig, UnBlockSig, and StartupBlockSig. if (IsUnderPostmaster) { /* We allow SIGQUIT (quickdie) at all times */ sigdelset(&BlockSig, SIGQUIT); } PG_SETMASK(&BlockSig); /* block everything except SIGQUIT */ if (!IsUnderPostmaster) { /* * Validate we have been given a reasonable-looking DataDir (if under * postmaster, assume postmaster did this already). */ checkDataDir();//确认数据库路径OK,使用stat命令 /* Change into DataDir (if under postmaster, was done already) */ ChangeToDataDir();//切换至数据库路径,使用chdir命令 /* * Create lockfile for data directory. */ CreateDataDirLockFile(false);//创建锁定文件,CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, "", true, DataDir); /* read control file (error checking and contains config ) */ LocalProcessControlFile(false);//Read the control file, set respective GUCs. /* Initialize MaxBackends (if under postmaster, was done already) */ InitializeMaxBackends();//Initialize MaxBackends value from config options. } /* Early initialization */ BaseInit();//基础的初始化 /* * Create a per-backend PGPROC struct in shared memory, except in the * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do * this before we can use LWLocks (and in the EXEC_BACKEND case we already * had to do some stuff with LWLocks). */ // initialize a per-process data structure for this backend #ifdef EXEC_BACKEND if (!IsUnderPostmaster) InitProcess(); #else InitProcess(); #endif /* We need to allow SIGINT, etc during the initial transaction */ PG_SETMASK(&UnBlockSig); /* * General initialization. * * NOTE: if you are tempted to add code in this vicinity, consider putting * it inside InitPostgres() instead. In particular, anything that * involves database access should be there, not here. */ InitPostgres(dbname, InvalidOid, username, InvalidOid, NULL, false);//Initialize POSTGRES /* * If the PostmasterContext is still around, recycle the space; we don't * need it anymore after InitPostgres completes. Note this does not trash * *MyProcPort, because ConnCreate() allocated that space with malloc() * ... else we'd need to copy the Port data first. Also, subsidiary data * such as the username isn't lost either; see ProcessStartupPacket(). */ if (PostmasterContext) { MemoryContextDelete(PostmasterContext); PostmasterContext = NULL; } SetProcessingMode(NormalProcessing);//完成初始化后,设置进程模式为NormalProcessing /* * Now all GUC states are fully set up. Report them to client if * appropriate. */ BeginReportingGUCOptions();//Report GUC /* * Also set up handler to log session end; we have to wait till now to be * sure Log_disconnections has its final value. */ if (IsUnderPostmaster && Log_disconnections) on_proc_exit(log_disconnections, 0);//this function adds a callback function to the list of functions invoked by proc_exit() /* Perform initialization specific to a WAL sender process. */ if (am_walsender) InitWalSender();//初始化 WAL sender process /* * process any libraries that should be preloaded at backend start (this * likewise can't be done until GUC settings are complete) */ process_session_preload_libraries();//加载LIB /* * Send this backend's cancellation info to the frontend. */ if (whereToSendOutput == DestRemote) { StringInfoData buf; pq_beginmessage(&buf, 'K'); pq_sendint32(&buf, (int32) MyProcPid); pq_sendint32(&buf, (int32) MyCancelKey); pq_endmessage(&buf); /* Need not flush since ReadyForQuery will do it. */ } /* Welcome banner for standalone case */ if (whereToSendOutput == DestDebug) printf("/nPostgreSQL stand-alone backend %s/n", PG_VERSION); /* * Create the memory context we will use in the main loop. * * MessageContext is reset once per iteration of the main loop, ie, upon * completion of processing of each command message from the client. */ //初始化内存上下文:MessageContext MessageContext = AllocSetContextCreate(TopMemoryContext, "MessageContext", ALLOCSET_DEFAULT_SIZES); /* * Create memory context and buffer used for RowDescription messages. As * SendRowDescriptionMessage(), via exec_describe_statement_message(), is * frequently executed for ever single statement, we don't want to * allocate a separate buffer every time. */ //TODO 传输RowDescription messages? row_description_context = AllocSetContextCreate(TopMemoryContext, "RowDescriptionContext", ALLOCSET_DEFAULT_SIZES); MemoryContextSwitchTo(row_description_context); initStringInfo(&row_description_buf); MemoryContextSwitchTo(TopMemoryContext); /* * Remember stand-alone backend startup time */ if (!IsUnderPostmaster) PgStartTime = GetCurrentTimestamp();//记录启动时间 /* * POSTGRES main processing loop begins here * * If an exception is encountered, processing resumes here so we abort the * current transaction and start a new one. * * You might wonder why this isn't coded as an infinite loop around a * PG_TRY construct. The reason is that this is the bottom of the * exception stack, and so with PG_TRY there would be no exception handler * in force at all during the CATCH part. By leaving the outermost setjmp * always active, we have at least some chance of recovering from an error * during error recovery. (If we get into an infinite loop thereby, it * will soon be stopped by overflow of elog.c's internal state stack.) * * Note that we use sigsetjmp(..., 1), so that this function's signal mask * (to wit, UnBlockSig) will be restored when longjmp'ing to here. This * is essential in case we longjmp'd out of a signal handler on a platform * where that leaves the signal blocked. It's not redundant with the * unblock in AbortTransaction() because the latter is only called if we * were inside a transaction. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0)// { /* * NOTE: if you are tempted to add more code in this if-block, * consider the high probability that it should be in * AbortTransaction() instead. The only stuff done directly here * should be stuff that is guaranteed to apply *only* for outer-level * error recovery, such as adjusting the FE/BE protocol status. */ /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; /* Prevent interrupts while cleaning up */ HOLD_INTERRUPTS(); /* * Forget any pending QueryCancel request, since we're returning to * the idle loop anyway, and cancel any active timeout requests. (In * future we might want to allow some timeout requests to survive, but * at minimum it'd be necessary to do reschedule_timeouts(), in case * we got here because of a query cancel interrupting the SIGALRM * interrupt handler.) Note in particular that we must clear the * statement and lock timeout indicators, to prevent any future plain * query cancels from being misreported as timeouts in case we're * forgetting a timeout cancel. */ disable_all_timeouts(false); QueryCancelPending = false; /* second to avoid race condition */ stmt_timeout_active = false; /* Not reading from the client anymore. */ DoingCommandRead = false; /* Make sure libpq is in a good state */ pq_comm_reset(); /* Report the error to the client and/or server log */ EmitErrorReport(); /* * Make sure debug_query_string gets reset before we possibly clobber * the storage it points at. */ debug_query_string = NULL; /* * Abort the current transaction in order to recover. */ AbortCurrentTransaction(); if (am_walsender) WalSndErrorCleanup(); PortalErrorCleanup(); SPICleanup(); /* * We can't release replication slots inside AbortTransaction() as we * need to be able to start and abort transactions while having a slot * acquired. But we never need to hold them across top level errors, * so releasing here is fine. There's another cleanup in ProcKill() * ensuring we'll correctly cleanup on FATAL errors as well. */ if (MyReplicationSlot != NULL) ReplicationSlotRelease(); /* We also want to cleanup temporary slots on error. */ ReplicationSlotCleanup(); jit_reset_after_error(); /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(TopMemoryContext); FlushErrorState(); /* * If we were handling an extended-query-protocol message, initiate * skip till next Sync. This also causes us not to issue * ReadyForQuery (until we get Sync). */ if (doing_extended_query_message) ignore_till_sync = true; /* We don't have a transaction command open anymore */ xact_started = false; /* * If an error occurred while we were reading a message from the * client, we have potentially lost track of where the previous * message ends and the next one begins. Even though we have * otherwise recovered from the error, we cannot safely read any more * messages from the client, so there isn't much we can do with the * connection anymore. */ if (pq_is_reading_msg()) ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("terminating connection because protocol synchronization was lost"))); /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; if (!ignore_till_sync) send_ready_for_query = true; /* initially, or after error */ /* * Non-error queries loop here. */ for (;;)//主循环 { /* * At top of loop, reset extended-query-message flag, so that any * errors encountered in "idle" state don't provoke skip. */ doing_extended_query_message = false; /* * Release storage left over from prior query cycle, and create a new * query input buffer in the cleared MessageContext. */ MemoryContextSwitchTo(MessageContext);//切换至MessageContext MemoryContextResetAndDeleteChildren(MessageContext); initStringInfo(&input_message);//初始化输入的信息 /* * Also consider releasing our catalog snapshot if any, so that it's * not preventing advance of global xmin while we wait for the client. */ InvalidateCatalogSnapshotConditionally(); /* * (1) If we've reached idle state, tell the frontend we're ready for * a new query. * * Note: this includes fflush()'ing the last of the prior output. * * This is also a good time to send collected statistics to the * collector, and to update the PS stats display. We avoid doing * those every time through the message loop because it'd slow down * processing of batched messages, and because we don't want to report * uncommitted updates (that confuses autovacuum). The notification * processor wants a call too, if we are not in a transaction block. */ if (send_ready_for_query)//I am ready! { if (IsAbortedTransactionBlockState()) { set_ps_display("idle in transaction (aborted)", false); pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL); /* Start the idle-in-transaction timer */ if (IdleInTransactionSessionTimeout > 0) { disable_idle_in_transaction_timeout = true; enable_timeout_after(IDLE_IN_TRANSACTION_SESSION_TIMEOUT, IdleInTransactionSessionTimeout); } } else if (IsTransactionOrTransactionBlock()) { set_ps_display("idle in transaction", false); pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL); /* Start the idle-in-transaction timer */ if (IdleInTransactionSessionTimeout > 0) { disable_idle_in_transaction_timeout = true; enable_timeout_after(IDLE_IN_TRANSACTION_SESSION_TIMEOUT, IdleInTransactionSessionTimeout); } } else { ProcessCompletedNotifies(); pgstat_report_stat(false); set_ps_display("idle", false); pgstat_report_activity(STATE_IDLE, NULL); } ReadyForQuery(whereToSendOutput); send_ready_for_query = false; } /* * (2) Allow asynchronous signals to be executed immediately if they * come in while we are waiting for client input. (This must be * conditional since we don't want, say, reads on behalf of COPY FROM * STDIN doing the same thing.) */ DoingCommandRead = true; /* * (3) read a command (loop blocks here) */ firstchar = ReadCommand(&input_message);//读取命令 /* * (4) disable async signal conditions again. * * Query cancel is supposed to be a no-op when there is no query in * progress, so if a query cancel arrived while we were idle, just * reset QueryCancelPending. ProcessInterrupts() has that effect when * it's called when DoingCommandRead is set, so check for interrupts * before resetting DoingCommandRead. */ CHECK_FOR_INTERRUPTS(); DoingCommandRead = false; /* * (5) turn off the idle-in-transaction timeout */ if (disable_idle_in_transaction_timeout) { disable_timeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT, false); disable_idle_in_transaction_timeout = false; } /* * (6) check for any other interesting events that happened while we * slept. */ if (ConfigReloadPending) { ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); } /* * (7) process the command. But ignore it if we're skipping till * Sync. */ if (ignore_till_sync && firstchar != EOF) continue; switch (firstchar) { case 'Q': /* simple query */ { const char *query_string; /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); query_string = pq_getmsgstring(&input_message);//SQL语句 pq_getmsgend(&input_message); if (am_walsender) { if (!exec_replication_command(query_string)) exec_simple_query(query_string); } else exec_simple_query(query_string);//执行SQL语句 send_ready_for_query = true; } break; case 'P': /* parse */ { const char *stmt_name; const char *query_string; int numParams; Oid *paramTypes = NULL; forbidden_in_wal_sender(firstchar); /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); stmt_name = pq_getmsgstring(&input_message); query_string = pq_getmsgstring(&input_message); numParams = pq_getmsgint(&input_message, 2); if (numParams > 0) { int i; paramTypes = (Oid *) palloc(numParams * sizeof(Oid)); for (i = 0; i < numParams; i++) paramTypes[i] = pq_getmsgint(&input_message, 4); } pq_getmsgend(&input_message); exec_parse_message(query_string, stmt_name, paramTypes, numParams); } break; case 'B': /* bind */ forbidden_in_wal_sender(firstchar); /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); /* * this message is complex enough that it seems best to put * the field extraction out-of-line */ exec_bind_message(&input_message); break; case 'E': /* execute */ { const char *portal_name; int max_rows; forbidden_in_wal_sender(firstchar); /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); portal_name = pq_getmsgstring(&input_message); max_rows = pq_getmsgint(&input_message, 4); pq_getmsgend(&input_message); exec_execute_message(portal_name, max_rows); } break; case 'F': /* fastpath function call */ forbidden_in_wal_sender(firstchar); /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); /* Report query to various monitoring facilities. */ pgstat_report_activity(STATE_FASTPATH, NULL); set_ps_display("<FASTPATH>", false); /* start an xact for this function invocation */ start_xact_command(); /* * Note: we may at this point be inside an aborted * transaction. We can't throw error for that until we've * finished reading the function-call message, so * HandleFunctionRequest() must check for it after doing so. * Be careful not to do anything that assumes we're inside a * valid transaction here. */ /* switch back to message context */ MemoryContextSwitchTo(MessageContext); HandleFunctionRequest(&input_message); /* commit the function-invocation transaction */ finish_xact_command(); send_ready_for_query = true; break; case 'C': /* close */ { int close_type; const char *close_target; forbidden_in_wal_sender(firstchar); close_type = pq_getmsgbyte(&input_message); close_target = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); switch (close_type) { case 'S': if (close_target[0] != '/0') DropPreparedStatement(close_target, false); else { /* special-case the unnamed statement */ drop_unnamed_stmt(); } break; case 'P': { Portal portal; portal = GetPortalByName(close_target); if (PortalIsValid(portal)) PortalDrop(portal, false); } break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid CLOSE message subtype %d", close_type))); break; } if (whereToSendOutput == DestRemote) pq_putemptymessage('3'); /* CloseComplete */ } break; case 'D': /* describe */ { int describe_type; const char *describe_target; forbidden_in_wal_sender(firstchar); /* Set statement_timestamp() (needed for xact) */ SetCurrentStatementStartTimestamp(); describe_type = pq_getmsgbyte(&input_message); describe_target = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); switch (describe_type) { case 'S': exec_describe_statement_message(describe_target); break; case 'P': exec_describe_portal_message(describe_target); break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid DESCRIBE message subtype %d", describe_type))); break; } } break; case 'H': /* flush */ pq_getmsgend(&input_message); if (whereToSendOutput == DestRemote) pq_flush(); break; case 'S': /* sync */ pq_getmsgend(&input_message); finish_xact_command(); send_ready_for_query = true; break; /* * 'X' means that the frontend is closing down the socket. EOF * means unexpected loss of frontend connection. Either way, * perform normal shutdown. */ case 'X': case EOF: /* * Reset whereToSendOutput to prevent ereport from attempting * to send any more messages to client. */ if (whereToSendOutput == DestRemote) whereToSendOutput = DestNone; /* * NOTE: if you are tempted to add more code here, DON'T! * Whatever you had in mind to do should be set up as an * on_proc_exit or on_shmem_exit callback, instead. Otherwise * it will fail to be called during other backend-shutdown * scenarios. */ proc_exit(0); case 'd': /* copy data */ case 'c': /* copy done */ case 'f': /* copy fail */ /* * Accept but ignore these messages, per protocol spec; we * probably got here because a COPY failed, and the frontend * is still sending data. */ break; default: ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %d", firstchar))); } } /* end of input-reading loop */ }
二、基础信息
PostgresMain函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
1、StringInfoData
/*------------------------- * StringInfoData holds information about an extensible string. * data is the current buffer for the string (allocated with palloc). * len is the current string length. There is guaranteed to be * a terminating '/0' at data[len], although this is not very * useful when the string holds binary data rather than text. * maxlen is the allocated size in bytes of 'data', i.e. the maximum * string size (including the terminating '/0' char) that we can * currently store in 'data' without having to reallocate * more space. We must always have maxlen > len. * cursor is initialized to zero by makeStringInfo or initStringInfo, * but is not otherwise touched by the stringinfo.c routines. * Some routines use it to scan through a StringInfo. *------------------------- */ typedef struct StringInfoData { char *data; int len; int maxlen; int cursor; } StringInfoData; typedef StringInfoData *StringInfo;
2、宏定义
#ifndef WIN32 #define PG_SETMASK(mask) sigprocmask(SIG_SETMASK, mask, NULL) #else /* Emulate POSIX sigset_t APIs on Windows */ typedef int sigset_t; extern int pqsigsetmask(int mask); #define PG_SETMASK(mask) pqsigsetmask(*(mask)) #define sigemptyset(set) (*(set) = 0) #define sigfillset(set) (*(set) = ~0) #define sigaddset(set, signum) (*(set) |= (sigmask(signum))) #define sigdelset(set, signum) (*(set) &= ~(sigmask(signum))) #endif /* WIN32 */
3、全局变量
/* * IsPostmasterEnvironment is true in a postmaster process and any postmaster * child process; it is false in a standalone process (bootstrap or * standalone backend). IsUnderPostmaster is true in postmaster child * processes. Note that "child process" includes all children, not only * regular backends. These should be set correctly as early as possible * in the execution of a process, so that error handling will do the right * things if an error should occur during process initialization. * * These are initialized for the bootstrap/standalone case. */ bool IsPostmasterEnvironment = false; bool IsUnderPostmaster = false; bool IsBinaryUpgrade = false; bool IsBackgroundWorker = false; bool am_walsender = false; /* Am I a walsender process? */
依赖的函数
1、InitStandaloneProcess
/* * Initialize the basic environment for a standalone process. * * argv0 has to be suitable to find the program's executable. */ void InitStandaloneProcess(const char *argv0) { Assert(!IsPostmasterEnvironment); MyProcPid = getpid(); /* reset MyProcPid */ MyStartTime = time(NULL); /* set our start time in case we call elog */ /* Initialize process-local latch support */ InitializeLatchSupport(); MyLatch = &LocalLatchData; InitLatch(MyLatch); /* Compute paths, no postmaster to inherit from */ if (my_exec_path[0] == '/0') { if (find_my_exec(argv0, my_exec_path) < 0) elog(FATAL, "%s: could not locate my own executable path", argv0); } if (pkglib_path[0] == '/0') get_pkglib_path(my_exec_path, pkglib_path); }
2、InitializeGUCOptions
/* * Initialize GUC options during program startup. * * Note that we cannot read the config file yet, since we have not yet * processed command-line switches. */ void InitializeGUCOptions(void) { int i; /* * Before log_line_prefix could possibly receive a nonempty setting, make * sure that timezone processing is minimally alive (see elog.c). */ pg_timezone_initialize(); /* * Build sorted array of all GUC variables. */ build_guc_variables(); /* * Load all variables with their compiled-in defaults, and initialize * status fields as needed. */ for (i = 0; i < num_guc_variables; i++) { InitializeOneGUCOption(guc_variables[i]); } guc_dirty = false; reporting_enabled = false; /* * Prevent any attempt to override the transaction modes from * non-interactive sources. */ SetConfigOption("transaction_isolation", "default", PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("transaction_deferrable", "no", PGC_POSTMASTER, PGC_S_OVERRIDE); /* * For historical reasons, some GUC parameters can receive defaults from * environment variables. Process those settings. */ InitializeGUCOptionsFromEnvironment(); }
3、process_postgres_switches
/* ---------------------------------------------------------------- * process_postgres_switches * Parse command line arguments for PostgresMain * * This is called twice, once for the "secure" options coming from the * postmaster or command line, and once for the "insecure" options coming * from the client's startup packet. The latter have the same syntax but * may be restricted in what they can do. * * argv[0] is ignored in either case (it's assumed to be the program name). * * ctx is PGC_POSTMASTER for secure options, PGC_BACKEND for insecure options * coming from the client, or PGC_SU_BACKEND for insecure options coming from * a superuser client. * * If a database name is present in the command line arguments, it's * returned into *dbname (this is allowed only if *dbname is initially NULL). * ---------------------------------------------------------------- */ void process_postgres_switches(int argc, char *argv[], GucContext ctx, const char **dbname) { bool secure = (ctx == PGC_POSTMASTER); int errs = 0; GucSource gucsource; int flag; if (secure) { gucsource = PGC_S_ARGV; /* switches came from command line */ /* Ignore the initial --single argument, if present */ if (argc > 1 && strcmp(argv[1], "--single") == 0) { argv++; argc--; } } else { gucsource = PGC_S_CLIENT; /* switches came from client */ } #ifdef HAVE_INT_OPTERR /* * Turn this off because it's either printed to stderr and not the log * where we'd want it, or argv[0] is now "--single", which would make for * a weird error message. We print our own error message below. */ opterr = 0; #endif /* * Parse command-line options. CAUTION: keep this in sync with * postmaster/postmaster.c (the option sets should not conflict) and with * the common help() function in main/main.c. */ while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1) { switch (flag) { case 'B': SetConfigOption("shared_buffers", optarg, ctx, gucsource); break; case 'b': /* Undocumented flag used for binary upgrades */ if (secure) IsBinaryUpgrade = true; break; case 'C': /* ignored for consistency with the postmaster */ break; case 'D': if (secure) userDoption = strdup(optarg); break; case 'd': set_debug_options(atoi(optarg), ctx, gucsource); break; case 'E': if (secure) EchoQuery = true; break; case 'e': SetConfigOption("datestyle", "euro", ctx, gucsource); break; case 'F': SetConfigOption("fsync", "false", ctx, gucsource); break; case 'f': if (!set_plan_disabling_options(optarg, ctx, gucsource)) errs++; break; case 'h': SetConfigOption("listen_addresses", optarg, ctx, gucsource); break; case 'i': SetConfigOption("listen_addresses", "*", ctx, gucsource); break; case 'j': if (secure) UseSemiNewlineNewline = true; break; case 'k': SetConfigOption("unix_socket_directories", optarg, ctx, gucsource); break; case 'l': SetConfigOption("ssl", "true", ctx, gucsource); break; case 'N': SetConfigOption("max_connections", optarg, ctx, gucsource); break; case 'n': /* ignored for consistency with postmaster */ break; case 'O': SetConfigOption("allow_system_table_mods", "true", ctx, gucsource); break; case 'o': errs++; break; case 'P': SetConfigOption("ignore_system_indexes", "true", ctx, gucsource); break; case 'p': SetConfigOption("port", optarg, ctx, gucsource); break; case 'r': /* send output (stdout and stderr) to the given file */ if (secure) strlcpy(OutputFileName, optarg, MAXPGPATH); break; case 'S': SetConfigOption("work_mem", optarg, ctx, gucsource); break; case 's': SetConfigOption("log_statement_stats", "true", ctx, gucsource); break; case 'T': /* ignored for consistency with the postmaster */ break; case 't': { const char *tmp = get_stats_option_name(optarg); if (tmp) SetConfigOption(tmp, "true", ctx, gucsource); else errs++; break; } case 'v': /* * -v is no longer used in normal operation, since * FrontendProtocol is already set before we get here. We keep * the switch only for possible use in standalone operation, * in case we ever support using normal FE/BE protocol with a * standalone backend. */ if (secure) FrontendProtocol = (ProtocolVersion) atoi(optarg); break; case 'W': SetConfigOption("post_auth_delay", optarg, ctx, gucsource); 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, ctx, gucsource); free(name); if (value) free(value); break; } default: errs++; break; } if (errs) break; } /* * Optional database name should be there only if *dbname is NULL. */ if (!errs && dbname && *dbname == NULL && argc - optind >= 1) *dbname = strdup(argv[optind++]); if (errs || argc != optind) { if (errs) optind--; /* complain about the previous argument */ /* spell the error message a bit differently depending on context */ if (IsUnderPostmaster) ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid command-line argument for server process: %s", argv[optind]), errhint("Try /"%s --help/" for more information.", progname))); else ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s: invalid command-line argument: %s", progname, argv[optind]), errhint("Try /"%s --help/" for more information.", progname))); } /* * Reset getopt(3) library so that it will work correctly in subprocesses * or when this function is called a second time with another array. */ optind = 1; #ifdef HAVE_INT_OPTRESET optreset = 1; /* some systems need this too */ #endif }
4、SelectConfigFiles
/* * Select the configuration files and data directory to be used, and * do the initial read of postgresql.conf. * * This is called after processing command-line switches. * userDoption is the -D switch value if any (NULL if unspecified). * progname is just for use in error messages. * * Returns true on success; on failure, prints a suitable error message * to stderr and returns false. */ bool SelectConfigFiles(const char *userDoption, const char *progname) { char *configdir; char *fname; struct stat stat_buf; /* configdir is -D option, or $PGDATA if no -D */ if (userDoption) configdir = make_absolute_path(userDoption); else configdir = make_absolute_path(getenv("PGDATA")); if (configdir && stat(configdir, &stat_buf) != 0) { write_stderr("%s: could not access directory /"%s/": %s/n", progname, configdir, strerror(errno)); if (errno == ENOENT) write_stderr("Run initdb or pg_basebackup to initialize a PostgreSQL data directory./n"); return false; } /* * Find the configuration file: if config_file was specified on the * command line, use it, else use configdir/postgresql.conf. In any case * ensure the result is an absolute path, so that it will be interpreted * the same way by future backends. */ if (ConfigFileName) fname = make_absolute_path(ConfigFileName); else if (configdir) { fname = guc_malloc(FATAL, strlen(configdir) + strlen(CONFIG_FILENAME) + 2); sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME); } else { write_stderr("%s does not know where to find the server configuration file./n" "You must specify the --config-file or -D invocation " "option or set the PGDATA environment variable./n", progname); return false; } /* * Set the ConfigFileName GUC variable to its final value, ensuring that * it can't be overridden later. */ SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); free(fname); /* * Now read the config file for the first time. */ if (stat(ConfigFileName, &stat_buf) != 0) { write_stderr("%s: could not access the server configuration file /"%s/": %s/n", progname, ConfigFileName, strerror(errno)); free(configdir); return false; } /* * Read the configuration file for the first time. This time only the * data_directory parameter is picked up to determine the data directory, * so that we can read the PG_AUTOCONF_FILENAME file next time. */ ProcessConfigFile(PGC_POSTMASTER); /* * If the data_directory GUC variable has been set, use that as DataDir; * otherwise use configdir if set; else punt. * * Note: SetDataDir will copy and absolute-ize its argument, so we don't * have to. */ if (data_directory) SetDataDir(data_directory); else if (configdir) SetDataDir(configdir); else { write_stderr("%s does not know where to find the database system data./n" "This can be specified as /"data_directory/" in /"%s/", " "or by the -D invocation option, or by the " "PGDATA environment variable./n", progname, ConfigFileName); return false; } /* * Reflect the final DataDir value back into the data_directory GUC var. * (If you are wondering why we don't just make them a single variable, * it's because the EXEC_BACKEND case needs DataDir to be transmitted to * child backends specially. XXX is that still true? Given that we now * chdir to DataDir, EXEC_BACKEND can read the config file without knowing * DataDir in advance.) */ SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE); /* * Now read the config file a second time, allowing any settings in the * PG_AUTOCONF_FILENAME file to take effect. (This is pretty ugly, but * since we have to determine the DataDir before we can find the autoconf * file, the alternatives seem worse.) */ ProcessConfigFile(PGC_POSTMASTER); /* * If timezone_abbreviations wasn't set in the configuration file, install * the default value. We do it this way because we can't safely install a * "real" value until my_exec_path is set, which may not have happened * when InitializeGUCOptions runs, so the bootstrap default value cannot * be the real desired default. */ pg_timezone_abbrev_initialize(); /* * Figure out where pg_hba.conf is, and make sure the path is absolute. */ if (HbaFileName) fname = make_absolute_path(HbaFileName); else if (configdir) { fname = guc_malloc(FATAL, strlen(configdir) + strlen(HBA_FILENAME) + 2); sprintf(fname, "%s/%s", configdir, HBA_FILENAME); } else { write_stderr("%s does not know where to find the /"hba/" configuration file./n" "This can be specified as /"hba_file/" in /"%s/", " "or by the -D invocation option, or by the " "PGDATA environment variable./n", progname, ConfigFileName); return false; } SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); free(fname); /* * Likewise for pg_ident.conf. */ if (IdentFileName) fname = make_absolute_path(IdentFileName); else if (configdir) { fname = guc_malloc(FATAL, strlen(configdir) + strlen(IDENT_FILENAME) + 2); sprintf(fname, "%s/%s", configdir, IDENT_FILENAME); } else { write_stderr("%s does not know where to find the /"ident/" configuration file./n" "This can be specified as /"ident_file/" in /"%s/", " "or by the -D invocation option, or by the " "PGDATA environment variable./n", progname, ConfigFileName); return false; } SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); free(fname); free(configdir); return true; }
5、pqinitmask
/* * Initialize BlockSig, UnBlockSig, and StartupBlockSig. * * BlockSig is the set of signals to block when we are trying to block * signals. This includes all signals we normally expect to get, but NOT * signals that should never be turned off. * * StartupBlockSig is the set of signals to block during startup packet * collection; it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM. * * UnBlockSig is the set of signals to block when we don't want to block * signals (is this ever nonzero??) */ void pqinitmask(void) { sigemptyset(&UnBlockSig); /* First set all signals, then clear some. */ sigfillset(&BlockSig); sigfillset(&StartupBlockSig); /* * Unmark those signals that should never be blocked. Some of these signal * names don't exist on all platforms. Most do, but might as well ifdef * them all for consistency... */ #ifdef SIGTRAP sigdelset(&BlockSig, SIGTRAP); sigdelset(&StartupBlockSig, SIGTRAP); #endif #ifdef SIGABRT sigdelset(&BlockSig, SIGABRT); sigdelset(&StartupBlockSig, SIGABRT); #endif #ifdef SIGILL sigdelset(&BlockSig, SIGILL); sigdelset(&StartupBlockSig, SIGILL); #endif #ifdef SIGFPE sigdelset(&BlockSig, SIGFPE); sigdelset(&StartupBlockSig, SIGFPE); #endif #ifdef SIGSEGV sigdelset(&BlockSig, SIGSEGV); sigdelset(&StartupBlockSig, SIGSEGV); #endif #ifdef SIGBUS sigdelset(&BlockSig, SIGBUS); sigdelset(&StartupBlockSig, SIGBUS); #endif #ifdef SIGSYS sigdelset(&BlockSig, SIGSYS); sigdelset(&StartupBlockSig, SIGSYS); #endif #ifdef SIGCONT sigdelset(&BlockSig, SIGCONT); sigdelset(&StartupBlockSig, SIGCONT); #endif /* Signals unique to startup */ #ifdef SIGQUIT sigdelset(&StartupBlockSig, SIGQUIT); #endif #ifdef SIGTERM sigdelset(&StartupBlockSig, SIGTERM); #endif #ifdef SIGALRM sigdelset(&StartupBlockSig, SIGALRM); #endif }
6、BaseInit
/* * Early initialization of a backend (either standalone or under postmaster). * This happens even before InitPostgres. * * This is separate from InitPostgres because it is also called by auxiliary * processes, such as the background writer process, which may not call * InitPostgres at all. */ void BaseInit(void) { /* * Attach to shared memory and semaphores, and initialize our * input/output/debugging file descriptors. */ InitCommunication(); DebugFileOpen(); /* Do local initialization of file, storage and buffer managers */ InitFileAccess(); smgrinit(); InitBufferPoolAccess(); }
7、InitProcess
/* * InitProcess -- initialize a per-process data structure for this backend */ void InitProcess(void) { PGPROC *volatile *procgloballist; /* * ProcGlobal should be set up already (if we are a backend, we inherit * this by fork() or EXEC_BACKEND mechanism from the postmaster). */ if (ProcGlobal == NULL) elog(PANIC, "proc header uninitialized"); if (MyProc != NULL) elog(ERROR, "you already exist"); /* Decide which list should supply our PGPROC. */ if (IsAnyAutoVacuumProcess()) procgloballist = &ProcGlobal->autovacFreeProcs; else if (IsBackgroundWorker) procgloballist = &ProcGlobal->bgworkerFreeProcs; else procgloballist = &ProcGlobal->freeProcs; /* * Try to get a proc struct from the appropriate free list. If this * fails, we must be out of PGPROC structures (not to mention semaphores). * * While we are holding the ProcStructLock, also copy the current shared * estimate of spins_per_delay to local storage. */ SpinLockAcquire(ProcStructLock); set_spins_per_delay(ProcGlobal->spins_per_delay); MyProc = *procgloballist; if (MyProc != NULL) { *procgloballist = (PGPROC *) MyProc->links.next; SpinLockRelease(ProcStructLock); } else { /* * If we reach here, all the PGPROCs are in use. This is one of the * possible places to detect "too many backends", so give the standard * error message. XXX do we need to give a different failure message * in the autovacuum case? */ SpinLockRelease(ProcStructLock); ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); } MyPgXact = &ProcGlobal->allPgXact[MyProc->pgprocno]; /* * Cross-check that the PGPROC is of the type we expect; if this were not * the case, it would get returned to the wrong list. */ Assert(MyProc->procgloballist == procgloballist); /* * Now that we have a PGPROC, mark ourselves as an active postmaster * child; this is so that the postmaster can detect it if we exit without * cleaning up. (XXX autovac launcher currently doesn't participate in * this; it probably should.) */ if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess()) MarkPostmasterChildActive(); /* * Initialize all fields of MyProc, except for those previously * initialized by InitProcGlobal. */ SHMQueueElemInit(&(MyProc->links)); MyProc->waitStatus = STATUS_OK; MyProc->lxid = InvalidLocalTransactionId; MyProc->fpVXIDLock = false; MyProc->fpLocalTransactionId = InvalidLocalTransactionId; MyPgXact->xid = InvalidTransactionId; MyPgXact->xmin = InvalidTransactionId; MyProc->pid = MyProcPid; /* backendId, databaseId and roleId will be filled in later */ MyProc->backendId = InvalidBackendId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->isBackgroundWorker = IsBackgroundWorker; MyPgXact->delayChkpt = false; MyPgXact->vacuumFlags = 0; /* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */ if (IsAutoVacuumWorkerProcess()) MyPgXact->vacuumFlags |= PROC_IS_AUTOVACUUM; MyProc->lwWaiting = false; MyProc->lwWaitMode = 0; MyProc->waitLock = NULL; MyProc->waitProcLock = NULL; #ifdef USE_ASSERT_CHECKING { int i; /* Last process should have released all locks. */ for (i = 0; i < NUM_LOCK_PARTITIONS; i++) Assert(SHMQueueEmpty(&(MyProc->myProcLocks[i]))); } #endif MyProc->recoveryConflictPending = false; /* Initialize fields for sync rep */ MyProc->waitLSN = 0; MyProc->syncRepState = SYNC_REP_NOT_WAITING; SHMQueueElemInit(&(MyProc->syncRepLinks)); /* Initialize fields for group XID clearing. */ MyProc->procArrayGroupMember = false; MyProc->procArrayGroupMemberXid = InvalidTransactionId; pg_atomic_init_u32(&MyProc->procArrayGroupNext, INVALID_PGPROCNO); /* Check that group locking fields are in a proper initial state. */ Assert(MyProc->lockGroupLeader == NULL); Assert(dlist_is_empty(&MyProc->lockGroupMembers)); /* Initialize wait event information. */ MyProc->wait_event_info = 0; /* Initialize fields for group transaction status update. */ MyProc->clogGroupMember = false; MyProc->clogGroupMemberXid = InvalidTransactionId; MyProc->clogGroupMemberXidStatus = TRANSACTION_STATUS_IN_PROGRESS; MyProc->clogGroupMemberPage = -1; MyProc->clogGroupMemberLsn = InvalidXLogRecPtr; pg_atomic_init_u32(&MyProc->clogGroupNext, INVALID_PGPROCNO); /* * Acquire ownership of the PGPROC's latch, so that we can use WaitLatch * on it. That allows us to repoint the process latch, which so far * points to process local one, to the shared one. */ OwnLatch(&MyProc->procLatch); SwitchToSharedLatch(); /* * We might be reusing a semaphore that belonged to a failed process. So * be careful and reinitialize its value here. (This is not strictly * necessary anymore, but seems like a good idea for cleanliness.) */ PGSemaphoreReset(MyProc->sem); /* * Arrange to clean up at backend exit. */ on_shmem_exit(ProcKill, 0); /* * Now that we have a PGPROC, we could try to acquire locks, so initialize * local state needed for LWLocks, and the deadlock checker. */ InitLWLockAccess(); InitDeadLockChecking(); }
8、InitPostgres
/* -------------------------------- * InitPostgres * Initialize POSTGRES. * * The database can be specified by name, using the in_dbname parameter, or by * OID, using the dboid parameter. In the latter case, the actual database * name can be returned to the caller in out_dbname. If out_dbname isn't * NULL, it must point to a buffer of size NAMEDATALEN. * * Similarly, the username can be passed by name, using the username parameter, * or by OID using the useroid parameter. * * In bootstrap mode no parameters are used. The autovacuum launcher process * doesn't use any parameters either, because it only goes far enough to be * able to read pg_database; it doesn't connect to any particular database. * In walsender mode only username is used. * * As of PostgreSQL 8.2, we expect InitProcess() was already called, so we * already have a PGPROC struct ... but it's not completely filled in yet. * * Note: * Be very careful with the order of calls in the InitPostgres function. * -------------------------------- */ void InitPostgres(const char *in_dbname, Oid dboid, const char *username, Oid useroid, char *out_dbname, bool override_allow_connections) { bool bootstrap = IsBootstrapProcessingMode(); bool am_superuser; char *fullpath; char dbname[NAMEDATALEN]; elog(DEBUG3, "InitPostgres"); /* * Add my PGPROC struct to the ProcArray. * * Once I have done this, I am visible to other backends! */ InitProcessPhase2(); /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. * * Sets up MyBackendId, a unique backend identifier. */ MyBackendId = InvalidBackendId; SharedInvalBackendInit(false); if (MyBackendId > MaxBackends || MyBackendId <= 0) elog(FATAL, "bad backend ID: %d", MyBackendId); /* Now that we have a BackendId, we can participate in ProcSignal */ ProcSignalInit(MyBackendId); /* * Also set up timeout handlers needed for backend operation. We need * these in every case except bootstrap. */ if (!bootstrap) { RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert); RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler); RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler); RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT, IdleInTransactionSessionTimeoutHandler); } /* * bufmgr needs another initialization call too */ InitBufferPoolBackend(); /* * Initialize local process's access to XLOG. */ if (IsUnderPostmaster) { /* * The postmaster already started the XLOG machinery, but we need to * call InitXLOGAccess(), if the system isn't in hot-standby mode. * This is handled by calling RecoveryInProgress and ignoring the * result. */ (void) RecoveryInProgress(); } else { /* * We are either a bootstrap process or a standalone backend. Either * way, start up the XLOG machinery, and register to have it closed * down at exit. * * We don't yet have an aux-process resource owner, but StartupXLOG * and ShutdownXLOG will need one. Hence, create said resource owner * (and register a callback to clean it up after ShutdownXLOG runs). */ CreateAuxProcessResourceOwner(); StartupXLOG(); /* Release (and warn about) any buffer pins leaked in StartupXLOG */ ReleaseAuxProcessResources(true); /* Reset CurrentResourceOwner to nothing for the moment */ CurrentResourceOwner = NULL; on_shmem_exit(ShutdownXLOG, 0); } /* * Initialize the relation cache and the system catalog caches. Note that * no catalog access happens here; we only set up the hashtable structure. * We must do this before starting a transaction because transaction abort * would try to touch these hashtables. */ RelationCacheInitialize(); InitCatalogCache(); InitPlanCache(); /* Initialize portal manager */ EnablePortalManager(); /* Initialize stats collection --- must happen before first xact */ if (!bootstrap) pgstat_initialize(); /* * Load relcache entries for the shared system catalogs. This must create * at least entries for pg_database and catalogs used for authentication. */ RelationCacheInitializePhase2(); /* * Set up process-exit callback to do pre-shutdown cleanup. This is the * first before_shmem_exit callback we register; thus, this will be the * last thing we do before low-level modules like the buffer manager begin * to close down. We need to have this in place before we begin our first * transaction --- if we fail during the initialization transaction, as is * entirely possible, we need the AbortTransaction call to clean up. */ before_shmem_exit(ShutdownPostgres, 0); /* The autovacuum launcher is done here */ if (IsAutoVacuumLauncherProcess()) { /* report this backend in the PgBackendStatus array */ pgstat_bestart(); return; } /* * Start a new transaction here before first access to db, and get a * snapshot. We don't have a use for the snapshot itself, but we're * interested in the secondary effect that it sets RecentGlobalXmin. (This * is critical for anything that reads heap pages, because HOT may decide * to prune them even if the process doesn't attempt to modify any * tuples.) */ if (!bootstrap) { /* statement_timestamp must be set for timeouts to work correctly */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); /* * transaction_isolation will have been set to the default by the * above. If the default is "serializable", and we are in hot * standby, we will fail if we don't change it to something lower. * Fortunately, "read committed" is plenty good enough. */ XactIsoLevel = XACT_READ_COMMITTED; (void) GetTransactionSnapshot(); } /* * Perform client authentication if necessary, then figure out our * postgres user ID, and see if we are a superuser. * * In standalone mode and in autovacuum worker processes, we use a fixed * ID, otherwise we figure it out from the authenticated user name. */ if (bootstrap || IsAutoVacuumWorkerProcess()) { InitializeSessionUserIdStandalone(); am_superuser = true; } else if (!IsUnderPostmaster) { InitializeSessionUserIdStandalone(); am_superuser = true; if (!ThereIsAtLeastOneRole()) ereport(WARNING, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no roles are defined in this database system"), errhint("You should immediately run CREATE USER /"%s/" SUPERUSER;.", username != NULL ? username : "postgres"))); } else if (IsBackgroundWorker) { if (username == NULL && !OidIsValid(useroid)) { InitializeSessionUserIdStandalone(); am_superuser = true; } else { InitializeSessionUserId(username, useroid); am_superuser = superuser(); } } else { /* normal multiuser case */ Assert(MyProcPort != NULL); PerformAuthentication(MyProcPort); InitializeSessionUserId(username, useroid); am_superuser = superuser(); } /* * If we're trying to shut down, only superusers can connect, and new * replication connections are not allowed. */ if ((!am_superuser || am_walsender) && MyProcPort != NULL && MyProcPort->canAcceptConnections == CAC_WAITBACKUP) { if (am_walsender) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("new replication connections are not allowed during database shutdown"))); else ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect during database shutdown"))); } /* * Binary upgrades only allowed super-user connections */ if (IsBinaryUpgrade && !am_superuser) { ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect in binary upgrade mode"))); } /* * The last few connection slots are reserved for superusers. Although * replication connections currently require superuser privileges, we * don't allow them to consume the reserved slots, which are intended for * interactive use. */ if ((!am_superuser || am_walsender) && ReservedBackends > 0 && !HaveNFreeProcs(ReservedBackends)) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("remaining connection slots are reserved for non-replication superuser connections"))); /* Check replication permissions needed for walsender processes. */ if (am_walsender) { Assert(!bootstrap); if (!superuser() && !has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); } /* * If this is a plain walsender only supporting physical replication, we * don't want to connect to any particular database. Just finish the * backend startup by processing any options from the startup packet, and * we're done. */ if (am_walsender && !am_db_walsender) { /* process any options passed in the startup packet */ if (MyProcPort != NULL) process_startup_options(MyProcPort, am_superuser); /* Apply PostAuthDelay as soon as we've read all options */ if (PostAuthDelay > 0) pg_usleep(PostAuthDelay * 1000000L); /* initialize client encoding */ InitializeClientEncoding(); /* report this backend in the PgBackendStatus array */ pgstat_bestart(); /* close the transaction we started above */ CommitTransactionCommand(); return; } /* * Set up the global variables holding database id and default tablespace. * But note we won't actually try to touch the database just yet. * * We take a shortcut in the bootstrap case, otherwise we have to look up * the db's entry in pg_database. */ if (bootstrap) { MyDatabaseId = TemplateDbOid; MyDatabaseTableSpace = DEFAULTTABLESPACE_OID; } else if (in_dbname != NULL) { HeapTuple tuple; Form_pg_database dbform; tuple = GetDatabaseTuple(in_dbname); if (!HeapTupleIsValid(tuple)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database /"%s/" does not exist", in_dbname))); dbform = (Form_pg_database) GETSTRUCT(tuple); MyDatabaseId = HeapTupleGetOid(tuple); MyDatabaseTableSpace = dbform->dattablespace; /* take database name from the caller, just for paranoia */ strlcpy(dbname, in_dbname, sizeof(dbname)); } else if (OidIsValid(dboid)) { /* caller specified database by OID */ HeapTuple tuple; Form_pg_database dbform; tuple = GetDatabaseTupleByOid(dboid); if (!HeapTupleIsValid(tuple)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database %u does not exist", dboid))); dbform = (Form_pg_database) GETSTRUCT(tuple); MyDatabaseId = HeapTupleGetOid(tuple); MyDatabaseTableSpace = dbform->dattablespace; Assert(MyDatabaseId == dboid); strlcpy(dbname, NameStr(dbform->datname), sizeof(dbname)); /* pass the database name back to the caller */ if (out_dbname) strcpy(out_dbname, dbname); } else { /* * If this is a background worker not bound to any particular * database, we're done now. Everything that follows only makes sense * if we are bound to a specific database. We do need to close the * transaction we started before returning. */ if (!bootstrap) { pgstat_bestart(); CommitTransactionCommand(); } return; } /* * Now, take a writer's lock on the database we are trying to connect to. * If there is a concurrently running DROP DATABASE on that database, this * will block us until it finishes (and has committed its update of * pg_database). * * Note that the lock is not held long, only until the end of this startup * transaction. This is OK since we will advertise our use of the * database in the ProcArray before dropping the lock (in fact, that's the * next thing to do). Anyone trying a DROP DATABASE after this point will * see us in the array once they have the lock. Ordering is important for * this because we don't want to advertise ourselves as being in this * database until we have the lock; otherwise we create what amounts to a * deadlock with CountOtherDBBackends(). * * Note: use of RowExclusiveLock here is reasonable because we envision * our session as being a concurrent writer of the database. If we had a * way of declaring a session as being guaranteed-read-only, we could use * AccessShareLock for such sessions and thereby not conflict against * CREATE DATABASE. */ if (!bootstrap) LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, RowExclusiveLock); /* * Now we can mark our PGPROC entry with the database ID. * * We assume this is an atomic store so no lock is needed; though actually * things would work fine even if it weren't atomic. Anyone searching the * ProcArray for this database's ID should hold the database lock, so they * would not be executing concurrently with this store. A process looking * for another database's ID could in theory see a chance match if it read * a partially-updated databaseId value; but as long as all such searches * wait and retry, as in CountOtherDBBackends(), they will certainly see * the correct value on their next try. */ MyProc->databaseId = MyDatabaseId; /* * We established a catalog snapshot while reading pg_authid and/or * pg_database; but until we have set up MyDatabaseId, we won't react to * incoming sinval messages for unshared catalogs, so we won't realize it * if the snapshot has been invalidated. Assume it's no good anymore. */ InvalidateCatalogSnapshot(); /* * Recheck pg_database to make sure the target database hasn't gone away. * If there was a concurrent DROP DATABASE, this ensures we will die * cleanly without creating a mess. */ if (!bootstrap) { HeapTuple tuple; tuple = GetDatabaseTuple(dbname); if (!HeapTupleIsValid(tuple) || MyDatabaseId != HeapTupleGetOid(tuple) || MyDatabaseTableSpace != ((Form_pg_database) GETSTRUCT(tuple))->dattablespace) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database /"%s/" does not exist", dbname), errdetail("It seems to have just been dropped or renamed."))); } /* * Now we should be able to access the database directory safely. Verify * it's there and looks reasonable. */ fullpath = GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace); if (!bootstrap) { if (access(fullpath, F_OK) == -1) { if (errno == ENOENT) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database /"%s/" does not exist", dbname), errdetail("The database subdirectory /"%s/" is missing.", fullpath))); else ereport(FATAL, (errcode_for_file_access(), errmsg("could not access directory /"%s/": %m", fullpath))); } ValidatePgVersion(fullpath); } SetDatabasePath(fullpath); /* * It's now possible to do real access to the system catalogs. * * Load relcache entries for the system catalogs. This must create at * least the minimum set of "nailed-in" cache entries. */ RelationCacheInitializePhase3(); /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); /* * Re-read the pg_database row for our database, check permissions and set * up database-specific GUC settings. We can't do this until all the * database-access infrastructure is up. (Also, it wants to know if the * user is a superuser, so the above stuff has to happen first.) */ if (!bootstrap) CheckMyDatabase(dbname, am_superuser, override_allow_connections); /* * Now process any command-line switches and any additional GUC variable * settings passed in the startup packet. We couldn't do this before * because we didn't know if client is a superuser. */ if (MyProcPort != NULL) process_startup_options(MyProcPort, am_superuser); /* Process pg_db_role_setting options */ process_settings(MyDatabaseId, GetSessionUserId()); /* Apply PostAuthDelay as soon as we've read all options */ if (PostAuthDelay > 0) pg_usleep(PostAuthDelay * 1000000L); /* * Initialize various default states that can't be set up until we've * selected the active user and gotten the right GUC settings. */ /* set default namespace search path */ InitializeSearchPath(); /* initialize client encoding */ InitializeClientEncoding(); /* Initialize this backend's session state. */ InitializeSession(); /* report this backend in the PgBackendStatus array */ if (!bootstrap) pgstat_bestart(); /* close the transaction we started above */ if (!bootstrap) CommitTransactionCommand(); }
三、跟踪分析
插入测试数据:
testdb=# -- 获取pid testdb=# select pg_backend_pid(); pg_backend_pid ---------------- 1893 (1 row) testdb=# -- 插入1行 testdb=# insert into t_insert values(23,'I am PostgresMain','I am PostgresMain','I am PostgresMain'); testdb=# -- 插入1行 insert into t_insert values(23,'I am PostgresMain','I am PostgresMain','I am PostgresMain'); (挂起)
启动gdb,跟踪调试:
[root@localhost ~]# gdb -p 1893 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7 Copyright (C) 2013 Free Software Foundation, Inc. ... #断点设置在循环中 (gdb) b postgres.c:4013 Breakpoint 1 at 0x850d26: file postgres.c, line 4013. ... (gdb) p input_message $7 = {data = 0x1508ef0 "insert into t_insert values(23,'I am PostgresMain','I am PostgresMain','I am PostgresMain');", len = 93, maxlen = 1024, cursor = 0} (gdb) n ... 4135 switch (firstchar) (gdb) 4142 SetCurrentStatementStartTimestamp(); (gdb) p firstchar $8 = 81 ... (gdb) finish Run till exit from #0 PostgresMain (argc=1, argv=0x1532aa8, dbname=0x1532990 "testdb", username=0x1532978 "xdb") at postgres.c:4020 #DONE!
使用gdb跟踪postgres进程启动过程:
[xdb@localhost ~]$ gdb postgres ... (gdb) set follow-fork-mode child (gdb) start Temporary breakpoint 1 at 0x6f1735: file main.c, line 62. Starting program: /appdb/xdb/bin/postgres [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe538) at main.c:62 62 bool do_check_root = true; (gdb) b PostgresMain Breakpoint 2 at 0x8507bb: file postgres.c, line 3631. (gdb) del 1 No breakpoint number 1. ... (gdb) attach 3028 Attaching to program: /appdb/xdb/bin/postgres, process 3028 ... #连接DB [xdb@localhost ~]$ psql -d testdb #回到gdb #finish 直至进入postmaster.c中的PostgresMain (gdb) finish Run till exit from #0 ServerLoop () at postmaster.c:1704 [New process 3042] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [Switching to Thread 0x7ffff7feb740 (LWP 3042)] Breakpoint 2, PostgresMain (argc=1, argv=0xf28ac8, dbname=0xf289b0 "testdb", username=0xf28998 "xdb") at postgres.c:3631 3631 volatile bool send_ready_for_query = true; (gdb) next 3632 bool disable_idle_in_transaction_timeout = false; (gdb) 3635 if (!IsUnderPostmaster) (gdb) p IsUnderPostmaster $3 = true (gdb) p dbname $4 = 0xf289b0 "testdb" (gdb) p username $5 = 0xf28998 "xdb" ... 3845 MessageContext = AllocSetContextCreate(TopMemoryContext, (gdb) 3855 row_description_context = AllocSetContextCreate(TopMemoryContext, (gdb) 3858 MemoryContextSwitchTo(row_description_context); (gdb) 3859 initStringInfo(&row_description_buf); (gdb) 3860 MemoryContextSwitchTo(TopMemoryContext); (gdb) 3865 if (!IsUnderPostmaster) (gdb) 3890 if (sigsetjmp(local_sigjmp_buf, 1) != 0) (gdb) p *MessageContext $6 = {type = T_AllocSetContext, isReset = true, allowInCritSection = false, methods = 0xb8c720 <AllocSetMethods>, parent = 0xef9b90, firstchild = 0x0, prevchild = 0xf74c80, nextchild = 0xfabb50, name = 0xb4e87c "MessageContext", ident = 0x0, reset_cbs = 0x0} ... (gdb) n 4090 DoingCommandRead = true; (gdb) 4095 firstchar = ReadCommand(&input_message); (gdb) 4106 CHECK_FOR_INTERRUPTS(); (gdb) 4107 DoingCommandRead = false; (gdb) p firstchar $8 = 81 (gdb) p input_message $9 = {data = 0xeff010 "insert into t_insert values(24,'I am PostgresMain','I am PostgresMain','I am PostgresMain');", len = 93, maxlen = 1024, cursor = 0} (gdb) n ... #DONE!
到此,相信大家对“PostgreSQL中PostgresMain函数的实现逻辑是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
原创文章,作者:kirin,如若转载,请注明出处:https://blog.ytso.com/tech/database/205346.html