summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Suzuki2011-11-29 04:16:16 +0000
committerKoichi Suzuki2011-11-29 04:16:16 +0000
commita43534d7519176e6dcdaf4517b329c2fed935c8b (patch)
treed38427ba59524e1bde9dc997df8d94873d89c1f1
parent66a9181ee348aa2149b42ef79fa0ef6da95e2390 (diff)
This commit includes configuration file support for GTM and GTM-Proxy,
together with a couple of bug fix of GTM Standby and GTM-Proxy reconnect. Now GTM and GTM-Proxy configuration files are mandatory. They should be placed at the working directory, as specified by -D command line option, as "gtm.conf" and "gtm_proxy.conf", respectively. Their format is the same as Details will be found in the documentation. Similar to coordinator/datanode, you can override some of the parameters in configuration file by command line options. Modified/affected files are as follows: modified: doc-xc/src/sgml/ref/gtm.sgmlin modified: doc-xc/src/sgml/ref/gtm_proxy.sgmlin modified: src/gtm/Makefile modified: src/gtm/README modified: src/gtm/common/Makefile new file: src/gtm/common/gtm_opt_handler.c new file: src/gtm/common/gtm_opt_scanner.l modified: src/gtm/gtm_ctl/gtm_ctl.c modified: src/gtm/main/Makefile new file: src/gtm/main/gtm.conf.sample new file: src/gtm/main/gtm_opt.c modified: src/gtm/main/gtm_standby.c modified: src/gtm/main/main.c modified: src/gtm/path/path.c modified: src/gtm/proxy/Makefile new file: src/gtm/proxy/gtm_proxy.conf.sample new file: src/gtm/proxy/gtm_proxy_opt.c modified: src/gtm/proxy/proxy_main.c modified: src/gtm/recovery/register.c modified: src/gtm/recovery/standby_utils.c modified: src/include/gtm/gtm.h new file: src/include/gtm/gtm_opt.h new file: src/include/gtm/gtm_opt_tables.h modified: src/include/gtm/gtm_proxy.h modified: src/include/gtm/gtm_standby.h modified: src/include/gtm/path.h
-rw-r--r--doc-xc/src/sgml/ref/gtm.sgmlin229
-rw-r--r--doc-xc/src/sgml/ref/gtm_proxy.sgmlin208
-rw-r--r--src/gtm/Makefile4
-rw-r--r--src/gtm/README40
-rw-r--r--src/gtm/common/Makefile13
-rw-r--r--src/gtm/common/gtm_opt_handler.c3522
-rw-r--r--src/gtm/common/gtm_opt_scanner.l92
-rw-r--r--src/gtm/gtm_ctl/gtm_ctl.c46
-rw-r--r--src/gtm/main/Makefile4
-rw-r--r--src/gtm/main/gtm.conf.sample58
-rw-r--r--src/gtm/main/gtm_opt.c342
-rw-r--r--src/gtm/main/gtm_standby.c12
-rw-r--r--src/gtm/main/main.c153
-rw-r--r--src/gtm/path/path.c62
-rw-r--r--src/gtm/proxy/Makefile4
-rw-r--r--src/gtm/proxy/gtm_proxy.conf.sample64
-rw-r--r--src/gtm/proxy/gtm_proxy_opt.c392
-rw-r--r--src/gtm/proxy/proxy_main.c218
-rw-r--r--src/gtm/recovery/register.c144
-rw-r--r--src/gtm/recovery/standby_utils.c6
-rw-r--r--src/include/gtm/gtm.h2
-rw-r--r--src/include/gtm/gtm_opt.h328
-rw-r--r--src/include/gtm/gtm_opt_tables.h194
-rw-r--r--src/include/gtm/gtm_proxy.h2
-rw-r--r--src/include/gtm/gtm_standby.h6
-rw-r--r--src/include/gtm/path.h3
26 files changed, 6025 insertions, 123 deletions
diff --git a/doc-xc/src/sgml/ref/gtm.sgmlin b/doc-xc/src/sgml/ref/gtm.sgmlin
index 24a2e56696..c27703ef81 100644
--- a/doc-xc/src/sgml/ref/gtm.sgmlin
+++ b/doc-xc/src/sgml/ref/gtm.sgmlin
@@ -39,8 +39,232 @@ PostgreSQL documentation
using gtm_ctl(8).
</para>
+ <para>
+ You must provide gtm configuration
+ file <filename>gtm.conf</filename> placed at gtm working directory
+ as specified by <literal>-D</literal> command line option. The
+ configuration file specifies gtm running envinment and resources.
+ </para>
+
+ <para>
+ Some of the parameters specified in the control file can be overriden by
+ command line options.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Configuration File</title>
+
+&xconly
+ <para>
+ <literal>GTM</literal> configuration parameters are specified in the configuration file
+ <filename>gtm.conf</filename> placed in the working directory
+ specified as <literal>-D</literal> option
+ of <application>gtm</application> command line option as described
+ in the next section.
+ </para>
+ <para>
+ Format of the configuration file is the same as <filename>postgresql.conf</filename>.
+ Options are as follows.
+ </para>
+
+<!-- Add description of each gtm.conf entry -->
+<!-- Notice
+ The following options are found in the source code (gtm_opt.c) but is only for
+ internal use and will not be presented in the following list.
+
+ 1. data_di
+ 2. config_file
+
+ Also the following options are for high-availability hook. This will be
+ documented later.
+
+ 1. error_reporter
+ 2. status_reader
+-->
+ <variablelist>
+
+ <varlistentry id="gtm-opt-active-host" xreflabel="gtm_opt_active_host">
+ <term><varname>active_host</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>active_host</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies listen addresses (host name or IP address) of active <application>gtm</application>. This
+ parameter is effective only when this <application>gtm</application>
+ is specified as a standby. There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-active-port" xreflabel="gtm_opt_active_port">
+ <term><varname>active_port</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>active_port</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the port number of active <application>gtm</application>. This
+ parameter is effective only when this <application>gtm</application>
+ is specified as a standby.
+ There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-keepalives-count" xreflabel="gtm_opt_keepalives_count">
+ <term><varname>keepalives_count</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_count</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_count</literal> option for the
+ connection to <application>gtm</application>. This option is
+ effective only when it runs as GTM-Standby.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-keepalives-idle" xreflabel="gtm_opt_keepalives_idle">
+ <term><varname>keepalives_idle</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_idle</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_idle</literal> option for the
+ connection to <application>gtm</application>. This option is
+ effective only when it runs as GTM-Standby.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-keepalives-interval" xreflabel="gtm_opt_keepalives_interval">
+ <term><varname>keepalives_interval</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_interval</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_interval</literal> option for the
+ connection to <application>gtm</application>. This option is
+ effective only when it runs as GTM-Standby.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-listen-addresses" xreflabel="gtm_opt_listen_addresses">
+ <term><varname>listen_addresses</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>listen_addresses</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies listen addresses (host name or IP address) of
+ this <application>gtm</application>
+ or <application>gtm</application> standby.
+ Default value is '*'.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-log-file" xreflabel="gtm_opt_log_file">
+ <term><varname>log_file</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>log_file</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <filename>log</filename> file name. This file will be
+ created at the working directory of
+ this <application>gtm</application> as specified
+ by <literal>-D</literal> command line option.
+ The default is <filename>gtm.log</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-log-min-messages" xreflabel="gtm_opt_log_min_messages">
+ <term><varname>log_min_messages</varname> (<type>enum</type>)</term>
+ <indexterm>
+ <primary><varname>log_min_messages</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Controls which message levels are written to the log.
+ Valid values are <literal>DEBUG</literal>, <literal>DEBUG5</literal>,
+ <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+ <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+ <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+ <literal>FATAL</literal> and <literal>PANIC</literal>.
+ Each level includes all the levels that follow it.
+ The later the level, the fewer messages are sent.
+ The default is <literal>WARNING</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-nodename" xreflabel="gtm_opt_nodename">
+ <term><varname>nodename</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>nodename</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the name of this <application>gtm</application> or <application>gtm</application> standby.
+ There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-port" xreflabel="gtm_opt_port">
+ <term><varname>port</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>port</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the port number of <application>gtm</application> or <application>gtm</application> standby.
+ Default value is 6666.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-opt-startup" xreflabel="gtm_opt_startup">
+ <term><varname>startup</varname> (<type>enum</type>)</term>
+ <indexterm>
+ <primary><varname>startup</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the startup mode of this <application>gtm</application>.
+ Valied values are <literal>act</literal> or <literal>standby</literal>.
+ <literal>act</literal> means to start up
+ this <application>gtm</application> as usual so
+ that <application>gtm</application> clients (coordinators, data
+ nodes or gtm-proxies) can connect for transaction
+ management. <literal>standby</literal> means
+ this <application>gtm</application> starts up as a backup
+ of <literal>act</literal>
+ gtm. <literal>standby</literal> <literal>gtm</literal> can be
+ promoted to <literal>act</literal> when <literal>act</literal>
+ fails.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
+ </variablelist>
+
</refsect1>
+
<refsect1>
<title>Options</title>
@@ -51,6 +275,11 @@ PostgreSQL documentation
</para>
<para>
+ Parameters specified as command line options override these specified in the configuration file described
+ in the previous section.
+ </para>
+
+ <para>
Options are as follows:
</para>
diff --git a/doc-xc/src/sgml/ref/gtm_proxy.sgmlin b/doc-xc/src/sgml/ref/gtm_proxy.sgmlin
index fc434a7bd1..d8ee7f6327 100644
--- a/doc-xc/src/sgml/ref/gtm_proxy.sgmlin
+++ b/doc-xc/src/sgml/ref/gtm_proxy.sgmlin
@@ -44,6 +44,214 @@ PostgreSQL documentation
It is highly advised to start and stop gtm_proxy with gtm_ctl(8).
</para>
+ <para>
+ You must provide gtm configuration
+ file <filename>gtm_proxy.conf</filename> placed at gtm working directory
+ as specified by <literal>-D</literal> command line option. The
+ configuration file specifies gtm running envinment and resources.
+ </para>
+
+ <para>
+ Some of the parameters specified in the control file can be overriden by
+ command line options.
+ </para>
+
+ </refsect1>
+
+<refsect1>
+ <title>Configuration File</title>
+
+&xconly
+ <para>
+ <literal>GTM-Proxy</literal> configuration parameters are specified in the configuration file
+ <filename>gtm_proxy.conf</filename> placed in the working directory
+ specified as <literal>-D</literal> option
+ of <application>gtm_proxy</application> command line option as described
+ in the next section.
+ </para>
+
+ <para>
+ Format of the configuration file is the same as <filename>postgresql.conf</filename>.
+ Options are as follows.
+ </para>
+
+<!-- Add description of each gtm.conf entry -->
+<!-- Notice
+ The following options are found in the source code (gtm_opt.c) but is only for
+ internal use and will not be presented in the following list.
+
+ 1. data_dir
+ 2. config_file
+
+ Also the following options are for high-availability hook. This will be
+ documented later.
+ 1. error_reporter
+ 2. status_reader
+ 3. err_wait_opt
+ 4. err_wait_count
+ 5. err_wait_interval
+-->
+ <variablelist>
+
+ <varlistentry id="gtm-proxy-opt-gtm-host" xreflabel="gtm_proxy_opt_gtm_host">
+ <term><varname>gtm_host</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>gtm_host</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies listen addresses (host name or IP address) of <application>gtm</application>.
+ There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-gtm-port" xreflabel="gtm_proxy_opt_gtm_port">
+ <term><varname>gtm_port</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>gtm_port</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the port number of <application>gtm</application>.
+ There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-keepalives-count" xreflabel="gtm_proxy_opt_keepalives_count">
+ <term><varname>keepalives_count</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_count</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_count</literal> option for the
+ connection to <application>gtm</application>.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-keepalives-idle" xreflabel="gtm_proxy_opt_keepalives_idle">
+ <term><varname>keepalives_idle</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_idle</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_idle</literal> option for the
+ connection to <application>gtm</application>.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-keepalives-interval" xreflabel="gtm_proxy_opt_keepalives_interval">
+ <term><varname>keepalives_interval</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>keepalives_interval</varname> (<type>integer</type>)</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <literal>keepalives_interval</literal> option for the
+ connection to <application>gtm</application>.
+ Default value is zero and keepalives feature is disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-log-file" xreflabel="gtm_proxy_opt_log_file">
+ <term><varname>log_file</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>log_file</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies <filename>log</filename> file name. This file will be
+ created at the working directory of
+ this <application>gtm_proxy</application> as specified
+ by <literal>-D</literal> command line option.
+ The default is <filename>gtm_proxy.log</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-log-min-messages" xreflabel="gtm_proxy_opt_log_min_messages">
+ <term><varname>log_min_messages</varname> (<type>enum</type>)</term>
+ <indexterm>
+ <primary><varname>log_min_messages</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Controls which message levels are written to the log.
+ Valid values are <literal>DEBUG</literal>, <literal>DEBUG5</literal>,
+ <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+ <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+ <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+ <literal>FATAL</literal> and <literal>PANIC</literal>.
+ Each level includes all the levels that follow it.
+ The later the level, the fewer messages are sent.
+ The default is <literal>WARNING</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-listen-addresses" xreflabel="gtm_proxy_opt_listen_addresses">
+ <term><varname>listen_addresses</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>listen_addresses</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies listen addresses (host name or IP address) of
+ this <application>gtm_proxy</application>.
+ Default value is '*'.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-nodename" xreflabel="gtm_proxy_opt_nodename">
+ <term><varname>nodename</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>nodename</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the name of this <application>gtm_proxy</application>.
+ There is no default value for this parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-port" xreflabel="gtm_proxy_opt_port">
+ <term><varname>port</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>port</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the port number of this <application>gtm_proxy</application>.
+ Default port value is 6666.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="gtm-proxy-opt-worker-threads" xreflabel="gtm_proxy_opt_worker_threads">
+ <term><varname>worker_threads</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>worker_threads</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the number of worker threads for this <literal>gtm_proxy</literal>.
+ Default value is 1.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
</refsect1>
<refsect1>
diff --git a/src/gtm/Makefile b/src/gtm/Makefile
index 2616cec901..ee176c3b8a 100644
--- a/src/gtm/Makefile
+++ b/src/gtm/Makefile
@@ -36,8 +36,12 @@ install: all
$(INSTALL_PROGRAM) ./main/gtm$(X) '$(DESTDIR)$(bindir)/gtm$(X)'
$(INSTALL_PROGRAM) ./gtm_ctl/gtm_ctl$(X) '$(DESTDIR)$(bindir)/gtm_ctl$(X)'
$(INSTALL_PROGRAM) ./proxy/gtm_proxy$(X) '$(DESTDIR)$(bindir)/gtm_proxy$(X)'
+ $(INSTALL_PROGRAM) ./main/gtm.conf.sample '$(DESTDIR)$(datadir)/gtm.conf.sample'
+ $(INSTALL_PROGRAM) ./proxy/gtm_proxy.conf.sample '$(DESTDIR)$(datadir)/gtm_proxy.conf.sample'
uninstall:
rm -f $(DESTDIR)$(bindir)/gtm$(X)
rm -f $(DESTDIR)$(bindir)/gtm_ctl$(X)
rm -f $(DESTDIR)$(bindir)/gtm_proxy$(X)
+ rm -f $(DESTDIR)$(datadir)/gtm.conf.sample
+ rm -f $(DESTDIR)$(datadir)/gtm_proxy.conf.sample
diff --git a/src/gtm/README b/src/gtm/README
index 77cff3695b..cb1f17804b 100644
--- a/src/gtm/README
+++ b/src/gtm/README
@@ -59,3 +59,43 @@ This would build various test clients, statically linking to the libgtmclient.a
library in the client directory. You may need to change the connect string
appropriately connect to the GTM server.
+============================================================
+Additional Information for NODE_NAME, GTM_CONFIGURATION and GTM-Standby
+
+Nov. 25, 2011
+============================================================
+
+1. NODE_NAME project
+
+Now XC changed its naming convention of each component from number to string
+name. According to this, all the "id" is now changed to "name".
+
+2. GTM_CONFIGURATION
+
+Because GTM and GTM-Proxy has more options, it is not convenient to specify all
+of these in command line options. Instaead, as in PostgreSQL, GTM/GTM-Proxy
+now need their configuration file placed on their working directries.
+
+Details will be available in the documentation, gtm.conf.sample and gtm_proxy.conf.sample.
+Sample files will be installed in the "share" directory at the installation directory.
+
+3. GTM-Standby
+
+To prevent GTM from SPOF, GTM-Standby feature is now under the development.
+At present, only a basic feature is available. GTM-Standby can backup GTM.
+When GTM fails, GTM-Standby can fail-over. GTM-Proxy can reconnect to the
+new GTM without sacrificing running transactions. However, please note that
+GTM-Standby has to connect to GTM before any GTM-Proxy connects to it.
+
+Peple who modify GTM code should be careful to test this feature works.
+The test will be:
+
+1) Start GTM in ACT mode, and the another GTM as Standby mode.
+2) Start more than one GTM-Proxies.
+3) Start datanode/coordinator
+4) Run psql
+5) While psql is in a transaction block, promote GTM-Standby and then
+ reconnect GTM-Proxy to the promoted GTM.
+6) See if transaction keeps running.
+
+The test sequence will be expanded as GTM-Standby feature improves.
diff --git a/src/gtm/common/Makefile b/src/gtm/common/Makefile
index c295fb43b1..769ed16876 100644
--- a/src/gtm/common/Makefile
+++ b/src/gtm/common/Makefile
@@ -13,9 +13,16 @@ SO_MINOR_VERSION= 0
LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq
LIBS=-lpthread
-OBJS=aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o
+OBJS=gtm_opt_scanner.o aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o gtm_opt_handler.o
-all:all-lib
+all:gtm_opt_scanner.c all-lib
+
+gtm_opt_hander.o:gtm_opt_scanner.h gtm_opt_handler.c
+
+gtm_opt_scanner.h:gtm_opt_scanner.c
+
+gtm_opt_scanner.c:gtm_opt_scanner.l
+ $(FLEX) $(FLEXFLAGS) --header-file=gtm_opt_scanner.h -o'$@' $<
# Shared library stuff
include $(top_srcdir)/src/Makefile.shlib
@@ -23,6 +30,8 @@ include $(top_srcdir)/src/Makefile.shlib
clean:
rm -f $(OBJS)
rm -f libgtm.so libgtm.so.1 libgtm.so.1.0
+ rm -f gtm_opt_scanner.c
+ rm -f gtm_opt_scanner.h
distclean: clean
diff --git a/src/gtm/common/gtm_opt_handler.c b/src/gtm/common/gtm_opt_handler.c
new file mode 100644
index 0000000000..63490386b0
--- /dev/null
+++ b/src/gtm/common/gtm_opt_handler.c
@@ -0,0 +1,3522 @@
+/* -*-pgsql-c-*- */
+/*
+ * Scanner for the configuration file
+ *
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/misc/guc-file.l
+ */
+
+#include "gtm/gtm.h"
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mb/pg_wchar.h"
+#include "gtm/path.h"
+#include "gtm/assert.h"
+#include "gtm/gtm_opt.h"
+#include "gtm/gtm_opt_tables.h"
+#include "gtm/elog.h"
+#include "./gtm_opt_scanner.h"
+
+
+/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
+#undef fprintf
+#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg)))
+
+enum {
+ GTMOPT_ID = 1,
+ GTMOPT_STRING = 2,
+ GTMOPT_INTEGER = 3,
+ GTMOPT_REAL = 4,
+ GTMOPT_EQUALS = 5,
+ GTMOPT_UNQUOTED_STRING = 6,
+ GTMOPT_QUALIFIED_ID = 7,
+ GTMOPT_EOL = 99,
+ GTMOPT_ERROR = 100
+};
+
+static unsigned int ConfigFileLineno;
+
+/* flex fails to supply a prototype for GTMOPT_yylex, so provide one */
+int GTMOPT_GTMOPT_yylex(void);
+
+/* Functions defeined in this file */
+static char *GTMOPT_scanstr(const char *s);
+static struct config_generic *find_option(const char *name, bool create_placeholders, int elevel);
+static char *gtm_opt_strdup(int elevel, const char *src);
+static int gtm_opt_name_compare(const char *namea, const char *nameb);
+struct config_generic **get_gtm_opt_variables(void);
+void build_gtm_opt_variables(void);
+static bool gtm_opt_parse_bool(const char *value, bool *result);
+static bool gtm_opt_parse_bool_with_len(const char *value, size_t len, bool *result);
+static void set_config_sourcefile(const char *name, char *sourcefile, int sourceline);
+static int gtm_opt_var_compare(const void *a, const void *b);
+static void InitializeOneGTMOption(struct config_generic * gconf);
+static void ReportGTMOption(struct config_generic * record);
+static char *_ShowOption(struct config_generic * record, bool use_units);
+
+/*
+ * Variables to bel fed by specific option definition: gtm_opt.c and gtm_proxy_opt.c
+ */
+extern char *GTMConfigFileName;
+extern char *data_directory;
+extern struct config_generic **gtm_opt_variables;
+extern int num_gtm_opt_variables;
+extern int size_gtm_opt_variables;
+extern bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */
+extern char *config_filename; /* Default configuration file name */
+extern int GTMOptUpdateCount; /* Indicates when specific option is updated */
+extern bool isStartUp;
+
+/*
+ * Tables of options: to be defined in gtm_opt.c and gtm_proxy_opt.c
+ */
+extern struct config_bool ConfigureNamesBool[];
+extern struct config_int ConfigureNamesInt[];
+extern struct config_real ConfigureNamesReal[];
+extern struct config_string ConfigureNamesString[];
+extern struct config_enum ConfigureNamesEnum[];
+
+/*
+ * Note: MAX_BACKENDS is limited to 2^23-1 because inval.c stores the
+ * backend ID as a 3-byte signed integer. Even if that limitation were
+ * removed, we still could not exceed INT_MAX/4 because some places compute
+ * 4*MaxBackends without any overflow check. This is rechecked in
+ * check_maxconnections, since MaxBackends is computed as MaxConnections
+ * plus autovacuum_max_workers plus one (for the autovacuum launcher).
+ */
+#define MAX_BACKENDS 0x7fffff
+
+#define KB_PER_MB (1024)
+#define KB_PER_GB (1024*1024)
+
+#define MS_PER_S 1000
+#define S_PER_MIN 60
+#define MS_PER_MIN (1000 * 60)
+#define MIN_PER_H 60
+#define S_PER_H (60 * 60)
+#define MS_PER_H (1000 * 60 * 60)
+#define MIN_PER_D (60 * 24)
+#define S_PER_D (60 * 60 * 24)
+#define MS_PER_D (1000 * 60 * 60 * 24)
+
+/*
+ * Exported function to read and process the configuration file. The
+ * parameter indicates in what context the file is being read --- either
+ * postmaster startup (including standalone-backend startup) or SIGHUP.
+ * All options mentioned in the configuration file are set to new values.
+ * If an error occurs, no values will be changed.
+ */
+void
+ProcessConfigFile(GtmOptContext context)
+{
+ int elevel;
+ ConfigVariable *item,
+ *head,
+ *tail;
+ char *cvc = NULL;
+ int i;
+
+ Assert((context == GTMC_STARTUP || context == GTMC_SIGHUP));
+
+ if (context == GTMC_SIGHUP)
+ elevel = DEBUG2;
+ else
+ elevel = ERROR;
+
+ /* Parse the file into a list of option names and values */
+ head = tail = NULL;
+
+ if (!ParseConfigFile(GTMConfigFileName, NULL, 0, elevel, &head, &tail))
+ goto cleanup_list;
+
+#if 0
+ /* No custom_variable_classes now */
+ /*
+ * This part of the code remained the same as original guc.c because
+ * we might want to have custom variable class for gtm.conf.
+ */
+ /*
+ * We need the proposed new value of custom_variable_classes to check
+ * custom variables with. ParseConfigFile ensured that if it's in
+ * the file, it's first in the list. But first check to see if we
+ * have an active value from the command line, which should override
+ * the file in any case. (Since there's no relevant env var, the
+ * only possible nondefault sources are the file and ARGV.)
+ */
+ cvc_struct = (struct config_string *)
+ find_option("custom_variable_classes", false, elevel);
+ Assert(cvc_struct);
+ if (cvc_struct->gen.reset_source > GTMC_S_FILE)
+ {
+ cvc = gtm_opt_strdup(elevel, cvc_struct->reset_val);
+ if (cvc == NULL)
+ goto cleanup_list;
+ }
+ else if (head != NULL &&
+ gtm_opt_name_compare(head->name, "custom_variable_classes") == 0)
+ {
+ /*
+ * Need to canonicalize the value by calling the check hook.
+ */
+ void *extra = NULL;
+
+ cvc = gtm_opt_strdup(elevel, head->value);
+ if (cvc == NULL)
+ goto cleanup_list;
+ if (extra)
+ free(extra);
+ }
+#endif
+
+ /*
+ * Mark all extant GUC variables as not present in the config file.
+ * We need this so that we can tell below which ones have been removed
+ * from the file since we last processed it.
+ */
+ for (i = 0; i < num_gtm_opt_variables; i++)
+ {
+ struct config_generic *gconf = gtm_opt_variables[i];
+
+ gconf->status &= ~GTMOPT_IS_IN_FILE;
+ }
+
+ /*
+ * Check if all options are valid. As a side-effect, the GTMOPT_IS_IN_FILE
+ * flag is set on each GUC variable mentioned in the list.
+ */
+ for (item = head; item; item = item->next)
+ {
+ char *sep = strchr(item->name, GTMOPT_QUALIFIER_SEPARATOR);
+
+ if (sep)
+ {
+ /*
+ * There is no GUC entry. If we called set_config_option then
+ * it would make a placeholder, which we don't want to do yet,
+ * since we could still fail further down the list. Do nothing
+ * (assuming that making the placeholder will succeed later).
+ */
+ if (find_option(item->name, false, elevel) == NULL)
+ continue;
+ /*
+ * 3. There is already a GUC entry (either real or placeholder) for
+ * the variable. In this case we should let set_config_option
+ * check it, since the assignment could well fail if it's a real
+ * entry.
+ */
+ }
+
+ if (!set_config_option(item->name, item->value, context,
+ GTMC_S_FILE, false))
+ goto cleanup_list;
+ }
+
+ /*
+ * Check for variables having been removed from the config file, and
+ * revert their reset values (and perhaps also effective values) to the
+ * boot-time defaults. If such a variable can't be changed after startup,
+ * just throw a warning and continue. (This is analogous to the fact that
+ * set_config_option only throws a warning for a new but different value.
+ * If we wanted to make it a hard error, we'd need an extra pass over the
+ * list so that we could throw the error before starting to apply
+ * changes.)
+ */
+ for (i = 0; i < num_gtm_opt_variables; i++)
+ {
+ struct config_generic *gconf = gtm_opt_variables[i];
+ GtmOptStack *stack;
+
+ if (gconf->reset_source != GTMC_S_FILE ||
+ (gconf->status & GTMOPT_IS_IN_FILE))
+ continue;
+ if (gconf->context < GTMC_SIGHUP)
+ {
+ /*
+ * In the original code, errcode() stores specified error code to sqlerrcode, which does not
+ * exist in GTM.
+ */
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server",
+ gconf->name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ gconf->name)));
+ }
+ continue;
+ }
+
+ /*
+ * Reset any "file" sources to "default", else set_config_option
+ * will not override those settings.
+ */
+ if (gconf->reset_source == GTMC_S_FILE)
+ gconf->reset_source = GTMC_S_DEFAULT;
+ if (gconf->source == GTMC_S_FILE)
+ gconf->source = GTMC_S_DEFAULT;
+ for (stack = gconf->stack; stack; stack = stack->prev)
+ {
+ if (stack->source == GTMC_S_FILE)
+ stack->source = GTMC_S_DEFAULT;
+ }
+
+ /* Now we can re-apply the wired-in default (i.e., the boot_val) */
+ set_config_option(gconf->name, NULL, context, GTMC_S_DEFAULT,
+ true);
+ if (context == GTMC_SIGHUP)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" removed from configuration file, reset to default\n",
+ gconf->name);
+ }
+ else
+ {
+ ereport(elevel,
+ (errmsg("parameter \"%s\" removed from configuration file, reset to default",
+ gconf->name)));
+ }
+ }
+ }
+
+ /*
+ * Restore any variables determined by environment variables or
+ * dynamically-computed defaults. This is a no-op except in the case
+ * where one of these had been in the config file and is now removed.
+ *
+ * In particular, we *must not* do this during the postmaster's
+ * initial loading of the file, since the timezone functions in
+ * particular should be run only after initialization is complete.
+ *
+ * XXX this is an unmaintainable crock, because we have to know how
+ * to set (or at least what to call to set) every variable that could
+ * potentially have GTMC_S_DYNAMIC_DEFAULT or GTMC_S_ENV_VAR source.
+ * However, there's no time to redesign it for 9.1.
+ */
+
+ /* If we got here all the options checked out okay, so apply them. */
+ for (item = head; item; item = item->next)
+ {
+ char *pre_value = NULL;
+
+ if (set_config_option(item->name, item->value, context,
+ GTMC_S_FILE, true))
+ {
+ set_config_sourcefile(item->name, item->filename,
+ item->sourceline);
+
+ if (pre_value)
+ {
+ const char *post_value = GetConfigOption(item->name, false);
+
+ if (!post_value)
+ post_value = "";
+ if (strcmp(pre_value, post_value) != 0)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" changed to \"%s\"\n",
+ item->name, item->value);
+ }
+ else
+ {
+ ereport(elevel,
+ (errmsg("parameter \"%s\" changed to \"%s\"",
+ item->name, item->value)));
+ }
+ }
+ }
+ }
+
+ if (pre_value)
+ free(pre_value);
+ }
+
+ /* PGXCTODO: configuration file reload time update */
+
+cleanup_list:
+ FreeConfigVariables(head);
+ if (cvc)
+ free(cvc);
+}
+
+/*
+ * See next function for details. This one will just work with a config_file
+ * name rather than an already opened File Descriptor
+ */
+bool
+ParseConfigFile(const char *config_file, const char *calling_file,
+ int depth, int elevel,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p)
+{
+ bool OK = true;
+ FILE *fp;
+ char abs_path[MAXPGPATH];
+
+ /*
+ * Reject too-deep include nesting depth. This is just a safety check
+ * to avoid dumping core due to stack overflow if an include file loops
+ * back to itself. The maximum nesting depth is pretty arbitrary.
+ */
+ if (depth > 10)
+ {
+ if (isStartUp)
+ {
+ write_stderr("could not open configuration file \"%s\": maximum nesting depth exceeded\n",
+ config_file);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
+ config_file)));
+ }
+ return false;
+ }
+
+ /*
+ * If config_file is a relative path, convert to absolute. We consider
+ * it to be relative to the directory holding the calling file.
+ */
+ if (!is_absolute_path(config_file))
+ {
+ if (calling_file != NULL)
+ {
+ strlcpy(abs_path, calling_file, sizeof(abs_path));
+ get_parent_directory(abs_path);
+ join_path_components(abs_path, abs_path, config_file);
+ canonicalize_path(abs_path);
+ config_file = abs_path;
+ }
+ else
+ {
+ /*
+ * calling_file is NULL, we make an absolute path from $PGDATA
+ */
+ join_path_components(abs_path, data_directory, config_file);
+ canonicalize_path(abs_path);
+ config_file = abs_path;
+ }
+ }
+
+ fp = fopen(config_file, "r");
+ if (!fp)
+ {
+ if (isStartUp)
+ {
+ write_stderr("could not open configuration file \"%s\": %m\n",
+ config_file);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("could not open configuration file \"%s\": %m",
+ config_file)));
+ }
+ return false;
+ }
+
+ OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p);
+
+ fclose(fp);
+
+ return OK;
+}
+
+/*
+ * Read and parse a single configuration file. This function recurses
+ * to handle "include" directives.
+ *
+ * Input parameters:
+ * fp: file pointer from AllocateFile for the configuration file to parse
+ * config_file: absolute or relative path of file to read
+ * depth: recursion depth (used only to prevent infinite recursion)
+ * elevel: error logging level determined by ProcessConfigFile()
+ * Output parameters:
+ * head_p, tail_p: head and tail of linked list of name/value pairs
+ *
+ * *head_p and *tail_p must be initialized to NULL before calling the outer
+ * recursion level. On exit, they contain a list of name-value pairs read
+ * from the input file(s).
+ *
+ * Returns TRUE if successful, FALSE if an error occurred. The error has
+ * already been ereport'd, it is only necessary for the caller to clean up
+ * its own state and release the name/value pairs list.
+ *
+ * Note: if elevel >= ERROR then an error will not return control to the
+ * caller, and internal state such as open files will not be cleaned up.
+ * This case occurs only during postmaster or standalone-backend startup,
+ * where an error will lead to immediate process exit anyway; so there is
+ * no point in contorting the code so it can clean up nicely.
+ */
+bool
+ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
+ ConfigVariable **head_p, ConfigVariable **tail_p)
+{
+ bool OK = true;
+ YY_BUFFER_STATE lex_buffer;
+ int token;
+
+ /*
+ * Parse
+ */
+ lex_buffer = GTMOPT_yy_create_buffer(fp, YY_BUF_SIZE);
+ GTMOPT_yy_switch_to_buffer(lex_buffer);
+
+ ConfigFileLineno = 1;
+
+ /* This loop iterates once per logical line */
+ while ((token = GTMOPT_yylex()))
+ {
+ char *opt_name, *opt_value;
+ ConfigVariable *item;
+
+ if (token == GTMOPT_EOL) /* empty or comment line */
+ continue;
+
+ /* first token on line is option name */
+ if (token != GTMOPT_ID && token != GTMOPT_QUALIFIED_ID)
+ goto parse_error;
+ opt_name = strdup(GTMOPT_yytext);
+
+ /* next we have an optional equal sign; discard if present */
+ token = GTMOPT_yylex();
+ if (token == GTMOPT_EQUALS)
+ token = GTMOPT_yylex();
+
+ /* now we must have the option value */
+ if (token != GTMOPT_ID &&
+ token != GTMOPT_STRING &&
+ token != GTMOPT_INTEGER &&
+ token != GTMOPT_REAL &&
+ token != GTMOPT_UNQUOTED_STRING)
+ goto parse_error;
+ if (token == GTMOPT_STRING) /* strip quotes and escapes */
+ opt_value = GTMOPT_scanstr(GTMOPT_yytext);
+ else
+ opt_value = strdup(GTMOPT_yytext);
+
+ /* now we'd like an end of line, or possibly EOF */
+ token = GTMOPT_yylex();
+ if (token != GTMOPT_EOL)
+ {
+ if (token != 0)
+ goto parse_error;
+ /* treat EOF like \n for line numbering purposes, cf bug 4752 */
+ ConfigFileLineno++;
+ }
+
+ /* OK, process the option name and value */
+ if (gtm_opt_name_compare(opt_name, "include") == 0)
+ {
+ /*
+ * An include directive isn't a variable and should be processed
+ * immediately.
+ */
+ unsigned int save_ConfigFileLineno = ConfigFileLineno;
+
+ if (!ParseConfigFile(opt_value, config_file,
+ depth + 1, elevel,
+ head_p, tail_p))
+ {
+ free(opt_name);
+ free(opt_value);
+ OK = false;
+ goto cleanup_exit;
+ }
+ GTMOPT_yy_switch_to_buffer(lex_buffer);
+ ConfigFileLineno = save_ConfigFileLineno;
+ free(opt_name);
+ free(opt_value);
+ }
+ else if (gtm_opt_name_compare(opt_name, "custom_variable_classes") == 0)
+ {
+ /*
+ * This variable must be processed first as it controls
+ * the validity of other variables; so it goes at the head
+ * of the result list. If we already found a value for it,
+ * replace with this one.
+ */
+ item = *head_p;
+ if (item != NULL &&
+ gtm_opt_name_compare(item->name, "custom_variable_classes") == 0)
+ {
+ /* replace existing head item */
+ free(item->name);
+ free(item->value);
+ item->name = opt_name;
+ item->value = opt_value;
+ item->filename = strdup(config_file);
+ item->sourceline = ConfigFileLineno-1;
+ }
+ else
+ {
+ /* prepend to list */
+ item = malloc(sizeof *item);
+ item->name = opt_name;
+ item->value = opt_value;
+ item->filename = strdup(config_file);
+ item->sourceline = ConfigFileLineno-1;
+ item->next = *head_p;
+ *head_p = item;
+ if (*tail_p == NULL)
+ *tail_p = item;
+ }
+ }
+ else
+ {
+ /* ordinary variable, append to list */
+ item = malloc(sizeof *item);
+ item->name = opt_name;
+ item->value = opt_value;
+ item->filename = strdup(config_file);
+ item->sourceline = ConfigFileLineno-1;
+ item->next = NULL;
+ if (*head_p == NULL)
+ *head_p = item;
+ else
+ (*tail_p)->next = item;
+ *tail_p = item;
+ }
+
+ /* break out of loop if read EOF, else loop for next line */
+ if (token == 0)
+ break;
+ }
+
+ /* successful completion of parsing */
+ goto cleanup_exit;
+
+ parse_error:
+ if (token == GTMOPT_EOL || token == 0)
+ {
+ if (isStartUp)
+ {
+ write_stderr("syntax error in file \"%s\" line %u, near end of line\n",
+ config_file, ConfigFileLineno - 1);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("syntax error in file \"%s\" line %u, near end of line",
+ config_file, ConfigFileLineno - 1)));
+ }
+ }
+ else
+ {
+ if (isStartUp)
+ {
+ write_stderr("syntax error in file \"%s\" line %u, near token \"%s\"\n",
+ config_file, ConfigFileLineno, GTMOPT_yytext);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
+ config_file, ConfigFileLineno, GTMOPT_yytext)));
+ }
+ }
+ OK = false;
+
+cleanup_exit:
+ GTMOPT_yy_delete_buffer(lex_buffer);
+ return OK;
+}
+
+
+/*
+ * Free a list of ConfigVariables, including the names and the values
+ */
+void
+FreeConfigVariables(ConfigVariable *list)
+{
+ ConfigVariable *item;
+
+ item = list;
+ while (item)
+ {
+ ConfigVariable *next = item->next;
+
+ free(item->name);
+ free(item->value);
+ free(item->filename);
+ free(item);
+ item = next;
+ }
+}
+
+
+/*
+ * scanstr
+ *
+ * Strip the quotes surrounding the given string, and collapse any embedded
+ * '' sequences and backslash escapes.
+ *
+ * the string returned is malloc'd and should eventually be free'd by the
+ * caller.
+ */
+static char *
+GTMOPT_scanstr(const char *s)
+{
+ char *newStr;
+ int len,
+ i,
+ j;
+
+ Assert(s != NULL && s[0] == '\'');
+ len = strlen(s);
+ Assert(len >= 2);
+ Assert(s[len-1] == '\'');
+
+ /* Skip the leading quote; we'll handle the trailing quote below */
+ s++, len--;
+
+ /* Since len still includes trailing quote, this is enough space */
+ newStr = malloc(len);
+
+ for (i = 0, j = 0; i < len; i++)
+ {
+ if (s[i] == '\\')
+ {
+ i++;
+ switch (s[i])
+ {
+ case 'b':
+ newStr[j] = '\b';
+ break;
+ case 'f':
+ newStr[j] = '\f';
+ break;
+ case 'n':
+ newStr[j] = '\n';
+ break;
+ case 'r':
+ newStr[j] = '\r';
+ break;
+ case 't':
+ newStr[j] = '\t';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int k;
+ long octVal = 0;
+
+ for (k = 0;
+ s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
+ k++)
+ octVal = (octVal << 3) + (s[i + k] - '0');
+ i += k - 1;
+ newStr[j] = ((char) octVal);
+ }
+ break;
+ default:
+ newStr[j] = s[i];
+ break;
+ } /* switch */
+ }
+ else if (s[i] == '\'' && s[i+1] == '\'')
+ {
+ /* doubled quote becomes just one quote */
+ newStr[j] = s[++i];
+ }
+ else
+ newStr[j] = s[i];
+ j++;
+ }
+
+ /* We copied the ending quote to newStr, so replace with \0 */
+ Assert(j > 0 && j <= len);
+ newStr[--j] = '\0';
+
+ return newStr;
+}
+
+/*
+ * The following code includes most of the code ported from guc.c.
+ * Because they should be shared by gtm_opt.c and gtm_proxy_opt.c, they are placed here.
+ */
+
+/*
+ * Some infrastructure for checking malloc/strdup/realloc calls
+ */
+static void *
+gtm_opt_malloc(int elevel, size_t size)
+{
+ void *data;
+
+ data = malloc(size);
+ if (data == NULL)
+ {
+ if (isStartUp)
+ {
+ write_stderr("out of memory\n");
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("out of memory")));
+ }
+ }
+ return data;
+}
+
+#if 0
+/* PGXCTODO: this will be used for future extensions */
+static void *
+gtm_opt_realloc(int elevel, void *old, size_t size)
+{
+ void *data;
+
+ data = realloc(old, size);
+ if (data == NULL)
+ {
+ if (isStartUp)
+ {
+ write_stderr("out of memory\n");
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("out of memory")));
+ }
+ }
+ return data;
+}
+#endif
+
+static char *
+gtm_opt_strdup(int elevel, const char *src)
+{
+ char *data;
+
+ data = strdup(src);
+ if (data == NULL)
+ {
+ if (isStartUp)
+ {
+ write_stderr("out of memory\n");
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("out of memory")));
+ }
+ }
+ return data;
+}
+
+/*
+ * Detect whether strval is referenced anywhere in a GTM string item
+ */
+static bool
+string_field_used(struct config_string * conf, char *strval)
+{
+ GtmOptStack *stack;
+
+ if (strval == *(conf->variable) ||
+ strval == conf->reset_val ||
+ strval == conf->boot_val)
+ return true;
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (strval == stack->prior.val.stringval ||
+ strval == stack->masked.val.stringval)
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ * Support for assigning to a field of a string GTM item. Free the prior
+ * value if it's not referenced anywhere else in the item (including stacked
+ * states).
+ */
+static void
+set_string_field(struct config_string * conf, char **field, char *newval)
+{
+ char *oldval = *field;
+
+ /* Do the assignment */
+ *field = newval;
+
+ /* Free old value if it's not NULL and isn't referenced anymore */
+ if (oldval && !string_field_used(conf, oldval))
+ free(oldval);
+}
+
+
+/*
+ * Detect whether an "extra" struct is referenced anywhere in a GTM item
+ */
+static bool
+extra_field_used(struct config_generic * gconf, void *extra)
+{
+ GtmOptStack *stack;
+
+ if (extra == gconf->extra)
+ return true;
+ switch (gconf->vartype)
+ {
+ case GTMC_BOOL:
+ if (extra == ((struct config_bool *) gconf)->reset_extra)
+ return true;
+ break;
+ case GTMC_INT:
+ if (extra == ((struct config_int *) gconf)->reset_extra)
+ return true;
+ break;
+ case GTMC_REAL:
+ if (extra == ((struct config_real *) gconf)->reset_extra)
+ return true;
+ break;
+ case GTMC_STRING:
+ if (extra == ((struct config_string *) gconf)->reset_extra)
+ return true;
+ break;
+ case GTMC_ENUM:
+ if (extra == ((struct config_enum *) gconf)->reset_extra)
+ return true;
+ break;
+ }
+ for (stack = gconf->stack; stack; stack = stack->prev)
+ {
+ if (extra == stack->prior.extra ||
+ extra == stack->masked.extra)
+ return true;
+ }
+
+ return false;
+}
+
+
+/*
+ * Support for assigning to an "extra" field of a GTM item. Free the prior
+ * value if it's not referenced anywhere else in the item (including stacked
+ * states).
+ */
+static void
+set_extra_field(struct config_generic * gconf, void **field, void *newval)
+{
+ void *oldval = *field;
+
+ /* Do the assignment */
+ *field = newval;
+
+ /* Free old value if it's not NULL and isn't referenced anymore */
+ if (oldval && !extra_field_used(gconf, oldval))
+ free(oldval);
+}
+
+
+/*
+ * Support for copying a variable's active value into a stack entry.
+ * The "extra" field associated with the active value is copied, too.
+ *
+ * NB: be sure stringval and extra fields of a new stack entry are
+ * initialized to NULL before this is used, else we'll try to free() them.
+ */
+static void
+set_stack_value(struct config_generic * gconf, config_var_value *val)
+{
+ switch (gconf->vartype)
+ {
+ case GTMC_BOOL:
+ val->val.boolval =
+ *((struct config_bool *) gconf)->variable;
+ break;
+ case GTMC_INT:
+ val->val.intval =
+ *((struct config_int *) gconf)->variable;
+ break;
+ case GTMC_REAL:
+ val->val.realval =
+ *((struct config_real *) gconf)->variable;
+ break;
+ case GTMC_STRING:
+ set_string_field((struct config_string *) gconf,
+ &(val->val.stringval),
+ *((struct config_string *) gconf)->variable);
+ break;
+ case GTMC_ENUM:
+ val->val.enumval =
+ *((struct config_enum *) gconf)->variable;
+ break;
+ }
+ set_extra_field(gconf, &(val->extra), gconf->extra);
+}
+
+#if 0
+/* PGXCTODO: This is let for future extension support */
+/*
+ * Support for discarding a no-longer-needed value in a stack entry.
+ * The "extra" field associated with the stack entry is cleared, too.
+ */
+static void
+discard_stack_value(struct config_generic * gconf, config_var_value *val)
+{
+ switch (gconf->vartype)
+ {
+ case GTMC_BOOL:
+ case GTMC_INT:
+ case GTMC_REAL:
+ case GTMC_ENUM:
+ /* no need to do anything */
+ break;
+ case GTMC_STRING:
+ set_string_field((struct config_string *) gconf,
+ &(val->val.stringval),
+ NULL);
+ break;
+ }
+ set_extra_field(gconf, &(val->extra), NULL);
+}
+#endif
+
+/*
+ * Fetch the sorted array pointer (exported for help_config.c's use ONLY)
+ */
+struct config_generic **
+get_gtm_opt_variables(void)
+{
+ return gtm_opt_variables;
+}
+
+/*
+ * Build the sorted array. This is split out so that it could be
+ * re-executed after startup (eg, we could allow loadable modules to
+ * add vars, and then we'd need to re-sort).
+ */
+void
+build_gtm_opt_variables(void)
+{
+ int size_vars;
+ int num_vars = 0;
+ struct config_generic **gtm_opt_vars;
+ int i;
+
+ for (i = 0; ConfigureNamesBool[i].gen.name; i++)
+ {
+ struct config_bool *conf = &ConfigureNamesBool[i];
+
+ /* Rather than requiring vartype to be filled in by hand, do this: */
+ conf->gen.vartype = GTMC_BOOL;
+ num_vars++;
+ }
+
+ for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+ {
+ struct config_int *conf = &ConfigureNamesInt[i];
+
+ conf->gen.vartype = GTMC_INT;
+ num_vars++;
+ }
+
+ for (i = 0; ConfigureNamesReal[i].gen.name; i++)
+ {
+ struct config_real *conf = &ConfigureNamesReal[i];
+
+ conf->gen.vartype = GTMC_REAL;
+ num_vars++;
+ }
+
+ for (i = 0; ConfigureNamesString[i].gen.name; i++)
+ {
+ struct config_string *conf = &ConfigureNamesString[i];
+
+ conf->gen.vartype = GTMC_STRING;
+ num_vars++;
+ }
+
+ for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
+ {
+ struct config_enum *conf = &ConfigureNamesEnum[i];
+
+ conf->gen.vartype = GTMC_ENUM;
+ num_vars++;
+ }
+
+ /*
+ * Create table with 20% slack
+ */
+ size_vars = num_vars + num_vars / 4;
+
+ gtm_opt_vars = (struct config_generic **)
+ gtm_opt_malloc(FATAL, size_vars * sizeof(struct config_generic *));
+
+ num_vars = 0;
+
+ for (i = 0; ConfigureNamesBool[i].gen.name; i++)
+ gtm_opt_vars[num_vars++] = &ConfigureNamesBool[i].gen;
+
+ for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+ gtm_opt_vars[num_vars++] = &ConfigureNamesInt[i].gen;
+
+ for (i = 0; ConfigureNamesReal[i].gen.name; i++)
+ gtm_opt_vars[num_vars++] = &ConfigureNamesReal[i].gen;
+
+ for (i = 0; ConfigureNamesString[i].gen.name; i++)
+ gtm_opt_vars[num_vars++] = &ConfigureNamesString[i].gen;
+
+ for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
+ gtm_opt_vars[num_vars++] = &ConfigureNamesEnum[i].gen;
+
+ if (gtm_opt_variables)
+ free(gtm_opt_variables);
+ gtm_opt_variables = gtm_opt_vars;
+ num_gtm_opt_variables = num_vars;
+ size_gtm_opt_variables = size_vars;
+ qsort((void *) gtm_opt_variables, num_gtm_opt_variables,
+ sizeof(struct config_generic *), gtm_opt_var_compare);
+}
+
+
+#if 0
+/* PGXCTODO: This is let for future extension support */
+/*
+ * Add a new GTM variable to the list of known variables. The
+ * list is expanded if needed.
+ */
+static bool
+add_gtm_opt_variable(struct config_generic * var, int elevel)
+{
+ if (num_gtm_opt_variables + 1 >= size_gtm_opt_variables)
+ {
+ /*
+ * Increase the vector by 25%
+ */
+ int size_vars = size_gtm_opt_variables + size_gtm_opt_variables / 4;
+ struct config_generic **gtm_opt_vars;
+
+ if (size_vars == 0)
+ {
+ size_vars = 100;
+ gtm_opt_vars = (struct config_generic **)
+ gtm_opt_malloc(elevel, size_vars * sizeof(struct config_generic *));
+ }
+ else
+ {
+ gtm_opt_vars = (struct config_generic **)
+ gtm_opt_realloc(elevel, gtm_opt_variables, size_vars * sizeof(struct config_generic *));
+ }
+
+ if (gtm_opt_vars == NULL)
+ return false; /* out of memory */
+
+ gtm_opt_variables = gtm_opt_vars;
+ size_gtm_opt_variables = size_vars;
+ }
+ gtm_opt_variables[num_gtm_opt_variables++] = var;
+ qsort((void *) gtm_opt_variables, num_gtm_opt_variables,
+ sizeof(struct config_generic *), gtm_opt_var_compare);
+ return true;
+}
+
+
+/*
+ * Create and add a placeholder variable. It's presumed to belong
+ * to a valid custom variable class at this point.
+ */
+static struct config_generic *
+add_placeholder_variable(const char *name, int elevel)
+{
+ size_t sz = sizeof(struct config_string) + sizeof(char *);
+ struct config_string *var;
+ struct config_generic *gen;
+
+ var = (struct config_string *) gtm_opt_malloc(elevel, sz);
+ if (var == NULL)
+ return NULL;
+ memset(var, 0, sz);
+ gen = &var->gen;
+
+ gen->name = gtm_opt_strdup(elevel, name);
+ if (gen->name == NULL)
+ {
+ free(var);
+ return NULL;
+ }
+
+ gen->context = GTMC_USERSET;
+ gen->short_desc = "GTM placeholder variable";
+ gen->flags = GTMOPT_NO_SHOW_ALL | GTMOPT_NOT_IN_SAMPLE | GTMOPT_CUSTOM_PLACEHOLDER;
+ gen->vartype = GTMC_STRING;
+
+ /*
+ * The char* is allocated at the end of the struct since we have no
+ * 'static' place to point to. Note that the current value, as well as
+ * the boot and reset values, start out NULL.
+ */
+ var->variable = (char **) (var + 1);
+
+ if (!add_gtm_opt_variable((struct config_generic *) var, elevel))
+ {
+ free((void *) gen->name);
+ free(var);
+ return NULL;
+ }
+
+ return gen;
+}
+#endif
+
+/*
+ * Look up option NAME. If it exists, return a pointer to its record,
+ * else return NULL. If create_placeholders is TRUE, we'll create a
+ * placeholder record for a valid-looking custom variable name.
+ */
+static struct config_generic *
+find_option(const char *name, bool create_placeholders, int elevel)
+{
+ const char **key = &name;
+ struct config_generic **res;
+
+ Assert(name);
+
+ /*
+ * By equating const char ** with struct config_generic *, we are assuming
+ * the name field is first in config_generic.
+ */
+ res = (struct config_generic **) bsearch((void *) &key,
+ (void *) gtm_opt_variables,
+ num_gtm_opt_variables,
+ sizeof(struct config_generic *),
+ gtm_opt_var_compare);
+ if (res)
+ return *res;
+
+ /* Unknown name */
+ return NULL;
+}
+
+
+/*
+ * comparator for qsorting and bsearching gtm_opt_variables array
+ */
+static int
+gtm_opt_var_compare(const void *a, const void *b)
+{
+ struct config_generic *confa = *(struct config_generic **) a;
+ struct config_generic *confb = *(struct config_generic **) b;
+
+ return gtm_opt_name_compare(confa->name, confb->name);
+}
+
+
+/*
+ * the bare comparison function for GTM names
+ */
+static int
+gtm_opt_name_compare(const char *namea, const char *nameb)
+{
+ /*
+ * The temptation to use strcasecmp() here must be resisted, because the
+ * array ordering has to remain stable across setlocale() calls. So, build
+ * our own with a simple ASCII-only downcasing.
+ */
+ while (*namea && *nameb)
+ {
+ char cha = *namea++;
+ char chb = *nameb++;
+
+ if (cha >= 'A' && cha <= 'Z')
+ cha += 'a' - 'A';
+ if (chb >= 'A' && chb <= 'Z')
+ chb += 'a' - 'A';
+ if (cha != chb)
+ return cha - chb;
+ }
+ if (*namea)
+ return 1; /* a is longer */
+ if (*nameb)
+ return -1; /* b is longer */
+ return 0;
+}
+
+
+/*
+ * Initialize GTM options during program startup.
+ *
+ * Note that we cannot read the config file yet, since we have not yet
+ * processed command-line switches.
+ */
+void
+InitializeGTMOptions(void)
+{
+ int i;
+
+ /*
+ * Build sorted array of all GTM variables.
+ */
+ build_gtm_opt_variables();
+
+ /*
+ * Load all variables with their compiled-in defaults, and initialize
+ * status fields as needed.
+ */
+ for (i = 0; i < num_gtm_opt_variables; i++)
+ {
+ InitializeOneGTMOption(gtm_opt_variables[i]);
+ }
+
+ reporting_enabled = false;
+
+}
+
+
+/*
+ * Initialize one GTM option variable to its compiled-in default.
+ *
+ * Note: the reason for calling check_hooks is not that we think the boot_val
+ * might fail, but that the hooks might wish to compute an "extra" struct.
+ */
+static void
+InitializeOneGTMOption(struct config_generic * gconf)
+{
+ gconf->status = 0;
+ gconf->reset_source = GTMC_S_DEFAULT;
+ gconf->source = GTMC_S_DEFAULT;
+ gconf->stack = NULL;
+ gconf->extra = NULL;
+ gconf->sourcefile = NULL;
+ gconf->sourceline = 0;
+ gconf->context = GTMC_DEFAULT;
+
+ switch (gconf->vartype)
+ {
+ case GTMC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
+ bool newval = conf->boot_val;
+ void *extra = NULL;
+
+ *conf->variable = conf->reset_val = newval;
+ conf->gen.extra = conf->reset_extra = extra;
+ break;
+ }
+ case GTMC_INT:
+ {
+ struct config_int *conf = (struct config_int *) gconf;
+ int newval = conf->boot_val;
+ void *extra = NULL;
+
+ Assert(newval >= conf->min);
+ Assert(newval <= conf->max);
+ *conf->variable = conf->reset_val = newval;
+ conf->gen.extra = conf->reset_extra = extra;
+ break;
+ }
+ case GTMC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
+ double newval = conf->boot_val;
+ void *extra = NULL;
+
+ Assert(newval >= conf->min);
+ Assert(newval <= conf->max);
+ *conf->variable = conf->reset_val = newval;
+ conf->gen.extra = conf->reset_extra = extra;
+ break;
+ }
+ case GTMC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+ char *newval;
+ void *extra = NULL;
+
+ /* non-NULL boot_val must always get strdup'd */
+ if (conf->boot_val != NULL)
+ newval = gtm_opt_strdup(FATAL, conf->boot_val);
+ else
+ newval = NULL;
+
+ *conf->variable = conf->reset_val = newval;
+ conf->gen.extra = conf->reset_extra = extra;
+ break;
+ }
+ case GTMC_ENUM:
+ {
+ struct config_enum *conf = (struct config_enum *) gconf;
+ int newval = conf->boot_val;
+ void *extra = NULL;
+
+ *conf->variable = conf->reset_val = newval;
+ conf->gen.extra = conf->reset_extra = extra;
+ break;
+ }
+ }
+}
+
+
+/*
+ * 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 = NULL;
+
+ /*
+ * 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 (GTMConfigFileName)
+ {
+ if (GTMConfigFileName[0] == '/')
+ fname = make_absolute_path(GTMConfigFileName);
+ else
+ {
+ if (configdir)
+ {
+ fname = gtm_opt_malloc(FATAL,
+ strlen(configdir) + strlen(GTMConfigFileName) + 2);
+ sprintf(fname, "%s/%s", configdir, GTMConfigFileName);
+ }
+ else
+ fname = make_absolute_path(GTMConfigFileName);
+ }
+ }
+ else if (configdir)
+ {
+ fname = gtm_opt_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 GTMConfigFileName GTM variable to its final value, ensuring that
+ * it can't be overridden later.
+ */
+ SetConfigOption("config_file", fname, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(fname);
+
+ /*
+ * Now read the config file for the first time.
+ */
+ if (stat(GTMConfigFileName, &stat_buf) != 0)
+ {
+ write_stderr("%s cannot access the server configuration file \"%s\": %s\n",
+ progname, GTMConfigFileName, strerror(errno));
+ return false;
+ }
+
+ ProcessConfigFile(GTMC_STARTUP);
+
+ free(configdir);
+
+ return true;
+}
+
+/*
+ * Reset all options to their saved default values (implements RESET ALL)
+ */
+void
+ResetAllOptions(void)
+{
+ int i;
+
+ for (i = 0; i < num_gtm_opt_variables; i++)
+ {
+ struct config_generic *gconf = gtm_opt_variables[i];
+
+ /* Don't reset if special exclusion from RESET ALL */
+ if (gconf->flags & GTMOPT_NO_RESET_ALL)
+ continue;
+ /* No need to reset if wasn't SET */
+ if (gconf->source <= GTMC_S_OVERRIDE)
+ continue;
+
+ switch (gconf->vartype)
+ {
+ case GTMC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
+
+ *conf->variable = conf->reset_val;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ conf->reset_extra);
+ break;
+ }
+ case GTMC_INT:
+ {
+ struct config_int *conf = (struct config_int *) gconf;
+
+ *conf->variable = conf->reset_val;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ conf->reset_extra);
+ break;
+ }
+ case GTMC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
+
+ *conf->variable = conf->reset_val;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ conf->reset_extra);
+ break;
+ }
+ case GTMC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+
+ set_string_field(conf, conf->variable, conf->reset_val);
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ conf->reset_extra);
+ break;
+ }
+ case GTMC_ENUM:
+ {
+ struct config_enum *conf = (struct config_enum *) gconf;
+
+ *conf->variable = conf->reset_val;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ conf->reset_extra);
+ break;
+ }
+ }
+
+ gconf->source = gconf->reset_source;
+
+ if (gconf->flags & GTMOPT_REPORT)
+ ReportGTMOption(gconf);
+ }
+}
+
+
+
+/*
+ * push_old_value
+ * Push previous state during transactional assignment to a GTM variable.
+ */
+static void
+push_old_value(struct config_generic * gconf)
+{
+ GtmOptStack *stack;
+
+ /* If we're not inside a nest level, do nothing */
+ if (GTMOptUpdateCount == 0)
+ return;
+
+ /* Do we already have a stack entry of the current nest level? */
+ stack = gconf->stack;
+ if (stack && stack->nest_level >= GTMOptUpdateCount)
+ return;
+
+ /*
+ * Push a new stack entry
+ *
+ * We keep all the stack entries in TopTransactionContext for simplicity.
+ */
+ stack = (GtmOptStack *) MemoryContextAllocZero(TopMemoryContext,
+ sizeof(GtmOptStack));
+
+ stack->prev = gconf->stack;
+ stack->nest_level = GTMOptUpdateCount;
+ stack->source = gconf->source;
+ set_stack_value(gconf, &stack->prior);
+
+ gconf->stack = stack;
+}
+
+
+
+/*
+ * Enter a new nesting level for GTM values. This is called at subtransaction
+ * start and when entering a function that has proconfig settings. NOTE that
+ * we must not risk error here, else subtransaction start will be unhappy.
+ */
+int
+NewGTMNestLevel(void)
+{
+ return ++GTMOptUpdateCount;
+}
+
+/*
+ * Try to parse value as an integer. The accepted formats are the
+ * usual decimal, octal, or hexadecimal formats, optionally followed by
+ * a unit name if "flags" indicates a unit is allowed.
+ *
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
+ * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable
+ * HINT message, or NULL if no hint provided.
+ */
+bool
+parse_int(const char *value, int *result, int flags, const char **hintmsg)
+{
+ int64 val;
+ char *endptr;
+
+ /* To suppress compiler warnings, always set output params */
+ if (result)
+ *result = 0;
+ if (hintmsg)
+ *hintmsg = NULL;
+
+ /* We assume here that int64 is at least as wide as long */
+ errno = 0;
+ val = strtol(value, &endptr, 0);
+
+ if (endptr == value)
+ return false; /* no HINT for integer syntax error */
+
+ if (errno == ERANGE || val != (int64) ((int32) val))
+ {
+ if (hintmsg)
+ *hintmsg = gettext_noop("Value exceeds integer range.");
+ return false;
+ }
+
+ /* allow whitespace between integer and unit */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+
+ /* Handle possible unit */
+ if (*endptr != '\0')
+ {
+ /*
+ * Note: the multiple-switch coding technique here is a bit tedious,
+ * but seems necessary to avoid intermediate-value overflows.
+ */
+ if (flags & GTMOPT_UNIT_MEMORY)
+ {
+ /* Set hint for use if no match or trailing garbage */
+ if (hintmsg)
+ *hintmsg = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", and \"GB\".");
+
+#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
+#error BLCKSZ must be between 1KB and 1MB
+#endif
+#if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
+#error XLOG_BLCKSZ must be between 1KB and 1MB
+#endif
+
+ if (strncmp(endptr, "kB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GTMOPT_UNIT_MEMORY)
+ {
+ case GTMOPT_UNIT_BLOCKS:
+ val /= (BLCKSZ / 1024);
+ break;
+ case GTMOPT_UNIT_XBLOCKS:
+ val /= (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
+ else if (strncmp(endptr, "MB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GTMOPT_UNIT_MEMORY)
+ {
+ case GTMOPT_UNIT_KB:
+ val *= KB_PER_MB;
+ break;
+ case GTMOPT_UNIT_BLOCKS:
+ val *= KB_PER_MB / (BLCKSZ / 1024);
+ break;
+ case GTMOPT_UNIT_XBLOCKS:
+ val *= KB_PER_MB / (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
+ else if (strncmp(endptr, "GB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GTMOPT_UNIT_MEMORY)
+ {
+ case GTMOPT_UNIT_KB:
+ val *= KB_PER_GB;
+ break;
+ case GTMOPT_UNIT_BLOCKS:
+ val *= KB_PER_GB / (BLCKSZ / 1024);
+ break;
+ case GTMOPT_UNIT_XBLOCKS:
+ val *= KB_PER_GB / (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
+ }
+ else if (flags & GTMOPT_UNIT_TIME)
+ {
+ /* Set hint for use if no match or trailing garbage */
+ if (hintmsg)
+ *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\".");
+
+ if (strncmp(endptr, "ms", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_S:
+ val /= MS_PER_S;
+ break;
+ case GTMOPT_UNIT_MIN:
+ val /= MS_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "s", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_MS:
+ val *= MS_PER_S;
+ break;
+ case GTMOPT_UNIT_MIN:
+ val /= S_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "min", 3) == 0)
+ {
+ endptr += 3;
+ switch (flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_MS:
+ val *= MS_PER_MIN;
+ break;
+ case GTMOPT_UNIT_S:
+ val *= S_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "h", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_MS:
+ val *= MS_PER_H;
+ break;
+ case GTMOPT_UNIT_S:
+ val *= S_PER_H;
+ break;
+ case GTMOPT_UNIT_MIN:
+ val *= MIN_PER_H;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "d", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_MS:
+ val *= MS_PER_D;
+ break;
+ case GTMOPT_UNIT_S:
+ val *= S_PER_D;
+ break;
+ case GTMOPT_UNIT_MIN:
+ val *= MIN_PER_D;
+ break;
+ }
+ }
+ }
+
+ /* allow whitespace after unit */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+
+ if (*endptr != '\0')
+ return false; /* appropriate hint, if any, already set */
+
+ /* Check for overflow due to units conversion */
+ if (val != (int64) ((int32) val))
+ {
+ if (hintmsg)
+ *hintmsg = gettext_noop("Value exceeds integer range.");
+ return false;
+ }
+ }
+
+ if (result)
+ *result = (int) val;
+ return true;
+}
+
+
+
+/*
+ * Try to parse value as a floating point number in the usual format.
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
+ */
+bool
+parse_real(const char *value, double *result)
+{
+ double val;
+ char *endptr;
+
+ if (result)
+ *result = 0; /* suppress compiler warning */
+
+ errno = 0;
+ val = strtod(value, &endptr);
+ if (endptr == value || errno == ERANGE)
+ return false;
+
+ /* allow whitespace after number */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+ if (*endptr != '\0')
+ return false;
+
+ if (result)
+ *result = val;
+ return true;
+}
+
+
+
+/*
+ * Lookup the value for an enum option with the selected name
+ * (case-insensitive).
+ * If the enum option is found, sets the retval value and returns
+ * true. If it's not found, return FALSE and retval is set to 0.
+ */
+bool
+config_enum_lookup_by_name(struct config_enum * record, const char *value,
+ int *retval)
+{
+ const struct config_enum_entry *entry;
+
+ for (entry = record->options; entry && entry->name; entry++)
+ {
+ if (pg_strcasecmp(value, entry->name) == 0)
+ {
+ *retval = entry->val;
+ return TRUE;
+ }
+ }
+
+ *retval = 0;
+ return FALSE;
+}
+
+
+
+/*
+ * Return a list of all available options for an enum, excluding
+ * hidden ones, separated by the given separator.
+ * If prefix is non-NULL, it is added before the first enum value.
+ * If suffix is non-NULL, it is added to the end of the string.
+ */
+static char *
+config_enum_get_options(struct config_enum * record, const char *prefix,
+ const char *suffix, const char *separator)
+{
+ const struct config_enum_entry *entry;
+ StringInfoData retstr;
+ int seplen;
+
+ initStringInfo(&retstr);
+ appendStringInfoString(&retstr, prefix);
+
+ seplen = strlen(separator);
+ for (entry = record->options; entry && entry->name; entry++)
+ {
+ if (!entry->hidden)
+ {
+ appendStringInfoString(&retstr, entry->name);
+ appendBinaryStringInfo(&retstr, separator, seplen);
+ }
+ }
+
+ /*
+ * All the entries may have been hidden, leaving the string empty if no
+ * prefix was given. This indicates a broken GTM setup, since there is no
+ * use for an enum without any values, so we just check to make sure we
+ * don't write to invalid memory instead of actually trying to do
+ * something smart with it.
+ */
+ if (retstr.len >= seplen)
+ {
+ /* Replace final separator */
+ retstr.data[retstr.len - seplen] = '\0';
+ retstr.len -= seplen;
+ }
+
+ appendStringInfoString(&retstr, suffix);
+
+ return retstr.data;
+}
+
+
+/*
+ * Sets option `name' to given value. The value should be a string
+ * which is going to be parsed and converted to the appropriate data
+ * type. The context and source parameters indicate in which context this
+ * function is being called so it can apply the access restrictions
+ * properly.
+ *
+ * If value is NULL, set the option to its default value (normally the
+ * reset_val, but if source == GTMC_S_DEFAULT we instead use the boot_val).
+ *
+ * action indicates whether to set the value globally in the session, locally
+ * to the current top transaction, or just for the duration of a function call.
+ *
+ * If changeVal is false then don't really set the option but do all
+ * the checks to see if it would work.
+ *
+ * If there is an error (non-existing option, invalid value) then an
+ * ereport(ERROR) is thrown *unless* this is called in a context where we
+ * don't want to ereport (currently, startup or SIGHUP config file reread).
+ * In that case we write a suitable error message via ereport(LOG) and
+ * return false. This is working around the deficiencies in the ereport
+ * mechanism, so don't blame me. In all other cases, the function
+ * returns true, including cases where the input is valid but we chose
+ * not to apply it because of context or source-priority considerations.
+ *
+ * See also SetConfigOption for an external interface.
+ */
+bool
+set_config_option(const char *name, const char *value,
+ GtmOptContext context, GtmOptSource source,
+ bool changeVal)
+{
+ struct config_generic *record;
+ int elevel;
+ bool prohibitValueChange = false;
+ bool makeDefault;
+
+ if (context == GTMC_SIGHUP || source == GTMC_S_DEFAULT)
+ {
+ /*
+ * To avoid cluttering the log, only the postmaster bleats loudly
+ * about problems with the config file.
+ */
+ elevel = DEBUG3;
+ }
+ else if (source == GTMC_S_DATABASE || source == GTMC_S_USER ||
+ source == GTMC_S_DATABASE_USER)
+ elevel = WARNING;
+ else
+ elevel = ERROR;
+
+ record = find_option(name, true, elevel);
+ if (record == NULL)
+ {
+ if (isStartUp)
+ {
+ write_stderr("unrecognized configuration parameter \"%s\"\n", name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ }
+ return false;
+ }
+
+ /*
+ * If source is postgresql.conf, mark the found record with
+ * GTMOPT_IS_IN_FILE. This is for the convenience of ProcessConfigFile. Note
+ * that we do it even if changeVal is false, since ProcessConfigFile wants
+ * the marking to occur during its testing pass.
+ */
+ if (source == GTMC_S_FILE)
+ record->status |= GTMOPT_IS_IN_FILE;
+
+ /*
+ * Check if the option can be set at this time. See guc.h for the precise
+ * rules.
+ */
+ switch (record->context)
+ {
+ case GTMC_DEFAULT:
+ case GTMC_STARTUP:
+ if (context == GTMC_SIGHUP)
+ {
+ /*
+ * We are re-reading a GTMC_POSTMASTER variable from
+ * postgresql.conf. We can't change the setting, so we should
+ * give a warning if the DBA tries to change it. However,
+ * because of variant formats, canonicalization by check
+ * hooks, etc, we can't just compare the given string directly
+ * to what's stored. Set a flag to check below after we have
+ * the final storable value.
+ *
+ * During the "checking" pass we just do nothing, to avoid
+ * printing the warning twice.
+ */
+ if (!changeVal)
+ return true;
+
+ prohibitValueChange = true;
+ }
+ else if (context != GTMC_STARTUP)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ return false;
+ }
+ break;
+ case GTMC_SIGHUP:
+ if (context != GTMC_SIGHUP && context != GTMC_STARTUP)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed now\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed now",
+ name)));
+ }
+ return false;
+ }
+
+ /*
+ * Hmm, the idea of the SIGHUP context is "ought to be global, but
+ * can be changed after postmaster start". But there's nothing
+ * that prevents a crafty administrator from sending SIGHUP
+ * signals to individual backends only.
+ */
+ break;
+ default:
+ if (isStartUp)
+ {
+ write_stderr("GtmOptContext invalid (%d)\n",
+ context);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("GtmOptContext invalid (%d)",
+ context)));
+ }
+ return false;
+ }
+
+ /*
+ * Should we set reset/stacked values? (If so, the behavior is not
+ * transactional.) This is done either when we get a default value from
+ * the database's/user's/client's default settings or when we reset a
+ * value to its default.
+ */
+ makeDefault = changeVal && (source <= GTMC_S_OVERRIDE) &&
+ ((value != NULL) || source == GTMC_S_DEFAULT);
+
+ /*
+ * Ignore attempted set if overridden by previously processed setting.
+ * However, if changeVal is false then plow ahead anyway since we are
+ * trying to find out if the value is potentially good, not actually use
+ * it. Also keep going if makeDefault is true, since we may want to set
+ * the reset/stacked values even if we can't set the variable itself.
+ */
+ if (record->source > source)
+ {
+ if (changeVal && !makeDefault)
+ {
+ if (isStartUp)
+ {
+ write_stderr("\"%s\": setting ignored because previous source is higher priority\n",
+ name);
+ }
+ else
+ {
+ elog(DEBUG3, "\"%s\": setting ignored because previous source is higher priority",
+ name);
+ }
+ return true;
+ }
+ changeVal = false;
+ }
+
+ /*
+ * Evaluate value and set variable.
+ */
+ switch (record->vartype)
+ {
+ case GTMC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+ bool newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ if (!gtm_opt_parse_bool(value, &newval))
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" requires a Boolean value\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" requires a Boolean value",
+ name)));
+ }
+ return false;
+ }
+ }
+ else if (source == GTMC_S_DEFAULT)
+ {
+ newval = conf->boot_val;
+ }
+ else
+ {
+ newval = conf->reset_val;
+ newextra = conf->reset_extra;
+ source = conf->gen.reset_source;
+ }
+
+ if (prohibitValueChange)
+ {
+ if (*conf->variable != newval)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ }
+ return false;
+ }
+
+ if (changeVal)
+ {
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
+
+ *conf->variable = newval;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ newextra);
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ GtmOptStack *stack;
+
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ set_extra_field(&conf->gen, &conf->reset_extra,
+ newextra);
+ conf->gen.reset_source = source;
+ }
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (stack->source <= source)
+ {
+ stack->prior.val.boolval = newval;
+ set_extra_field(&conf->gen, &stack->prior.extra,
+ newextra);
+ stack->source = source;
+ }
+ }
+ }
+
+ /* Perhaps we didn't install newextra anywhere */
+ if (newextra && !extra_field_used(&conf->gen, newextra))
+ free(newextra);
+ break;
+ }
+
+ case GTMC_INT:
+ {
+ struct config_int *conf = (struct config_int *) record;
+ int newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ const char *hintmsg;
+
+ if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
+ {
+ if (isStartUp)
+ {
+ write_stderr("invalid value for parameter \"%s\": \"%s\"\n",
+ name, value);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value),
+ hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ }
+ return false;
+ }
+ if (newval < conf->min || newval > conf->max)
+ {
+ if (isStartUp)
+ {
+ write_stderr("%d is outside the valid range for parameter \"%s\" (%d .. %d)\n",
+ newval, name, conf->min, conf->max);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
+ newval, name, conf->min, conf->max)));
+ }
+ return false;
+ }
+ }
+ else if (source == GTMC_S_DEFAULT)
+ {
+ newval = conf->boot_val;
+ }
+ else
+ {
+ newval = conf->reset_val;
+ newextra = conf->reset_extra;
+ source = conf->gen.reset_source;
+ }
+
+ if (prohibitValueChange)
+ {
+ if (*conf->variable != newval)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ }
+ return false;
+ }
+
+ if (changeVal)
+ {
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
+
+ *conf->variable = newval;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ newextra);
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ GtmOptStack *stack;
+
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ set_extra_field(&conf->gen, &conf->reset_extra,
+ newextra);
+ conf->gen.reset_source = source;
+ }
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (stack->source <= source)
+ {
+ stack->prior.val.intval = newval;
+ set_extra_field(&conf->gen, &stack->prior.extra,
+ newextra);
+ stack->source = source;
+ }
+ }
+ }
+
+ /* Perhaps we didn't install newextra anywhere */
+ if (newextra && !extra_field_used(&conf->gen, newextra))
+ free(newextra);
+ break;
+ }
+
+ case GTMC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) record;
+ double newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ if (!parse_real(value, &newval))
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" requires a numeric value\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" requires a numeric value",
+ name)));
+ }
+ return false;
+ }
+ if (newval < conf->min || newval > conf->max)
+ {
+ if (isStartUp)
+ {
+ write_stderr("%g is outside the valid range for parameter \"%s\" (%g .. %g)\n",
+ newval, name, conf->min, conf->max);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
+ newval, name, conf->min, conf->max)));
+ }
+ return false;
+ }
+ }
+ else if (source == GTMC_S_DEFAULT)
+ {
+ newval = conf->boot_val;
+ }
+ else
+ {
+ newval = conf->reset_val;
+ newextra = conf->reset_extra;
+ source = conf->gen.reset_source;
+ }
+
+ if (prohibitValueChange)
+ {
+ if (*conf->variable != newval)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ }
+ return false;
+ }
+
+ if (changeVal)
+ {
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
+
+ *conf->variable = newval;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ newextra);
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ GtmOptStack *stack;
+
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ set_extra_field(&conf->gen, &conf->reset_extra,
+ newextra);
+ conf->gen.reset_source = source;
+ }
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (stack->source <= source)
+ {
+ stack->prior.val.realval = newval;
+ set_extra_field(&conf->gen, &stack->prior.extra,
+ newextra);
+ stack->source = source;
+ }
+ }
+ }
+
+ /* Perhaps we didn't install newextra anywhere */
+ if (newextra && !extra_field_used(&conf->gen, newextra))
+ free(newextra);
+ break;
+ }
+
+ case GTMC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+ char *newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ /*
+ * The value passed by the caller could be transient, so
+ * we always strdup it.
+ */
+ newval = gtm_opt_strdup(elevel, value);
+ if (newval == NULL)
+ return false;
+ }
+ else if (source == GTMC_S_DEFAULT)
+ {
+ /* non-NULL boot_val must always get strdup'd */
+ if (conf->boot_val != NULL)
+ {
+ newval = gtm_opt_strdup(elevel, conf->boot_val);
+ if (newval == NULL)
+ return false;
+ }
+ else
+ newval = NULL;
+
+ }
+ else
+ {
+ /*
+ * strdup not needed, since reset_val is already under
+ * guc.c's control
+ */
+ newval = conf->reset_val;
+ newextra = conf->reset_extra;
+ source = conf->gen.reset_source;
+ }
+
+ if (prohibitValueChange)
+ {
+ /* newval shouldn't be NULL, so we're a bit sloppy here */
+ if (*conf->variable == NULL || newval == NULL ||
+ strcmp(*conf->variable, newval) != 0)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ }
+ return false;
+ }
+
+ if (changeVal)
+ {
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
+
+ set_string_field(conf, conf->variable, newval);
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ newextra);
+ conf->gen.source = source;
+ }
+
+ if (makeDefault)
+ {
+ GtmOptStack *stack;
+
+ if (conf->gen.reset_source <= source)
+ {
+ set_string_field(conf, &conf->reset_val, newval);
+ set_extra_field(&conf->gen, &conf->reset_extra,
+ newextra);
+ conf->gen.reset_source = source;
+ }
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (stack->source <= source)
+ {
+ set_string_field(conf, &stack->prior.val.stringval,
+ newval);
+ set_extra_field(&conf->gen, &stack->prior.extra,
+ newextra);
+ stack->source = source;
+ }
+ }
+ }
+
+ /* Perhaps we didn't install newval anywhere */
+ if (newval && !string_field_used(conf, newval))
+ free(newval);
+ /* Perhaps we didn't install newextra anywhere */
+ if (newextra && !extra_field_used(&conf->gen, newextra))
+ free(newextra);
+ break;
+ }
+
+ case GTMC_ENUM:
+ {
+ struct config_enum *conf = (struct config_enum *) record;
+ int newval;
+ void *newextra = NULL;
+
+ if (value)
+ {
+ if (!config_enum_lookup_by_name(conf, value, &newval))
+ {
+ char *hintmsg;
+
+ hintmsg = config_enum_get_options(conf,
+ "Available values: ",
+ ".", ", ");
+
+ if (isStartUp)
+ {
+ write_stderr("invalid value for parameter \"%s\": \"%s\". %s\n",
+ name, value, hintmsg);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value),
+ hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ }
+
+ if (hintmsg)
+ free(hintmsg);
+ return false;
+ }
+ }
+ else if (source == GTMC_S_DEFAULT)
+ {
+ newval = conf->boot_val;
+ }
+ else
+ {
+ newval = conf->reset_val;
+ newextra = conf->reset_extra;
+ source = conf->gen.reset_source;
+ }
+
+ if (prohibitValueChange)
+ {
+ if (*conf->variable != newval)
+ {
+ if (isStartUp)
+ {
+ write_stderr("parameter \"%s\" cannot be changed without restarting the server\n",
+ name);
+ }
+ else
+ {
+ ereport(elevel,
+ (0,
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ name)));
+ }
+ }
+ return false;
+ }
+
+ if (changeVal)
+ {
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
+
+ *conf->variable = newval;
+ set_extra_field(&conf->gen, &conf->gen.extra,
+ newextra);
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ GtmOptStack *stack;
+
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ set_extra_field(&conf->gen, &conf->reset_extra,
+ newextra);
+ conf->gen.reset_source = source;
+ }
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (stack->source <= source)
+ {
+ stack->prior.val.enumval = newval;
+ set_extra_field(&conf->gen, &stack->prior.extra,
+ newextra);
+ stack->source = source;
+ }
+ }
+ }
+
+ /* Perhaps we didn't install newextra anywhere */
+ if (newextra && !extra_field_used(&conf->gen, newextra))
+ free(newextra);
+ break;
+ }
+ }
+
+ if (changeVal && (record->flags & GTMOPT_REPORT))
+ ReportGTMOption(record);
+
+ return true;
+}
+
+
+
+
+/*
+ * Set the fields for source file and line number the setting came from.
+ */
+static void
+set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
+{
+ struct config_generic *record;
+ int elevel;
+
+ /*
+ * To avoid cluttering the log, only the postmaster bleats loudly about
+ * problems with the config file.
+ */
+ elevel = DEBUG3;
+
+ record = find_option(name, true, elevel);
+ /* should not happen */
+ if (record == NULL)
+ {
+ if (isStartUp)
+ write_stderr("unrecognized configuration parameter \"%s\"\n", name);
+ else
+ elog(ERROR, "unrecognized configuration parameter \"%s\"", name);
+ }
+
+ sourcefile = gtm_opt_strdup(elevel, sourcefile);
+ if (record->sourcefile)
+ free(record->sourcefile);
+ record->sourcefile = sourcefile;
+ record->sourceline = sourceline;
+}
+
+
+/*
+ * Set a config option to the given value. See also set_config_option,
+ * this is just the wrapper to be called from outside GTM. NB: this
+ * is used only for non-transactional operations.
+ *
+ * Note: there is no support here for setting source file/line, as it
+ * is currently not needed.
+ */
+void
+SetConfigOption(const char *name, const char *value,
+ GtmOptContext context, GtmOptSource source)
+{
+ (void) set_config_option(name, value, context, source,
+ true);
+}
+
+
+
+
+/*
+ * Fetch the current value of the option `name'. If the option doesn't exist,
+ * throw an ereport and don't return.
+ *
+ * If restrict_superuser is true, we also enforce that only superusers can
+ * see GTMOPT_SUPERUSER_ONLY variables. This should only be passed as true
+ * in user-driven calls.
+ *
+ * The string is *not* allocated for modification and is really only
+ * valid until the next call to configuration related functions.
+ */
+const char *
+GetConfigOption(const char *name, bool restrict_superuser)
+{
+ struct config_generic *record;
+ static char buffer[256];
+
+ record = find_option(name, false, ERROR);
+ if (record == NULL)
+ {
+ if (isStartUp)
+ write_stderr("unrecognized configuration parameter \"%s\"\n", name);
+ else
+ ereport(ERROR,
+ (0,
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ }
+ switch (record->vartype)
+ {
+ case GTMC_BOOL:
+ return *((struct config_bool *) record)->variable ? "on" : "off";
+
+ case GTMC_INT:
+ snprintf(buffer, sizeof(buffer), "%d",
+ *((struct config_int *) record)->variable);
+ return buffer;
+
+ case GTMC_REAL:
+ snprintf(buffer, sizeof(buffer), "%g",
+ *((struct config_real *) record)->variable);
+ return buffer;
+
+ case GTMC_STRING:
+ return *((struct config_string *) record)->variable;
+
+ case GTMC_ENUM:
+ return config_enum_lookup_by_value((struct config_enum *) record,
+ *((struct config_enum *) record)->variable);
+ }
+ return NULL;
+}
+
+
+/*
+ * Get the RESET value associated with the given option.
+ *
+ * Note: this is not re-entrant, due to use of static result buffer;
+ * not to mention that a string variable could have its reset_val changed.
+ * Beware of assuming the result value is good for very long.
+ */
+const char *
+GetConfigOptionResetString(const char *name)
+{
+ struct config_generic *record;
+ static char buffer[256];
+
+ record = find_option(name, false, ERROR);
+ if (record == NULL)
+ {
+ if (isStartUp)
+ write_stderr("unrecognized configuration parameter \"%s\"\n", name);
+ else
+ ereport(ERROR,
+ (0,
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ }
+
+ switch (record->vartype)
+ {
+ case GTMC_BOOL:
+ return ((struct config_bool *) record)->reset_val ? "on" : "off";
+
+ case GTMC_INT:
+ snprintf(buffer, sizeof(buffer), "%d",
+ ((struct config_int *) record)->reset_val);
+ return buffer;
+
+ case GTMC_REAL:
+ snprintf(buffer, sizeof(buffer), "%g",
+ ((struct config_real *) record)->reset_val);
+ return buffer;
+
+ case GTMC_STRING:
+ return ((struct config_string *) record)->reset_val;
+
+ case GTMC_ENUM:
+ return config_enum_lookup_by_value((struct config_enum *) record,
+ ((struct config_enum *) record)->reset_val);
+ }
+ return NULL;
+}
+
+
+void
+EmitWarningsOnPlaceholders(const char *className)
+{
+ int classLen = strlen(className);
+ int i;
+
+ for (i = 0; i < num_gtm_opt_variables; i++)
+ {
+ struct config_generic *var = gtm_opt_variables[i];
+
+ if ((var->flags & GTMOPT_CUSTOM_PLACEHOLDER) != 0 &&
+ strncmp(className, var->name, classLen) == 0 &&
+ var->name[classLen] == GTMOPT_QUALIFIER_SEPARATOR)
+ {
+ if (isStartUp)
+ write_stderr("unrecognized configuration parameter \"%s\"\n",
+ var->name);
+ else
+ ereport(WARNING,
+ (0,
+ errmsg("unrecognized configuration parameter \"%s\"",
+ var->name)));
+ }
+ }
+}
+
+
+/*
+ * Return GTM variable value by name; optionally return canonical
+ * form of name. Return value is malloc'd.
+ */
+char *
+GetConfigOptionByName(const char *name, const char **varname)
+{
+ struct config_generic *record;
+
+ record = find_option(name, false, ERROR);
+ if (record == NULL)
+ {
+ if (isStartUp)
+ write_stderr("unrecognized configuration parameter \"%s\"\n", name);
+ else
+ ereport(ERROR,
+ (0,
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ }
+ if (varname)
+ *varname = record->name;
+
+ return _ShowOption(record, true);
+}
+
+/*
+ * Return GTM variable value by variable number; optionally return canonical
+ * form of name. Return value is malloc'd.
+ */
+void
+GetConfigOptionByNum(int varnum, const char **values, bool *noshow)
+{
+ char buffer[256];
+ struct config_generic *conf;
+
+ /* check requested variable number valid */
+ Assert((varnum >= 0) && (varnum < num_gtm_opt_variables));
+
+ conf = gtm_opt_variables[varnum];
+
+ if (noshow)
+ {
+ if (conf->flags & GTMOPT_NO_SHOW_ALL)
+ *noshow = true;
+ else
+ *noshow = false;
+ }
+
+ /* first get the generic attributes */
+
+ /* name */
+ values[0] = conf->name;
+
+ /* setting : use _ShowOption in order to avoid duplicating the logic */
+ values[1] = _ShowOption(conf, false);
+
+ /* unit */
+ if (conf->vartype == GTMC_INT)
+ {
+ static char buf[8];
+
+ switch (conf->flags & (GTMOPT_UNIT_MEMORY | GTMOPT_UNIT_TIME))
+ {
+ case GTMOPT_UNIT_KB:
+ values[2] = "kB";
+ break;
+ case GTMOPT_UNIT_BLOCKS:
+ snprintf(buf, sizeof(buf), "%dkB", BLCKSZ / 1024);
+ values[2] = buf;
+ break;
+ case GTMOPT_UNIT_XBLOCKS:
+ snprintf(buf, sizeof(buf), "%dkB", XLOG_BLCKSZ / 1024);
+ values[2] = buf;
+ break;
+ case GTMOPT_UNIT_MS:
+ values[2] = "ms";
+ break;
+ case GTMOPT_UNIT_S:
+ values[2] = "s";
+ break;
+ case GTMOPT_UNIT_MIN:
+ values[2] = "min";
+ break;
+ default:
+ values[2] = "";
+ break;
+ }
+ }
+ else
+ values[2] = NULL;
+
+#if 0
+ /* PGXCTODO: Group parameters are not used yet */
+ /* group */
+ values[3] = config_group_names[conf->group];
+#endif
+
+ /* short_desc */
+ values[4] = conf->short_desc;
+
+ /* extra_desc */
+ values[5] = conf->long_desc;
+
+ /* context */
+ values[6] = GtmOptContext_Names[conf->context];
+
+ /* vartype */
+ values[7] = config_type_names[conf->vartype];
+
+ /* source */
+ values[8] = GtmOptSource_Names[conf->source];
+
+ /* now get the type specifc attributes */
+ switch (conf->vartype)
+ {
+ case GTMC_BOOL:
+ {
+ struct config_bool *lconf = (struct config_bool *) conf;
+
+ /* min_val */
+ values[9] = NULL;
+
+ /* max_val */
+ values[10] = NULL;
+
+ /* enumvals */
+ values[11] = NULL;
+
+ /* boot_val */
+ values[12] = strdup(lconf->boot_val ? "on" : "off");
+
+ /* reset_val */
+ values[13] = strdup(lconf->reset_val ? "on" : "off");
+ }
+ break;
+
+ case GTMC_INT:
+ {
+ struct config_int *lconf = (struct config_int *) conf;
+
+ /* min_val */
+ snprintf(buffer, sizeof(buffer), "%d", lconf->min);
+ values[9] = strdup(buffer);
+
+ /* max_val */
+ snprintf(buffer, sizeof(buffer), "%d", lconf->max);
+ values[10] = strdup(buffer);
+
+ /* enumvals */
+ values[11] = NULL;
+
+ /* boot_val */
+ snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val);
+ values[12] = strdup(buffer);
+
+ /* reset_val */
+ snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val);
+ values[13] = strdup(buffer);
+ }
+ break;
+
+ case GTMC_REAL:
+ {
+ struct config_real *lconf = (struct config_real *) conf;
+
+ /* min_val */
+ snprintf(buffer, sizeof(buffer), "%g", lconf->min);
+ values[9] = strdup(buffer);
+
+ /* max_val */
+ snprintf(buffer, sizeof(buffer), "%g", lconf->max);
+ values[10] = strdup(buffer);
+
+ /* enumvals */
+ values[11] = NULL;
+
+ /* boot_val */
+ snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val);
+ values[12] = strdup(buffer);
+
+ /* reset_val */
+ snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val);
+ values[13] = strdup(buffer);
+ }
+ break;
+
+ case GTMC_STRING:
+ {
+ struct config_string *lconf = (struct config_string *) conf;
+
+ /* min_val */
+ values[9] = NULL;
+
+ /* max_val */
+ values[10] = NULL;
+
+ /* enumvals */
+ values[11] = NULL;
+
+ /* boot_val */
+ if (lconf->boot_val == NULL)
+ values[12] = NULL;
+ else
+ values[12] = strdup(lconf->boot_val);
+
+ /* reset_val */
+ if (lconf->reset_val == NULL)
+ values[13] = NULL;
+ else
+ values[13] = strdup(lconf->reset_val);
+ }
+ break;
+
+ case GTMC_ENUM:
+ {
+ struct config_enum *lconf = (struct config_enum *) conf;
+
+ /* min_val */
+ values[9] = NULL;
+
+ /* max_val */
+ values[10] = NULL;
+
+ /* enumvals */
+
+ /*
+ * NOTE! enumvals with double quotes in them are not
+ * supported!
+ */
+ values[11] = config_enum_get_options((struct config_enum *) conf,
+ "{\"", "\"}", "\",\"");
+
+ /* boot_val */
+ values[12] = strdup(config_enum_lookup_by_value(lconf,
+ lconf->boot_val));
+
+ /* reset_val */
+ values[13] = strdup(config_enum_lookup_by_value(lconf,
+ lconf->reset_val));
+ }
+ break;
+
+ default:
+ {
+ /*
+ * should never get here, but in case we do, set 'em to NULL
+ */
+
+ /* min_val */
+ values[9] = NULL;
+
+ /* max_val */
+ values[10] = NULL;
+
+ /* enumvals */
+ values[11] = NULL;
+
+ /* boot_val */
+ values[12] = NULL;
+
+ /* reset_val */
+ values[13] = NULL;
+ }
+ break;
+ }
+
+ /*
+ * If the setting came from a config file, set the source location. For
+ * security reasons, we don't show source file/line number for
+ * non-superusers.
+ */
+ if (conf->source == GTMC_S_FILE)
+ {
+ values[14] = conf->sourcefile;
+ snprintf(buffer, sizeof(buffer), "%d", conf->sourceline);
+ values[15] = strdup(buffer);
+ }
+ else
+ {
+ values[14] = NULL;
+ values[15] = NULL;
+ }
+}
+
+/*
+ * Return the total number of GTM variables
+ */
+int
+GetNumConfigOptions(void)
+{
+ return num_gtm_opt_variables;
+}
+
+
+static char *
+_ShowOption(struct config_generic * record, bool use_units)
+{
+ char buffer[256];
+ const char *val;
+
+ switch (record->vartype)
+ {
+ case GTMC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+
+ val = *conf->variable ? "on" : "off";
+ }
+ break;
+
+ case GTMC_INT:
+ {
+ struct config_int *conf = (struct config_int *) record;
+
+ /*
+ * Use int64 arithmetic to avoid overflows in units
+ * conversion.
+ */
+ int64 result = *conf->variable;
+ const char *unit;
+
+ if (use_units && result > 0 &&
+ (record->flags & GTMOPT_UNIT_MEMORY))
+ {
+ switch (record->flags & GTMOPT_UNIT_MEMORY)
+ {
+ case GTMOPT_UNIT_BLOCKS:
+ result *= BLCKSZ / 1024;
+ break;
+ case GTMOPT_UNIT_XBLOCKS:
+ result *= XLOG_BLCKSZ / 1024;
+ break;
+ }
+
+ if (result % KB_PER_GB == 0)
+ {
+ result /= KB_PER_GB;
+ unit = "GB";
+ }
+ else if (result % KB_PER_MB == 0)
+ {
+ result /= KB_PER_MB;
+ unit = "MB";
+ }
+ else
+ {
+ unit = "kB";
+ }
+ }
+ else if (use_units && result > 0 &&
+ (record->flags & GTMOPT_UNIT_TIME))
+ {
+ switch (record->flags & GTMOPT_UNIT_TIME)
+ {
+ case GTMOPT_UNIT_S:
+ result *= MS_PER_S;
+ break;
+ case GTMOPT_UNIT_MIN:
+ result *= MS_PER_MIN;
+ break;
+ }
+
+ if (result % MS_PER_D == 0)
+ {
+ result /= MS_PER_D;
+ unit = "d";
+ }
+ else if (result % MS_PER_H == 0)
+ {
+ result /= MS_PER_H;
+ unit = "h";
+ }
+ else if (result % MS_PER_MIN == 0)
+ {
+ result /= MS_PER_MIN;
+ unit = "min";
+ }
+ else if (result % MS_PER_S == 0)
+ {
+ result /= MS_PER_S;
+ unit = "s";
+ }
+ else
+ {
+ unit = "ms";
+ }
+ }
+ else
+ unit = "";
+
+ snprintf(buffer, sizeof(buffer), INT64_FORMAT "%s",
+ result, unit);
+ val = buffer;
+
+ }
+ break;
+
+ case GTMC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) record;
+
+ snprintf(buffer, sizeof(buffer), "%g",
+ *conf->variable);
+ val = buffer;
+ }
+ break;
+
+ case GTMC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+
+ if (*conf->variable && **conf->variable)
+ val = *conf->variable;
+ else
+ val = "";
+ }
+ break;
+
+ case GTMC_ENUM:
+ {
+ struct config_enum *conf = (struct config_enum *) record;
+
+ val = config_enum_lookup_by_value(conf, *conf->variable);
+ }
+ break;
+
+ default:
+ /* just to keep compiler quiet */
+ val = "???";
+ break;
+ }
+
+ return strdup(val);
+}
+
+
+
+/*
+ * A little "long argument" simulation, although not quite GNU
+ * compliant. Takes a string of the form "some-option=some value" and
+ * returns name = "some_option" and value = "some value" in malloc'ed
+ * storage. Note that '-' is converted to '_' in the option name. If
+ * there is no '=' in the input string then value will be NULL.
+ */
+void
+ParseLongOption(const char *string, char **name, char **value)
+{
+ size_t equal_pos;
+ char *cp;
+
+ AssertArg(string);
+ AssertArg(name);
+ AssertArg(value);
+
+ equal_pos = strcspn(string, "=");
+
+ if (string[equal_pos] == '=')
+ {
+ *name = gtm_opt_malloc(FATAL, equal_pos + 1);
+ strlcpy(*name, string, equal_pos + 1);
+
+ *value = gtm_opt_strdup(FATAL, &string[equal_pos + 1]);
+ }
+ else
+ {
+ /* no equal sign in string */
+ *name = gtm_opt_strdup(FATAL, string);
+ *value = NULL;
+ }
+
+ for (cp = *name; *cp; cp++)
+ if (*cp == '-')
+ *cp = '_';
+}
+
+#if 0
+/*
+ * keep-alive related APIs will be used in future extensions
+ */
+void
+gtm_assign_tcp_keepalives_idle(int newval, void *extra)
+{
+ /*
+ * The kernel API provides no way to test a value without setting it; and
+ * once we set it we might fail to unset it. So there seems little point
+ * in fully implementing the check-then-assign GTM API for these
+ * variables. Instead we just do the assignment on demand. pqcomm.c
+ * reports any problems via elog(LOG).
+ *
+ * This approach means that the GTM value might have little to do with the
+ * actual kernel value, so we use a show_hook that retrieves the kernel
+ * value rather than trusting GTM's copy.
+ */
+#if 0
+ (void) pq_setkeepalivesidle(newval, MyProcPort);
+#else
+ (void) pq_setkeepalivesidle_all(newval);
+#endif
+}
+
+const char *
+gtm_show_tcp_keepalives_idle(void)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+ static char nbuf[16];
+
+#if 0
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle(MyProcPort));
+#else
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle_all());
+#endif
+ return nbuf;
+}
+
+void
+gtm_assign_tcp_keepalives_interval(int newval, void *extra)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+#if 0
+ (void) pq_setkeepalivesinterval(newval, MyProcPort);
+#else
+ (void) pq_setkeepalivesinterval_all(newval);
+#endif
+}
+
+const char *
+gtm_show_tcp_keepalives_interval(void)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+ static char nbuf[16];
+
+#if 0
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval(MyProcPort));
+#else
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval_all());
+#endif
+ return nbuf;
+}
+
+void
+gtm_assign_tcp_keepalives_count(int newval, void *extra)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+#if 0
+ (void) pq_setkeepalivescount(newval, MyProcPort);
+#else
+ (void) pq_setkeepalivescount_all(newval);
+#endif
+}
+
+const char *
+gtm_show_tcp_keepalives_count(void)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+ static char nbuf[16];
+
+#if 0
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount(MyProcPort));
+#else
+ snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount_all());
+#endif
+ return nbuf;
+}
+#endif
+
+/*
+ * Try to interpret value as boolean value. Valid values are: true,
+ * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
+ */
+static bool
+gtm_opt_parse_bool(const char *value, bool *result)
+{
+ return gtm_opt_parse_bool_with_len(value, strlen(value), result);
+}
+
+static bool
+gtm_opt_parse_bool_with_len(const char *value, size_t len, bool *result)
+{
+ switch (*value)
+ {
+ case 't':
+ case 'T':
+ if (pg_strncasecmp(value, "true", len) == 0)
+ {
+ if (result)
+ *result = true;
+ return true;
+ }
+ break;
+ case 'f':
+ case 'F':
+ if (pg_strncasecmp(value, "false", len) == 0)
+ {
+ if (result)
+ *result = false;
+ return true;
+ }
+ break;
+ case 'y':
+ case 'Y':
+ if (pg_strncasecmp(value, "yes", len) == 0)
+ {
+ if (result)
+ *result = true;
+ return true;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (pg_strncasecmp(value, "no", len) == 0)
+ {
+ if (result)
+ *result = false;
+ return true;
+ }
+ break;
+ case 'o':
+ case 'O':
+ /* 'o' is not unique enough */
+ if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
+ {
+ if (result)
+ *result = true;
+ return true;
+ }
+ else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
+ {
+ if (result)
+ *result = false;
+ return true;
+ }
+ break;
+ case '1':
+ if (len == 1)
+ {
+ if (result)
+ *result = true;
+ return true;
+ }
+ break;
+ case '0':
+ if (len == 1)
+ {
+ if (result)
+ *result = false;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (result)
+ *result = false; /* suppress compiler warning */
+ return false;
+}
+
+/*
+ * ReportGUCOption: if appropriate, transmit option value to frontend
+ */
+static void
+ReportGTMOption(struct config_generic * record)
+{
+ /* So far, it is empty. */
+}
+
+/*
+ * Lookup the name for an enum option with the selected value.
+ * Should only ever be called with known-valid values, so throws
+ * an elog(ERROR) if the enum option is not found.
+ *
+ * The returned string is a pointer to static data and not
+ * allocated for modification.
+ */
+const char *
+config_enum_lookup_by_value(struct config_enum * record, int val)
+{
+ const struct config_enum_entry *entry;
+
+ for (entry = record->options; entry && entry->name; entry++)
+ {
+ if (entry->val == val)
+ return entry->name;
+ }
+
+ if (isStartUp)
+ write_stderr("could not find enum option %d for %s\n",
+ val, record->gen.name);
+ else
+ elog(ERROR, "could not find enum option %d for %s",
+ val, record->gen.name);
+ return NULL; /* silence compiler */
+}
diff --git a/src/gtm/common/gtm_opt_scanner.l b/src/gtm/common/gtm_opt_scanner.l
new file mode 100644
index 0000000000..f9be2cbfbe
--- /dev/null
+++ b/src/gtm/common/gtm_opt_scanner.l
@@ -0,0 +1,92 @@
+/* -*-pgsql-c-*- */
+/*
+ * Scanner for the configuration file
+ *
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/misc/guc-file.l
+ */
+
+%{
+
+#include "gtm/gtm.h"
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mb/pg_wchar.h"
+#include "gtm/assert.h"
+#include "gtm/gtm_opt.h"
+#include "gtm/elog.h"
+
+
+/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
+#undef fprintf
+#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg)))
+
+enum {
+ GTMOPT_ID = 1,
+ GTMOPT_STRING = 2,
+ GTMOPT_INTEGER = 3,
+ GTMOPT_REAL = 4,
+ GTMOPT_EQUALS = 5,
+ GTMOPT_UNQUOTED_STRING = 6,
+ GTMOPT_QUALIFIED_ID = 7,
+ GTMOPT_EOL = 99,
+ GTMOPT_ERROR = 100
+};
+
+static unsigned int ConfigFileLineno;
+
+/* flex fails to supply a prototype for yylex, so provide one */
+int GTMOPT_yylex(void);
+
+%}
+
+%option 8bit
+%option never-interactive
+%option nodefault
+%option noinput
+%option nounput
+%option noyywrap
+%option prefix="GTMOPT_yy"
+
+
+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]|\\.|\'\')*\'
+
+%%
+
+\n ConfigFileLineno++; return GTMOPT_EOL;
+[ \t\r]+ /* eat whitespace */
+#.* /* eat comment (.* matches anything until newline) */
+
+{ID} return GTMOPT_ID;
+{QUALIFIED_ID} return GTMOPT_QUALIFIED_ID;
+{STRING} return GTMOPT_STRING;
+{UNQUOTED_STRING} return GTMOPT_UNQUOTED_STRING;
+{INTEGER} return GTMOPT_INTEGER;
+{REAL} return GTMOPT_REAL;
+= return GTMOPT_EQUALS;
+
+. return GTMOPT_ERROR;
+
+%%
diff --git a/src/gtm/gtm_ctl/gtm_ctl.c b/src/gtm/gtm_ctl/gtm_ctl.c
index dcb586206c..4ced20d1f7 100644
--- a/src/gtm/gtm_ctl/gtm_ctl.c
+++ b/src/gtm/gtm_ctl/gtm_ctl.c
@@ -646,11 +646,7 @@ do_reconnect(void)
fprintf(reconnect_point_file, "%s\n", gtm_opts);
fclose(reconnect_point_file);
free(reconnect_point_file_nam);
-#if 0 /* GTM_SBY_DEBUG */
- write_stderr("Now about to send SIGUSR1 to pid %ld.\n", pid);
- write_stderr("Returning. This is the debug. Don't send signal actually.\n");
- return;
-#endif
+
if (kill((pid_t) pid, SIGUSR1) != 0)
{
write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), progname, pid,
@@ -882,7 +878,7 @@ do_help(void)
printf(_("\nCommon options:\n"));
printf(_(" -D DATADIR location of the database storage area\n"));
- printf(_(" -i NODE_ID set gtm_proxy ID registered on GTM\n"));
+ printf(_(" -i nodename set gtm_proxy nodename registered on GTM\n"));
printf(_(" (option ignored if used with GTM)\n"));
printf(_(" -t SECS seconds to wait when using -w option\n"));
printf(_(" -w wait until operation completes\n"));
@@ -940,7 +936,7 @@ int
main(int argc, char **argv)
{
int c;
- int node_id = 0; /* GTM Proxy ID */
+ char *nodename = NULL; /* GTM Proxy nodename */
progname = "gtm_ctl";
@@ -1014,7 +1010,7 @@ main(int argc, char **argv)
break;
}
case 'i':
- node_id = atoi(optarg);
+ nodename = strdup(optarg);
break;
case 'l':
log_file = xstrdup(optarg);
@@ -1128,6 +1124,7 @@ main(int argc, char **argv)
}
if (strcmp(gtm_app,"gtm_proxy") != 0 &&
+ strcmp(gtm_app, "gtm_standby") != 0 &&
strcmp(gtm_app,"gtm") != 0)
{
write_stderr(_("%s: launch option incorrect\n"),
@@ -1140,18 +1137,14 @@ main(int argc, char **argv)
if (ctl_command == START_COMMAND ||
ctl_command == RESTART_COMMAND)
{
- if (node_id == 0 && strcmp(gtm_app, "gtm_proxy") == 0)
- {
- write_stderr("%s: GTM Proxy ID not specified\n",
- progname);
- do_advice();
- exit(1);
- }
/* Rebuild option string to include Proxy ID */
if (strcmp(gtm_app, "gtm_proxy") == 0)
{
gtmdata_opt = (char *) pg_realloc(gtmdata_opt, strlen(gtmdata_opt) + 9);
- sprintf(gtmdata_opt, "%s -i %d ", gtmdata_opt, node_id);
+ if (nodename)
+ sprintf(gtmdata_opt, "%s -i %s ", gtmdata_opt, nodename);
+ else
+ sprintf(gtmdata_opt, "%s ", gtmdata_opt);
}
}
@@ -1173,26 +1166,6 @@ main(int argc, char **argv)
}
}
-#if 0
- if (gtm_data)
- {
- if (strcmp(gtm_app,"gtm_proxy") == 0)
- {
- snprintf(pid_file, MAXPGPATH, "%s/gtm_proxy.pid", gtm_data);
- snprintf(gtmopts_file, MAXPGPATH, "%s/gtm_proxy.opts", gtm_data);
- }
- else if (strcmp(gtm_app,"gtm") == 0)
- {
- snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
- snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
- }
- else if (strcmp(gtm_app,"gtm_standby") == 0)
- {
- snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
- snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
- }
- }
-#else
/* Build strings for pid file and option file */
if (strcmp(gtm_app,"gtm_proxy") == 0)
{
@@ -1209,7 +1182,6 @@ main(int argc, char **argv)
snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
}
-#endif
if (ctl_command==STATUS_COMMAND)
gtm_opts = xstrdup("-c");
diff --git a/src/gtm/main/Makefile b/src/gtm/main/Makefile
index 5874143311..f5d1684d37 100644
--- a/src/gtm/main/Makefile
+++ b/src/gtm/main/Makefile
@@ -3,13 +3,13 @@
top_builddir=../../..
include $(top_builddir)/src/Makefile.global
-OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
+OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o gtm_opt.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq
LIBS=-lpthread
gtm:$(OBJS)
- $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o gtm
+ $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ ../../port/libpgport.a -o gtm
all:gtm
diff --git a/src/gtm/main/gtm.conf.sample b/src/gtm/main/gtm.conf.sample
new file mode 100644
index 0000000000..4121849eef
--- /dev/null
+++ b/src/gtm/main/gtm.conf.sample
@@ -0,0 +1,58 @@
+# ----------------------
+# GTM configuration file
+# ----------------------
+#
+# This file must be placed on gtm working directory
+# specified by -D command line option of gtm or gtm_ctl. The
+# configuration file name must be "gtm.conf"
+#
+#
+# This file consists of lines of the form
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are
+# introduced with "#" anywhere on a line. The complete list of
+# parameter names and allowed values can be found in the
+# Postgres-XC documentation.
+#
+# The commented-out settings shown in this file represent the default
+# values.
+#
+# Re-commenting a setting is NOT sufficient to revert it to the default
+# value.
+#
+# You need to restart the server.
+
+#------------------------------------------------------------------------------
+# GENERAL PARAMETERS
+#------------------------------------------------------------------------------
+#nodename = '' # Specifies the node name.
+ # (changes requires restart)
+#listen_addresses = '*' # Listen addresses of this GTM.
+ # (changes requires restart)
+#port = 6666 # Port number of this GTM.
+ # (changes requires restart)
+
+#startup = ACT # Start mode. ACT/STANDBY.
+
+#------------------------------------------------------------------------------
+# GTM STANDBY PARAMETERS
+#------------------------------------------------------------------------------
+#Those parameters are effective when GTM is activated as a standby server
+#active_host = '' # Listen address of active GTM.
+ # (changes requires restart)
+#active_port = # Port number of active GTM.
+ # (changes requires restart)
+
+#---------------------------------------
+# OTHER OPTIONS
+#---------------------------------------
+#keepalives_idle = 0 # Keepalives_idle parameter.
+#keepalives_interval = 0 # Keepalives_interval parameter.
+#keepalives_count = 0 # Keepalives_count internal parameter.
+#log_file = 'gtm.log' # Log file name
+#log_min_messages = WARNING # log_min_messages. Default WARNING.
+ # Valid value: DEBUG, DEBUG5, DEBUG4, DEBU3,
+ # DEBUG2, DEBUG1, INFO, NOTICE, WARNING,
+ # ERROR, LOG, FATAL, PANIC
diff --git a/src/gtm/main/gtm_opt.c b/src/gtm/main/gtm_opt.c
new file mode 100644
index 0000000000..580a4646cf
--- /dev/null
+++ b/src/gtm/main/gtm_opt.c
@@ -0,0 +1,342 @@
+/*--------------------------------------------------------------------
+ * guc.c
+ *
+ * Support for grand unified configuration scheme, including SET
+ * command, configuration file, and
+ command line options.
+ * See src/backend/utils/misc/README for more information.
+ *
+ *
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ * Written by Peter Eisentraut <[email protected]>.
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/guc.c
+ *
+ *--------------------------------------------------------------------
+ */
+#include "gtm/gtm_c.h"
+
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "gtm/gtm.h"
+#include "gtm/path.h"
+#include "gtm/gtm_opt_tables.h"
+#include "gtm/gtm_opt.h"
+#include "gtm/gtm_standby.h"
+
+
+#define CONFIG_FILENAME "gtm.conf"
+const char *config_filename = CONFIG_FILENAME;
+
+/*
+ * Variables declared elsewhere for gtm, mainly option variables.
+ */
+
+extern char *NodeName;
+extern char *ListenAddresses;
+extern int GTMPortNumber;
+extern char *active_addr;
+extern int active_port;
+extern int GTM_StandbyMode;
+extern char *error_reporter;
+extern char *status_reader;
+extern int log_min_messages;
+extern int keepalives_idle;
+extern int keepalives_count;
+extern int keepalives_interval;
+extern char *GTMDataDir;
+
+
+
+/*
+ * We have different sets for client and server message level options because
+ * they sort slightly different (see "log" level)
+ */
+
+Server_Message_Level_Options();
+Gtm_Startup_Mode_Options();
+
+/*
+ * GTM option variables that are exported from this module
+ */
+char *data_directory;
+char *GTMConfigFileName;
+
+/*
+ * Displayable names for context types (enum GtmContext)
+ */
+gtmOptContext_Names();
+
+/*
+ * Displayable names for source types (enum GtmSource)
+ */
+gtmOptSource_Names();
+
+
+/*
+ * Displayable names for GTM variable types (enum config_type)
+ */
+Config_Type_Names();
+
+/*
+ * Contents of GTM tables
+ *
+ * See src/backend/utils/misc/README for design notes.
+ *
+ * TO ADD AN OPTION:
+ *
+ * 1. Declare a global variable of type bool, int, double, or char*
+ * and make use of it.
+ *
+ * 2. Decide at what times it's safe to set the option. See guc.h for
+ * details.
+ *
+ * 3. Decide on a name, a default value, upper and lower bounds (if
+ * applicable), etc.
+ *
+ * 4. Add a record below.
+ *
+ * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if
+ * appropriate.
+ *
+ * 6. Don't forget to document the option (at least in config.sgml).
+ *
+ * 7. If it's a new GTMOPT_LIST option you must edit pg_dumpall.c to ensure
+ * it is not single quoted at dump time.
+ */
+
+
+/******** option records follow ********/
+
+struct config_bool ConfigureNamesBool[] =
+{
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, false, false, NULL
+ }
+};
+
+
+struct config_int ConfigureNamesInt[] =
+{
+ {
+ {"port", GTMC_STARTUP,
+ gettext_noop("Listen Port of GTM or GTM standby server."),
+ NULL,
+ 0
+ },
+ &GTMPortNumber,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"active_port", GTMC_SIGHUP,
+ gettext_noop("GTM server port number when it works as GTM-Standby."),
+ NULL,
+ 0
+ },
+ &active_port,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_idle", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_idle\" option for the connection to GTM."),
+ gettext_noop("This option is effective only when it runs as GTM-Standby."),
+ GTMOPT_UNIT_TIME
+ },
+ &keepalives_idle,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_interval", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_interval\" option fo the connetion to GTM."),
+ gettext_noop("This option is effective only when it runs as GTM-Standby."),
+ GTMOPT_UNIT_TIME
+ },
+ &keepalives_interval,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_count", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_count\" option to the connection to GTM."),
+ gettext_noop("This option is effective only when it runs as GTM-Standby."),
+ 0
+ },
+ &keepalives_count,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0, 0, 0, 0, NULL
+ }
+};
+
+
+struct config_real ConfigureNamesReal[] =
+{
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0.0, 0.0, 0.0, 0.0, NULL
+ }
+};
+
+struct config_string ConfigureNamesString[] =
+{
+ {
+ {"data_dir", GTMC_STARTUP,
+ gettext_noop("Work directory."),
+ NULL,
+ 0
+ },
+ &GTMDataDir,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ {"config_file", GTMC_SIGHUP,
+ gettext_noop("Configuration file name."),
+ NULL,
+ 0
+ },
+ &GTMConfigFileName,
+ CONFIG_FILENAME,
+ NULL,
+ NULL
+ },
+
+ {
+ {"nodename", GTMC_STARTUP,
+ gettext_noop("Name of this GTM/GTM-Standby."),
+ NULL,
+ 0
+ },
+ &NodeName,
+ "",
+ NULL,
+ NULL
+ },
+
+ {
+ {"listen_addresses", GTMC_STARTUP,
+ gettext_noop("Listen address."),
+ NULL,
+ 0
+ },
+ &ListenAddresses,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"active_host", GTMC_SIGHUP,
+ gettext_noop("Address of target GTM ACT."),
+ gettext_noop("This parameter is effective only when it runs as GTM-Standby"),
+ 0
+ },
+ &active_addr,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"log_file", GTMC_SIGHUP,
+ gettext_noop("Log file name."),
+ NULL,
+ 0
+ },
+ &GTMLogFile,
+ "gtm.log",
+ NULL, NULL
+ },
+
+ {
+ {"error_reporter", GTMC_SIGHUP,
+ gettext_noop("Command to report various errors."),
+ NULL,
+ 0
+ },
+ &error_reporter,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"status_reader", GTMC_SIGHUP,
+ gettext_noop("Command to get status of global XC node status."),
+ gettext_noop("Runs when configuration file is read by SIGHUP"),
+ 0
+ },
+ &status_reader,
+ NULL,
+ NULL, NULL
+ },
+
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL}, NULL, NULL, NULL, NULL
+ }
+};
+
+
+struct config_enum ConfigureNamesEnum[] =
+{
+ {
+ {"log_min_messages", GTMC_SIGHUP,
+ gettext_noop("Minimum message level to write to the log file."),
+ NULL,
+ 0
+ },
+ &log_min_messages,
+ WARNING,
+ server_message_level_options,
+ WARNING, NULL
+ },
+
+ {
+ {"startup", GTMC_SIGHUP,
+ gettext_noop("Specifies startup mode, act or standby."),
+ NULL,
+ 0
+ },
+ &GTM_StandbyMode,
+ GTM_ACT_MODE,
+ gtm_startup_mode_options,
+ GTM_ACT_MODE, NULL
+ },
+
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0, NULL, 0, NULL
+ }
+};
+
+/******** end of options list ********/
+
+/*
+ * Actual lookup of variables is done through this single, sorted array.
+ */
+struct config_generic **gtm_opt_variables;
+
+/* Current number of variables contained in the vector */
+int num_gtm_opt_variables;
+
+/* Vector capacity */
+int size_gtm_opt_variables;
+
+
+bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */
+
+int GTMOptUpdateCount = 0; /* Indicates when specific option is updated */
diff --git a/src/gtm/main/gtm_standby.c b/src/gtm/main/gtm_standby.c
index 8d9bad0c97..6e5a31d32d 100644
--- a/src/gtm/main/gtm_standby.c
+++ b/src/gtm/main/gtm_standby.c
@@ -33,6 +33,8 @@ static char *standbyDataDir;
static GTM_Conn *gtm_standby_connect_to_standby_int(int *);
+extern char *NodeName; /* Defined in main.c */
+
int
gtm_standby_start_startup(void)
{
@@ -42,8 +44,8 @@ gtm_standby_start_startup(void)
elog(LOG, "Connecting the GTM active on %s:%d...", active_address, active_port);
- sprintf(connect_string, "host=%s port=%d node_name=one remote_type=%d",
- active_address, active_port, PGXC_NODE_GTM);
+ sprintf(connect_string, "host=%s port=%d node_name=%s remote_type=%d",
+ active_address, active_port, NodeName, PGXC_NODE_GTM);
GTM_ActiveConn = PQconnectGTM(connect_string);
if (GTM_ActiveConn == NULL)
@@ -336,7 +338,7 @@ find_standby_node_info(void)
node[i]->port,
node[i]->status);
- if ( (strcmp(standbyNodeName, node[i]->nodename) == 0) &&
+ if ( (strcmp(standbyNodeName, node[i]->nodename) != 0) &&
node[i]->status == NODE_CONNECTED)
return node[i];
}
@@ -396,8 +398,8 @@ gtm_standby_connect_to_standby_int(int *report_needed)
*report_needed = 1;
snprintf(conn_string, sizeof(conn_string),
- "host=%s port=%d node_name=one remote_type=4",
- n->ipaddress, n->port);
+ "host=%s port=%d node_name=%s remote_type=4",
+ n->ipaddress, n->port, NodeName);
standby = PQconnectGTM(conn_string);
diff --git a/src/gtm/main/main.c b/src/gtm/main/main.c
index 4648e9c3ac..44d40bae4b 100644
--- a/src/gtm/main/main.c
+++ b/src/gtm/main/main.c
@@ -44,6 +44,7 @@
#include "gtm/gtm_txn.h"
#include "gtm/gtm_seq.h"
#include "gtm/gtm_msg.h"
+#include "gtm/gtm_opt.h"
extern int optind;
extern char *optarg;
@@ -61,6 +62,14 @@ int GTMPortNumber;
char GTMControlFile[GTM_MAX_PATH];
char *GTMDataDir;
char *NodeName;
+char *active_addr;
+int active_port;
+int keepalives_idle;
+int keepalives_interval;
+int keepalives_count;
+char *error_reporter;
+char *status_reader;
+bool isStartUp;
GTM_ThreadID TopMostThreadID;
@@ -117,6 +126,7 @@ MainThreadInit()
* use malloc
*/
thrinfo = (GTM_ThreadInfo *)malloc(sizeof (GTM_ThreadInfo));
+ memset(thrinfo, 0, sizeof(GTM_ThreadInfo));
if (thrinfo == NULL)
{
@@ -261,10 +271,47 @@ main(int argc, char *argv[])
int i;
GlobalTransactionId next_gxid = InvalidGlobalTransactionId;
int ctlfd;
- char *active_addr;
- int active_port;
/*
+ * Local variable to hold command line options.
+ *
+ * if -c option is specified, then only -D option will be taken to locate
+ * GTM data directory. All the other options are ignored. GTM status
+ * will be printed out based on the specified data directory and GTM will
+ * simply exit. If -D option is not specified in this case, current directory
+ * will be used.
+ *
+ * In other case, all the command line options are analyzed.
+ *
+ * They're first analyzed and then -D option are used to locate configuration file.
+ * If -D option is not specified, then the default value will be used.
+ *
+ * Please note that configuration specified in the configuration file (gtm.conf)
+ * will be analyzed first and then will be overridden by the value specified
+ * in command line options. -D and -C options are handled separately and used
+ * to determine configuration file location.
+ *
+ * Please also note that -x option (startup GXID) will be handled in this section.
+ * It has no corresponding configuration from the configuration file.
+ */
+
+ bool is_gtm_status = false; /* "false" means no -c option was found */
+ char *listen_addresses = NULL;
+ char *node_name = NULL;
+ char *port_number = NULL;
+ char *data_dir = NULL;
+ char *log_file = NULL;
+ char *is_standby_mode = NULL;
+ char *dest_addr = NULL;
+ char *dest_port = NULL;
+
+ isStartUp = true;
+
+ /*
+ * At first, initialize options. Also moved something from BaseInit() here.
+ */
+ InitializeGTMOptions();
+ /*
* Catch standard options before doing much else
*/
if (argc > 1)
@@ -276,7 +323,7 @@ main(int argc, char *argv[])
}
}
- ListenAddresses = GTM_DEFAULT_HOSTNAME;
+ ListenAddresses = strdup(GTM_DEFAULT_HOSTNAME);
GTMPortNumber = GTM_DEFAULT_PORT;
/*
@@ -291,15 +338,21 @@ main(int argc, char *argv[])
break; /* never reach here. */
case 'h':
- ListenAddresses = strdup(optarg);
+ if (listen_addresses)
+ free(listen_addresses);
+ listen_addresses = strdup(optarg);
break;
case 'n':
+ if (NodeName)
+ free(NodeName);
NodeName = strdup(optarg);
break;
case 'p':
- GTMPortNumber = atoi(optarg);
+ if (port_number)
+ free(port_number);
+ port_number = strdup(optarg);
break;
case 'x':
@@ -307,24 +360,34 @@ main(int argc, char *argv[])
break;
case 'D':
- GTMDataDir = strdup(optarg);
- canonicalize_path(GTMDataDir);
+ if (data_dir)
+ free(data_dir);
+ data_dir = strdup(optarg);
+ canonicalize_path(data_dir);
break;
case 'l':
- GTMLogFile = strdup(optarg);
+ if (log_file)
+ free(log_file);
+ log_file = strdup(optarg);
break;
case 's':
- Recovery_StandbySetStandby(true);
+ if (is_standby_mode)
+ free(is_standby_mode);
+ is_standby_mode = strdup("standby");
break;
case 'i':
- active_addr = strdup(optarg);
+ if (dest_addr)
+ free(dest_addr);
+ dest_addr = strdup(optarg);
break;
case 'q':
- active_port = atoi(optarg);
+ if (dest_port)
+ free(dest_port);
+ dest_port = strdup(optarg);
break;
default:
@@ -334,6 +397,74 @@ main(int argc, char *argv[])
}
/*
+ * Handle status, if any
+ */
+ if (is_gtm_status)
+ gtm_status();
+ /* Never reach beyond here */
+
+ /*
+ * Setup work directory
+ */
+ if (data_dir)
+ SetConfigOption("data_dir", data_dir, GTMC_STARTUP, GTMC_S_OVERRIDE);
+
+ /*
+ * Setup configuration file
+ */
+ if (!SelectConfigFiles(data_dir, progname))
+ exit(1);
+
+ /*
+ * Parse config file
+ */
+ ProcessConfigFile(GTMC_STARTUP);
+ /*
+ * Override with command line options
+ */
+ if (log_file)
+ {
+ SetConfigOption("log_file", log_file, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(log_file);
+ log_file = NULL;
+ }
+ if (listen_addresses)
+ {
+ SetConfigOption("listen_addreses", listen_addresses, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(listen_addresses);
+ listen_addresses = NULL;
+ }
+ if (node_name)
+ {
+ SetConfigOption("nodename", node_name, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(node_name);
+ node_name = NULL;
+ }
+ if (port_number)
+ {
+ SetConfigOption("port", port_number, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(port_number);
+ port_number = NULL;
+ }
+ if (is_standby_mode)
+ {
+ SetConfigOption("startup", is_standby_mode, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(is_standby_mode);
+ is_standby_mode = NULL;
+ }
+ if (dest_addr)
+ {
+ SetConfigOption("active_host", dest_addr, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(dest_addr);
+ dest_addr = NULL;
+ }
+ if (dest_port)
+ {
+ SetConfigOption("active_port", dest_port, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(dest_port);
+ dest_port = NULL;
+ }
+ /*
* Check options for the standby mode.
*/
if (Recovery_IsStandby())
diff --git a/src/gtm/path/path.c b/src/gtm/path/path.c
index 32009a3bcf..ccf19c7af6 100644
--- a/src/gtm/path/path.c
+++ b/src/gtm/path/path.c
@@ -129,6 +129,18 @@ canonicalize_path(char *path)
}
/*
+ * get_parent_directory
+ *
+ * Modify the given string in-place to name the parent directory of the
+ * named file.
+ */
+void
+get_parent_directory(char *path)
+{
+ trim_directory(path);
+}
+
+/*
* trim_directory
*
* Trim trailing directory from path, that is, remove any trailing slashes,
@@ -238,3 +250,53 @@ make_absolute_path(const char *path)
return new;
}
+
+
+/*
+ * join_path_components - join two path components, inserting a slash
+ *
+ * ret_path is the output area (must be of size MAXPGPATH)
+ *
+ * ret_path can be the same as head, but not the same as tail.
+ */
+void
+join_path_components(char *ret_path,
+ const char *head, const char *tail)
+{
+ if (ret_path != head)
+ strlcpy(ret_path, head, MAXPGPATH);
+
+ /*
+ * Remove any leading "." and ".." in the tail component, adjusting head
+ * as needed.
+ */
+ for (;;)
+ {
+ if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
+ {
+ tail += 2;
+ }
+ else if (tail[0] == '.' && tail[1] == '\0')
+ {
+ tail += 1;
+ break;
+ }
+ else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
+ {
+ trim_directory(ret_path);
+ tail += 3;
+ }
+ else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
+ {
+ trim_directory(ret_path);
+ tail += 2;
+ break;
+ }
+ else
+ break;
+ }
+ if (*tail)
+ snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
+ "/%s", tail);
+}
+
diff --git a/src/gtm/proxy/Makefile b/src/gtm/proxy/Makefile
index baac5309c7..4eeb482425 100644
--- a/src/gtm/proxy/Makefile
+++ b/src/gtm/proxy/Makefile
@@ -3,14 +3,14 @@
top_builddir=../../..
include $(top_builddir)/src/Makefile.global
-OBJS=proxy_main.o proxy_thread.o proxy_utils.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
+OBJS=proxy_main.o proxy_thread.o proxy_utils.o gtm_proxy_opt.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq
LIBS=-lpthread
gtm_proxy:$(OBJS)
- $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o gtm_proxy
+ $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ ../../port/libpgport_srv.a -o gtm_proxy
all:gtm_proxy
diff --git a/src/gtm/proxy/gtm_proxy.conf.sample b/src/gtm/proxy/gtm_proxy.conf.sample
new file mode 100644
index 0000000000..97eefd0c3d
--- /dev/null
+++ b/src/gtm/proxy/gtm_proxy.conf.sample
@@ -0,0 +1,64 @@
+#-----------------------------
+# GTM Proxy configuration file
+#-----------------------------
+#
+# This file must be placed on gtm working directory
+# specified by -D command line option of gtm_proxy or gtm_ctl.
+# The configuration file name must be "gtm_proxy.conf"
+#
+#
+# This file consists of lines of the form
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are
+# introduced with "#" anywhere on a line. The complete list of
+# parameter names and allowed values can be found in the
+# Postgres-XC documentation.
+#
+# The commented-out settings shown in this file represent the default
+# values.
+#
+# Re-commenting a setting is NOT sufficient to revert it to the default
+# value.
+#
+# You need to restart the server.
+
+#------------------------------------------------------------------------------
+# GENERAL PARAMETERS
+#------------------------------------------------------------------------------
+#nodename = '' # Specifies the node name.
+ # (changes requires restart)
+#listen_addresses = '*' # Listen addresses of this GTM.
+ # (changes requires restart)
+#port = 6666 # Port number of this GTM.
+ # (changes requires restart)
+
+#------------------------------------------------------------------------------
+# GTM PROXY PARAMETERS
+#------------------------------------------------------------------------------
+#worker_threads = 1 # Number of the worker thread of this
+ # GTM proxy
+ # (changes requires restart)
+
+#------------------------------------------------------------------------------
+# GTM CONNECTION PARAMETERS
+#------------------------------------------------------------------------------
+# Those parameters are used to connect to a GTM server
+#gtm_host = '' # Listen address of the active GTM.
+ # (changes requires restart)
+#gtm_port = # Port number of the active GTM.
+ # (changes requires restart)
+
+#------------------------------------------------------------------------------
+# Other options
+#------------------------------------------------------------------------------
+#keepalives_idle = 0 # Keepalives_idle parameter.
+#keepalives_interval = 0 # Keepalives_interval parameter.
+#keepalives_count = 0 # Keepalives_count internal parameter.
+#log_file = 'gtm_proxy.log' # Log file name
+#log_min_messages = WARNING # log_min_messages. Default WARNING.
+ # Valid value: DEBUG, DEBUG5, DEBUG4, DEBU3,
+ # DEBUG2, DEBUG1, INFO, NOTICE, WARNING,
+ # ERROR, LOG, FATAL, PANIC.
+
diff --git a/src/gtm/proxy/gtm_proxy_opt.c b/src/gtm/proxy/gtm_proxy_opt.c
new file mode 100644
index 0000000000..0492aaece1
--- /dev/null
+++ b/src/gtm/proxy/gtm_proxy_opt.c
@@ -0,0 +1,392 @@
+/*--------------------------------------------------------------------
+ * guc.c
+ *
+ * Support for grand unified configuration scheme, including SET
+ * command, configuration file, and
+ command line options.
+ * See src/backend/utils/misc/README for more information.
+ *
+ *
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ * Written by Peter Eisentraut <[email protected]>.
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/guc.c
+ *
+ *--------------------------------------------------------------------
+ */
+#include "gtm/gtm_c.h"
+
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "gtm/gtm.h"
+#include "gtm/path.h"
+#include "gtm/gtm_opt_tables.h"
+#include "gtm/gtm_opt.h"
+#include "gtm/gtm_standby.h"
+
+#define CONFIG_FILENAME "gtm_proxy.conf"
+const char *config_filename = CONFIG_FILENAME;
+
+/*
+ * Variables declared elsewhere for gtm, mainly option variables.
+ */
+
+extern char *GTMProxyNodeName;
+extern char *ListenAddresses;
+extern int GTMPortNumber;
+extern char *error_reporter;
+extern char *status_reader;
+extern int log_min_messages;
+extern int keepalives_idle;
+extern int keepalives_count;
+extern int keepalives_interval;
+extern bool GTMErrorWaitOpt;
+extern char *GTMServerHost;
+extern int GTMProxyPortNumber;
+extern int GTMServerPortNumber;
+extern int GTMServerKeepalivesIdle;
+extern int GTMServerKeepalivesInterval;
+extern int GTMServerKeepalivesCount;
+extern int GTMErrorWaitSecs;
+extern int GTMErrorWaitCount;
+extern int GTMProxyWorkerThreads;
+extern char *GTMProxyDataDir;
+extern char *GTMProxyConfigFileName;
+extern char *GTMConfigFileName;
+
+
+/*
+ * Macros for values
+ *
+ * Some of them are declared also in proxy_main.c.
+ */
+#define GTM_PROXY_DEFAULT_WORKERS 2
+
+/*
+ * We have different sets for client and server message level options because
+ * they sort slightly different (see "log" level)
+ */
+
+Server_Message_Level_Options();
+
+static const struct config_enum_entry gtm_startup_mode_options[] = {
+ {"act", GTM_ACT_MODE, false},
+ {"standby", GTM_STANDBY_MODE, false},
+ {NULL, 0, false}
+};
+
+
+/*
+ * GTM option variables that are exported from this module
+ */
+char *data_directory;
+char *GTMConfigFileName;
+
+
+/*
+ * Displayable names for context types (enum GtmContext)
+ */
+gtmOptContext_Names();
+
+/*
+ * Displayable names for source types (enum GtmSource)
+ *
+ */
+gtmOptSource_Names();
+
+/*
+ * Displayable names for GTM variable types (enum config_type)
+ *
+ * Note: these strings are deliberately not localized.
+ */
+Config_Type_Names();
+
+
+/*
+ * Contents of GTM tables
+ *
+ * See src/backend/utils/misc/README for design notes.
+ *
+ * TO ADD AN OPTION:
+ *
+ * 1. Declare a global variable of type bool, int, double, or char*
+ * and make use of it.
+ *
+ * 2. Decide at what times it's safe to set the option. See guc.h for
+ * details.
+ *
+ * 3. Decide on a name, a default value, upper and lower bounds (if
+ * applicable), etc.
+ *
+ * 4. Add a record below.
+ *
+ * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if
+ * appropriate.
+ *
+ * 6. Don't forget to document the option (at least in config.sgml).
+ *
+ * 7. If it's a new GTMOPT_LIST option you must edit pg_dumpall.c to ensure
+ * it is not single quoted at dump time.
+ */
+
+
+/******** option records follow ********/
+
+struct config_bool ConfigureNamesBool[] =
+{
+ {
+ {"err_wait_opt", GTMC_SIGHUP,
+ gettext_noop("If GTM_Proxy waits for reconnect when GTM communication error is encountered."),
+ NULL,
+ 0
+ },
+ &GTMErrorWaitOpt,
+ false, false, NULL
+ },
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, false, false, NULL
+ }
+};
+
+
+struct config_int ConfigureNamesInt[] =
+{
+ {
+ {"port", GTMC_STARTUP,
+ gettext_noop("Listen Port of GTM_Proxy server."),
+ NULL,
+ 0
+ },
+ &GTMProxyPortNumber,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"gtm_port", GTMC_SIGHUP,
+ gettext_noop("GTM server port number."),
+ NULL,
+ 0
+ },
+ &GTMServerPortNumber,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_idle", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_idle\" option for the connection to GTM."),
+ NULL,
+ GTMOPT_UNIT_TIME
+ },
+ &GTMServerKeepalivesIdle,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_interval", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_interval\" option fo the connetion to GTM."),
+ NULL,
+ GTMOPT_UNIT_TIME
+ },
+ &GTMServerKeepalivesInterval,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"keepalives_count", GTMC_STARTUP,
+ gettext_noop("Sets \"keepalives_count\" option to the connection to GTM."),
+ NULL,
+ 0
+ },
+ &GTMServerKeepalivesCount,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"err_wait_interval", GTMC_SIGHUP,
+ gettext_noop("Wait interval to wait for reconnect."),
+ gettext_noop("This parameter determines GTM Proxy behavior when GTM communication error is encountered."),
+ 0
+ },
+ &GTMErrorWaitSecs,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"err_wait_count", GTMC_SIGHUP,
+ gettext_noop("Number of err_wait_interval to wait for reconnect."),
+ gettext_noop("This parameter determines GTM Prox behavior when GTM communication error is encountered."),
+ 0
+ },
+ &GTMErrorWaitCount,
+ 0, 0, INT_MAX,
+ 0, NULL
+ },
+ {
+ {"worker_threads", GTMC_STARTUP,
+ gettext_noop("Number of worker thread."),
+ NULL,
+ 0
+ },
+ &GTMProxyWorkerThreads,
+ GTM_PROXY_DEFAULT_WORKERS, 1, INT_MAX,
+ 0, NULL
+ },
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0, 0, 0, 0, NULL
+ }
+};
+
+
+struct config_real ConfigureNamesReal[] =
+{
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0.0, 0.0, 0.0, 0.0, NULL
+ }
+};
+
+struct config_string ConfigureNamesString[] =
+{
+ {
+ {"data_dir", GTMC_STARTUP,
+ gettext_noop("Work directory."),
+ NULL,
+ 0
+ },
+ &GTMProxyDataDir,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ {"config_file", GTMC_SIGHUP,
+ gettext_noop("Configuration file name."),
+ NULL,
+ 0
+ },
+ &GTMConfigFileName,
+ CONFIG_FILENAME,
+ NULL,
+ NULL
+ },
+
+ {
+ {"listen_addresses", GTMC_STARTUP,
+ gettext_noop("Listen address."),
+ NULL,
+ 0
+ },
+ &ListenAddresses,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"nodename", GTMC_STARTUP,
+ gettext_noop("My node name."),
+ NULL,
+ 0,
+ },
+ &GTMProxyNodeName,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"gtm_host", GTMC_SIGHUP,
+ gettext_noop("Address of target GTM ACT."),
+ NULL,
+ 0
+ },
+ &GTMServerHost,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"log_file", GTMC_SIGHUP,
+ gettext_noop("Log file name."),
+ NULL,
+ 0
+ },
+ &GTMLogFile,
+ "gtm_proxy.log",
+ NULL, NULL
+ },
+
+ {
+ {"error_reporter", GTMC_SIGHUP,
+ gettext_noop("Command to report various errors."),
+ NULL,
+ 0
+ },
+ &error_reporter,
+ NULL,
+ NULL, NULL
+ },
+
+ {
+ {"status_reader", GTMC_SIGHUP,
+ gettext_noop("Command to get status of global XC node status."),
+ gettext_noop("Runs when configuration file is read by SIGHUP"),
+ 0
+ },
+ &status_reader,
+ NULL,
+ NULL, NULL
+ },
+
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL}, NULL, NULL, NULL, NULL
+ }
+};
+
+
+struct config_enum ConfigureNamesEnum[] =
+{
+ {
+ {"log_min_messages", GTMC_SIGHUP,
+ gettext_noop("Minimum message level to write to the log file."),
+ NULL,
+ 0
+ },
+ &log_min_messages,
+ WARNING,
+ server_message_level_options,
+ WARNING, NULL
+ },
+
+ /* End-of-list marker */
+ {
+ {NULL, 0, NULL, NULL, 0}, NULL, 0, NULL, 0, NULL
+ }
+};
+
+/******** end of options list ********/
+
+/*
+ * Actual lookup of variables is done through this single, sorted array.
+ */
+struct config_generic **gtm_opt_variables;
+
+/* Current number of variables contained in the vector */
+int num_gtm_opt_variables;
+
+/* Vector capacity */
+int size_gtm_opt_variables;
+
+
+bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */
+
+int GTMOptUpdateCount = 0; /* Indicates when specific option is updated */
diff --git a/src/gtm/proxy/proxy_main.c b/src/gtm/proxy/proxy_main.c
index f3d80272b0..928d80dc3f 100644
--- a/src/gtm/proxy/proxy_main.c
+++ b/src/gtm/proxy/proxy_main.c
@@ -44,6 +44,7 @@
#include "gtm/gtm_standby.h"
/* For reconnect control lock */
#include "gtm/gtm_lock.h"
+#include "gtm/gtm_opt.h"
extern int optind;
extern char *optarg;
@@ -61,15 +62,24 @@ char *ListenAddresses;
int GTMProxyPortNumber;
int GTMProxyWorkerThreads;
char *GTMProxyDataDir;
+char *GTMProxyConfigFileName;
+char *GTMConfigFileName;
/* GTM communication error handling options */
-int GTMErrorWaitOpt = FALSE; /* Wait and assume XCM if TRUE */
+bool GTMErrorWaitOpt = FALSE; /* Wait and assume XCM if TRUE */
int GTMErrorWaitSecs = 0; /* Duration of each wait */
int GTMErrorWaitCount = 0; /* How many durations to wait */
char *GTMServerHost;
int GTMServerPortNumber;
+/*
+ * Keepalives setup for the connection with GTM server
+ */
+int GTMServerKeepalivesIdle = 0;
+int GTMServerKeepalivesInterval = 0;
+int GTMServerKeepalivesCount = 0;
+
char *GTMProxyNodeName = NULL;
GTM_ThreadID TopMostThreadID;
@@ -79,6 +89,13 @@ short ReadyToReconnect = FALSE;
char *NewGTMServerHost;
int NewGTMServerPortNumber;
+/* Status reader/reporter */
+char *error_reporter;
+char *status_reader;
+
+/* Mode */
+bool isStartUp = false;
+
/* Reconnect Control Lock */
GTM_RWLock ReconnectControlLock;
jmp_buf mainThreadSIGUSR1_buf;
@@ -92,6 +109,16 @@ pthread_key_t threadinfo_key;
static bool GTMProxyAbortPending = false;
static GTM_Conn *master_conn;
+
+/*
+ * External Routines
+ */
+extern void InitializeGTMOptions(void);
+
+
+/*
+ * Internal Routines
+ */
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *conn);
static int ServerLoop(void);
@@ -502,7 +529,7 @@ help(const char *progname)
printf(_(" -p port GTM proxy port number\n"));
printf(_(" -s hostname GTM server hostname/IP \n"));
printf(_(" -t port GTM server port number\n"));
- printf(_(" -i ID number GTM proxy ID number\n"));
+ printf(_(" -i nodename GTM proxy nodename\n"));
printf(_(" -n count Number of worker threads\n"));
printf(_(" -D directory GTM proxy working directory\n"));
printf(_(" -l filename GTM proxy log file name \n"));
@@ -518,6 +545,27 @@ main(int argc, char *argv[])
int i;
/*
+ * Variable to store option parameters
+ */
+ char *listen_addresses = NULL;
+ char *node_name = NULL;
+ char *proxy_port_number = NULL;
+ char *proxy_worker_threads = NULL;
+ char *data_dir = NULL;
+ char *log_file = NULL;
+ char *gtm_host = NULL;
+ char *gtm_port = NULL;
+ char *gtm_err_wait_secs = NULL;
+ char *gtm_err_wait_count = NULL;
+
+ isStartUp = true;
+
+ /*
+ * At first, initialize options. Also moved something from BaseInit() here.
+ */
+ InitializeGTMOptions();
+
+ /*
* Catch standard options before doing much else
*/
if (argc > 1)
@@ -529,7 +577,9 @@ main(int argc, char *argv[])
}
}
- ListenAddresses = GTM_PROXY_DEFAULT_HOSTNAME;
+/*
+ ListenAddresses = strdup(GTM_PROXY_DEFAULT_HOSTNAME);
+*/
GTMProxyPortNumber = GTM_PROXY_DEFAULT_PORT;
GTMProxyWorkerThreads = GTM_PROXY_DEFAULT_WORKERS;
@@ -544,52 +594,72 @@ main(int argc, char *argv[])
{
case 'h':
/* Listen address of the proxy */
- ListenAddresses = strdup(optarg);
+ if (listen_addresses)
+ free(listen_addresses);
+ listen_addresses = strdup(optarg);
break;
case 'i':
/* GTM Proxy identification name */
- GTMProxyNodeName = strdup(optarg);
+ if (node_name)
+ free(node_name);
+ node_name = strdup(optarg);
break;
case 'p':
/* Port number for the proxy to listen on */
- GTMProxyPortNumber = atoi(optarg);
+ if (proxy_port_number)
+ free(proxy_port_number);
+ proxy_port_number = strdup(optarg);
break;
case 'n':
/* Number of worker threads */
- GTMProxyWorkerThreads = atoi(optarg);
+ if (proxy_worker_threads)
+ free(proxy_worker_threads);
+ proxy_worker_threads = strdup(optarg);
break;
case 'D':
- GTMProxyDataDir = strdup(optarg);
- canonicalize_path(GTMProxyDataDir);
+ if (data_dir)
+ free(data_dir);
+ data_dir = strdup(optarg);
+ canonicalize_path(data_dir);
break;
case 'l':
/* The log file */
- GTMLogFile = strdup(optarg);
+ if (log_file)
+ free(log_file);
+ log_file = strdup(optarg);
break;
case 's':
/* GTM server host name */
- GTMServerHost = strdup(optarg);
+ if (gtm_host)
+ free(gtm_host);
+ gtm_host = strdup(optarg);
break;
case 't':
/* GTM server port number */
- GTMServerPortNumber = atoi(optarg);
+ if (gtm_port)
+ free(gtm_port);
+ gtm_port = strdup(optarg);
break;
case 'w':
/* Duration to wait at GTM communication error */
- GTMErrorWaitSecs = atoi(optarg);
+ if (gtm_err_wait_secs)
+ free(gtm_err_wait_secs);
+ gtm_err_wait_secs = strdup(optarg);
break;
case 'z':
/* How many durations to wait */
- GTMErrorWaitCount = atoi(optarg);
+ if (gtm_err_wait_count)
+ free(gtm_err_wait_count);
+ gtm_err_wait_count = strdup(optarg);
break;
default:
@@ -598,6 +668,85 @@ main(int argc, char *argv[])
}
}
+ /*
+ * Setup working directory
+ */
+ if (data_dir)
+ SetConfigOption("data_dir", data_dir, GTMC_STARTUP, GTMC_S_OVERRIDE);
+
+ /*
+ * Setup configuration file
+ */
+ if (!SelectConfigFiles(data_dir, progname))
+ exit(1);
+
+ /*
+ * Parse config file
+ */
+ ProcessConfigFile(GTMC_STARTUP);
+
+ /*
+ * Override with command line options. "data_dir" was handled in the privious line.
+ */
+ if (listen_addresses)
+ {
+ SetConfigOption("listen_addresses", listen_addresses, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(listen_addresses);
+ listen_addresses = NULL;
+ }
+ if (node_name)
+ {
+ SetConfigOption("nodename", node_name, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(node_name);
+ node_name = NULL;
+ }
+ if (proxy_port_number)
+ {
+ SetConfigOption("port", proxy_port_number, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(proxy_port_number);
+ proxy_port_number = NULL;
+ }
+ if (proxy_worker_threads)
+ {
+ SetConfigOption("worker_threads", proxy_worker_threads, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(proxy_worker_threads);
+ proxy_worker_threads = NULL;
+ }
+ if (log_file)
+ {
+ SetConfigOption("log_file", log_file, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(log_file);
+ log_file = NULL;
+ }
+ if (gtm_host)
+ {
+ SetConfigOption("gtm_host", gtm_host, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(gtm_host);
+ gtm_host = NULL;
+ }
+ if (gtm_port)
+ {
+ SetConfigOption("gtm_port", gtm_port, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(gtm_port);
+ gtm_port = NULL;
+ }
+ if (gtm_err_wait_secs)
+ {
+ SetConfigOption("err_wait_interval", gtm_err_wait_secs, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(gtm_err_wait_secs);
+ gtm_err_wait_secs = NULL;
+ }
+ if (gtm_err_wait_count)
+ {
+ SetConfigOption("err_wait_count", gtm_err_wait_count, GTMC_STARTUP, GTMC_S_OVERRIDE);
+ free(gtm_err_wait_count);
+ gtm_err_wait_count = NULL;
+ }
+
+
+ /*
+ * Check Options
+ */
if (GTMProxyDataDir == NULL)
{
write_stderr("GTM Proxy data directory must be specified\n");
@@ -618,11 +767,15 @@ main(int argc, char *argv[])
*/
if (GTMErrorWaitSecs > 0 && GTMErrorWaitCount > 0)
{
- GTMErrorWaitOpt = TRUE;
+ if (GTMErrorWaitOpt == false)
+ {
+ GTMErrorWaitSecs = 0;
+ GTMErrorWaitCount = 0;
+ }
}
else
{
- GTMErrorWaitOpt = FALSE;
+ GTMErrorWaitOpt = false;
GTMErrorWaitSecs = 0;
GTMErrorWaitCount = 0;
}
@@ -796,7 +949,9 @@ ServerLoop(void)
* the resource but this may not happen so many times.
*/
+ elog(LOG, "Main Thread reconnecting to new GTM.");
RegisterProxy(TRUE);
+ elog(LOG, "Reconnected.");
/* If it is done, then release the lock for worker threads. */
GTM_RWLockRelease(&ReconnectControlLock);
@@ -1808,14 +1963,18 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
/* Then obtain the node name */
len = pq_getmsgint(message, sizeof(GTM_StrLen));
- cmd_data.cd_reg.nodename = (char *)pq_getmsgbytes(message, len);
+ cmd_data.cd_reg.nodename = palloc(len + 1);
+ memcpy(cmd_data.cd_reg.nodename, (char *)pq_getmsgbytes(message, len), len);
+ cmd_data.cd_reg.nodename[len] = '\0';
/*
* Now we have to waste the following host information. It is taken from
* the address field in the conn.
*/
len = pq_getmsgint(message, sizeof(GTM_StrLen));
- cmd_data.cd_reg.ipaddress = (char *)pq_getmsgbytes(message, len);
+ cmd_data.cd_reg.ipaddress = palloc(len + 1);
+ memcpy(cmd_data.cd_reg.ipaddress, (char *)pq_getmsgbytes(message, len), len);
+ cmd_data.cd_reg.ipaddress[len] = '\0';
/* Then the next is the port number */
memcpy(&cmd_data.cd_reg.port,
@@ -1825,19 +1984,24 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
/* Proxy name */
len = pq_getmsgint(message, sizeof(GTM_StrLen));
- cmd_data.cd_reg.gtm_proxy_nodename = (char *)pq_getmsgbytes(message, len);
+ cmd_data.cd_reg.gtm_proxy_nodename = palloc(len + 1);
+ memcpy(cmd_data.cd_reg.gtm_proxy_nodename, (char *)pq_getmsgbytes(message, len), len);
+ cmd_data.cd_reg.gtm_proxy_nodename[len] = '\0';
/* get data folder data */
len = pq_getmsgint(message, sizeof (int));
- cmd_data.cd_reg.datafolder = (char *)pq_getmsgbytes(message, len);
+ cmd_data.cd_reg.datafolder = palloc(len + 1);
+ memcpy(cmd_data.cd_reg.datafolder, (char *)pq_getmsgbytes(message, len), len);
+ cmd_data.cd_reg.datafolder[len] = '\0';
/* Now we have one more data to waste, "status" */
cmd_data.cd_reg.status = pq_getmsgint(message, sizeof(GTM_PGXCNodeStatus));
pq_getmsgend(message);
/* Copy also remote host address in data to be proxied */
- cmd_data.cd_reg.ipaddress = (char *) palloc(strlen(remote_host));
+ cmd_data.cd_reg.ipaddress = (char *) palloc(strlen(remote_host) + 1);
memcpy(cmd_data.cd_reg.ipaddress, remote_host, strlen(remote_host));
+ cmd_data.cd_reg.ipaddress[strlen(remote_host)] = '\0';
/* Registering has to be saved where it can be seen by all the threads */
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
@@ -1867,10 +2031,14 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
{
int len;
MemoryContext oldContext;
+ char *nodename;
memcpy(&cmd_data.cd_reg.type, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeType)), sizeof (GTM_PGXCNodeType));
len = pq_getmsgint(message, sizeof(GTM_StrLen));
- memcpy(&cmd_data.cd_reg.nodename, pq_getmsgbytes(message, len), len);
+ nodename = palloc(len + 1);
+ memcpy(nodename, pq_getmsgbytes(message, len), len);
+ nodename[len] = '\0'; /* Need null-terminate */
+ cmd_data.cd_reg.nodename = nodename;
pq_getmsgend(message);
/* Unregistering has to be saved in a place where it can be seen by all the threads */
@@ -3002,12 +3170,14 @@ workerThreadReconnectToGTMstandby(void)
/* Disconnect the current connection and re-connect to the new GTM */
GTMPQfinish(GetMyThreadInfo->thr_gtm_conn);
- sprintf(gtm_connect_string, "host=%s port=%d node name=%s remote_type=%d",
+ sprintf(gtm_connect_string, "host=%s port=%d node_name=%s remote_type=%d",
NewGTMServerHost, NewGTMServerPortNumber, GTMProxyNodeName, PGXC_NODE_GTM_PROXY);
+ elog(LOG, "Worker thread connecting to %s", gtm_connect_string);
GetMyThreadInfo->thr_gtm_conn = PQconnectGTM(gtm_connect_string);
if (GetMyThreadInfo->thr_gtm_conn == NULL)
- elog(FATAL, "GTM connection failed.");
+ elog(FATAL, "Worker thread GTM connection failed.");
+ elog(LOG, "Worker thread connection done.");
/* Set GTM communication error handling option */
GetMyThreadInfo->thr_gtm_conn->gtmErrorWaitOpt = GTMErrorWaitOpt;
diff --git a/src/gtm/recovery/register.c b/src/gtm/recovery/register.c
index b081326287..21d416d5e5 100644
--- a/src/gtm/recovery/register.c
+++ b/src/gtm/recovery/register.c
@@ -381,11 +381,15 @@ Recovery_PGXCNodeRegister(GTM_PGXCNodeType type,
/* Fill in structure */
nodeinfo->type = type;
- nodeinfo->nodename = pgxcnode_copy_char(nodename);
+ if (nodename)
+ nodeinfo->nodename = pgxcnode_copy_char(nodename);
nodeinfo->port = port;
- nodeinfo->proxyname = pgxcnode_copy_char(proxyname);
- nodeinfo->datafolder = pgxcnode_copy_char(datafolder);
- nodeinfo->ipaddress = pgxcnode_copy_char(ipaddress);
+ if (proxyname)
+ nodeinfo->proxyname = pgxcnode_copy_char(proxyname);
+ if (datafolder)
+ nodeinfo->datafolder = pgxcnode_copy_char(datafolder);
+ if (ipaddress)
+ nodeinfo->ipaddress = pgxcnode_copy_char(ipaddress);
nodeinfo->status = status;
nodeinfo->socket = socket;
@@ -462,8 +466,6 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
memcpy(proxyname, (char *)pq_getmsgbytes(message, len), len);
proxyname[len] = '\0';
- elog(LOG, "ProcessPGXCNodeRegister: ipaddress = %s", ipaddress);
-
/*
* Finish by reading Data Folder (length and then string)
*/
@@ -472,6 +474,11 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
memcpy(datafolder, (char *)pq_getmsgbytes(message, len), len);
datafolder[len] = '\0';
+ elog(LOG,
+ "ProcessPGXCNodeRegister: ipaddress = \"%s\", node name = \"%s\", proxy name = \"%s\", "
+ "datafolder \"%s\"",
+ ipaddress, node_name, proxyname, datafolder);
+
status = pq_getmsgint(message, sizeof (GTM_PGXCNodeStatus));
if ((type!=PGXC_NODE_GTM_PROXY) &&
@@ -483,6 +490,7 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
ereport(ERROR,
(EINVAL,
errmsg("Unknown node type.")));
+ elog(LOG, "Node type = %d", type);
/*
* We must use the TopMostMemoryContext because the Node ID information is
@@ -729,10 +737,10 @@ ProcessPGXCNodeList(Port *myport, StringInfo message)
void
Recovery_SaveRegisterInfo(void)
{
-GTM_PGXCNodeInfoHashBucket *bucket;
-gtm_ListCell *elem;
-GTM_PGXCNodeInfo *nodeinfo = NULL;
-int hash, ctlfd;
+ GTM_PGXCNodeInfoHashBucket *bucket;
+ gtm_ListCell *elem;
+ GTM_PGXCNodeInfo *nodeinfo = NULL;
+ int hash, ctlfd;
char filebkp[GTM_NODE_FILE_MAX_PATH];
GTM_RWLockAcquire(&RegisterFileLock, GTM_LOCKMODE_WRITE);
@@ -749,7 +757,7 @@ int hash, ctlfd;
return;
}
-for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++)
+ for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++)
{
bucket = &GTM_PGXCNodes[hash];
@@ -769,24 +777,57 @@ for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++)
write(ctlfd, &NodeRegisterMagic, sizeof (NodeRegisterMagic));
write(ctlfd, &nodeinfo->type, sizeof (GTM_PGXCNodeType));
- len = strlen(nodeinfo->nodename);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->nodename, len);
+ if (nodeinfo->nodename)
+ {
+ len = strlen(nodeinfo->nodename);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->nodename, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
+
write(ctlfd, &nodeinfo->port, sizeof (GTM_PGXCNodePort));
- len = strlen(nodeinfo->proxyname);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->proxyname, len);
+ if (nodeinfo->proxyname)
+ {
+ len = strlen(nodeinfo->proxyname);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->proxyname, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
write(ctlfd, &nodeinfo->status, sizeof (GTM_PGXCNodeStatus));
- len = strlen(nodeinfo->ipaddress);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->ipaddress, len);
+ if (nodeinfo->ipaddress)
+ {
+ len = strlen(nodeinfo->ipaddress);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->ipaddress, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
- len = strlen(nodeinfo->datafolder);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->datafolder, len);
+ if (nodeinfo->datafolder)
+ {
+ len = strlen(nodeinfo->datafolder);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->datafolder, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
write(ctlfd, &NodeEndMagic, sizeof(NodeEndMagic));
@@ -835,9 +876,18 @@ Recovery_RecordRegisterInfo(GTM_PGXCNodeInfo *nodeinfo, bool is_register)
write(ctlfd, &NodeUnregisterMagic, sizeof (NodeUnregisterMagic));
write(ctlfd, &nodeinfo->type, sizeof (GTM_PGXCNodeType));
- len = strlen(nodeinfo->nodename);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->nodename, len);
+
+ if (nodeinfo->nodename)
+ {
+ len = strlen(nodeinfo->nodename);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->nodename, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
if (is_register)
{
@@ -845,19 +895,43 @@ Recovery_RecordRegisterInfo(GTM_PGXCNodeInfo *nodeinfo, bool is_register)
write(ctlfd, &nodeinfo->port, sizeof (GTM_PGXCNodePort));
- len = strlen(nodeinfo->proxyname);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->proxyname, len);
+ if (nodeinfo->proxyname)
+ {
+ len = strlen(nodeinfo->proxyname);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->proxyname, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
write(ctlfd, &nodeinfo->status, sizeof (GTM_PGXCNodeStatus));
- len = strlen(nodeinfo->ipaddress);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->ipaddress, len);
+ if (nodeinfo->ipaddress)
+ {
+ len = strlen(nodeinfo->ipaddress);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->ipaddress, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
- len = strlen(nodeinfo->datafolder);
- write(ctlfd, &len, sizeof(uint32));
- write(ctlfd, nodeinfo->datafolder, len);
+ if (nodeinfo->datafolder)
+ {
+ len = strlen(nodeinfo->datafolder);
+ write(ctlfd, &len, sizeof(uint32));
+ write(ctlfd, nodeinfo->datafolder, len);
+ }
+ else
+ {
+ len = 0;
+ write(ctlfd, &len, sizeof(uint32));
+ }
}
write(ctlfd, &NodeEndMagic, sizeof(NodeEndMagic));
diff --git a/src/gtm/recovery/standby_utils.c b/src/gtm/recovery/standby_utils.c
index 4acf81c2b3..a28c0f933a 100644
--- a/src/gtm/recovery/standby_utils.c
+++ b/src/gtm/recovery/standby_utils.c
@@ -21,9 +21,9 @@
/*
* Variables to interact with GTM active under GTM standby mode.
*/
-static bool GTM_StandbyMode = false;
-static char *GTM_ActiveAddress;
-static int GTM_ActivePort;
+bool GTM_StandbyMode = false;
+char *GTM_ActiveAddress;
+int GTM_ActivePort;
/* For thread safety, values above are protected by a lock */
static GTM_RWLock StandbyLock;
diff --git a/src/include/gtm/gtm.h b/src/include/gtm/gtm.h
index 9643c6b840..ab970fb925 100644
--- a/src/include/gtm/gtm.h
+++ b/src/include/gtm/gtm.h
@@ -36,7 +36,7 @@ typedef enum GTM_ThreadStatus
struct GTM_ConnectionInfo;
-#define ERRORDATA_STACK_SIZE 5
+#define ERRORDATA_STACK_SIZE 20
typedef struct GTM_ThreadInfo
{
diff --git a/src/include/gtm/gtm_opt.h b/src/include/gtm/gtm_opt.h
new file mode 100644
index 0000000000..7d0de94fd6
--- /dev/null
+++ b/src/include/gtm/gtm_opt.h
@@ -0,0 +1,328 @@
+/*--------------------------------------------------------------------
+ * gtm_opt.h
+ *
+ * External declarations pertaining to gtm/main/gtm_opt.c, gtm/proxy/gtm_proxy_opt.c and
+ * gtm/common/gtm_opt_file.l
+ *
+ * Portions Copyright (c) 2011, Nippon Telegraph and Telephone Corporation
+ * Portions Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ * Written by Peter Eisentraut <[email protected]>.
+ * Modified by Koichi Suzuki <[email protected]>
+ *
+ * src/include/gtm/gtm_opt.h
+ *--------------------------------------------------------------------
+ */
+#ifndef GTM_OPT_H
+#define GTM_OPT_H
+
+/*
+ * Certain options can only be set at certain times. The rules are
+ * like this:
+ *
+ * INTERNAL options cannot be set by the user at all, but only through
+ * internal processes ("server_version" is an example). These are GUC
+ * variables only so they can be shown by SHOW, etc.
+ *
+ * POSTMASTER options can only be set when the postmaster starts,
+ * either from the configuration file or the command line.
+ *
+ * SIGHUP options can only be set at postmaster startup or by changing
+ * the configuration file and sending the HUP signal to the postmaster
+ * or a backend process. (Notice that the signal receipt will not be
+ * evaluated immediately. The postmaster and the backend check it at a
+ * certain point in their main loop. It's safer to wait than to read a
+ * file asynchronously.)
+ *
+ * BACKEND options can only be set at postmaster startup, from the
+ * configuration file, or by client request in the connection startup
+ * packet (e.g., from libpq's PGOPTIONS variable). Furthermore, an
+ * already-started backend will ignore changes to such an option in the
+ * configuration file. The idea is that these options are fixed for a
+ * given backend once it's started, but they can vary across backends.
+ *
+ * SUSET options can be set at postmaster startup, with the SIGHUP
+ * mechanism, or from SQL if you're a superuser.
+ *
+ * USERSET options can be set by anyone any time.
+ */
+typedef enum
+{
+ GTMC_DEFAULT,
+ GTMC_STARTUP,
+ GTMC_SIGHUP,
+ GTMC_USERSET
+} GtmOptContext;
+
+/*
+ * The following type records the source of the current setting. A
+ * new setting can only take effect if the previous setting had the
+ * same or lower level. (E.g, changing the config file doesn't
+ * override the postmaster command line.) Tracking the source allows us
+ * to process sources in any convenient order without affecting results.
+ * Sources <= GTMC_S_OVERRIDE will set the default used by RESET, as well
+ * as the current value. Note that source == GTMC_S_OVERRIDE should be
+ * used when setting a GTMC_INTERNAL option.
+ *
+ * GTMC_S_INTERACTIVE isn't actually a source value, but is the
+ * dividing line between "interactive" and "non-interactive" sources for
+ * error reporting purposes.
+ *
+ * GTMC_S_TEST is used when testing values to be stored as per-database or
+ * per-user defaults ("doit" will always be false, so this never gets stored
+ * as the actual source of any value). This is an interactive case, but
+ * it needs its own source value because some assign hooks need to make
+ * different validity checks in this case.
+ *
+ * NB: see GtmOptSource_Names in gtm_opt.c and gtm_proxy_opt.c if you change this.
+ */
+typedef enum
+{
+ GTMC_S_DEFAULT, /* hard-wired default ("boot_val") */
+ GTMC_S_DYNAMIC_DEFAULT, /* default computed during initialization */
+ GTMC_S_ENV_VAR, /* postmaster environment variable *//* Not used in GTM */
+ GTMC_S_FILE, /* gtm.conf or gtm_proxy.conf */
+ GTMC_S_ARGV, /* postmaster command line */
+ GTMC_S_DATABASE, /* per-database setting *//* Not used in GTM */
+ GTMC_S_USER, /* per-user setting *//* Not used in GTM */
+ GTMC_S_DATABASE_USER, /* per-user-and-database setting *//* Not used in GTM */
+ GTMC_S_CLIENT, /* from client connection request *//* Not used in GTM */
+ GTMC_S_OVERRIDE, /* special case to forcibly set default *//* Not used in GTM */
+ GTMC_S_INTERACTIVE, /* dividing line for error reporting *//* Not used in GTM */
+ GTMC_S_TEST, /* test per-database or per-user setting *//* Not used in GTM */
+ GTMC_S_SESSION /* SET command *//* Not used in GTM */
+} GtmOptSource;
+
+/*
+ * Parsing the configuration file will return a list of name-value pairs
+ * with source location info.
+ */
+typedef struct ConfigVariable
+{
+ char *name;
+ char *value;
+ char *filename;
+ int sourceline;
+ struct ConfigVariable *next;
+} ConfigVariable;
+
+extern bool ParseConfigFile(const char *config_file, const char *calling_file,
+ int depth, int elevel,
+ ConfigVariable **head_p, ConfigVariable **tail_p);
+extern bool ParseConfigFp(FILE *fp, const char *config_file,
+ int depth, int elevel,
+ ConfigVariable **head_p, ConfigVariable **tail_p);
+extern void FreeConfigVariables(ConfigVariable *list);
+
+/*
+ * The possible values of an enum variable are specified by an array of
+ * name-value pairs. The "hidden" flag means the value is accepted but
+ * won't be displayed when guc.c is asked for a list of acceptable values.
+ */
+struct config_enum_entry
+{
+ const char *name;
+ int val;
+ bool hidden;
+};
+
+/*
+ * Signatures for per-variable check/assign/show hook functions
+ */
+/* No hook in GTM */
+#if 0
+typedef bool (*GtmOptBoolCheckHook) (bool *newval, void **extra, GtmOptSource source);
+typedef bool (*GtmOptIntCheckHook) (int *newval, void **extra, GtmOptSource source);
+typedef bool (*GtmOptRealCheckHook) (double *newval, void **extra, GtmOptSource source);
+typedef bool (*GtmOptStringCheckHook) (char **newval, void **extra, GtmOptSource source);
+typedef bool (*GtmOptEnumCheckHook) (int *newval, void **extra, GtmOptSource source);
+
+typedef void (*GtmOptBoolAssignHook) (bool newval, void *extra);
+typedef void (*GtmOptIntAssignHook) (int newval, void *extra);
+typedef void (*GtmOptRealAssignHook) (double newval, void *extra);
+typedef void (*GtmOptStringAssignHook) (const char *newval, void *extra);
+typedef void (*GtmOptEnumAssignHook) (int newval, void *extra);
+
+typedef const char *(*GtmOptShowHook) (void);
+#endif
+
+/*
+ * Miscellaneous
+ */
+/*
+ * GTM does not have SET command so it is not used in GTM. It's a dummy.
+ */
+typedef enum
+{
+ /* Types of set_config_option actions */
+ GTMOPT_ACTION_SET, /* regular SET command */
+ GTMOPT_ACTION_LOCAL, /* SET LOCAL command */
+ GTMOPT_ACTION_SAVE /* function SET option */
+} GtmOptAction;
+
+#define GTMOPT_QUALIFIER_SEPARATOR '.'
+
+/*
+ * bit values in "flags" of a GUC variable
+ */
+#define GTMOPT_LIST_INPUT 0x0001 /* input can be list format */
+#define GTMOPT_LIST_QUOTE 0x0002 /* double-quote list elements */
+#define GTMOPT_NO_SHOW_ALL 0x0004 /* exclude from SHOW ALL */
+#define GTMOPT_NO_RESET_ALL 0x0008 /* exclude from RESET ALL */
+#define GTMOPT_REPORT 0x0010 /* auto-report changes to client */
+#define GTMOPT_NOT_IN_SAMPLE 0x0020 /* not in postgresql.conf.sample */
+#define GTMOPT_DISALLOW_IN_FILE 0x0040 /* can't set in postgresql.conf */
+#define GTMOPT_CUSTOM_PLACEHOLDER 0x0080 /* placeholder for custom variable */
+#define GTMOPT_SUPERUSER_ONLY 0x0100 /* show only to superusers */
+#define GTMOPT_IS_NAME 0x0200 /* limit string to NAMEDATALEN-1 */
+
+#define GTMOPT_UNIT_KB 0x0400 /* value is in kilobytes */
+#define GTMOPT_UNIT_BLOCKS 0x0800 /* value is in blocks */
+#define GTMOPT_UNIT_XBLOCKS 0x0C00 /* value is in xlog blocks */
+#define GTMOPT_UNIT_MEMORY 0x0C00 /* mask for KB, BLOCKS, XBLOCKS */
+
+#define GTMOPT_UNIT_MS 0x1000 /* value is in milliseconds */
+#define GTMOPT_UNIT_S 0x2000 /* value is in seconds */
+#define GTMOPT_UNIT_MIN 0x4000 /* value is in minutes */
+#define GTMOPT_UNIT_TIME 0x7000 /* mask for MS, S, MIN */
+
+#define GTMOPT_NOT_WHILE_SEC_REST 0x8000 /* can't set if security restricted */
+
+/*
+ * Functions exported by gtm_opt.c
+ */
+extern void SetConfigOption(const char *name, const char *value,
+ GtmOptContext context, GtmOptSource source);
+
+extern void EmitWarningsOnPlaceholders(const char *className);
+
+extern const char *GetConfigOption(const char *name, bool restrict_superuser);
+extern const char *GetConfigOptionResetString(const char *name);
+extern void ProcessConfigFile(GtmOptContext context);
+extern void InitializeGTMOptions(void);
+extern bool SelectConfigFiles(const char *userDoption, const char *progname);
+extern void ResetAllOptions(void);
+extern int NewGTMNestLevel(void);
+extern bool parse_int(const char *value, int *result, int flags,
+ const char **hintmsg);
+extern bool parse_real(const char *value, double *result);
+extern bool set_config_option(const char *name, const char *value,
+ GtmOptContext context, GtmOptSource source,
+ bool changeVal);
+
+extern char *GetConfigOptionByName(const char *name, const char **varname);
+extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
+extern int GetNumConfigOptions(void);
+extern void ParseLongOption(const char *string, char **name, char **value);
+
+#ifndef PG_KRB_SRVTAB
+#define PG_KRB_SRVTAB ""
+#endif
+#ifndef PG_KRB_SRVNAM
+#define PG_KRB_SRVNAM ""
+#endif
+
+/* upper limit for GUC variables measured in kilobytes of memory */
+/* note that various places assume the byte size fits in a "long" variable */
+#if SIZEOF_SIZE_T > 4 && SIZEOF_LONG > 4
+#define MAX_KILOBYTES INT_MAX
+#else
+#define MAX_KILOBYTES (INT_MAX / 1024)
+#endif
+
+#ifdef TRACE_SORT
+extern bool trace_sort;
+#endif
+#ifdef TRACE_SYNCSCAN
+extern bool trace_syncscan;
+#endif
+#ifdef DEBUG_BOUNDED_SORT
+extern bool optimize_bounded_sort;
+#endif
+
+
+/*
+ * Log_min_messages ENUM strings
+ */
+#define Server_Message_Level_Options()\
+static const struct config_enum_entry server_message_level_options[] = {\
+ {"debug", DEBUG2, true},\
+ {"debug5", DEBUG5, false},\
+ {"debug4", DEBUG4, false},\
+ {"debug3", DEBUG3, false},\
+ {"debug2", DEBUG2, false},\
+ {"debug1", DEBUG1, false},\
+ {"info", INFO, false},\
+ {"notice", NOTICE, false},\
+ {"warning", WARNING, false},\
+ {"error", ERROR, false},\
+ {"log", LOG, false},\
+ {"fatal", FATAL, false},\
+ {"panic", PANIC, false},\
+ {NULL, 0, false}\
+}
+
+/*
+ * Server Startup Option ENUM strings
+ */
+#define Gtm_Startup_Mode_Options()\
+static const struct config_enum_entry gtm_startup_mode_options[] = {\
+ {"act", GTM_ACT_MODE, false},\
+ {"standby", GTM_STANDBY_MODE, false},\
+ {NULL, 0, false}\
+}
+
+/*
+ * Displayable names for context types (enum GtmContext)
+ *
+ * Note: these strings are deliberately not localized.
+ */
+#define gtmOptContext_Names()\
+const char *const GtmOptContext_Names[] =\
+{\
+ /* GTMC_STGARTUP */ "startup",\
+ /* GTMC_SIGHUP */ "sighup"\
+}
+
+/*
+ * Displayable names for source types (enum GtmSource)
+ *
+ * Note: these strings are deliberately not localized.
+ */
+#define gtmOptSource_Names()\
+const char *const GtmOptSource_Names[] =\
+{\
+ /* GTMC_S_DEFAULT */ "default",\
+ /* GTMC_S_DYNAMIC_DEFAULT */ "default",\
+ /* GTMC_S_ENV_VAR */ "environment variable",\
+ /* GTMC_S_FILE */ "configuration file",\
+ /* GTMC_S_ARGV */ "command line",\
+ /* GTMC_S_DATABASE */ "database",\
+ /* GTMC_S_USER */ "user",\
+ /* GTMC_S_DATABASE_USER */ "database user",\
+ /* GTMC_S_CLIENT */ "client",\
+ /* GTMC_S_OVERRIDE */ "override",\
+ /* GTMC_S_INTERACTIVE */ "interactive",\
+ /* GTMC_S_TEST */ "test",\
+ /* GTMC_S_SESSION */ "session"\
+}
+
+/*
+ * Displayable names for GTM variable types (enum config_type)
+ *
+ * Note: these strings are deliberately not localized.
+ */
+#define Config_Type_Names()\
+const char *const config_type_names[] =\
+{\
+ /* GTMC_BOOL */ "bool",\
+ /* GTMC_INT */ "integer",\
+ /* GTMC_REAL */ "real",\
+ /* GTMC_STRING */ "string",\
+ /* GTMC_ENUM */ "enum"\
+}
+
+
+
+
+
+#endif /* GTM_OPT_H */
diff --git a/src/include/gtm/gtm_opt_tables.h b/src/include/gtm/gtm_opt_tables.h
new file mode 100644
index 0000000000..a0035e6631
--- /dev/null
+++ b/src/include/gtm/gtm_opt_tables.h
@@ -0,0 +1,194 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_opt_tables.h
+ * Declarations of tables used by GTM configuration file.
+ *
+ * Portions Copyright (c) 2011, Nippon Telegraph and Telephone Corporation
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ *
+ * src/include/gtm/gtm_opt_tables.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GTM_OPT_TABLES_H
+#define GTM_OPT_TABLES_H 1
+
+#include "gtm/gtm_opt.h"
+
+/*
+ * GUC supports these types of variables:
+ */
+enum config_type
+{
+ GTMC_BOOL,
+ GTMC_INT,
+ GTMC_REAL,
+ GTMC_STRING,
+ GTMC_ENUM
+};
+
+union config_var_val
+{
+ bool boolval;
+ int intval;
+ double realval;
+ char *stringval;
+ int enumval;
+};
+
+/*
+ * The actual value of a GUC variable can include a malloc'd opaque struct
+ * "extra", which is created by its check_hook and used by its assign_hook.
+ */
+typedef struct config_var_value
+{
+ union config_var_val val;
+ void *extra;
+} config_var_value;
+
+/*
+ * Stack entry for saving the state a variable had prior to an uncommitted
+ * transactional change. In GTM, only chance to update options is SIGINT.
+ */
+typedef enum
+{
+ /* This is almost GtmOptAction, but we need a fourth state for SET+LOCAL */
+ GTMOPT_SAVE, /* entry caused by function SET option */
+ GTMOPT_SET, /* entry caused by plain SET command */
+ GTMOPT_LOCAL, /* entry caused by SET LOCAL command */
+ GTMOPT_SET_LOCAL /* entry caused by SET then SET LOCAL */
+} GtmOptStackState;
+
+typedef struct guc_stack
+{
+ struct guc_stack *prev; /* previous stack item, if any */
+ int nest_level; /* nesting depth at which we made entry */
+ GtmOptStackState state; /* see enum above */
+ GtmOptSource source; /* source of the prior value */
+ config_var_value prior; /* previous value of variable */
+ config_var_value masked; /* SET value in a GTMOPT_SET_LOCAL entry */
+ /* masked value's source must be GTMC_S_SESSION, so no need to store it */
+} GtmOptStack;
+
+/*
+ * Generic fields applicable to all types of variables
+ *
+ * The short description should be less than 80 chars in length. Some
+ * applications may use the long description as well, and will append
+ * it to the short description. (separated by a newline or '. ')
+ *
+ * Note that sourcefile/sourceline are kept here, and not pushed into stacked
+ * values, although in principle they belong with some stacked value if the
+ * active value is session- or transaction-local. This is to avoid bloating
+ * stack entries. We know they are only relevant when source == GTMC_S_FILE.
+ */
+struct config_generic
+{
+ /* constant fields, must be set correctly in initial value: */
+ const char *name; /* name of variable - MUST BE FIRST */
+ GtmOptContext context; /* context required to set the variable */
+ const char *short_desc; /* short desc. of this variable's purpose */
+ const char *long_desc; /* long desc. of this variable's purpose */
+ int flags; /* flag bits, see below */
+ /* variable fields, initialized at runtime: */
+ enum config_type vartype; /* type of variable (set only at startup) */
+ int status; /* status bits, see below */
+ GtmOptSource reset_source; /* source of the reset_value */
+ GtmOptSource source; /* source of the current actual value */
+ GtmOptStack *stack; /* stacked prior values */
+ void *extra; /* "extra" pointer for current actual value */
+ char *sourcefile; /* file current setting is from (NULL if not
+ * file) */
+ int sourceline; /* line in source file */
+};
+
+/* bit values in flags field are defined in guc.h */
+
+/* bit values in status field */
+#define GTMOPT_IS_IN_FILE 0x0001 /* found it in config file */
+/*
+ * Caution: the GTMOPT_IS_IN_FILE bit is transient state for ProcessConfigFile.
+ * Do not assume that its value represents useful information elsewhere.
+ */
+
+
+/* GUC records for specific variable types */
+
+struct config_bool
+{
+ struct config_generic gen;
+ /* constant fields, must be set correctly in initial value: */
+ bool *variable;
+ bool boot_val;
+ /* variable fields, initialized at runtime: */
+ bool reset_val;
+ void *reset_extra;
+};
+
+struct config_int
+{
+ struct config_generic gen;
+ /* constant fields, must be set correctly in initial value: */
+ int *variable;
+ int boot_val;
+ int min;
+ int max;
+ /* variable fields, initialized at runtime: */
+ int reset_val;
+ void *reset_extra;
+};
+
+struct config_real
+{
+ struct config_generic gen;
+ /* constant fields, must be set correctly in initial value: */
+ double *variable;
+ double boot_val;
+ double min;
+ double max;
+ /* variable fields, initialized at runtime: */
+ double reset_val;
+ void *reset_extra;
+};
+
+struct config_string
+{
+ struct config_generic gen;
+ /* constant fields, must be set correctly in initial value: */
+ char **variable;
+ const char *boot_val;
+ /* variable fields, initialized at runtime: */
+ char *reset_val;
+ void *reset_extra;
+};
+
+struct config_enum
+{
+ struct config_generic gen;
+ /* constant fields, must be set correctly in initial value: */
+ int *variable;
+ int boot_val;
+ const struct config_enum_entry *options;
+ /* variable fields, initialized at runtime: */
+ int reset_val;
+ void *reset_extra;
+};
+
+/* constant tables corresponding to enums above and in guc.h */
+extern const char *const config_group_names[];
+extern const char *const config_type_names[];
+extern const char *const GtmOptContext_Names[];
+extern const char *const GtmOptSource_Names[];
+
+/* get the current set of variables */
+extern struct config_generic **get_guc_variables(void);
+
+extern void build_guc_variables(void);
+
+/* search in enum options */
+extern const char *config_enum_lookup_by_value(struct config_enum * record, int val);
+extern bool config_enum_lookup_by_name(struct config_enum * record,
+ const char *value, int *retval);
+
+
+#endif /* GTM_OPT_TABLES_H */
diff --git a/src/include/gtm/gtm_proxy.h b/src/include/gtm/gtm_proxy.h
index 7e77220366..b0d30c03b6 100644
--- a/src/include/gtm/gtm_proxy.h
+++ b/src/include/gtm/gtm_proxy.h
@@ -59,7 +59,7 @@ typedef struct GTMProxy_Connections
GTM_RWLock gc_lock;
} GTMProxy_Connections;
-#define ERRORDATA_STACK_SIZE 5
+#define ERRORDATA_STACK_SIZE 20
#define GTM_PROXY_MAX_CONNECTIONS 1024
typedef struct GTMProxy_ThreadInfo
diff --git a/src/include/gtm/gtm_standby.h b/src/include/gtm/gtm_standby.h
index 2072599a51..97cce3e33a 100644
--- a/src/include/gtm/gtm_standby.h
+++ b/src/include/gtm/gtm_standby.h
@@ -42,4 +42,10 @@ void gtm_standby_disconnect_from_standby(GTM_Conn *conn);
GTM_Conn *gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max);
bool gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn);
+/*
+ * Startup mode
+ */
+#define GTM_ACT_MODE 0
+#define GTM_STANDBY_MODE 1
+
#endif /* GTM_STANDBY_H */
diff --git a/src/include/gtm/path.h b/src/include/gtm/path.h
index cc07813ace..e8d98fcbf0 100644
--- a/src/include/gtm/path.h
+++ b/src/include/gtm/path.h
@@ -18,4 +18,7 @@
extern void canonicalize_path(char *path);
extern char *make_absolute_path(const char *path);
+extern void get_parent_directory(char *path);
+extern void join_path_components(char *ret_path, const char *head, const char *tail);
+
#endif