├── .gitignore ├── LICENCE ├── Makefile ├── README.md ├── debian ├── changelog ├── control ├── copyright ├── gitlab-ci.yml ├── pgversions ├── postgresql-walbouncer.docs ├── postgresql-walbouncer.install ├── rules ├── source │ └── format ├── tests │ ├── control │ └── test └── watch ├── src ├── Makefile ├── include │ ├── parser │ │ ├── parser.h │ │ ├── scansup.h │ │ └── stringinfo.h │ ├── wb_pg_config.h │ ├── wbclientconn.h │ ├── wbconfig.h │ ├── wbcrc32c.h │ ├── wbfilter.h │ ├── wbglobals.h │ ├── wbmasterconn.h │ ├── wbpgtypes.h │ ├── wbproto.h │ ├── wbsignals.h │ ├── wbsocket.h │ └── wbutils.h ├── main.c ├── parser │ ├── gram_support.c │ ├── repl_gram.y │ ├── repl_scanner.l │ ├── scansup.c │ └── stringinfo.c ├── unittests │ └── test.c ├── wbclientconn.c ├── wbconfig.c ├── wbcrc32c.c ├── wbfilter.c ├── wbmasterconn.c ├── wbsignals.c ├── wbsocket.c └── wbutils.c ├── tests ├── .gitignore ├── democonf.yaml └── run_demo.sh └── walbouncer.conf.sample /.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | src/parser/*.o 3 | src/parser/repl_gram.c 4 | src/parser/repl_scanner.c 5 | src/walbouncer 6 | src/unittests/test 7 | .cproject 8 | .project 9 | .settings/** 10 | 11 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016-2025, CYBERTEC PostgreSQL International GmbH 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PG_CONFIG = pg_config 2 | PGXS = $(shell $(PG_CONFIG) --pgxs) 3 | include $(PGXS) 4 | 5 | all: 6 | 7 | installcheck: 8 | WALBOUNCER=walbouncer PATH=$(DESTDIR)$(bindir):$(bindir):$(PATH) tests/run_demo.sh 9 | 10 | all install clean: 11 | $(MAKE) -C src $@ 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Walbouncer provides a proxy server for PostgreSQL replication connections. It 5 | also has the capability to replace a subset of WAL records will no-ops. 6 | 7 | Use cases where you would use walbouncer: 8 | 9 | - For clusters with more than two replicas you can change a replica servers' 10 | effective `primary_conninfo` without restarting PostgreSQL by proxying 11 | it through walbouncer and changing walbouncer config when master server 12 | location changes. 13 | 14 | In this setup you would usually place walbouncer on the same host as the 15 | replica. 16 | 17 | - You can choose to replicate a subset of data to save space on geographically 18 | distributed databases. Use a separate tablespace per location and configure 19 | walbouncer to filter out irrelevant data from the WAL stream. 20 | 21 | To also save on bandwidth use vtun to compress the stream. 22 | 23 | NB: filtering the WAL stream is by definition introducing data loss to your 24 | system. Replicas with filtered data are not usable after promotion to master. 25 | You may encounter errors about missing files that prevent you from using 26 | the database. 27 | 28 | Building and installing 29 | ======================= 30 | 31 | To build walbouncer you need to have libyaml-dev and PostgreSQL installed 32 | on your system. The correct PostgreSQL version is located using the pg_config 33 | binary. Ensure that pg_config for your PostgreSQL version is in your path. 34 | 35 | To build walbouncer change into the src/ directory and run: 36 | 37 | make 38 | 39 | Walbouncer is built as a self-contained binary. It can be installed into your 40 | PostgreSQL binaries directory using: 41 | 42 | make install 43 | 44 | You can run a self-test procedure by running `make test`. Ensure that you have 45 | ports 5432..5434 free and don't have any necessary walbouncer running. The test 46 | run will leave two PostgreSQL instances and walbouncer running for further 47 | experimentation. 48 | 49 | Using walbouncer 50 | ================ 51 | 52 | Running 53 | ------- 54 | 55 | Walbouncer is started by either providing essential configuration options on 56 | the command line: 57 | 58 | walbouncer --port=5433 --host=master.example.com 59 | 60 | For advanced use cases you need to provide a config file in YAML format: 61 | 62 | walbouncer -c path/to/myconfig.yaml 63 | 64 | No built-in daemonization or init scripts are currently included. Log output 65 | from walbouncer goes to stderr. You can use nohup or daemonize to run it in 66 | the background. 67 | 68 | Configuration file 69 | ------------------ 70 | 71 | The configuration file is encoded in YAML format. The following directives are 72 | used: 73 | 74 | ```yaml 75 | # The port that walbouncer will listen on. 76 | listen_port: 5433 77 | 78 | # Connection settings for the replication master server 79 | master: 80 | host: localhost 81 | port: 5432 82 | 83 | # A list of configurations, each one a one entry mapping with the key 84 | # specifying a name for the configuration. First matching configuration 85 | # is chosen. If none of the configurations match the client is denied access. 86 | configurations: 87 | # Name of the configuration 88 | - examplereplica1: 89 | # Conditions under which this configuration matches. All of the entries 90 | # must match. 91 | match: 92 | # Check application_name of the client for an exact match. 93 | application_name: replica1 94 | # Matches the IP address the client is connecting from. Can be a 95 | # specific IP or a hostmask 96 | source: 192.168.0.0/16 97 | # Filter clauses can be omitted if filtering is not necessary. A record 98 | # is replicated if all of the include directives match and none of the 99 | # exclude directives match. 100 | filter: 101 | # If specified only tablespaces named in this list and default 102 | # tablespaces (pg_default, pg_global) are replicated. 103 | include_tablespaces: [spc_replica1, spc_replica2] 104 | # If specified tablespaces named in this list are not replicated. 105 | exclude_tablespaces: 106 | - spc_replica3 # Can also use alternate list syntax 107 | # If specified only databases in this list and template databases 108 | # are replicated 109 | include_databases: [postgres] 110 | # If specified databases in this list are skipped. 111 | exclude_databases: [test] 112 | # Second configuration 113 | - examplereplica2: 114 | match: 115 | application_name: replica2 116 | filter: 117 | include_tablespaces: [spc_replica2] 118 | ``` 119 | 120 | Additional Information 121 | ====================== 122 | 123 | Project homepage: https://www.cybertec-postgresql.com/en/products/walbouncer-partial-replication/ 124 | 125 | Blog posts on Walbouncer: 126 | * https://www.cybertec-postgresql.com/en/walbouncer-filtering-transaction-log/ 127 | * https://www.cybertec-postgresql.com/en/walbouncer-refreshed-a-proxy-for-selective-postgresql-physical-replication/ 128 | 129 | Gotchas 130 | ======= 131 | 132 | Adding/renaming databases 133 | ------------------------- 134 | 135 | List of databases used for filtering (OID-s are fetched from master on client connect) is only read on Walbouncer startup so to make sure the new/renamed 136 | database will be filtered out on the replica, you need to stop the replica and the Walbouncer, adjust the Walbouncer config, issue the CREATE/RENAME 137 | command, start Walbouncer and the replica. See above PDF for more details. 138 | 139 | Dropping databases 140 | ------------------ 141 | 142 | Have all replicas that want to filter out the dropped database actively streaming before you execute the drop. 143 | Otherwise the replicas will not know to skip the drop record and xlog replay will fail with an error. 144 | 145 | Potential future features 146 | ========================= 147 | 148 | - Also provide filtering for pg_basebackup. 149 | - Provide quorum for synchronous replication. (k of n servers have the data) 150 | - Create a protocol to use multicast to stream data. 151 | 152 | Pull requests and any other input are very welcome! 153 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | walbouncer (17.1-1) unstable; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- Christoph Berg Wed, 19 Mar 2025 16:33:37 +0100 6 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: walbouncer 2 | Section: database 3 | Priority: optional 4 | Maintainer: Debian PostgreSQL Maintainers 5 | Uploaders: 6 | Christoph Berg , 7 | Build-Depends: 8 | architecture-is-64-bit , 9 | debhelper-compat (= 13), 10 | libyaml-dev, 11 | postgresql-all , 12 | postgresql-server-dev-all (>= 217~), 13 | Standards-Version: 4.7.2 14 | Rules-Requires-Root: no 15 | Homepage: https://www.cybertec-postgresql.com/products/walbouncer-partial-replication/ 16 | Vcs-Browser: https://github.com/cybertec-postgresql/walbouncer 17 | Vcs-Git: https://github.com/cybertec-postgresql/walbouncer.git 18 | 19 | Package: postgresql-walbouncer 20 | Architecture: any 21 | Depends: 22 | ${misc:Depends}, 23 | ${postgresql:Depends}, 24 | ${shlibs:Depends}, 25 | Description: proxy server for PostgreSQL replication connections 26 | Walbouncer provides a proxy server for PostgreSQL replication connections. It 27 | also has the capability to replace a subset of WAL records will no-ops. 28 | . 29 | Use cases where you would use walbouncer: 30 | . 31 | * For clusters with more than two replicas you can change a replica servers' 32 | effective `primary_conninfo` without restarting PostgreSQL by proxying 33 | it through walbouncer and changing walbouncer config when master server 34 | location changes. 35 | . 36 | * You can choose to replicate a subset of data to save space on geographically 37 | distributed databases. Use a separate tablespace per location and configure 38 | walbouncer to filter out irrelevant data from the WAL stream. 39 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: walbouncer 3 | Source: https://www.cybertec-postgresql.com/products/walbouncer-partial-replication/ 4 | 5 | Files: * 6 | Copyright: 7 | Copyright (c) 2016-2025, CYBERTEC PostgreSQL International GmbH 8 | License: BSD 9 | All rights reserved. 10 | . 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are met: 13 | . 14 | * Redistributions of source code must retain the above copyright notice, this 15 | list of conditions and the following disclaimer. 16 | . 17 | * Redistributions in binary form must reproduce the above copyright notice, 18 | this list of conditions and the following disclaimer in the documentation 19 | and/or other materials provided with the distribution. 20 | . 21 | * Neither the name of the copyright holder nor the names of its 22 | contributors may be used to endorse or promote products derived from 23 | this software without specific prior written permission. 24 | . 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /debian/gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: https://salsa.debian.org/postgresql/postgresql-common/raw/master/gitlab/gitlab-ci.yml 2 | -------------------------------------------------------------------------------- /debian/pgversions: -------------------------------------------------------------------------------- 1 | 15+ 2 | -------------------------------------------------------------------------------- /debian/postgresql-walbouncer.docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/postgresql-walbouncer.install: -------------------------------------------------------------------------------- 1 | src/walbouncer usr/bin 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ 5 | 6 | override_dh_auto_install: 7 | # handled by dh_install 8 | 9 | override_dh_auto_test: 10 | pg_buildext run tests/run_demo.sh 11 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Depends: 2 | postgresql-all, 3 | postgresql-common-dev, 4 | @, 5 | Tests: test 6 | Restrictions: allow-stderr 7 | -------------------------------------------------------------------------------- /debian/tests/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eux 4 | 5 | WALBOUNCER=walbouncer \ 6 | pg_buildext run tests/run_demo.sh 7 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | https://github.com/cybertec-postgresql/walbouncer/tags .*/v?(.*).tar.gz 3 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: walbouncer 2 | 3 | CFLAGS=-O2 -Wall -Werror -g -std=gnu99 4 | 5 | PG_CONFIG = pg_config 6 | pglibdir = $(shell $(PG_CONFIG) --libdir) 7 | pgincludedir = $(shell $(PG_CONFIG) --includedir) 8 | pgbindir = $(shell $(PG_CONFIG) --bindir) 9 | 10 | objects = main.o wbsocket.o wbutils.o parser/repl_gram.o parser/scansup.o parser/stringinfo.o parser/gram_support.o wbcrc32c.o wbmasterconn.o wbfilter.o wbclientconn.o wbsignals.o wbconfig.o 11 | 12 | walbouncer: $(objects) 13 | gcc $(CFLAGS) -o walbouncer $(objects) -L$(pglibdir)/ -lpq -lyaml 14 | 15 | $(objects): %.o: %.c $(sort $(wildcard include/*.h)) 16 | gcc $(CFLAGS) -I$(pgincludedir) -Iinclude -c $< -o $@ 17 | 18 | clean: 19 | rm -f walbouncer parser/repl_scanner.c parser/repl_gram.c $(objects) 20 | 21 | parser/repl_scanner.c : parser/repl_scanner.l 22 | flex -o $@ $< 23 | 24 | parser/repl_gram.c : parser/repl_gram.y parser/repl_scanner.c 25 | bison -Wno-deprecated -o $@ $< 26 | 27 | test: all 28 | cd ../tests; ./run_demo.sh 29 | 30 | unittests/test: unittests/test.c wbutils.o 31 | gcc $(CFLAGS) -o $@ $^ -I$(pgincludedir) -Iinclude -L$(pglibdir) -lpq -lyaml 32 | 33 | run-unit: walbouncer unittests/test 34 | unittests/test 35 | 36 | install: walbouncer 37 | install -d $(DESTDIR)$(pgbindir) 38 | install walbouncer $(DESTDIR)$(pgbindir)/walbouncer 39 | 40 | uninstall: 41 | rm -f $(DESTDIR)$(pgbindir)/walbouncer 42 | -------------------------------------------------------------------------------- /src/include/parser/parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PARSER_H 3 | #define PARSER_H 4 | 5 | #include "wbglobals.h" 6 | 7 | typedef enum { 8 | REPL_IDENTIFY_SYSTEM, 9 | REPL_BASE_BACKUP, 10 | REPL_CREATE_SLOT, 11 | REPL_DROP_SLOT, 12 | REPL_START_PHYSICAL, 13 | REPL_START_LOGICAL, 14 | REPL_TIMELINE, 15 | REPL_SHOW_VAR 16 | } ReplCommandType; 17 | 18 | typedef struct ReplicationCommand { 19 | ReplCommandType command; 20 | char *slotname; 21 | char *varname; 22 | TimeLineID timeline; 23 | XLogRecPtr startpoint; 24 | } ReplicationCommand; 25 | 26 | // implemented by gram_support.c 27 | 28 | ReplicationCommand* 29 | MakeReplCommand(ReplCommandType cmd); 30 | 31 | // implemented by repl_gram.c 32 | 33 | extern int replication_yyparse(void); 34 | extern int replication_yylex(void); 35 | extern void replication_yyerror(const char *str); 36 | extern void replication_scanner_init(const char *query_string); 37 | extern void replication_scanner_finish(void); 38 | 39 | extern ReplicationCommand *replication_parse_result; 40 | 41 | 42 | 43 | #endif /* PARSER_H */ 44 | -------------------------------------------------------------------------------- /src/include/parser/scansup.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * scansup.h 4 | * scanner support routines. used by both the bootstrap lexer 5 | * as well as the normal lexer 6 | * 7 | * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group 8 | * Portions Copyright (c) 1994, Regents of the University of California 9 | * 10 | * src/include/parser/scansup.h 11 | * 12 | *------------------------------------------------------------------------- 13 | */ 14 | 15 | #ifndef SCANSUP_H 16 | #define SCANSUP_H 17 | 18 | extern char *scanstr(const char *s); 19 | 20 | extern char *downcase_truncate_identifier(const char *ident, int len, 21 | bool warn); 22 | 23 | extern void truncate_identifier(char *ident, int len, bool warn); 24 | 25 | extern bool scanner_isspace(char ch); 26 | 27 | #endif /* SCANSUP_H */ 28 | -------------------------------------------------------------------------------- /src/include/parser/stringinfo.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * stringinfo.h 4 | * Declarations/definitions for "StringInfo" functions. 5 | * 6 | * StringInfo provides an indefinitely-extensible string data type. 7 | * It can be used to buffer either ordinary C strings (null-terminated text) 8 | * or arbitrary binary data. All storage is allocated with palloc(). 9 | * 10 | * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group 11 | * Portions Copyright (c) 1994, Regents of the University of California 12 | * 13 | * src/include/lib/stringinfo.h 14 | * 15 | *------------------------------------------------------------------------- 16 | */ 17 | #ifndef STRINGINFO_H 18 | #define STRINGINFO_H 19 | 20 | /*------------------------- 21 | * StringInfoData holds information about an extensible string. 22 | * data is the current buffer for the string (allocated with palloc). 23 | * len is the current string length. There is guaranteed to be 24 | * a terminating '\0' at data[len], although this is not very 25 | * useful when the string holds binary data rather than text. 26 | * maxlen is the allocated size in bytes of 'data', i.e. the maximum 27 | * string size (including the terminating '\0' char) that we can 28 | * currently store in 'data' without having to reallocate 29 | * more space. We must always have maxlen > len. 30 | * cursor is initialized to zero by makeStringInfo or initStringInfo, 31 | * but is not otherwise touched by the stringinfo.c routines. 32 | * Some routines use it to scan through a StringInfo. 33 | *------------------------- 34 | */ 35 | typedef struct StringInfoData 36 | { 37 | char *data; 38 | int len; 39 | int maxlen; 40 | int cursor; 41 | } StringInfoData; 42 | 43 | typedef StringInfoData *StringInfo; 44 | 45 | 46 | /*------------------------ 47 | * There are two ways to create a StringInfo object initially: 48 | * 49 | * StringInfo stringptr = makeStringInfo(); 50 | * Both the StringInfoData and the data buffer are palloc'd. 51 | * 52 | * StringInfoData string; 53 | * initStringInfo(&string); 54 | * The data buffer is palloc'd but the StringInfoData is just local. 55 | * This is the easiest approach for a StringInfo object that will 56 | * only live as long as the current routine. 57 | * 58 | * To destroy a StringInfo, pfree() the data buffer, and then pfree() the 59 | * StringInfoData if it was palloc'd. There's no special support for this. 60 | * 61 | * NOTE: some routines build up a string using StringInfo, and then 62 | * release the StringInfoData but return the data string itself to their 63 | * caller. At that point the data string looks like a plain palloc'd 64 | * string. 65 | *------------------------- 66 | */ 67 | 68 | /*------------------------ 69 | * makeStringInfo 70 | * Create an empty 'StringInfoData' & return a pointer to it. 71 | */ 72 | extern StringInfo makeStringInfo(void); 73 | 74 | /*------------------------ 75 | * initStringInfo 76 | * Initialize a StringInfoData struct (with previously undefined contents) 77 | * to describe an empty string. 78 | */ 79 | extern void initStringInfo(StringInfo str); 80 | 81 | /*------------------------ 82 | * resetStringInfo 83 | * Clears the current content of the StringInfo, if any. The 84 | * StringInfo remains valid. 85 | */ 86 | extern void resetStringInfo(StringInfo str); 87 | 88 | /*------------------------ 89 | * appendStringInfo 90 | * Format text data under the control of fmt (an sprintf-style format string) 91 | * and append it to whatever is already in str. More space is allocated 92 | * to str if necessary. This is sort of like a combination of sprintf and 93 | * strcat. 94 | */ 95 | extern void 96 | appendStringInfo(StringInfo str, const char *fmt,...); 97 | /* This extension allows gcc to check the format string */ 98 | //__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); 99 | 100 | /*------------------------ 101 | * appendStringInfoVA 102 | * Attempt to format text data under the control of fmt (an sprintf-style 103 | * format string) and append it to whatever is already in str. If successful 104 | * return zero; if not (because there's not enough space), return an estimate 105 | * of the space needed, without modifying str. Typically the caller should 106 | * pass the return value to enlargeStringInfo() before trying again; see 107 | * appendStringInfo for standard usage pattern. 108 | */ 109 | //TODO 110 | //extern int 111 | //appendStringInfoVA(StringInfo str, const char *fmt, va_list args) 112 | //__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 0))); 113 | 114 | /*------------------------ 115 | * appendStringInfoString 116 | * Append a null-terminated string to str. 117 | * Like appendStringInfo(str, "%s", s) but faster. 118 | */ 119 | extern void appendStringInfoString(StringInfo str, const char *s); 120 | 121 | /*------------------------ 122 | * appendStringInfoChar 123 | * Append a single byte to str. 124 | * Like appendStringInfo(str, "%c", ch) but much faster. 125 | */ 126 | extern void appendStringInfoChar(StringInfo str, char ch); 127 | 128 | /*------------------------ 129 | * appendStringInfoCharMacro 130 | * As above, but a macro for even more speed where it matters. 131 | * Caution: str argument will be evaluated multiple times. 132 | */ 133 | #define appendStringInfoCharMacro(str,ch) \ 134 | (((str)->len + 1 >= (str)->maxlen) ? \ 135 | appendStringInfoChar(str, ch) : \ 136 | (void)((str)->data[(str)->len] = (ch), (str)->data[++(str)->len] = '\0')) 137 | 138 | /*------------------------ 139 | * appendStringInfoSpaces 140 | * Append a given number of spaces to str. 141 | */ 142 | extern void appendStringInfoSpaces(StringInfo str, int count); 143 | 144 | /*------------------------ 145 | * appendBinaryStringInfo 146 | * Append arbitrary binary data to a StringInfo, allocating more space 147 | * if necessary. 148 | */ 149 | extern void appendBinaryStringInfo(StringInfo str, 150 | const char *data, int datalen); 151 | 152 | /*------------------------ 153 | * enlargeStringInfo 154 | * Make sure a StringInfo's buffer can hold at least 'needed' more bytes. 155 | */ 156 | extern void enlargeStringInfo(StringInfo str, int needed); 157 | 158 | typedef size_t Size; 159 | 160 | 161 | #endif /* STRINGINFO_H */ 162 | -------------------------------------------------------------------------------- /src/include/wb_pg_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_PG_CONFIG_H 2 | #define _WB_PG_CONFIG_H 3 | 4 | #define XLOG_BLCKSZ 8192 5 | #define MAXIMUM_ALIGNOF 8 6 | #define XLOG_SEG_SIZE (16 * 1024 * 1024) 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/include/wbclientconn.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_CLIENTCONN_H 2 | #define _WB_CLIENTCONN_H 1 3 | 4 | #include "wbsocket.h" 5 | 6 | void WbCCInitConnection(WbConn conn); 7 | void WbCCPerformAuthentication(WbConn conn); 8 | void WbCCCommandLoop(WbConn conn); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/include/wbconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_CONFIG_H 2 | #define _WB_CONFIG_H 1 3 | 4 | #include "wbutils.h" 5 | 6 | typedef struct { 7 | char *name; 8 | struct { 9 | hostmask source_ip; 10 | char *application_name; 11 | } match; 12 | struct { 13 | char **include_tablespaces; 14 | int n_include_tablespaces; 15 | char **exclude_tablespaces; 16 | int n_exclude_tablespaces; 17 | char **include_databases; 18 | int n_include_databases; 19 | char **exclude_databases; 20 | int n_exclude_databases; 21 | } filter; 22 | } wb_config_entry; 23 | 24 | typedef struct wb_config_list_entry { 25 | struct wb_config_list_entry *next; 26 | wb_config_entry entry; 27 | } wb_config_list_entry; 28 | 29 | typedef struct { 30 | int listen_port; 31 | struct { 32 | char *host; 33 | int port; 34 | } master; 35 | wb_config_list_entry *configurations; 36 | } wb_configuration; 37 | 38 | extern wb_configuration *CurrentConfig; 39 | 40 | wb_configuration* wb_new_config(); 41 | wb_configuration* wb_read_config(wb_configuration* config, char *filename); 42 | void wb_delete_config(wb_configuration* config); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/include/wbcrc32c.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_crc32c.h 4 | * Routines for computing CRC-32C checksums. 5 | * 6 | * The speed of CRC-32C calculation has a big impact on performance, so we 7 | * jump through some hoops to get the best implementation for each 8 | * platform. Some CPU architectures have special instructions for speeding 9 | * up CRC calculations (e.g. Intel SSE 4.2), on other platforms we use the 10 | * Slicing-by-8 algorithm which uses lookup tables. 11 | * 12 | * The public interface consists of four macros: 13 | * 14 | * INIT_CRC32C(crc) 15 | * Initialize a CRC accumulator 16 | * 17 | * COMP_CRC32C(crc, data, len) 18 | * Accumulate some (more) bytes into a CRC 19 | * 20 | * FIN_CRC32C(crc) 21 | * Finish a CRC calculation 22 | * 23 | * EQ_CRC32C(c1, c2) 24 | * Check for equality of two CRCs. 25 | * 26 | * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group 27 | * Portions Copyright (c) 1994, Regents of the University of California 28 | * 29 | * src/include/port/pg_crc32c.h 30 | * 31 | *------------------------------------------------------------------------- 32 | */ 33 | #ifndef _WB_CRC32C_H 34 | #define _WB_CRC32C_H 35 | 36 | #include "wbglobals.h" 37 | #include "wbpgtypes.h" 38 | 39 | 40 | /* The INIT and EQ macros are the same for all implementations. */ 41 | #define INIT_CRC32C(crc) ((crc) = 0xFFFFFFFF) 42 | #define EQ_CRC32C(c1, c2) ((c1) == (c2)) 43 | 44 | /* 45 | * Use slicing-by-8 algorithm. 46 | * 47 | * On big-endian systems, the intermediate value is kept in reverse byte 48 | * order, to avoid byte-swapping during the calculation. FIN_CRC32C reverses 49 | * the bytes to the final order. 50 | */ 51 | #define COMP_CRC32C(crc, data, len) \ 52 | ((crc) = pg_comp_crc32c_sb8((crc), (data), (len))) 53 | #define COMP_CRC32C_ZERO(crc, data, len) \ 54 | ((crc) = pg_comp_crc32c_sb8_zero((crc), (data), (len))) 55 | 56 | #define FIN_CRC32C(crc) ((crc) ^= 0xFFFFFFFF) 57 | 58 | 59 | extern pg_crc32c pg_comp_crc32c_sb8(pg_crc32c crc, const void *data, size_t len); 60 | extern pg_crc32c pg_comp_crc32c_sb8_zero(pg_crc32c crc, const void *data, size_t len); 61 | 62 | #endif /* WB_CRC32C_H */ 63 | -------------------------------------------------------------------------------- /src/include/wbfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_FILTER_H 2 | #define _WB_FILTER_H 1 3 | 4 | #include "wbglobals.h" 5 | #include "wbmasterconn.h" 6 | 7 | #define FS_BUFFERING_STATE (1 << 8) 8 | typedef enum { 9 | FS_SYNCHRONIZING = 0, 10 | FS_COPY_SWITCH = 1, 11 | FS_COPY_NORMAL = 2, 12 | FS_COPY_ZERO = 3, 13 | FS_BUFFER_RECORD = (4 | FS_BUFFERING_STATE), 14 | FS_BUFFER_BLOCK_ID = (5 | FS_BUFFERING_STATE), 15 | FS_BUFFER_BLOCK_HEADER = (6 | FS_BUFFERING_STATE), 16 | FS_BUFFER_IMAGE_HEADER = (7 | FS_BUFFERING_STATE), 17 | FS_BUFFER_COMPRESSION_HEADER = (8 | FS_BUFFERING_STATE), 18 | FS_BUFFER_FILENODE = (9 | FS_BUFFERING_STATE) 19 | } FilterState; 20 | 21 | #define FL_BUFFER_LEN 128 22 | 23 | typedef struct { 24 | FilterState state; 25 | int dataNeeded; 26 | int recordRemaining; 27 | 28 | bool synchronized; 29 | XLogRecPtr requestedStartPos; 30 | 31 | int recordStart; 32 | int headerPos; 33 | int headerLen; 34 | 35 | int bufferLen; 36 | char buffer[FL_BUFFER_LEN]; 37 | 38 | int unsentBufferLen; 39 | char unsentBuffer[FL_BUFFER_LEN]; 40 | 41 | Oid *include_tablespaces; 42 | Oid *include_databases; 43 | Oid *exclude_tablespaces; 44 | Oid *exclude_databases; 45 | } FilterData; 46 | 47 | FilterData* WbFCreateProcessingState(XLogRecPtr startPos); 48 | void WbFFreeProcessingState(FilterData* fl); 49 | bool WbFProcessWalDataBlock(ReplMessage* msg, FilterData* fl, XLogRecPtr *retryPos, int xlog_page_magic); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/include/wbglobals.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_GLOBALS_H 2 | #define _WB_GLOBALS_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "libpq-fe.h" 10 | 11 | typedef unsigned char bool; 12 | #define false 0 13 | #define true 1 14 | 15 | typedef uint8_t uint8; 16 | typedef uint16_t uint16; 17 | typedef uint32_t uint32; 18 | typedef uint64_t uint64; 19 | typedef int16_t int16; 20 | typedef int32_t int32; 21 | typedef int64_t int64; 22 | 23 | /* Compatibility stuff */ 24 | typedef uint64 XLogRecPtr; 25 | typedef uint32 TimeLineID; 26 | typedef uint64 TimestampTz; 27 | typedef uint32 TransactionId; 28 | 29 | #define DEBUG 1 30 | 31 | #ifndef EOF 32 | #define EOF (-1) 33 | #endif 34 | 35 | #define STATUS_OK 0 36 | #define STATUS_ERROR -1 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/include/wbmasterconn.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_MASTERCONN_H 2 | #define _WB_MASTERCONN_H 1 3 | 4 | #include "wbglobals.h" 5 | #include "wbsocket.h" 6 | 7 | typedef enum { 8 | MSG_NOTHING, 9 | MSG_END_OF_WAL, 10 | MSG_WAL_DATA, 11 | MSG_KEEPALIVE 12 | } WalMsgType; 13 | 14 | typedef struct { 15 | WalMsgType type; 16 | XLogRecPtr walEnd; 17 | TimestampTz sendTime; 18 | bool replyRequested; 19 | XLogRecPtr dataStart; 20 | 21 | int dataPtr; 22 | int dataLen; 23 | int nextPageBoundary; 24 | 25 | char *data; 26 | 27 | } ReplMessage; 28 | 29 | typedef struct { 30 | char *filename; 31 | size_t contentLen; 32 | char *content; 33 | } TimelineHistory; 34 | 35 | typedef enum { 36 | OID_RESOLVE_TABLESPACES, 37 | OID_RESOLVE_DATABASES 38 | } OidResolveKind; 39 | 40 | typedef struct MasterConn MasterConn; 41 | 42 | MasterConn* WbMcOpenConnection(const char *conninfo); 43 | void WbMcCloseConnection(MasterConn *master); 44 | int WbMcGetSocket(MasterConn *master); 45 | bool WbMcStartStreaming(MasterConn *master, XLogRecPtr pos, TimeLineID tli); 46 | void WbMcEndStreaming(MasterConn *master, TimeLineID *nextTli, char** nextTliStart); 47 | bool WbMcReceiveWalMessage(MasterConn *master, ReplMessage *msg); 48 | void WbMcSendReply(MasterConn *master, StandbyReplyMessage *reply, bool force, bool requestReply); 49 | void WbMcSendFeedback(MasterConn *master, HSFeedbackMessage *feedback); 50 | bool WbMcIdentifySystem(MasterConn* master, 51 | char** primary_sysid, char** primary_tli, char** primary_xpos); 52 | bool WbMcGetTimelineHistory(MasterConn* master, TimeLineID timeline, 53 | TimelineHistory *history); 54 | char *WbMcShowVariable(MasterConn* master, char *varname); 55 | Oid * WbMcResolveOids(MasterConn *master, OidResolveKind kind, bool include, char** names, int n_items); 56 | const char *WbMcParameterStatus(MasterConn *master, char *name); 57 | #endif 58 | -------------------------------------------------------------------------------- /src/include/wbpgtypes.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_PGTYPES_H 2 | #define _WB_PGTYPES_H 3 | 4 | #include "pg_config.h" 5 | 6 | #include "wbglobals.h" 7 | #include "wb_pg_config.h" 8 | 9 | typedef unsigned long int uintptr_t; 10 | 11 | #define TYPEALIGN(ALIGNVAL,LEN) \ 12 | (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1))) 13 | #define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN)) 14 | 15 | typedef unsigned int Oid; 16 | 17 | typedef struct RelFileNode 18 | { 19 | Oid spcNode; /* tablespace */ 20 | Oid dbNode; /* database */ 21 | Oid relNode; /* relation */ 22 | } RelFileNode; 23 | 24 | typedef enum ForkNumber 25 | { 26 | InvalidForkNumber = -1, 27 | MAIN_FORKNUM = 0, 28 | FSM_FORKNUM, 29 | VISIBILITYMAP_FORKNUM, 30 | INIT_FORKNUM 31 | 32 | /* 33 | * NOTE: if you add a new fork, change MAX_FORKNUM and possibly 34 | * FORKNAMECHARS below, and update the forkNames array in 35 | * src/common/relpath.c 36 | */ 37 | } ForkNumber; 38 | 39 | #define MAX_FORKNUM INIT_FORKNUM 40 | 41 | typedef uint32 BlockNumber; 42 | 43 | /* 44 | * Header info for a backup block appended to an XLOG record. 45 | * 46 | * As a trivial form of data compression, the XLOG code is aware that 47 | * PG data pages usually contain an unused "hole" in the middle, which 48 | * contains only zero bytes. If hole_length > 0 then we have removed 49 | * such a "hole" from the stored data (and it's not counted in the 50 | * XLOG record's CRC, either). Hence, the amount of block data actually 51 | * present following the BkpBlock struct is BLCKSZ - hole_length bytes. 52 | * 53 | * Note that we don't attempt to align either the BkpBlock struct or the 54 | * block's data. So, the struct must be copied to aligned local storage 55 | * before use. 56 | */ 57 | typedef struct BkpBlock 58 | { 59 | RelFileNode node; /* relation containing block */ 60 | ForkNumber fork; /* fork within the relation */ 61 | BlockNumber block; /* block number */ 62 | uint16 hole_offset; /* number of bytes before "hole" */ 63 | uint16 hole_length; /* number of bytes in "hole" */ 64 | 65 | /* ACTUAL BLOCK DATA FOLLOWS AT END OF STRUCT */ 66 | } BkpBlock; 67 | 68 | typedef struct XLogPageHeaderData 69 | { 70 | uint16 xlp_magic; /* magic value for correctness checks */ 71 | uint16 xlp_info; /* flag bits, see below */ 72 | TimeLineID xlp_tli; /* TimeLineID of first record on page */ 73 | XLogRecPtr xlp_pageaddr; /* XLOG address of this page */ 74 | 75 | /* 76 | * When there is not enough space on current page for whole record, we 77 | * continue on the next page. xlp_rem_len is the number of bytes 78 | * remaining from a previous page. 79 | * 80 | * Note that xl_rem_len includes backup-block data; that is, it tracks 81 | * xl_tot_len not xl_len in the initial header. Also note that the 82 | * continuation data isn't necessarily aligned. 83 | */ 84 | uint32 xlp_rem_len; /* total len of remaining data for record */ 85 | } XLogPageHeaderData; 86 | 87 | #define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData)) 88 | 89 | typedef XLogPageHeaderData *XLogPageHeader; 90 | 91 | /* 92 | * When the XLP_LONG_HEADER flag is set, we store additional fields in the 93 | * page header. (This is ordinarily done just in the first page of an 94 | * XLOG file.) The additional fields serve to identify the file accurately. 95 | */ 96 | typedef struct XLogLongPageHeaderData 97 | { 98 | XLogPageHeaderData std; /* standard header fields */ 99 | uint64 xlp_sysid; /* system identifier from pg_control */ 100 | uint32 xlp_seg_size; /* just as a cross-check */ 101 | uint32 xlp_xlog_blcksz; /* just as a cross-check */ 102 | } XLogLongPageHeaderData; 103 | 104 | #define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData)) 105 | 106 | typedef XLogLongPageHeaderData *XLogLongPageHeader; 107 | 108 | /* When record crosses page boundary, set this flag in new page's header */ 109 | #define XLP_FIRST_IS_CONTRECORD 0x0001 110 | /* This flag indicates a "long" page header */ 111 | #define XLP_LONG_HEADER 0x0002 112 | /* This flag indicates backup blocks starting in this page are optional */ 113 | #define XLP_BKP_REMOVABLE 0x0004 114 | /* All defined flag bits in xlp_info (used for validity checking of header) */ 115 | #define XLP_ALL_FLAGS 0x0007 116 | 117 | #define XLogPageHeaderSize(hdr) \ 118 | (((hdr)->xlp_info & XLP_LONG_HEADER) ? SizeOfXLogLongPHD : SizeOfXLogShortPHD) 119 | 120 | /* 121 | * The XLOG is split into WAL segments (physical files) of the size indicated 122 | * by XLOG_SEG_SIZE. 123 | */ 124 | #define XLogSegSize ((uint32) XLOG_SEG_SIZE) 125 | #define XLogSegmentsPerXLogId (UINT64CONST(0x100000000) / XLOG_SEG_SIZE) 126 | 127 | #define XLogSegNoOffsetToRecPtr(segno, offset, dest) \ 128 | (dest) = (segno) * XLOG_SEG_SIZE + (offset) 129 | 130 | /* 131 | * Compute ID and segment from an XLogRecPtr. 132 | * 133 | * For XLByteToSeg, do the computation at face value. For XLByteToPrevSeg, 134 | * a boundary byte is taken to be in the previous segment. This is suitable 135 | * for deciding which segment to write given a pointer to a record end, 136 | * for example. 137 | */ 138 | #define XLByteToSeg(xlrp, logSegNo) \ 139 | logSegNo = (xlrp) / XLogSegSize 140 | 141 | #define XLByteToPrevSeg(xlrp, logSegNo) \ 142 | logSegNo = ((xlrp) - 1) / XLogSegSize 143 | 144 | /* 145 | * Is an XLogRecPtr within a particular XLOG segment? 146 | * 147 | * For XLByteInSeg, do the computation at face value. For XLByteInPrevSeg, 148 | * a boundary byte is taken to be in the previous segment. 149 | */ 150 | #define XLByteInSeg(xlrp, logSegNo) \ 151 | (((xlrp) / XLogSegSize) == (logSegNo)) 152 | 153 | #define XLByteInPrevSeg(xlrp, logSegNo) \ 154 | ((((xlrp) - 1) / XLogSegSize) == (logSegNo)) 155 | 156 | /* Check if an XLogRecPtr value is in a plausible range */ 157 | #define XRecOffIsValid(xlrp) \ 158 | ((xlrp) % XLOG_BLCKSZ >= SizeOfXLogShortPHD) 159 | 160 | /* 161 | * These macros encapsulate knowledge about the exact layout of XLog file 162 | * names, timeline history file names, and archive-status file names. 163 | */ 164 | #define MAXFNAMELEN 64 165 | 166 | #define XLogFileName(fname, tli, logSegNo) \ 167 | snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli, \ 168 | (uint32) ((logSegNo) / XLogSegmentsPerXLogId), \ 169 | (uint32) ((logSegNo) % XLogSegmentsPerXLogId)) 170 | 171 | #define XLogFromFileName(fname, tli, logSegNo) \ 172 | do { \ 173 | uint32 log; \ 174 | uint32 seg; \ 175 | sscanf(fname, "%08X%08X%08X", tli, &log, &seg); \ 176 | *logSegNo = (uint64) log * XLogSegmentsPerXLogId + seg; \ 177 | } while (0) 178 | 179 | #define XLogFilePath(path, tli, logSegNo) \ 180 | snprintf(path, MAXPGPATH, XLOGDIR "/%08X%08X%08X", tli, \ 181 | (uint32) ((logSegNo) / XLogSegmentsPerXLogId), \ 182 | (uint32) ((logSegNo) % XLogSegmentsPerXLogId)) 183 | 184 | #define TLHistoryFileName(fname, tli) \ 185 | snprintf(fname, MAXFNAMELEN, "%08X.history", tli) 186 | 187 | #define TLHistoryFilePath(path, tli) \ 188 | snprintf(path, MAXPGPATH, XLOGDIR "/%08X.history", tli) 189 | 190 | #define StatusFilePath(path, xlog, suffix) \ 191 | snprintf(path, MAXPGPATH, XLOGDIR "/archive_status/%s%s", xlog, suffix) 192 | 193 | #define BackupHistoryFileName(fname, tli, logSegNo, offset) \ 194 | snprintf(fname, MAXFNAMELEN, "%08X%08X%08X.%08X.backup", tli, \ 195 | (uint32) ((logSegNo) / XLogSegmentsPerXLogId), \ 196 | (uint32) ((logSegNo) % XLogSegmentsPerXLogId), offset) 197 | 198 | #define BackupHistoryFilePath(path, tli, logSegNo, offset) \ 199 | snprintf(path, MAXPGPATH, XLOGDIR "/%08X%08X%08X.%08X.backup", tli, \ 200 | (uint32) ((logSegNo) / XLogSegmentsPerXLogId), \ 201 | (uint32) ((logSegNo) % XLogSegmentsPerXLogId), offset) 202 | 203 | typedef uint32 pg_crc32c; 204 | typedef uint8 RmgrId; 205 | 206 | typedef struct XLogRecord 207 | { 208 | uint32 xl_tot_len; /* total len of entire record */ 209 | TransactionId xl_xid; /* xact id */ 210 | XLogRecPtr xl_prev; /* ptr to previous record in log */ 211 | uint8 xl_info; /* flag bits, see below */ 212 | RmgrId xl_rmid; /* resource manager for this record */ 213 | /* 2 bytes of padding here, initialize to zero */ 214 | pg_crc32c xl_crc; /* CRC for this record */ 215 | 216 | /* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */ 217 | 218 | } XLogRecord; 219 | 220 | #define SizeOfXLogRecord (offsetof(XLogRecord, xl_crc) + sizeof(pg_crc32c)) 221 | 222 | #define XLogRecGetData(record) ((char*) (record) + SizeOfXLogRecord) 223 | 224 | #define XLR_BKP_BLOCK_MASK 0x0F /* all info bits used for bkp blocks */ 225 | #define XLR_MAX_BKP_BLOCKS 4 226 | #define XLR_BKP_BLOCK(iblk) (0x08 >> (iblk)) /* iblk in 0..3 */ 227 | 228 | 229 | 230 | 231 | #define RM_XLOG_ID 0 232 | #define RM_XACT_ID 1 233 | #define RM_SMGR_ID 2 234 | #define RM_CLOG_ID 3 235 | #define RM_DBASE_ID 4 236 | #define RM_TBLSPC_ID 5 237 | #define RM_MULTIXACT_ID 6 238 | #define RM_RELMAP_ID 7 239 | #define RM_STANDBY_ID 8 240 | #define RM_HEAP2_ID 9 241 | #define RM_HEAP_ID 10 242 | #define RM_BTREE_ID 11 243 | #define RM_HASH_ID 12 244 | #define RM_GIN_ID 13 245 | #define RM_GIST_ID 14 246 | #define RM_SEQ_ID 15 247 | #define RM_SPGIST_ID 16 248 | 249 | #define XLOG_NOOP 0x20 250 | #define XLOG_SWITCH 0x40 251 | #define XLOG_FPI 0xA0 252 | 253 | #define XLOG_SMGR_CREATE 0x10 254 | #define XLOG_SMGR_TRUNCATE 0x20 255 | 256 | #define XLOG_SEQ_LOG 0x00 257 | 258 | #define REC_HEADER_LEN 24 259 | 260 | #define XLR_MAX_BLOCK_ID 32 261 | 262 | typedef struct XLogRecordBlockHeader 263 | { 264 | uint8 id; /* block reference ID */ 265 | uint8 fork_flags; /* fork within the relation, and flags */ 266 | uint16 data_length; /* number of payload bytes (not including page 267 | * image) */ 268 | 269 | /* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows */ 270 | /* If BKPBLOCK_SAME_REL is not set, a RelFileNode follows */ 271 | /* BlockNumber follows */ 272 | } XLogRecordBlockHeader; 273 | 274 | #define SizeOfXLogRecordBlockHeader (offsetof(XLogRecordBlockHeader, data_length) + sizeof(uint16)) 275 | 276 | #define BKPBLOCK_FORK_MASK 0x0F 277 | #define BKPBLOCK_FLAG_MASK 0xF0 278 | #define BKPBLOCK_HAS_IMAGE 0x10 /* block data is an XLogRecordBlockImage */ 279 | #define BKPBLOCK_HAS_DATA 0x20 280 | #define BKPBLOCK_WILL_INIT 0x40 /* redo will re-init the page */ 281 | #define BKPBLOCK_SAME_REL 0x80 /* RelFileNode omitted, same as previous */ 282 | 283 | #define SizeOfXLogRecordDataHeaderLong (sizeof(uint8) + sizeof(uint32)) 284 | 285 | typedef struct XLogRecordBlockImageHeader 286 | { 287 | uint16 length; /* number of page image bytes */ 288 | uint16 hole_offset; /* number of bytes before "hole" */ 289 | uint8 bimg_info; /* flag bits, see below */ 290 | 291 | /* 292 | * If BKPIMAGE_HAS_HOLE and BKPIMAGE_IS_COMPRESSED, an 293 | * XLogRecordBlockCompressHeader struct follows. 294 | */ 295 | } XLogRecordBlockImageHeader; 296 | 297 | #define SizeOfXLogRecordBlockImageHeader \ 298 | (offsetof(XLogRecordBlockImageHeader, bimg_info) + sizeof(uint8)) 299 | 300 | /* Information stored in bimg_info */ 301 | #define BKPIMAGE_HAS_HOLE 0x01 /* page image has "hole" */ 302 | #define BKPIMAGE_APPLY 0x02 /* page image should be restored 303 | * during replay */ 304 | /* compression methods supported */ 305 | #define BKPIMAGE_COMPRESS_PGLZ 0x04 306 | #define BKPIMAGE_COMPRESS_LZ4 0x08 307 | #define BKPIMAGE_COMPRESS_ZSTD 0x10 308 | 309 | #define BKPIMAGE_COMPRESSED(info) \ 310 | ((info & (BKPIMAGE_COMPRESS_PGLZ | BKPIMAGE_COMPRESS_LZ4 | \ 311 | BKPIMAGE_COMPRESS_ZSTD)) != 0) 312 | 313 | /* 314 | * Extra header information used when page image has "hole" and 315 | * is compressed. 316 | */ 317 | typedef struct XLogRecordBlockCompressHeader 318 | { 319 | uint16 hole_length; /* number of bytes in "hole" */ 320 | } XLogRecordBlockCompressHeader; 321 | 322 | #define SizeOfXLogRecordBlockCompressHeader \ 323 | sizeof(XLogRecordBlockCompressHeader) 324 | 325 | #define XLR_BLOCK_ID_DATA_SHORT 255 326 | #define XLR_BLOCK_ID_DATA_LONG 254 327 | #define XLR_BLOCK_ID_ORIGIN 253 328 | 329 | #endif /* XF_PGTYPES_H */ 330 | -------------------------------------------------------------------------------- /src/include/wbproto.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_PROTO_H 2 | #define _WB_PROTO_H 1 3 | 4 | #define PG_PROTOCOL_MAJOR(v) ((v) >> 16) 5 | #define PG_PROTOCOL_MINOR(v) ((v) & 0x0000ffff) 6 | #define PG_PROTOCOL(m,n) (((m) << 16) | (n)) 7 | 8 | /* The earliest and latest frontend/backend protocol version supported. */ 9 | 10 | #define PG_PROTOCOL_EARLIEST PG_PROTOCOL(3,0) 11 | #define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0) 12 | 13 | typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ 14 | 15 | typedef ProtocolVersion MsgType; 16 | 17 | 18 | /* 19 | * Packet lengths are 4 bytes in network byte order. 20 | * 21 | * The initial length is omitted from the packet layouts appearing below. 22 | */ 23 | 24 | typedef uint32 PacketLen; 25 | 26 | 27 | /* 28 | * Old-style startup packet layout with fixed-width fields. This is used in 29 | * protocol 1.0 and 2.0, but not in later versions. Note that the fields 30 | * in this layout are '\0' terminated only if there is room. 31 | */ 32 | 33 | #define SM_DATABASE 64 34 | #define SM_USER 32 35 | /* We append database name if db_user_namespace true. */ 36 | #define SM_DATABASE_USER (SM_DATABASE+SM_USER+1) /* +1 for @ */ 37 | #define SM_OPTIONS 64 38 | #define SM_UNUSED 64 39 | #define SM_TTY 64 40 | 41 | typedef struct StartupPacket 42 | { 43 | ProtocolVersion protoVersion; /* Protocol version */ 44 | char database[SM_DATABASE]; /* Database name */ 45 | /* Db_user_namespace appends dbname */ 46 | char user[SM_USER]; /* User name */ 47 | char options[SM_OPTIONS]; /* Optional additional args */ 48 | char unused[SM_UNUSED]; /* Unused */ 49 | char tty[SM_TTY]; /* Tty for debug output */ 50 | } StartupPacket; 51 | 52 | extern bool Db_user_namespace; 53 | 54 | /* 55 | * In protocol 3.0 and later, the startup packet length is not fixed, but 56 | * we set an arbitrary limit on it anyway. This is just to prevent simple 57 | * denial-of-service attacks via sending enough data to run the server 58 | * out of memory. 59 | */ 60 | #define MAX_STARTUP_PACKET_LENGTH 10000 61 | 62 | 63 | /* These are the authentication request codes sent by the backend. */ 64 | 65 | #define AUTH_REQ_OK 0 /* User is authenticated */ 66 | #define AUTH_REQ_KRB4 1 /* Kerberos V4. Not supported any more. */ 67 | #define AUTH_REQ_KRB5 2 /* Kerberos V5. Not supported any more. */ 68 | #define AUTH_REQ_PASSWORD 3 /* Password */ 69 | #define AUTH_REQ_CRYPT 4 /* crypt password. Not supported any more. */ 70 | #define AUTH_REQ_MD5 5 /* md5 password */ 71 | #define AUTH_REQ_SCM_CREDS 6 /* transfer SCM credentials */ 72 | #define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */ 73 | #define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */ 74 | #define AUTH_REQ_SSPI 9 /* SSPI negotiate without wrap() */ 75 | 76 | typedef uint32 AuthRequest; 77 | 78 | 79 | /* 80 | * A client can also send a cancel-current-operation request to the postmaster. 81 | * This is uglier than sending it directly to the client's backend, but it 82 | * avoids depending on out-of-band communication facilities. 83 | * 84 | * The cancel request code must not match any protocol version number 85 | * we're ever likely to use. This random choice should do. 86 | */ 87 | #define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678) 88 | 89 | typedef struct CancelRequestPacket 90 | { 91 | /* Note that each field is stored in network byte order! */ 92 | MsgType cancelRequestCode; /* code to identify a cancel request */ 93 | uint32 backendPID; /* PID of client's backend */ 94 | uint32 cancelAuthCode; /* secret key to authorize cancel */ 95 | } CancelRequestPacket; 96 | 97 | 98 | /* 99 | * A client can also start by sending a SSL negotiation request, to get a 100 | * secure channel. 101 | */ 102 | #define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679) 103 | 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/include/wbsignals.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_SIGNALS_H 2 | #define _WB_SIGNALS_H 1 3 | 4 | #include "wbglobals.h" 5 | #include 6 | 7 | extern sig_atomic_t stopRequested; 8 | void WbInitializeSignals(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/include/wbsocket.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_SOCKET_H 2 | #define _WB_SOCKET_H 1 3 | 4 | #include 5 | 6 | #include "wbglobals.h" 7 | #include "wbproto.h" 8 | #include "wbconfig.h" 9 | 10 | typedef struct { 11 | int fd; 12 | } WbSocketStruct; 13 | typedef WbSocketStruct* WbSocket; 14 | 15 | typedef struct { 16 | XLogRecPtr writePtr; 17 | XLogRecPtr flushPtr; 18 | XLogRecPtr applyPtr; 19 | TimestampTz sendTime; 20 | bool replyRequested; 21 | } StandbyReplyMessage; 22 | 23 | typedef struct { 24 | TimestampTz sendTime; 25 | TransactionId xmin; 26 | uint32 xmin_epoch; 27 | uint32 catalog_xmin; 28 | uint32 catalog_xmin_epoch; 29 | } HSFeedbackMessage; 30 | 31 | typedef struct { 32 | int fd; 33 | char *recvBuffer; 34 | int recvPointer; 35 | int recvLength; 36 | 37 | struct { 38 | uint32 addr; 39 | uint16 port; 40 | } client; 41 | 42 | // Matched configuration entry 43 | wb_config_entry *configEntry; 44 | 45 | // Sending buffer management 46 | char *sendBuffer; 47 | int sendBufSize; 48 | int sendBufLen; 49 | int sendBufMsgLenPtr; 50 | int sendBufFlushPtr; 51 | 52 | ProtocolVersion proto; 53 | 54 | char *master_host; 55 | int master_port; 56 | 57 | char *database_name; 58 | char *user_name; 59 | char *application_name; 60 | char *cmdline_options; 61 | char *guc_options; 62 | int gucs_len; 63 | 64 | // Sending state 65 | XLogRecPtr sentPtr; 66 | TimestampTz lastSend; 67 | bool copyDoneSent; 68 | bool copyDoneReceived; 69 | 70 | // Receive state 71 | StandbyReplyMessage lastReply; 72 | bool replyForwarded; 73 | HSFeedbackMessage lastFeedback; 74 | bool feedbackForwarded; 75 | } WbPortStruct; 76 | typedef WbPortStruct* WbConn; 77 | 78 | typedef struct { 79 | int32 len; 80 | char data[1]; 81 | } WbMessage; 82 | 83 | typedef enum { 84 | FLUSH_IMMEDIATE, 85 | FLUSH_ASYNC 86 | } ConnFlushMode; 87 | 88 | WbSocket 89 | OpenServerSocket(int port); 90 | 91 | WbConn 92 | ConnCreate(WbSocket server); 93 | 94 | bool 95 | ConnHasDataToFlush(WbConn conn); 96 | 97 | int 98 | ConnFlush(WbConn conn, ConnFlushMode mode); 99 | 100 | void 101 | CloseConn(WbConn); 102 | 103 | void 104 | CloseSocket(WbSocket sock); 105 | 106 | int 107 | ConnGetSocket(WbConn conn); 108 | 109 | void 110 | ConnBeginMessage(WbConn conn, char type); 111 | 112 | void 113 | ConnSendInt(WbConn conn, int i, int b); 114 | 115 | void 116 | ConnSendInt64(WbConn conn, int64 i); 117 | 118 | void 119 | ConnSendString(WbConn conn, const char *str); 120 | 121 | void 122 | ConnSendBytes(WbConn conn, const char *str, int n); 123 | 124 | void 125 | ConnEndMessage(WbConn conn); 126 | 127 | int 128 | ConnGetByte(WbConn conn); 129 | 130 | int 131 | ConnGetBytes(WbConn conn, char *s, size_t len); 132 | 133 | int 134 | ConnGetByteIfAvailable(WbConn conn, char *c); 135 | 136 | int 137 | ConnGetMessage(WbConn conn, WbMessage **msg); 138 | 139 | void 140 | ConnFreeMessage(WbMessage *msg); 141 | 142 | 143 | void 144 | hexdump(char *buf, int amount); 145 | 146 | void InitDeathWatchHandle(); 147 | void CloseDeathwatchPort(); 148 | bool DaemonIsAlive(); 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/include/wbutils.h: -------------------------------------------------------------------------------- 1 | #ifndef _WB_UTILS_H 2 | #define _WB_UTILS_H 1 3 | 4 | #include "wbglobals.h" 5 | 6 | typedef enum LogLevel { 7 | LOG_DEBUG3, 8 | LOG_DEBUG2, 9 | LOG_DEBUG1, 10 | LOG_INFO, 11 | LOG_WARNING, 12 | LOG_ERROR 13 | } LogLevel; 14 | #define LOG_LOWEST_LEVEL LOG_DEBUG3 15 | 16 | extern LogLevel loggingLevel; 17 | 18 | #define wb_log(level, levelStr, ...) if (loggingLevel <= level)\ 19 | {\ 20 | do_wb_log(level, levelStr, __FILE__, __VA_ARGS__);\ 21 | } 22 | 23 | #define log_debug3(...) wb_log(LOG_DEBUG3, "DEBUG3", __VA_ARGS__) 24 | #define log_debug2(...) wb_log(LOG_DEBUG2, "DEBUG2", __VA_ARGS__) 25 | #define log_debug1(...) wb_log(LOG_DEBUG1, "DEBUG1", __VA_ARGS__) 26 | #define log_info(...) wb_log(LOG_INFO, "INFO", __VA_ARGS__) 27 | #define log_warning(...) wb_log(LOG_WARNING, "WARNING", __VA_ARGS__) 28 | #define log_error(...) wb_log(LOG_ERROR, "ERROR", __VA_ARGS__) 29 | 30 | void do_wb_log(LogLevel logLevel, const char* logLevelStr, const char* file, const char* message, ...); 31 | void __attribute__((noreturn)) error(const char *message, ...); 32 | void showPQerror(PGconn *mc, char *message); 33 | 34 | 35 | void *wballoc(size_t amount); 36 | void *wballoc0(size_t amount); 37 | void *rewballoc(void *ptr, size_t amount); 38 | char *wbstrdup(char *s); 39 | void wbfree(void *ptr); 40 | 41 | #define Assert(x) do {\ 42 | if (!(x)) {\ 43 | log_info("Assert failure at %s:%d", __FILE__, __LINE__);\ 44 | }\ 45 | } while(0) 46 | 47 | #define FormatRecPtr(x) (uint32)((x) >> 32), (uint32) (x) 48 | 49 | 50 | int ensure_atoi(char *s); 51 | uint64 fromnetwork64(char *buf); 52 | uint32 fromnetwork32(char *buf); 53 | void write64(char *buf, uint64 v); 54 | void write32(char *buf, uint32 v); 55 | 56 | const char * timestamptz_to_str(TimestampTz t); 57 | 58 | typedef struct { 59 | uint32 addr; 60 | uint8 mask; 61 | } hostmask; 62 | 63 | bool parse_hostmask(char *string, hostmask *result); 64 | bool match_hostmask(hostmask *match, uint32 host); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "wbconfig.h" 12 | #include "wbutils.h" 13 | #include "wbsocket.h" 14 | #include "wbsignals.h" 15 | #include "wbclientconn.h" 16 | 17 | typedef enum { 18 | SLOT_UNUSED, 19 | SLOT_ACTIVE 20 | } BouncerSlotState; 21 | 22 | typedef struct { 23 | pid_t pid; 24 | BouncerSlotState state; 25 | } BouncerSlot; 26 | typedef struct { 27 | BouncerSlot* slots; 28 | int numSlots; 29 | } BouncerArrayStruct; 30 | 31 | char* config_filename = NULL; 32 | BouncerArrayStruct BouncerArray; 33 | 34 | static pid_t fork_process(); 35 | static void InitializeBouncerArray(); 36 | static void ResizeBouncerArray(int newSize); 37 | 38 | static pid_t fork_process() 39 | { 40 | pid_t result; 41 | 42 | fflush(NULL); 43 | 44 | result = fork(); 45 | return result; 46 | } 47 | 48 | static void 49 | InitializeBouncerArray() 50 | { 51 | BouncerArray.numSlots = 0; 52 | ResizeBouncerArray(8); 53 | } 54 | static void 55 | ResizeBouncerArray(int newSize) 56 | { 57 | int i; 58 | int oldSize = BouncerArray.numSlots; 59 | 60 | log_debug2("Resizing bouncer array from %d to %d", oldSize, newSize); 61 | sleep(1); 62 | 63 | if (oldSize == 0) 64 | BouncerArray.slots = wballoc0(sizeof(BouncerSlot)*newSize); 65 | else 66 | BouncerArray.slots = rewballoc(BouncerArray.slots, sizeof(BouncerSlot)*newSize); 67 | 68 | for (i = oldSize; i < newSize; i++) 69 | { 70 | BouncerArray.slots[i].pid = 0; 71 | BouncerArray.slots[i].state = SLOT_UNUSED; 72 | } 73 | BouncerArray.numSlots = newSize; 74 | } 75 | 76 | static BouncerSlot* 77 | FindBouncerSlot() 78 | { 79 | do 80 | { 81 | int i; 82 | for (i = 0; i < BouncerArray.numSlots; i++) 83 | { 84 | if (BouncerArray.slots[i].state == SLOT_UNUSED) 85 | return &(BouncerArray.slots[i]); 86 | } 87 | ResizeBouncerArray(BouncerArray.numSlots * 2); 88 | } while (true); 89 | } 90 | 91 | static void 92 | CleanupBackend(int pid, int exitstatus) 93 | { 94 | int i; 95 | BouncerSlot *slot = NULL; 96 | 97 | for (i = 0; i < BouncerArray.numSlots; i++) 98 | if (BouncerArray.slots[i].pid == pid) 99 | slot = &(BouncerArray.slots[i]); 100 | 101 | if (!slot) 102 | { 103 | log_warning("Trying to clean non-existant backend with PID %d", pid); 104 | return; 105 | } 106 | 107 | log_debug2("Backend %d exited in state %d", pid, slot->state); 108 | 109 | if (((exitstatus & 0xff00) >> 8) != 1 && exitstatus != 0) 110 | { 111 | log_warning("Backend with PID %d crashed with exit code %d", pid, exitstatus); 112 | } 113 | 114 | /* Mark the slot as empty */ 115 | slot->pid = 0; 116 | slot->state = SLOT_UNUSED; 117 | } 118 | 119 | static void 120 | BlockSignals() 121 | {} 122 | 123 | static void 124 | UnblockSignals() 125 | {} 126 | 127 | static void 128 | reaper(int signum) 129 | { 130 | int save_errno = errno; 131 | int pid; 132 | int exitstatus; 133 | 134 | BlockSignals(); 135 | 136 | log_debug2("Reaping dead child process"); 137 | 138 | while ((pid = waitpid(-1, &exitstatus, WNOHANG)) > 0) 139 | CleanupBackend(pid, exitstatus); 140 | 141 | UnblockSignals(); 142 | errno = save_errno; 143 | } 144 | 145 | static int 146 | InitMasks(fd_set *rmask, WbSocket server) 147 | { 148 | int maxsock = - 1; 149 | int fd = server->fd; 150 | FD_ZERO(rmask); 151 | 152 | FD_SET(fd, rmask); 153 | if (fd > maxsock) 154 | maxsock = fd; 155 | 156 | return maxsock + 1; 157 | } 158 | 159 | void WalBouncerMain() 160 | { 161 | // set up signals for child reaper, etc. 162 | WbInitializeSignals(); 163 | signal(SIGCHLD, reaper); 164 | 165 | // open socket for listening 166 | WbSocket server = OpenServerSocket(CurrentConfig->listen_port); 167 | WbConn conn; 168 | fd_set readmask; 169 | int nSock; 170 | 171 | nSock = InitMasks(&readmask, server); 172 | 173 | 174 | while (!stopRequested) 175 | { 176 | pid_t pid; 177 | { 178 | fd_set rmask; 179 | int selres; 180 | struct timeval timeout; 181 | timeout.tv_sec = 60; 182 | timeout.tv_usec = 0; 183 | 184 | memcpy((char*) &rmask, (char*)&readmask, sizeof(fd_set)); 185 | selres = select(nSock, &rmask, NULL, NULL, &timeout); 186 | log_debug2("select returned %d", selres) 187 | /* Now check the select() result */ 188 | if (selres < 0) 189 | if (errno != EINTR && errno != EWOULDBLOCK) 190 | error("select failed"); 191 | if (selres <= 0) 192 | continue; 193 | } 194 | 195 | conn = ConnCreate(server); 196 | conn->master_host = CurrentConfig->master.host; 197 | conn->master_port = CurrentConfig->master.port; 198 | 199 | log_debug2("Received new connection"); 200 | 201 | pid = fork_process(); 202 | if (pid == 0) /* child */ 203 | { 204 | CloseSocket(server); 205 | CloseDeathwatchPort(); 206 | 207 | WbCCInitConnection(conn); 208 | 209 | WbCCPerformAuthentication(conn); 210 | 211 | WbCCCommandLoop(conn); 212 | 213 | CloseConn(conn); 214 | 215 | return; 216 | } 217 | 218 | if (pid < 0) 219 | { 220 | /* failed fork */ 221 | } 222 | else if (pid > 0) 223 | { 224 | BouncerSlot* slot = FindBouncerSlot(); 225 | slot->pid = pid; 226 | slot->state = SLOT_ACTIVE; 227 | CloseConn(conn); 228 | } 229 | } 230 | log_info("Stopping server."); 231 | CloseSocket(server); 232 | } 233 | 234 | const char* progname; 235 | 236 | static void usage() 237 | { 238 | printf("%s proxys PostgreSQL streaming replication connections and optionally does filtering\n\n", progname); 239 | printf("Options:\n"); 240 | printf(" -?, --help Print this message\n"); 241 | printf(" -c, --config=FILE Read configuration from this file.\n"); 242 | printf(" -h, --host=HOST Connect to master on this host. Default localhost\n"); 243 | printf(" -P, --masterport=PORT Connect to master on this port. Default 5432\n"); 244 | printf(" -p, --port=PORT Run proxy on this port. Default 5433\n"); 245 | printf(" -v, --verbose Output additional debugging information\n"); 246 | 247 | } 248 | 249 | int 250 | main(int argc, char **argv) 251 | { 252 | int c; 253 | progname = "walbouncer"; 254 | 255 | CurrentConfig = wb_new_config(); 256 | 257 | while (1) 258 | { 259 | static struct option long_options[] = 260 | { 261 | {"config", required_argument, 0, 'c'}, 262 | {"port", required_argument, 0, 'p'}, 263 | {"host", required_argument, 0, 'h'}, 264 | {"masterport", required_argument, 0, 'P'}, 265 | {"verbose", no_argument, 0, 'v'}, 266 | {"help", no_argument, 0, '?'}, 267 | {0,0,0,0} 268 | }; 269 | int option_index = 0; 270 | 271 | c = getopt_long(argc, argv, "c:p:h:P:v?", 272 | long_options, &option_index); 273 | 274 | if (c == -1) 275 | break; 276 | 277 | switch (c) 278 | { 279 | case 'c': 280 | config_filename = wbstrdup(optarg); 281 | wb_read_config(CurrentConfig, config_filename); 282 | break; 283 | case 'p': 284 | CurrentConfig->listen_port = ensure_atoi(optarg); 285 | break; 286 | case 'h': 287 | CurrentConfig->master.host = wbstrdup(optarg); 288 | break; 289 | case 'P': 290 | CurrentConfig->master.port = ensure_atoi(optarg); 291 | break; 292 | case 'v': 293 | if (loggingLevel > LOG_LOWEST_LEVEL) 294 | loggingLevel--; 295 | break; 296 | case '?': 297 | usage(); 298 | exit(0); 299 | break; 300 | default: 301 | fprintf(stderr, "Invalid arguments\n"); 302 | exit(1); 303 | } 304 | } 305 | 306 | InitializeBouncerArray(); 307 | InitDeathWatchHandle(); 308 | 309 | WalBouncerMain(); 310 | return 0; 311 | } 312 | -------------------------------------------------------------------------------- /src/parser/gram_support.c: -------------------------------------------------------------------------------- 1 | #include "parser/parser.h" 2 | #include "wbutils.h" 3 | 4 | ReplicationCommand* 5 | MakeReplCommand(ReplCommandType type) 6 | { 7 | ReplicationCommand *cmd = wballoc0(sizeof(ReplicationCommand)); 8 | cmd->command = type; 9 | return cmd; 10 | } 11 | -------------------------------------------------------------------------------- /src/parser/repl_gram.y: -------------------------------------------------------------------------------- 1 | %{ 2 | /*------------------------------------------------------------------------- 3 | * 4 | * repl_gram.y - Parser for the replication commands 5 | * 6 | * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group 7 | * Portions Copyright (c) 1994, Regents of the University of California 8 | * 9 | * 10 | * IDENTIFICATION 11 | * src/backend/replication/repl_gram.y 12 | * 13 | *------------------------------------------------------------------------- 14 | */ 15 | 16 | /*#include "postgres.h" 17 | 18 | #include "access/xlogdefs.h" 19 | #include "nodes/makefuncs.h" 20 | #include "nodes/replnodes.h" 21 | #include "replication/walsender.h" 22 | #include "replication/walsender_private.h" 23 | */ 24 | 25 | #include 26 | #include "wbutils.h" 27 | #include "parser/parser.h" 28 | 29 | /* Result of the parsing is returned here */ 30 | ReplicationCommand *replication_parse_result; 31 | 32 | 33 | /* 34 | * Bison doesn't allocate anything that needs to live across parser calls, 35 | * so we can easily have it use palloc instead of malloc. This prevents 36 | * memory leaks if we error out during parsing. Note this only works with 37 | * bison >= 2.0. However, in bison 1.875 the default is to use alloca() 38 | * if possible, so there's not really much problem anyhow, at least if 39 | * you're building with gcc. 40 | */ 41 | #define YYMALLOC wballoc 42 | #define YYFREE wbfree 43 | 44 | %} 45 | 46 | %expect 0 47 | %name-prefix="replication_yy" 48 | 49 | %union { 50 | char *str; 51 | bool boolval; 52 | uint32 uintval; 53 | 54 | XLogRecPtr recptr; 55 | ReplicationCommand *cmd; 56 | /*Node *node; 57 | List *list; 58 | DefElem *defelt;*/ 59 | } 60 | 61 | /* Non-keyword tokens */ 62 | %token SCONST IDENT 63 | %token UCONST 64 | %token RECPTR 65 | 66 | /* Keyword tokens. */ 67 | %token K_BASE_BACKUP 68 | %token K_IDENTIFY_SYSTEM 69 | %token K_SHOW 70 | %token K_START_REPLICATION 71 | %token K_CREATE_REPLICATION_SLOT 72 | %token K_DROP_REPLICATION_SLOT 73 | %token K_TIMELINE_HISTORY 74 | %token K_LABEL 75 | %token K_PROGRESS 76 | %token K_FAST 77 | %token K_NOWAIT 78 | %token K_MAX_RATE 79 | %token K_WAL 80 | %token K_TIMELINE 81 | %token K_PHYSICAL 82 | %token K_LOGICAL 83 | %token K_SLOT 84 | 85 | %type command 86 | %type base_backup start_replication start_logical_replication create_replication_slot drop_replication_slot identify_system timeline_history show 87 | //%type base_backup_opt_list 88 | //%type base_backup_opt 89 | %type base_backup_opt_list 90 | %type base_backup_opt 91 | 92 | %type opt_timeline 93 | //%type plugin_options plugin_opt_list 94 | //%type plugin_opt_elem 95 | //%type plugin_opt_arg 96 | %type plugin_options plugin_opt_list 97 | %type plugin_opt_elem 98 | %type plugin_opt_arg 99 | 100 | %type opt_slot var_name 101 | 102 | %% 103 | 104 | firstcmd: command opt_semicolon 105 | { 106 | replication_parse_result = $1; 107 | } 108 | ; 109 | 110 | opt_semicolon: ';' 111 | | /* EMPTY */ 112 | ; 113 | 114 | command: 115 | identify_system 116 | | base_backup 117 | | start_replication 118 | | start_logical_replication 119 | | create_replication_slot 120 | | drop_replication_slot 121 | | timeline_history 122 | | show 123 | ; 124 | 125 | /* 126 | * IDENTIFY_SYSTEM 127 | */ 128 | identify_system: 129 | K_IDENTIFY_SYSTEM 130 | { 131 | $$ = MakeReplCommand(REPL_IDENTIFY_SYSTEM); 132 | } 133 | ; 134 | 135 | /* 136 | * SHOW setting 137 | */ 138 | show: 139 | K_SHOW var_name 140 | { 141 | ReplicationCommand *cmd = MakeReplCommand(REPL_SHOW_VAR); 142 | 143 | cmd->varname = $2; 144 | $$ = cmd; 145 | } 146 | 147 | var_name: IDENT { $$ = $1; } 148 | | var_name '.' IDENT 149 | { char *s1 = $1; 150 | char *s2 = $3; 151 | char *res = wballoc(strlen(s1) + strlen(s2) + 1); 152 | 153 | sprintf(res, "%s.%s", s1, s2); 154 | $$ = res;} 155 | ; 156 | 157 | /* 158 | * BASE_BACKUP [LABEL '