├── .gitignore ├── .gitmodules ├── COPYRIGHT ├── FAQ.md ├── Makefile ├── README.md ├── README.tests94 ├── compat10 ├── pglogical_compat.c └── pglogical_compat.h ├── compat11 ├── pglogical_compat.c └── pglogical_compat.h ├── compat12 ├── pglogical_compat.c └── pglogical_compat.h ├── compat13 ├── pglogical_compat.c └── pglogical_compat.h ├── compat14 ├── pglogical_compat.c └── pglogical_compat.h ├── compat15 ├── pglogical_compat.c └── pglogical_compat.h ├── compat16 ├── pglogical_compat.c └── pglogical_compat.h ├── compat17 ├── pglogical_compat.c └── pglogical_compat.h ├── compat94 ├── access │ ├── commit_ts.h │ └── stratnum.h ├── pglogical_compat.c ├── pglogical_compat.h ├── pglogical_origin--1.0.0.sql ├── pglogical_origin.control └── replication │ └── origin.h ├── compat95 ├── pglogical_compat.c └── pglogical_compat.h ├── compat96 ├── pglogical_compat.c └── pglogical_compat.h ├── docs ├── README.md └── pglogical.yml ├── expected ├── add_table.out ├── apply_delay.out ├── att_list.out ├── basic.out ├── bidirectional.out ├── column_filter.out ├── conflict_secondary_unique.out ├── copy.out ├── drop.out ├── extended.out ├── foreign_key.out ├── functions.out ├── functions_1.out ├── huge_tx.out ├── huge_tx_100k_tables.out ├── huge_tx_many_tables.out ├── infofuncs.out ├── init.out ├── init_fail.out ├── interfaces.out ├── matview.out ├── multiple_upstreams.out ├── multiple_upstreams_1.out ├── node_origin_cascade.out ├── parallel.out ├── preseed.out ├── preseed_check.out ├── primary_key.out ├── replication_set.out ├── row_filter.out ├── row_filter_1.out ├── row_filter_sampling.out ├── sequence.out ├── toasted.out └── triggers.out ├── internals-doc ├── .gitignore ├── DESIGN.md ├── OUTPUT.md └── protocol.txt ├── pglogical--1.0.0--1.0.1.sql ├── pglogical--1.0.0.sql ├── pglogical--1.0.1--1.1.0.sql ├── pglogical--1.1.0--1.1.1.sql ├── pglogical--1.1.1--1.1.2.sql ├── pglogical--1.1.2--1.2.0.sql ├── pglogical--1.2.0--1.2.1.sql ├── pglogical--1.2.1--1.2.2.sql ├── pglogical--1.2.2--2.0.0.sql ├── pglogical--2.0.0--2.0.1.sql ├── pglogical--2.0.0--2.1.0.sql ├── pglogical--2.0.1--2.1.0.sql ├── pglogical--2.1.0--2.1.1.sql ├── pglogical--2.1.1--2.2.0.sql ├── pglogical--2.2.0--2.2.1.sql ├── pglogical--2.2.0.sql ├── pglogical--2.2.1--2.2.2.sql ├── pglogical--2.2.1.sql ├── pglogical--2.2.2--2.3.0.sql ├── pglogical--2.2.2--2.3.1.sql ├── pglogical--2.2.2.sql ├── pglogical--2.3.0--2.3.1.sql ├── pglogical--2.3.0.sql ├── pglogical--2.3.1--2.3.2.sql ├── pglogical--2.3.1.sql ├── pglogical--2.3.2--2.3.3.sql ├── pglogical--2.3.2.sql ├── pglogical--2.3.3--2.3.4.sql ├── pglogical--2.3.3.sql ├── pglogical--2.3.4--2.4.0.sql ├── pglogical--2.3.4.sql ├── pglogical--2.4.0--2.4.1.sql ├── pglogical--2.4.0.sql ├── pglogical--2.4.1--2.4.2.sql ├── pglogical--2.4.1.sql ├── pglogical--2.4.2--2.4.3.sql ├── pglogical--2.4.2.sql ├── pglogical--2.4.3--2.4.4.sql ├── pglogical--2.4.3.sql ├── pglogical--2.4.4--2.4.5.sql ├── pglogical--2.4.4.sql ├── pglogical--2.4.5.sql ├── pglogical.c ├── pglogical.control.in ├── pglogical.h ├── pglogical.supp ├── pglogical_apply.c ├── pglogical_apply.h ├── pglogical_apply_heap.c ├── pglogical_apply_heap.h ├── pglogical_apply_spi.c ├── pglogical_apply_spi.h ├── pglogical_conflict.c ├── pglogical_conflict.h ├── pglogical_create_subscriber.c ├── pglogical_dependency.c ├── pglogical_dependency.h ├── pglogical_executor.c ├── pglogical_executor.h ├── pglogical_fe.c ├── pglogical_fe.h ├── pglogical_functions.c ├── pglogical_manager.c ├── pglogical_monitoring.c ├── pglogical_node.c ├── pglogical_node.h ├── pglogical_origin--1.0.0.sql ├── pglogical_origin.control ├── pglogical_output.c ├── pglogical_output_config.c ├── pglogical_output_config.h ├── pglogical_output_plugin.c ├── pglogical_output_plugin.h ├── pglogical_output_proto.c ├── pglogical_output_proto.h ├── pglogical_proto_json.c ├── pglogical_proto_json.h ├── pglogical_proto_native.c ├── pglogical_proto_native.h ├── pglogical_queue.c ├── pglogical_queue.h ├── pglogical_relcache.c ├── pglogical_relcache.h ├── pglogical_repset.c ├── pglogical_repset.h ├── pglogical_rpc.c ├── pglogical_rpc.h ├── pglogical_sequences.c ├── pglogical_sync.c ├── pglogical_sync.h ├── pglogical_worker.c ├── pglogical_worker.h ├── regress-pg_hba.conf ├── regress-postgresql.conf ├── sql ├── add_table.sql ├── apply_delay.sql ├── att_list.sql ├── basic.sql ├── bidirectional.sql ├── column_filter.sql ├── conflict_secondary_unique.sql ├── copy.sql ├── drop.sql ├── extended.sql ├── foreign_key.sql ├── functions.sql ├── huge_tx.sql ├── huge_tx_100k_tables.sql ├── huge_tx_many_tables.sql ├── infofuncs.sql ├── init.sql ├── init_fail.sql ├── interfaces.sql ├── matview.sql ├── multiple_upstreams.sql ├── node_origin_cascade.sql ├── parallel.sql ├── preseed.sql ├── preseed_check.sql ├── primary_key.sql ├── replication_set.sql ├── row_filter.sql ├── row_filter_sampling.sql ├── sequence.sql ├── toasted.sql └── triggers.sql └── t ├── 010_pglogical_create_subscriber.pl ├── 011_pglogical_cascade_sub.pl ├── 020_non_default_replication_set.pl ├── 030_pglogical_daylight_times_switching.pl ├── 040_pglogical_sync_during_write.pl ├── basic.sql ├── perl-94-postgresql.conf └── perl-95-postgresql.conf /.gitignore: -------------------------------------------------------------------------------- 1 | results 2 | regression_output 3 | tmp_check 4 | pglogical_create_subscriber 5 | .vimrc 6 | *.o 7 | *.so 8 | *.gcda 9 | *.gcno 10 | *~ 11 | pglogical.control 12 | pglogical-*.tar.bz2 13 | pglogical-*.tar.bz2.md5 14 | pglogical-*.tar.bz2.asc 15 | valgrind-*-*.log 16 | /postgres 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pglogical_dump"] 2 | path = pglogical_dump 3 | url = git@github.com:2ndQuadrant/pglogical_dump.git 4 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | PostgreSQL Database Management System 2 | (formerly known as Postgres, then as Postgres95) 3 | 4 | Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group 5 | 6 | Portions Copyright (c) 1994, The Regents of the University of California 7 | 8 | Permission to use, copy, modify, and distribute this software and its 9 | documentation for any purpose, without fee, and without a written agreement 10 | is hereby granted, provided that the above copyright notice and this 11 | paragraph and the following two paragraphs appear in all copies. 12 | 13 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 14 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 15 | LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 16 | DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 17 | POSSIBILITY OF SUCH DAMAGE. 18 | 19 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 20 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 | ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | pglogical 2.0 introduces some new features, like column filter, row filter and apply 4 | delay. Some related discussion on them: 5 | 6 | ### The column filter 7 | 8 | * What happens if we column filter on a table with OIDS? Can we filter on xmin? 9 | - For a table with OIDs, column filter works fine. No, we cannot filter system columns 10 | like oid, xmin. 11 | 12 | * What happens if a column being filtered on is dropped? 13 | - Currently in pglogical replication, even primary key can be dropped at provider. 14 | If a column being filtered on is dropped, at provider it is removed from the column 15 | filter too. This can be seen using `pglogical.show_repset_table_info()`. 16 | Columns at subscriber remain as is, which is correct and expected. At subscriber, 17 | in this state INSERTs replicate, but UPDATEs and DELETEs do not. 18 | 19 | * What happens if we add a column, does that automatically get included? 20 | - If a column is added at provider, it does not automatically get added to the column filter. 21 | 22 | ### The row filter 23 | 24 | * Can we create `row_filter` on table with OIDS? Can we filter on xmin? 25 | - Yes, `row_filter` works fine for table with OIDs. No, we cannot filter on system columns like xmin. 26 | 27 | * What types of function can we execute in a `row_filter`? Can we use a volatile sampling 28 | function, for example? 29 | - We can execute immutable, stable and volatile functions in a `row_filter`. Caution must 30 | be exercised with regard to writes as any expression which will do writes will throw error and stop replication. 31 | Volatile sampling function in `row_filter`: This would not work in practice as it would 32 | not get correct snapshot of the data in live system. Theoretically with static data, it works. 33 | 34 | * Can we test a JSONB datatype that includes some form of attribute filtering? 35 | - Yes, `row_filter` on attributes of JSONB datatype works fine. 36 | 37 | ### The apply delay 38 | 39 | * Does `apply_delay` include TimeZone changes, for example Daylight Savings Time? There is a 40 | similar mechanism in physical replication - `recovery_min_apply_delay`. However, if we set some 41 | interval, during the daylight savings times, we might get that interval + the time change in 42 | practice (ie instead of 1h delay you can get 2h delay because of that). This may lead to 43 | stopping and starting the database service twice per year. 44 | - Yes, `apply_delay` include TimeZone changes, for example Daylight Savings Time. Value of 45 | `apply_delay` stays the same in practice, if daylight savings time switch happens after 46 | subscription was created. 47 | However, we do not recommend running heavy workloads during switching time as pglogical 48 | replication needs some time ( ~ 5 minutes) to recover fine. 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | docs/README.md -------------------------------------------------------------------------------- /README.tests94: -------------------------------------------------------------------------------- 1 | On 9.4 you can't run 'make check' since we don't have any way to make a temp 2 | instance using PGXS. So you'll need to roll a 9.4 install yourself, with 3 | something like: 4 | 5 | export PATH=$HOME/pg/94/bin:$PATH 6 | export PGUSER=postgres 7 | 8 | [ -e 94test ] && pg_ctl -D 94test -m immediate -w stop 9 | rm -rf 94test 10 | initdb -D 94test -U postgres -A trust 11 | grep -v '^track_commit_timestamp' regress-postgresql.conf >> 94test/postgresql.conf 12 | cp regress-pg_hba.conf 94test/ 13 | PGPORT=5495 pg_ctl -D 94test -w -l 94test.log start 14 | PGPORT=5495 make -s clean install installcheck 15 | -------------------------------------------------------------------------------- /compat10/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat10/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | #include "pgstat.h" 5 | #include "catalog/indexing.h" 6 | #include "commands/trigger.h" 7 | #include "executor/executor.h" 8 | #include "replication/origin.h" 9 | #include "utils/varlena.h" 10 | 11 | #define PGLCreateTrigger CreateTrigger 12 | 13 | #define WaitLatchOrSocket(latch, wakeEvents, sock, timeout) \ 14 | WaitLatchOrSocket(latch, wakeEvents, sock, timeout, PG_WAIT_EXTENSION) 15 | 16 | #define WaitLatch(latch, wakeEvents, timeout) \ 17 | WaitLatch(latch, wakeEvents, timeout, PG_WAIT_EXTENSION) 18 | 19 | #define GetCurrentIntegerTimestamp() GetCurrentTimestamp() 20 | 21 | #define PGLDoCopy(stmt, queryString, processed) \ 22 | do \ 23 | { \ 24 | ParseState* pstate = make_parsestate(NULL); \ 25 | DoCopy(pstate, stmt, -1, 0, processed); \ 26 | free_parsestate(pstate); \ 27 | } while (false); 28 | 29 | #define pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams) \ 30 | pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams, NULL) 31 | 32 | #define CreateCommandTag(raw_parsetree) \ 33 | CreateCommandTag(raw_parsetree->stmt) 34 | 35 | #define PortalRun(portal, count, isTopLevel, dest, altdest, qc) \ 36 | PortalRun(portal, count, isTopLevel, true, dest, altdest, qc) 37 | 38 | #define ExecAlterExtensionStmt(stmt) \ 39 | ExecAlterExtensionStmt(NULL, stmt) 40 | 41 | #undef ExecEvalExpr 42 | #define ExecEvalExpr(expr, econtext, isNull, isDone) \ 43 | ((*(expr)->evalfunc) (expr, econtext, isNull)) 44 | 45 | #define Form_pg_sequence Form_pg_sequence_data 46 | 47 | #define InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, instrument_options) \ 48 | InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, NULL, instrument_options) 49 | 50 | #define ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newtuple, recheckIndexes) \ 51 | ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newtuple, recheckIndexes, NULL) 52 | 53 | #define ExecARInsertTriggers(estate, relinfo, trigtuple, recheckIndexes) \ 54 | ExecARInsertTriggers(estate, relinfo, trigtuple, recheckIndexes, NULL) 55 | 56 | #define ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple) \ 57 | ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple, NULL) 58 | 59 | #define makeDefElem(name, arg) makeDefElem(name, arg, -1) 60 | 61 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 62 | standard_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, qc) 63 | 64 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 65 | next_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv, dest, qc) 66 | 67 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 68 | heap_attisnull(tup, attnum) 69 | 70 | #ifndef rbtxn_has_catalog_changes 71 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 72 | #endif 73 | 74 | #define IndexRelationGetNumberOfKeyAttributes(rel) RelationGetNumberOfAttributes(rel) 75 | 76 | /* deprecated in PG12, removed in PG13 */ 77 | #define table_open(r, l) heap_open(r, l) 78 | #define table_openrv(r, l) heap_openrv(r, l) 79 | #define table_openrv_extended(r, l, m) heap_openrv_extended(r, l, m) 80 | #define table_close(r, l) heap_close(r, l) 81 | 82 | /* 29c94e03c7 */ 83 | #define ExecStoreHeapTuple(tuple, slot, shouldFree) ExecStoreTuple(tuple, slot, InvalidBuffer, shouldFree) 84 | 85 | /* c2fe139c20 */ 86 | #define TableScanDesc HeapScanDesc 87 | #define table_beginscan(relation, snapshot, nkeys, keys) heap_beginscan(relation, snapshot, nkeys, keys) 88 | #define table_beginscan_catalog(relation, nkeys, keys) heap_beginscan_catalog(relation, nkeys, keys) 89 | #define table_endscan(scan) heap_endscan(scan) 90 | 91 | /* 578b229718e8 */ 92 | #define CreateTemplateTupleDesc(natts) \ 93 | CreateTemplateTupleDesc(natts, false) 94 | 95 | /* 2f9661311b83 */ 96 | #define CommandTag const char * 97 | #define QueryCompletion char 98 | 99 | /* 6aba63ef3e60 */ 100 | #define pg_plan_queries(querytrees, query_string, cursorOptions, boundParams) \ 101 | pg_plan_queries(querytrees, cursorOptions, boundParams) 102 | 103 | /* cd142e032ebd50ec7974b3633269477c2c72f1cc removed replorigin_drop */ 104 | inline static void 105 | replorigin_drop_by_name(char *name, bool missing_ok, bool nowait) 106 | { 107 | RepOriginId originid; 108 | 109 | originid = replorigin_by_name(name, missing_ok); 110 | if (originid != InvalidRepOriginId) 111 | replorigin_drop(originid, nowait); 112 | } 113 | 114 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /compat11/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat12/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat13/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat13/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | #include "access/amapi.h" 5 | #include "access/heapam.h" 6 | #include "access/table.h" 7 | #include "access/tableam.h" 8 | #include "replication/origin.h" 9 | #include "utils/varlena.h" 10 | 11 | #define WaitLatchOrSocket(latch, wakeEvents, sock, timeout) \ 12 | WaitLatchOrSocket(latch, wakeEvents, sock, timeout, PG_WAIT_EXTENSION) 13 | 14 | #define WaitLatch(latch, wakeEvents, timeout) \ 15 | WaitLatch(latch, wakeEvents, timeout, PG_WAIT_EXTENSION) 16 | 17 | #define GetCurrentIntegerTimestamp() GetCurrentTimestamp() 18 | 19 | #define pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams) \ 20 | pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams, NULL) 21 | 22 | #define CreateCommandTag(raw_parsetree) \ 23 | CreateCommandTag(raw_parsetree->stmt) 24 | 25 | #define PortalRun(portal, count, isTopLevel, dest, altdest, qc) \ 26 | PortalRun(portal, count, isTopLevel, true, dest, altdest, qc) 27 | 28 | #define ExecAlterExtensionStmt(stmt) \ 29 | ExecAlterExtensionStmt(NULL, stmt) 30 | 31 | /* 32 | * Pg 11 adds an argument here. We don't need to special-case 2ndQPostgres 33 | * anymore because it adds a separate ExecBRDeleteTriggers2 now, so this only 34 | * handles the stock Pg11 change. 35 | */ 36 | #define ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple) \ 37 | ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple, NULL) 38 | 39 | #undef ExecEvalExpr 40 | #define ExecEvalExpr(expr, econtext, isNull, isDone) \ 41 | ((*(expr)->evalfunc) (expr, econtext, isNull)) 42 | 43 | #define Form_pg_sequence Form_pg_sequence_data 44 | 45 | #define InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, instrument_options) \ 46 | InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, NULL, instrument_options) 47 | 48 | #define ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newslot, recheckIndexes) \ 49 | ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newslot, recheckIndexes, NULL) 50 | 51 | #define ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes) \ 52 | ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes, NULL) 53 | 54 | #define ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple) \ 55 | ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple, NULL) 56 | 57 | #define makeDefElem(name, arg) makeDefElem(name, arg, -1) 58 | 59 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 60 | standard_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, qc) 61 | 62 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 63 | next_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv, dest, qc) 64 | 65 | #define PGLCreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, isInternal) \ 66 | CreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, InvalidOid, InvalidOid, NULL, isInternal, false); 67 | 68 | #define PGLDoCopy(stmt, queryString, processed) \ 69 | do \ 70 | { \ 71 | ParseState* pstate = make_parsestate(NULL); \ 72 | DoCopy(pstate, stmt, -1, 0, processed); \ 73 | free_parsestate(pstate); \ 74 | } while (false); 75 | 76 | #define PGLReplicationSlotCreate(name, db_specific, persistency) ReplicationSlotCreate(name, db_specific, persistency) 77 | 78 | #ifndef rbtxn_has_catalog_changes 79 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 80 | #endif 81 | 82 | /* ad7dbee368a */ 83 | #define ExecInitExtraTupleSlot(estate) \ 84 | ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple) 85 | 86 | #define ACL_OBJECT_RELATION OBJECT_TABLE 87 | #define ACL_OBJECT_SEQUENCE OBJECT_SEQUENCE 88 | 89 | #define DatumGetJsonb DatumGetJsonbP 90 | 91 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 92 | heap_attisnull(tup, attnum, tupledesc) 93 | 94 | /* cd142e032ebd50ec7974b3633269477c2c72f1cc removed replorigin_drop */ 95 | inline static void 96 | replorigin_drop_by_name(char *name, bool missing_ok, bool nowait) 97 | { 98 | RepOriginId originid; 99 | 100 | originid = replorigin_by_name(name, missing_ok); 101 | if (originid != InvalidRepOriginId) 102 | replorigin_drop(originid, nowait); 103 | } 104 | 105 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /compat14/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat14/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | #include "access/amapi.h" 5 | #include "access/heapam.h" 6 | #include "access/table.h" 7 | #include "access/tableam.h" 8 | #include "utils/varlena.h" 9 | 10 | #define WaitLatchOrSocket(latch, wakeEvents, sock, timeout) \ 11 | WaitLatchOrSocket(latch, wakeEvents, sock, timeout, PG_WAIT_EXTENSION) 12 | 13 | #define WaitLatch(latch, wakeEvents, timeout) \ 14 | WaitLatch(latch, wakeEvents, timeout, PG_WAIT_EXTENSION) 15 | 16 | #define GetCurrentIntegerTimestamp() GetCurrentTimestamp() 17 | 18 | #define pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams) \ 19 | pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams, NULL) 20 | 21 | #define CreateCommandTag(raw_parsetree) \ 22 | CreateCommandTag(raw_parsetree->stmt) 23 | 24 | #define PortalRun(portal, count, isTopLevel, dest, altdest, qc) \ 25 | PortalRun(portal, count, isTopLevel, true, dest, altdest, qc) 26 | 27 | #define ExecAlterExtensionStmt(stmt) \ 28 | ExecAlterExtensionStmt(NULL, stmt) 29 | 30 | /* 31 | * Pg 11 adds an argument here. We don't need to special-case 2ndQPostgres 32 | * anymore because it adds a separate ExecBRDeleteTriggers2 now, so this only 33 | * handles the stock Pg11 change. 34 | */ 35 | #define ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple) \ 36 | ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple, NULL) 37 | 38 | #undef ExecEvalExpr 39 | #define ExecEvalExpr(expr, econtext, isNull, isDone) \ 40 | ((*(expr)->evalfunc) (expr, econtext, isNull)) 41 | 42 | #define Form_pg_sequence Form_pg_sequence_data 43 | 44 | #define InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, instrument_options) \ 45 | InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, NULL, instrument_options) 46 | 47 | #define ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newslot, recheckIndexes) \ 48 | ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newslot, recheckIndexes, NULL) 49 | 50 | #define ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes) \ 51 | ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes, NULL) 52 | 53 | #define ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple) \ 54 | ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple, NULL) 55 | 56 | #define makeDefElem(name, arg) makeDefElem(name, arg, -1) 57 | 58 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 59 | standard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc) 60 | 61 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 62 | next_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc) 63 | 64 | #define PGLCreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, isInternal) \ 65 | CreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, InvalidOid, InvalidOid, NULL, isInternal, false); 66 | 67 | #define PGLDoCopy(stmt, queryString, processed) \ 68 | do \ 69 | { \ 70 | ParseState* pstate = make_parsestate(NULL); \ 71 | DoCopy(pstate, stmt, -1, 0, processed); \ 72 | free_parsestate(pstate); \ 73 | } while (false); 74 | 75 | #define PGLReplicationSlotCreate(name, db_specific, persistency) ReplicationSlotCreate(name, db_specific, persistency) 76 | 77 | #ifndef rbtxn_has_catalog_changes 78 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 79 | #endif 80 | 81 | /* ad7dbee368a */ 82 | #define ExecInitExtraTupleSlot(estate) \ 83 | ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple) 84 | 85 | #define ACL_OBJECT_RELATION OBJECT_TABLE 86 | #define ACL_OBJECT_SEQUENCE OBJECT_SEQUENCE 87 | 88 | #define DatumGetJsonb DatumGetJsonbP 89 | 90 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 91 | heap_attisnull(tup, attnum, tupledesc) 92 | 93 | /* 2a10fdc4307a667883f7a3369cb93a721ade9680 */ 94 | #define getObjectDescription(object) getObjectDescription(object, false) 95 | 96 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /compat15/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat15/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | #include "access/amapi.h" 5 | #include "access/heapam.h" 6 | #include "access/table.h" 7 | #include "access/tableam.h" 8 | #include "utils/varlena.h" 9 | 10 | #define WaitLatchOrSocket(latch, wakeEvents, sock, timeout) \ 11 | WaitLatchOrSocket(latch, wakeEvents, sock, timeout, PG_WAIT_EXTENSION) 12 | 13 | #define WaitLatch(latch, wakeEvents, timeout) \ 14 | WaitLatch(latch, wakeEvents, timeout, PG_WAIT_EXTENSION) 15 | 16 | #define GetCurrentIntegerTimestamp() GetCurrentTimestamp() 17 | 18 | #define pg_analyze_and_rewrite(parsetree, query_string, paramTypes, numParams) \ 19 | pg_analyze_and_rewrite_fixedparams(parsetree, query_string, paramTypes, numParams, NULL) 20 | 21 | #define CreateCommandTag(raw_parsetree) \ 22 | CreateCommandTag(raw_parsetree->stmt) 23 | 24 | #define PortalRun(portal, count, isTopLevel, dest, altdest, qc) \ 25 | PortalRun(portal, count, isTopLevel, true, dest, altdest, qc) 26 | 27 | #define ExecAlterExtensionStmt(stmt) \ 28 | ExecAlterExtensionStmt(NULL, stmt) 29 | 30 | /* 31 | * Pg 11 adds an argument here. We don't need to special-case 2ndQPostgres 32 | * anymore because it adds a separate ExecBRDeleteTriggers2 now, so this only 33 | * handles the stock Pg11 change. 34 | */ 35 | #define ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple) \ 36 | ExecBRDeleteTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple, NULL) 37 | 38 | #undef ExecEvalExpr 39 | #define ExecEvalExpr(expr, econtext, isNull, isDone) \ 40 | ((*(expr)->evalfunc) (expr, econtext, isNull)) 41 | 42 | #define Form_pg_sequence Form_pg_sequence_data 43 | 44 | #define InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, instrument_options) \ 45 | InitResultRelInfo(resultRelInfo, resultRelationDesc, resultRelationIndex, NULL, instrument_options) 46 | 47 | #define ExecARUpdateTriggers(estate, relinfo, tupleid, fdw_trigtuple, newslot, recheckIndexes) \ 48 | ExecARUpdateTriggers(estate, relinfo, NULL, NULL, tupleid, fdw_trigtuple, newslot, recheckIndexes, NULL, false) 49 | 50 | #define ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes) \ 51 | ExecARInsertTriggers(estate, relinfo, slot, recheckIndexes, NULL) 52 | 53 | #define ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple) \ 54 | ExecARDeleteTriggers(estate, relinfo, tupleid, fdw_trigtuple, NULL, false) 55 | 56 | #define ExecBRUpdateTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple, slot) \ 57 | ExecBRUpdateTriggers(estate, epqstate, relinfo, tupleid, fdw_trigtuple, slot, NULL) 58 | 59 | #define makeDefElem(name, arg) makeDefElem(name, arg, -1) 60 | 61 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 62 | standard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc) 63 | 64 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 65 | next_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc) 66 | 67 | #define PGLCreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, isInternal) \ 68 | CreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, InvalidOid, InvalidOid, NULL, isInternal, false); 69 | 70 | #define PGLDoCopy(stmt, queryString, processed) \ 71 | do \ 72 | { \ 73 | ParseState* pstate = make_parsestate(NULL); \ 74 | DoCopy(pstate, stmt, -1, 0, processed); \ 75 | free_parsestate(pstate); \ 76 | } while (false); 77 | 78 | #define PGLReplicationSlotCreate(name, db_specific, persistency) ReplicationSlotCreate(name, db_specific, persistency) 79 | 80 | #ifndef rbtxn_has_catalog_changes 81 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 82 | #endif 83 | 84 | /* ad7dbee368a */ 85 | #define ExecInitExtraTupleSlot(estate) \ 86 | ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple) 87 | 88 | #define ACL_OBJECT_RELATION OBJECT_TABLE 89 | #define ACL_OBJECT_SEQUENCE OBJECT_SEQUENCE 90 | 91 | #define DatumGetJsonb DatumGetJsonbP 92 | 93 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 94 | heap_attisnull(tup, attnum, tupledesc) 95 | 96 | /* 2a10fdc4307a667883f7a3369cb93a721ade9680 */ 97 | #define getObjectDescription(object) getObjectDescription(object, false) 98 | 99 | /* e997a0c642860a96df0151cbeccfecbdf0450d08 */ 100 | #define GetFlushRecPtr() GetFlushRecPtr(NULL) 101 | 102 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /compat16/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat17/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | -------------------------------------------------------------------------------- /compat94/access/commit_ts.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMIT_TS_H 2 | #define COMMIT_TS_H 3 | 4 | #include "access/xlog.h" 5 | #include "datatype/timestamp.h" 6 | #include "replication/origin.h" 7 | 8 | extern PGDLLIMPORT bool track_commit_timestamp; 9 | 10 | extern bool TransactionIdGetCommitTsData(TransactionId xid, 11 | TimestampTz *ts, RepOriginId *nodeid); 12 | 13 | #endif /* COMMIT_TS_H */ 14 | -------------------------------------------------------------------------------- /compat94/access/stratnum.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/compat94/access/stratnum.h -------------------------------------------------------------------------------- /compat94/pglogical_origin--1.0.0.sql: -------------------------------------------------------------------------------- 1 | \echo Use "CREATE EXTENSION pglogical_origin" to load this file. \quit 2 | 3 | DO $$ 4 | BEGIN 5 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') != 904 THEN 6 | RAISE EXCEPTION 'pglogical_origin can only be installed into PostgreSQL 9.4'; 7 | END IF; 8 | END;$$; 9 | 10 | CREATE TABLE pglogical_origin.replication_origin ( 11 | roident oid NOT NULL, 12 | roname text NOT NULL, 13 | roremote_lsn pg_lsn NOT NULL 14 | ); 15 | 16 | CREATE UNIQUE INDEX replication_origin_roident_index ON pglogical_origin.replication_origin(roident); 17 | CREATE UNIQUE INDEX replication_origin_roname_index ON pglogical_origin.replication_origin(roname); 18 | -------------------------------------------------------------------------------- /compat94/pglogical_origin.control: -------------------------------------------------------------------------------- 1 | # pglogical_origin extension 2 | comment = 'PostgreSQL Logical Replication Origin Tracking Emulation for 9.4' 3 | default_version = '1.0.0' 4 | module_pathname = '$libdir/pglogical' 5 | relocatable = false 6 | schema = pglogical_origin 7 | -------------------------------------------------------------------------------- /compat94/replication/origin.h: -------------------------------------------------------------------------------- 1 | #ifndef PGLOGICAL_COMPAT_REPLICATION_ORIGIN_H 2 | #define PGLOGICAL_COMPAT_REPLICATION_ORIGIN_H 3 | 4 | #ifndef InvalidRepOriginId 5 | typedef uint16 RepOriginId; 6 | #define InvalidRepOriginId 0 7 | #define DoNotReplicateId PG_UINT16_MAX 8 | #endif 9 | 10 | extern PGDLLIMPORT RepOriginId replorigin_session_origin; 11 | extern PGDLLIMPORT XLogRecPtr replorigin_session_origin_lsn; 12 | extern PGDLLIMPORT TimestampTz replorigin_session_origin_timestamp; 13 | 14 | extern RepOriginId replorigin_create(char *name); 15 | extern void pgl_replorigin_drop(RepOriginId roident); 16 | extern void replorigin_drop_by_name(char *name, bool missing_ok, bool nowait); 17 | 18 | extern RepOriginId replorigin_by_name(char *name, bool missing_ok); 19 | extern void replorigin_session_setup(RepOriginId node); 20 | extern void replorigin_session_reset(void); 21 | extern XLogRecPtr replorigin_session_get_progress(bool flush); 22 | 23 | extern void replorigin_advance(RepOriginId node, 24 | XLogRecPtr remote_commit, 25 | XLogRecPtr local_commit, 26 | bool go_backward, bool wal_log); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /compat95/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | 14 | #include "postgres.h" 15 | 16 | #include "pglogical_compat.h" 17 | 18 | LWLockPadded * 19 | GetNamedLWLockTranche(const char *tranche_name) 20 | { 21 | LWLock *lock = LWLockAssign(); 22 | 23 | return (LWLockPadded *)lock; 24 | } 25 | 26 | void 27 | RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) 28 | { 29 | Assert(num_lwlocks == 1); 30 | 31 | RequestAddinLWLocks(num_lwlocks); 32 | } 33 | 34 | /* 35 | * CatalogTupleInsert - do heap and indexing work for a new catalog tuple 36 | * 37 | * Insert the tuple data in "tup" into the specified catalog relation. 38 | * The Oid of the inserted tuple is returned. 39 | * 40 | * This is a convenience routine for the common case of inserting a single 41 | * tuple in a system catalog; it inserts a new heap tuple, keeping indexes 42 | * current. Avoid using it for multiple tuples, since opening the indexes 43 | * and building the index info structures is moderately expensive. 44 | * (Use CatalogTupleInsertWithInfo in such cases.) 45 | */ 46 | Oid 47 | CatalogTupleInsert(Relation heapRel, HeapTuple tup) 48 | { 49 | CatalogIndexState indstate; 50 | Oid oid; 51 | 52 | indstate = CatalogOpenIndexes(heapRel); 53 | 54 | oid = simple_heap_insert(heapRel, tup); 55 | 56 | CatalogIndexInsert(indstate, tup); 57 | CatalogCloseIndexes(indstate); 58 | 59 | return oid; 60 | } 61 | 62 | /* 63 | * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple 64 | * 65 | * Update the tuple identified by "otid", replacing it with the data in "tup". 66 | * 67 | * This is a convenience routine for the common case of updating a single 68 | * tuple in a system catalog; it updates one heap tuple, keeping indexes 69 | * current. Avoid using it for multiple tuples, since opening the indexes 70 | * and building the index info structures is moderately expensive. 71 | * (Use CatalogTupleUpdateWithInfo in such cases.) 72 | */ 73 | void 74 | CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) 75 | { 76 | CatalogIndexState indstate; 77 | 78 | indstate = CatalogOpenIndexes(heapRel); 79 | 80 | simple_heap_update(heapRel, otid, tup); 81 | 82 | CatalogIndexInsert(indstate, tup); 83 | CatalogCloseIndexes(indstate); 84 | } 85 | 86 | 87 | /* 88 | * CatalogTupleDelete - do heap and indexing work for deleting a catalog tuple 89 | * 90 | * Delete the tuple identified by "tid" in the specified catalog. 91 | * 92 | * With Postgres heaps, there is no index work to do at deletion time; 93 | * cleanup will be done later by VACUUM. However, callers of this function 94 | * shouldn't have to know that; we'd like a uniform abstraction for all 95 | * catalog tuple changes. Hence, provide this currently-trivial wrapper. 96 | * 97 | * The abstraction is a bit leaky in that we don't provide an optimized 98 | * CatalogTupleDeleteWithInfo version, because there is currently nothing to 99 | * optimize. If we ever need that, rather than touching a lot of call sites, 100 | * it might be better to do something about caching CatalogIndexState. 101 | */ 102 | void 103 | CatalogTupleDelete(Relation heapRel, ItemPointer tid) 104 | { 105 | simple_heap_delete(heapRel, tid); 106 | } 107 | -------------------------------------------------------------------------------- /compat95/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | 5 | #include "pgstat.h" 6 | #include "catalog/indexing.h" 7 | #include "commands/trigger.h" 8 | #include "executor/executor.h" 9 | #include "replication/origin.h" 10 | #include "storage/lwlock.h" 11 | 12 | extern LWLockPadded *GetNamedLWLockTranche(const char *tranche_name); 13 | extern void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks); 14 | 15 | #define GetConfigOptionByName(name, varname, missing_ok) \ 16 | (\ 17 | AssertMacro(!missing_ok), \ 18 | GetConfigOptionByName(name, varname) \ 19 | ) 20 | 21 | #define PGLCreateTrigger CreateTrigger 22 | 23 | #define RawStmt Node 24 | 25 | #define PGLDoCopy(stmt, queryString, processed) DoCopy(stmt, queryString, processed) 26 | 27 | #ifdef PGXC 28 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 29 | standard_ProcessUtility(pstmt, queryString, context, params, dest, sentToRemote, qc) 30 | 31 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 32 | next_ProcessUtility_hook(pstmt, queryString, context, params, dest, sentToRemote, qc) 33 | 34 | #else 35 | 36 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 37 | standard_ProcessUtility(pstmt, queryString, context, params, dest, qc) 38 | 39 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 40 | next_ProcessUtility_hook(pstmt, queryString, context, params, dest, qc) 41 | #endif 42 | 43 | extern Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup); 44 | extern void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup); 45 | extern void CatalogTupleDelete(Relation heapRel, ItemPointer tid); 46 | 47 | /* 48 | * nowait=true is the standard behavior. If nowait=false is called, 49 | * we ignore that, meaning we don't wait even if the caller asked to 50 | * wait. This could lead to spurious errors in race conditions, but 51 | * it's the best we can do. 52 | */ 53 | #define replorigin_drop(roident, nowait) replorigin_drop(roident) 54 | 55 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 56 | heap_attisnull(tup, attnum) 57 | 58 | #define ALLOCSET_DEFAULT_SIZES \ 59 | ALLOCSET_DEFAULT_MINSIZE, \ 60 | ALLOCSET_DEFAULT_INITSIZE, \ 61 | ALLOCSET_DEFAULT_MAXSIZE 62 | 63 | #ifndef rbtxn_has_catalog_changes 64 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 65 | #endif 66 | 67 | #define IndexRelationGetNumberOfKeyAttributes(rel) RelationGetNumberOfAttributes(rel) 68 | 69 | /* deprecated in PG12, removed in PG13 */ 70 | #define table_open(r, l) heap_open(r, l) 71 | #define table_openrv(r, l) heap_openrv(r, l) 72 | #define table_openrv_extended(r, l, m) heap_openrv_extended(r, l, m) 73 | #define table_close(r, l) heap_close(r, l) 74 | 75 | /* 29c94e03c7 */ 76 | #define ExecStoreHeapTuple(tuple, slot, shouldFree) ExecStoreTuple(tuple, slot, InvalidBuffer, shouldFree) 77 | 78 | /* c2fe139c20 */ 79 | #define TableScanDesc HeapScanDesc 80 | #define table_beginscan(relation, snapshot, nkeys, keys) heap_beginscan(relation, snapshot, nkeys, keys) 81 | #define table_beginscan_catalog(relation, nkeys, keys) heap_beginscan_catalog(relation, nkeys, keys) 82 | #define table_endscan(scan) heap_endscan(scan) 83 | 84 | /* 578b229718e8 */ 85 | #define CreateTemplateTupleDesc(natts) \ 86 | CreateTemplateTupleDesc(natts, false) 87 | 88 | /* 2f9661311b83 */ 89 | #define CommandTag const char * 90 | #define QueryCompletion char 91 | 92 | /* 6aba63ef3e60 */ 93 | #define pg_plan_queries(querytrees, query_string, cursorOptions, boundParams) \ 94 | pg_plan_queries(querytrees, cursorOptions, boundParams) 95 | 96 | /* cd142e032ebd50ec7974b3633269477c2c72f1cc removed replorigin_drop */ 97 | inline static void 98 | replorigin_drop_by_name(char *name, bool missing_ok, bool nowait) 99 | { 100 | RepOriginId originid; 101 | 102 | originid = replorigin_by_name(name, missing_ok); 103 | if (originid != InvalidRepOriginId) 104 | replorigin_drop(originid, nowait); 105 | } 106 | 107 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /compat96/pglogical_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_compat.c 4 | * compatibility functions (mainly with different PG versions) 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_compat.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | 14 | #include "postgres.h" 15 | 16 | #include "pglogical_compat.h" 17 | 18 | /* 19 | * CatalogTupleInsert - do heap and indexing work for a new catalog tuple 20 | * 21 | * Insert the tuple data in "tup" into the specified catalog relation. 22 | * The Oid of the inserted tuple is returned. 23 | * 24 | * This is a convenience routine for the common case of inserting a single 25 | * tuple in a system catalog; it inserts a new heap tuple, keeping indexes 26 | * current. Avoid using it for multiple tuples, since opening the indexes 27 | * and building the index info structures is moderately expensive. 28 | * (Use CatalogTupleInsertWithInfo in such cases.) 29 | */ 30 | Oid 31 | CatalogTupleInsert(Relation heapRel, HeapTuple tup) 32 | { 33 | CatalogIndexState indstate; 34 | Oid oid; 35 | 36 | indstate = CatalogOpenIndexes(heapRel); 37 | 38 | oid = simple_heap_insert(heapRel, tup); 39 | 40 | CatalogIndexInsert(indstate, tup); 41 | CatalogCloseIndexes(indstate); 42 | 43 | return oid; 44 | } 45 | 46 | /* 47 | * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple 48 | * 49 | * Update the tuple identified by "otid", replacing it with the data in "tup". 50 | * 51 | * This is a convenience routine for the common case of updating a single 52 | * tuple in a system catalog; it updates one heap tuple, keeping indexes 53 | * current. Avoid using it for multiple tuples, since opening the indexes 54 | * and building the index info structures is moderately expensive. 55 | * (Use CatalogTupleUpdateWithInfo in such cases.) 56 | */ 57 | void 58 | CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) 59 | { 60 | CatalogIndexState indstate; 61 | 62 | indstate = CatalogOpenIndexes(heapRel); 63 | 64 | simple_heap_update(heapRel, otid, tup); 65 | 66 | CatalogIndexInsert(indstate, tup); 67 | CatalogCloseIndexes(indstate); 68 | } 69 | 70 | 71 | /* 72 | * CatalogTupleDelete - do heap and indexing work for deleting a catalog tuple 73 | * 74 | * Delete the tuple identified by "tid" in the specified catalog. 75 | * 76 | * With Postgres heaps, there is no index work to do at deletion time; 77 | * cleanup will be done later by VACUUM. However, callers of this function 78 | * shouldn't have to know that; we'd like a uniform abstraction for all 79 | * catalog tuple changes. Hence, provide this currently-trivial wrapper. 80 | * 81 | * The abstraction is a bit leaky in that we don't provide an optimized 82 | * CatalogTupleDeleteWithInfo version, because there is currently nothing to 83 | * optimize. If we ever need that, rather than touching a lot of call sites, 84 | * it might be better to do something about caching CatalogIndexState. 85 | */ 86 | void 87 | CatalogTupleDelete(Relation heapRel, ItemPointer tid) 88 | { 89 | simple_heap_delete(heapRel, tid); 90 | } 91 | -------------------------------------------------------------------------------- /compat96/pglogical_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_COMPAT_H 2 | #define PG_LOGICAL_COMPAT_H 3 | 4 | #include "pgstat.h" 5 | #include "catalog/indexing.h" 6 | #include "commands/trigger.h" 7 | #include "executor/executor.h" 8 | #include "replication/origin.h" 9 | 10 | #define PGLCreateTrigger CreateTrigger 11 | 12 | #define RawStmt Node 13 | 14 | #define PGLDoCopy(stmt, queryString, processed) DoCopy(stmt, queryString, processed) 15 | 16 | #ifdef PGXC 17 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 18 | standard_ProcessUtility(pstmt, queryString, context, params, dest, sentToRemote, qc) 19 | 20 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 21 | next_ProcessUtility_hook(pstmt, queryString, context, params, dest, sentToRemote, qc) 22 | 23 | #else 24 | 25 | #define PGLstandard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 26 | standard_ProcessUtility(pstmt, queryString, context, params, dest, qc) 27 | 28 | #define PGLnext_ProcessUtility_hook(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, sentToRemote, qc) \ 29 | next_ProcessUtility_hook(pstmt, queryString, context, params, dest, qc) 30 | #endif 31 | 32 | extern Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup); 33 | extern void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup); 34 | extern void CatalogTupleDelete(Relation heapRel, ItemPointer tid); 35 | 36 | /* 37 | * nowait=true is the standard behavior. If nowait=false is called, 38 | * we ignore that, meaning we don't wait even if the caller asked to 39 | * wait. This could lead to spurious errors in race conditions, but 40 | * it's the best we can do. 41 | */ 42 | #define replorigin_drop(roident, nowait) replorigin_drop(roident) 43 | 44 | #define pgl_heap_attisnull(tup, attnum, tupledesc) \ 45 | heap_attisnull(tup, attnum) 46 | 47 | #ifndef rbtxn_has_catalog_changes 48 | #define rbtxn_has_catalog_changes(txn) (txn->has_catalog_changes) 49 | #endif 50 | 51 | #define IndexRelationGetNumberOfKeyAttributes(rel) RelationGetNumberOfAttributes(rel) 52 | 53 | /* deprecated in PG12, removed in PG13 */ 54 | #define table_open(r, l) heap_open(r, l) 55 | #define table_openrv(r, l) heap_openrv(r, l) 56 | #define table_openrv_extended(r, l, m) heap_openrv_extended(r, l, m) 57 | #define table_close(r, l) heap_close(r, l) 58 | 59 | /* 29c94e03c7 */ 60 | #define ExecStoreHeapTuple(tuple, slot, shouldFree) ExecStoreTuple(tuple, slot, InvalidBuffer, shouldFree) 61 | 62 | /* c2fe139c20 */ 63 | #define TableScanDesc HeapScanDesc 64 | #define table_beginscan(relation, snapshot, nkeys, keys) heap_beginscan(relation, snapshot, nkeys, keys) 65 | #define table_beginscan_catalog(relation, nkeys, keys) heap_beginscan_catalog(relation, nkeys, keys) 66 | #define table_endscan(scan) heap_endscan(scan) 67 | 68 | /* 578b229718e8 */ 69 | #define CreateTemplateTupleDesc(natts) \ 70 | CreateTemplateTupleDesc(natts, false) 71 | 72 | /* 2f9661311b83 */ 73 | #define CommandTag const char * 74 | #define QueryCompletion char 75 | 76 | /* 6aba63ef3e60 */ 77 | #define pg_plan_queries(querytrees, query_string, cursorOptions, boundParams) \ 78 | pg_plan_queries(querytrees, cursorOptions, boundParams) 79 | 80 | /* cd142e032ebd50ec7974b3633269477c2c72f1cc removed replorigin_drop */ 81 | inline static void 82 | replorigin_drop_by_name(char *name, bool missing_ok, bool nowait) 83 | { 84 | RepOriginId originid; 85 | 86 | originid = replorigin_by_name(name, missing_ok); 87 | if (originid != InvalidRepOriginId) 88 | replorigin_drop(originid, nowait); 89 | } 90 | 91 | #define PGLreplorigin_session_setup(node) replorigin_session_setup(node) 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /docs/pglogical.yml: -------------------------------------------------------------------------------- 1 | site_name: pglogical 2 | docs_dir: . 3 | site_dir: ../docs-site 4 | theme: 5 | name: 2qmkdocs 6 | one_page: true 7 | copyright: 'Copyright 2019 © 2ndQuadrant Limited' 8 | nav: 9 | - pglogical: README.md 10 | markdown_extensions: 11 | - codehilite: 12 | css_class: highlight 13 | guess_lang: true 14 | plugins: 15 | - search 16 | -------------------------------------------------------------------------------- /expected/bidirectional.out: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | \c :provider_dsn 4 | SELECT E'\'' || current_database() || E'\'' AS pubdb; 5 | pubdb 6 | -------------- 7 | 'regression' 8 | (1 row) 9 | 10 | \gset 11 | \c :provider_dsn 12 | DO $$ 13 | BEGIN 14 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 15 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 16 | END IF; 17 | END;$$; 18 | SELECT * FROM pglogical.create_subscription( 19 | subscription_name := 'test_bidirectional', 20 | provider_dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=super', 21 | synchronize_structure := false, 22 | synchronize_data := false, 23 | forward_origins := '{}'); 24 | create_subscription 25 | --------------------- 26 | 4269973126 27 | (1 row) 28 | 29 | BEGIN; 30 | SET LOCAL statement_timeout = '10s'; 31 | SELECT pglogical.wait_for_subscription_sync_complete('test_bidirectional'); 32 | wait_for_subscription_sync_complete 33 | ------------------------------------- 34 | 35 | (1 row) 36 | 37 | COMMIT; 38 | \c :subscriber_dsn 39 | SELECT pglogical.replicate_ddl_command($$ 40 | CREATE TABLE public.basic_dml ( 41 | id serial primary key, 42 | other integer, 43 | data text, 44 | something interval 45 | ); 46 | $$); 47 | replicate_ddl_command 48 | ----------------------- 49 | t 50 | (1 row) 51 | 52 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 53 | replication_set_add_table 54 | --------------------------- 55 | t 56 | (1 row) 57 | 58 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 59 | wait_slot_confirm_lsn 60 | ----------------------- 61 | 62 | (1 row) 63 | 64 | \c :provider_dsn 65 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 66 | replication_set_add_table 67 | --------------------------- 68 | t 69 | (1 row) 70 | 71 | -- check basic insert replication 72 | INSERT INTO basic_dml(other, data, something) 73 | VALUES (5, 'foo', '1 minute'::interval), 74 | (4, 'bar', '12 weeks'::interval), 75 | (3, 'baz', '2 years 1 hour'::interval), 76 | (2, 'qux', '8 months 2 days'::interval), 77 | (1, NULL, NULL); 78 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 79 | wait_slot_confirm_lsn 80 | ----------------------- 81 | 82 | (1 row) 83 | 84 | \c :subscriber_dsn 85 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 86 | id | other | data | something 87 | ----+-------+------+------------------ 88 | 1 | 5 | foo | @ 1 min 89 | 2 | 4 | bar | @ 84 days 90 | 3 | 3 | baz | @ 2 years 1 hour 91 | 4 | 2 | qux | @ 8 mons 2 days 92 | 5 | 1 | | 93 | (5 rows) 94 | 95 | UPDATE basic_dml SET other = id, something = something - '10 seconds'::interval WHERE id < 3; 96 | UPDATE basic_dml SET other = id, something = something + '10 seconds'::interval WHERE id > 3; 97 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 98 | wait_slot_confirm_lsn 99 | ----------------------- 100 | 101 | (1 row) 102 | 103 | \c :provider_dsn 104 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 105 | id | other | data | something 106 | ----+-------+------+------------------------- 107 | 1 | 1 | foo | @ 50 secs 108 | 2 | 2 | bar | @ 84 days -10 secs 109 | 3 | 3 | baz | @ 2 years 1 hour 110 | 4 | 4 | qux | @ 8 mons 2 days 10 secs 111 | 5 | 5 | | 112 | (5 rows) 113 | 114 | \c :provider_dsn 115 | \set VERBOSITY terse 116 | SELECT pglogical.replicate_ddl_command($$ 117 | DROP TABLE public.basic_dml CASCADE; 118 | $$); 119 | NOTICE: drop cascades to table public.basic_dml membership in replication set default 120 | replicate_ddl_command 121 | ----------------------- 122 | t 123 | (1 row) 124 | 125 | SELECT pglogical.drop_subscription('test_bidirectional'); 126 | drop_subscription 127 | ------------------- 128 | 1 129 | (1 row) 130 | 131 | SET client_min_messages = 'warning'; 132 | DROP EXTENSION IF EXISTS pglogical_origin; 133 | \c :subscriber_dsn 134 | \a 135 | SELECT slot_name FROM pg_replication_slots WHERE database = current_database(); 136 | slot_name 137 | (0 rows) 138 | SELECT count(*) FROM pg_stat_replication WHERE application_name = 'test_bidirectional'; 139 | count 140 | 0 141 | (1 row) 142 | -------------------------------------------------------------------------------- /expected/conflict_secondary_unique.out: -------------------------------------------------------------------------------- 1 | --PRIMARY KEY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | -- Test conflicts where a secondary unique constraint with a predicate exits, 6 | -- ensuring we don't generate false conflicts. 7 | SELECT pglogical.replicate_ddl_command($$ 8 | CREATE TABLE public.secondary_unique_pred ( 9 | a integer PRIMARY KEY, 10 | b integer NOT NULL, 11 | check_unique boolean NOT NULL 12 | ); 13 | 14 | CREATE UNIQUE INDEX ON public.secondary_unique_pred (b) WHERE (check_unique); 15 | $$); 16 | replicate_ddl_command 17 | ----------------------- 18 | t 19 | (1 row) 20 | 21 | SELECT * FROM pglogical.replication_set_add_table('default', 'secondary_unique_pred'); 22 | replication_set_add_table 23 | --------------------------- 24 | t 25 | (1 row) 26 | 27 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (1, 1, false); 28 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (2, 1, false); 29 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (3, 2, true); 30 | -- must fail 31 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (5, 2, true); 32 | ERROR: duplicate key value violates unique constraint "secondary_unique_pred_b_idx" 33 | DETAIL: Key (b)=(2) already exists. 34 | SELECT * FROM secondary_unique_pred ORDER BY a; 35 | a | b | check_unique 36 | ---+---+-------------- 37 | 1 | 1 | f 38 | 2 | 1 | f 39 | 3 | 2 | t 40 | (3 rows) 41 | 42 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 43 | wait_slot_confirm_lsn 44 | ----------------------- 45 | 46 | (1 row) 47 | 48 | \c :subscriber_dsn 49 | SELECT * FROM secondary_unique_pred ORDER BY a; 50 | a | b | check_unique 51 | ---+---+-------------- 52 | 1 | 1 | f 53 | 2 | 1 | f 54 | 3 | 2 | t 55 | (3 rows) 56 | 57 | \c :provider_dsn 58 | -- This line doesn't conflict on the provider. On the subscriber 59 | -- we must not detect a conflict on (b), since the existing local 60 | -- row matches (check_unique) but the new remote row doesn't. So 61 | -- this must get applied. 62 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (4, 2, false); 63 | SELECT * FROM secondary_unique_pred ORDER BY a; 64 | a | b | check_unique 65 | ---+---+-------------- 66 | 1 | 1 | f 67 | 2 | 1 | f 68 | 3 | 2 | t 69 | 4 | 2 | f 70 | (4 rows) 71 | 72 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 73 | wait_slot_confirm_lsn 74 | ----------------------- 75 | 76 | (1 row) 77 | 78 | \c :subscriber_dsn 79 | SELECT * FROM secondary_unique_pred ORDER BY a; 80 | a | b | check_unique 81 | ---+---+-------------- 82 | 1 | 1 | f 83 | 2 | 1 | f 84 | 3 | 2 | t 85 | 4 | 2 | f 86 | (4 rows) 87 | 88 | \c :provider_dsn 89 | \set VERBOSITY terse 90 | SELECT pglogical.replicate_ddl_command($$ 91 | DROP TABLE public.secondary_unique_pred CASCADE; 92 | $$); 93 | NOTICE: drop cascades to table public.secondary_unique_pred membership in replication set default 94 | replicate_ddl_command 95 | ----------------------- 96 | t 97 | (1 row) 98 | 99 | -------------------------------------------------------------------------------- /expected/copy.out: -------------------------------------------------------------------------------- 1 | --test COPY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | SELECT pglogical.replicate_ddl_command($$ 6 | CREATE TABLE public.x ( 7 | a serial primary key, 8 | b int, 9 | c text not null default 'stuff', 10 | d text, 11 | e text 12 | ); 13 | $$); 14 | replicate_ddl_command 15 | ----------------------- 16 | t 17 | (1 row) 18 | 19 | SELECT * FROM pglogical.replication_set_add_table('default', 'x'); 20 | replication_set_add_table 21 | --------------------------- 22 | t 23 | (1 row) 24 | 25 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 26 | wait_slot_confirm_lsn 27 | ----------------------- 28 | 29 | (1 row) 30 | 31 | COPY x (a, b, c, d, e) from stdin; 32 | COPY x (b, d) from stdin; 33 | COPY x (b, d) from stdin; 34 | COPY x (a, b, c, d, e) from stdin; 35 | SELECT * FROM x ORDER BY a; 36 | a | b | c | d | e 37 | -------+----+-------+---------+---- 38 | 1 | 1 | stuff | test_1 | 39 | 2 | 2 | stuff | test_2 | 40 | 3 | 3 | stuff | test_3 | 41 | 4 | 4 | stuff | test_4 | 42 | 5 | 5 | stuff | test_5 | 43 | 6 | 6 | stuff | test_6 | 44 | 7 | 7 | stuff | test_7 | 45 | 8 | 8 | stuff | test_8 | 46 | 9 | 9 | stuff | test_9 | 47 | 10 | 10 | stuff | test_10 | 48 | 11 | 11 | stuff | test_11 | 49 | 12 | 12 | stuff | test_12 | 50 | 13 | 13 | stuff | test_13 | 51 | 14 | 14 | stuff | test_14 | 52 | 15 | 15 | stuff | test_15 | 53 | 9999 | | \N | NN | 54 | 10000 | 21 | 31 | 41 | 51 55 | 10001 | 22 | 32 | 42 | 52 56 | 10002 | 23 | 33 | 43 | 53 57 | 10003 | 24 | 34 | 44 | 54 58 | 10004 | 25 | 35 | 45 | 55 59 | 10005 | 26 | 36 | 46 | 56 60 | (22 rows) 61 | 62 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 63 | wait_slot_confirm_lsn 64 | ----------------------- 65 | 66 | (1 row) 67 | 68 | \c :subscriber_dsn 69 | SELECT * FROM x ORDER BY a; 70 | a | b | c | d | e 71 | -------+----+-------+---------+---- 72 | 1 | 1 | stuff | test_1 | 73 | 2 | 2 | stuff | test_2 | 74 | 3 | 3 | stuff | test_3 | 75 | 4 | 4 | stuff | test_4 | 76 | 5 | 5 | stuff | test_5 | 77 | 6 | 6 | stuff | test_6 | 78 | 7 | 7 | stuff | test_7 | 79 | 8 | 8 | stuff | test_8 | 80 | 9 | 9 | stuff | test_9 | 81 | 10 | 10 | stuff | test_10 | 82 | 11 | 11 | stuff | test_11 | 83 | 12 | 12 | stuff | test_12 | 84 | 13 | 13 | stuff | test_13 | 85 | 14 | 14 | stuff | test_14 | 86 | 15 | 15 | stuff | test_15 | 87 | 9999 | | \N | NN | 88 | 10000 | 21 | 31 | 41 | 51 89 | 10001 | 22 | 32 | 42 | 52 90 | 10002 | 23 | 33 | 43 | 53 91 | 10003 | 24 | 34 | 44 | 54 92 | 10004 | 25 | 35 | 45 | 55 93 | 10005 | 26 | 36 | 46 | 56 94 | (22 rows) 95 | 96 | \c :provider_dsn 97 | \set VERBOSITY terse 98 | SELECT pglogical.replicate_ddl_command($$ 99 | DROP TABLE public.x CASCADE; 100 | $$); 101 | NOTICE: drop cascades to table public.x membership in replication set default 102 | replicate_ddl_command 103 | ----------------------- 104 | t 105 | (1 row) 106 | 107 | -------------------------------------------------------------------------------- /expected/drop.out: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | \c :provider_dsn 4 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider'); 5 | ERROR: cannot drop node "test_provider" because one or more replication slots for the node are still active 6 | HINT: drop the subscriptions connected to the node first 7 | SELECT plugin, slot_type, active FROM pg_replication_slots; 8 | plugin | slot_type | active 9 | ------------------+-----------+-------- 10 | pglogical_output | logical | t 11 | (1 row) 12 | 13 | SELECT count(*) FROM pg_stat_replication; 14 | count 15 | ------- 16 | 1 17 | (1 row) 18 | 19 | \c :subscriber_dsn 20 | SELECT * FROM pglogical.drop_subscription('test_subscription'); 21 | drop_subscription 22 | ------------------- 23 | 1 24 | (1 row) 25 | 26 | SELECT * FROM pglogical.drop_node(node_name := 'test_subscriber'); 27 | drop_node 28 | ----------- 29 | t 30 | (1 row) 31 | 32 | \c :provider_dsn 33 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider'); 34 | drop_node 35 | ----------- 36 | t 37 | (1 row) 38 | 39 | \c :subscriber_dsn 40 | DROP OWNED BY nonsuper, super CASCADE; 41 | \c :provider_dsn 42 | DROP OWNED BY nonsuper, super CASCADE; 43 | \c :provider1_dsn 44 | DROP OWNED BY nonsuper, super CASCADE; 45 | \c :orig_provider_dsn 46 | DROP OWNED BY nonsuper, super CASCADE; 47 | \c :subscriber_dsn 48 | SET client_min_messages = 'warning'; 49 | DROP ROLE IF EXISTS nonsuper, super; 50 | \c :provider_dsn 51 | SET client_min_messages = 'warning'; 52 | DROP ROLE IF EXISTS nonsuper, super; 53 | \c :provider1_dsn 54 | SET client_min_messages = 'warning'; 55 | DROP ROLE IF EXISTS nonsuper, super; 56 | \c :orig_provider_dsn 57 | SET client_min_messages = 'warning'; 58 | DROP ROLE IF EXISTS nonsuper, super; 59 | -------------------------------------------------------------------------------- /expected/foreign_key.out: -------------------------------------------------------------------------------- 1 | --FOREIGN KEY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | SELECT pglogical.replicate_ddl_command($$ 6 | CREATE TABLE public.f1k_products ( 7 | product_no integer PRIMARY KEY, 8 | product_id integer, 9 | name text, 10 | price numeric 11 | ); 12 | 13 | CREATE TABLE public.f1k_orders ( 14 | order_id integer, 15 | product_no integer REFERENCES public.f1k_products (product_no), 16 | quantity integer 17 | ); 18 | --pass 19 | $$); 20 | replicate_ddl_command 21 | ----------------------- 22 | t 23 | (1 row) 24 | 25 | SELECT * FROM pglogical.replication_set_add_table('default', 'f1k_products'); 26 | replication_set_add_table 27 | --------------------------- 28 | t 29 | (1 row) 30 | 31 | SELECT * FROM pglogical.replication_set_add_table('default_insert_only', 'f1k_orders'); 32 | replication_set_add_table 33 | --------------------------- 34 | t 35 | (1 row) 36 | 37 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 38 | wait_slot_confirm_lsn 39 | ----------------------- 40 | 41 | (1 row) 42 | 43 | INSERT into public.f1k_products VALUES (1, 1, 'product1', 1.20); 44 | INSERT into public.f1k_products VALUES (2, 2, 'product2', 2.40); 45 | INSERT into public.f1k_orders VALUES (300, 1, 4); 46 | INSERT into public.f1k_orders VALUES (22, 2, 14); 47 | INSERT into public.f1k_orders VALUES (23, 2, 24); 48 | INSERT into public.f1k_orders VALUES (24, 2, 40); 49 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 50 | wait_slot_confirm_lsn 51 | ----------------------- 52 | 53 | (1 row) 54 | 55 | \c :subscriber_dsn 56 | SELECT * FROM public.f1k_products; 57 | product_no | product_id | name | price 58 | ------------+------------+----------+------- 59 | 1 | 1 | product1 | 1.20 60 | 2 | 2 | product2 | 2.40 61 | (2 rows) 62 | 63 | SELECT * FROM public.f1k_orders; 64 | order_id | product_no | quantity 65 | ----------+------------+---------- 66 | 300 | 1 | 4 67 | 22 | 2 | 14 68 | 23 | 2 | 24 69 | 24 | 2 | 40 70 | (4 rows) 71 | 72 | \c :provider_dsn 73 | \set VERBOSITY terse 74 | SELECT pglogical.replicate_ddl_command($$ 75 | DROP TABLE public.f1k_orders CASCADE; 76 | DROP TABLE public.f1k_products CASCADE; 77 | $$); 78 | NOTICE: drop cascades to table public.f1k_orders membership in replication set default_insert_only 79 | NOTICE: drop cascades to table public.f1k_products membership in replication set default 80 | replicate_ddl_command 81 | ----------------------- 82 | t 83 | (1 row) 84 | 85 | -------------------------------------------------------------------------------- /expected/huge_tx.out: -------------------------------------------------------------------------------- 1 | -- test huge transactions 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | -- lots of small rows replication with DDL outside transaction 6 | SELECT pglogical.replicate_ddl_command($$ 7 | CREATE TABLE public.a_huge ( 8 | id integer primary key, 9 | id1 integer, 10 | data text default 'data', 11 | data1 text default 'data1' 12 | ); 13 | $$); 14 | replicate_ddl_command 15 | ----------------------- 16 | t 17 | (1 row) 18 | 19 | SELECT * FROM pglogical.replication_set_add_table('default', 'a_huge'); 20 | replication_set_add_table 21 | ----------------------- 22 | t 23 | (1 row) 24 | 25 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 26 | wait_slot_confirm_lsn 27 | ----------------------- 28 | 29 | (1 row) 30 | 31 | BEGIN; 32 | INSERT INTO public.a_huge VALUES (generate_series(1, 20000000), generate_series(1, 20000000)); 33 | COMMIT; 34 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 35 | wait_slot_confirm_lsn 36 | ----------------------- 37 | 38 | (1 row) 39 | 40 | \c :subscriber_dsn 41 | SELECT count(*) FROM a_huge; 42 | count 43 | ---------- 44 | 20000000 45 | (1 row) 46 | 47 | \dtS+ a_huge; 48 | List of relations 49 | Schema | Name | Type | Owner | Size | Description 50 | --------+--------+-------+----------+--------+------------- 51 | public | a_huge | table | postgres | 996 MB | 52 | (1 row) 53 | 54 | \c :provider_dsn 55 | -- lots of small rows replication with DDL within transaction 56 | BEGIN; 57 | SELECT pglogical.replicate_ddl_command($$ 58 | CREATE TABLE public.b_huge ( 59 | id integer primary key, 60 | id1 integer, 61 | data text default 'data', 62 | data1 text default 'data1' 63 | ); 64 | $$); 65 | replicate_ddl_command 66 | ----------------------- 67 | t 68 | (1 row) 69 | 70 | SELECT * FROM pglogical.replication_set_add_table('default', 'b_huge'); 71 | replication_set_add_table 72 | ----------------------- 73 | t 74 | (1 row) 75 | 76 | INSERT INTO public.b_huge VALUES (generate_series(1,20000000), generate_series(1,20000000)); 77 | COMMIT; 78 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 79 | wait_slot_confirm_lsn 80 | ----------------------- 81 | 82 | (1 row) 83 | 84 | \c :subscriber_dsn 85 | SELECT count(*) FROM b_huge; 86 | count 87 | ---------- 88 | 20000000 89 | (1 row) 90 | 91 | \dtS+ b_huge; 92 | List of relations 93 | Schema | Name | Type | Owner | Size | Description 94 | --------+--------+-------+----------+--------+------------- 95 | public | b_huge | table | postgres | 996 MB | 96 | (1 row) 97 | 98 | \c :provider_dsn 99 | \set VERBOSITY terse 100 | SELECT pglogical.replicate_ddl_command($$ 101 | DROP TABLE public.a_huge CASCADE; 102 | DROP TABLE public.b_huge CASCADE; 103 | $$); 104 | NOTICE: drop cascades to 1 other object 105 | NOTICE: drop cascades to 1 other object 106 | replicate_ddl_command 107 | ----------------------- 108 | t 109 | (1 row) 110 | 111 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 112 | wait_slot_confirm_lsn 113 | ----------------------- 114 | 115 | (1 row) 116 | 117 | -------------------------------------------------------------------------------- /expected/infofuncs.out: -------------------------------------------------------------------------------- 1 | DO $$ 2 | BEGIN 3 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 4 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 5 | END IF; 6 | END;$$; 7 | CREATE EXTENSION pglogical; 8 | SELECT pglogical.pglogical_max_proto_version(); 9 | pglogical_max_proto_version 10 | ----------------------------- 11 | 1 12 | (1 row) 13 | 14 | SELECT pglogical.pglogical_min_proto_version(); 15 | pglogical_min_proto_version 16 | ----------------------------- 17 | 1 18 | (1 row) 19 | 20 | -- test extension version 21 | SELECT pglogical.pglogical_version() = extversion 22 | FROM pg_extension 23 | WHERE extname = 'pglogical'; 24 | ?column? 25 | ---------- 26 | t 27 | (1 row) 28 | 29 | DROP EXTENSION pglogical; 30 | -- test upgrades 31 | DO $$ 32 | BEGIN 33 | IF version() ~ 'Postgres-XL' THEN 34 | CREATE EXTENSION IF NOT EXISTS pglogical; 35 | ELSE 36 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 37 | END IF; 38 | END; 39 | $$; 40 | ALTER EXTENSION pglogical UPDATE; 41 | SELECT pglogical.pglogical_version() = extversion 42 | FROM pg_extension 43 | WHERE extname = 'pglogical'; 44 | ?column? 45 | ---------- 46 | t 47 | (1 row) 48 | 49 | DROP EXTENSION pglogical; 50 | -------------------------------------------------------------------------------- /expected/init.out: -------------------------------------------------------------------------------- 1 | -- This should be done with pg_regress's --create-role option 2 | -- but it's blocked by bug 37906 3 | SELECT * FROM pglogical_regress_variables() 4 | \gset 5 | \c :provider_dsn 6 | SET client_min_messages = 'warning'; 7 | DROP USER IF EXISTS nonsuper; 8 | DROP USER IF EXISTS super; 9 | CREATE USER nonsuper WITH replication; 10 | CREATE USER super SUPERUSER; 11 | \c :subscriber_dsn 12 | SET client_min_messages = 'warning'; 13 | DROP USER IF EXISTS nonsuper; 14 | DROP USER IF EXISTS super; 15 | CREATE USER nonsuper WITH replication; 16 | CREATE USER super SUPERUSER; 17 | -- Can't because of bug 37906 18 | --GRANT ALL ON DATABASE regress TO nonsuper; 19 | --GRANT ALL ON DATABASE regress TO nonsuper; 20 | \c :provider_dsn 21 | GRANT ALL ON SCHEMA public TO nonsuper; 22 | DO $$ 23 | BEGIN 24 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') >= 1000 THEN 25 | CREATE OR REPLACE FUNCTION public.pg_current_xlog_location() RETURNS pg_lsn 26 | LANGUAGE SQL AS 'SELECT pg_current_wal_lsn()'; 27 | ALTER FUNCTION public.pg_current_xlog_location() OWNER TO super; 28 | END IF; 29 | END; $$; 30 | \c :subscriber_dsn 31 | GRANT ALL ON SCHEMA public TO nonsuper; 32 | SELECT E'\'' || current_database() || E'\'' AS subdb; 33 | subdb 34 | ------------ 35 | 'postgres' 36 | (1 row) 37 | 38 | \gset 39 | \c :provider_dsn 40 | SET client_min_messages = 'warning'; 41 | DO $$ 42 | BEGIN 43 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 44 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 45 | END IF; 46 | END;$$; 47 | DO $$ 48 | BEGIN 49 | IF version() ~ 'Postgres-XL' THEN 50 | CREATE EXTENSION IF NOT EXISTS pglogical; 51 | ELSE 52 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 53 | END IF; 54 | END; 55 | $$; 56 | ALTER EXTENSION pglogical UPDATE; 57 | \dx pglogical 58 | List of installed extensions 59 | Name | Version | Schema | Description 60 | -----------+---------+-----------+-------------------------------- 61 | pglogical | 2.4.5 | pglogical | PostgreSQL Logical Replication 62 | (1 row) 63 | 64 | SELECT * FROM pglogical.create_node(node_name := 'test_provider', dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super'); 65 | create_node 66 | ------------- 67 | 2689511696 68 | (1 row) 69 | 70 | \c :subscriber_dsn 71 | SET client_min_messages = 'warning'; 72 | DO $$ 73 | BEGIN 74 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 75 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 76 | END IF; 77 | END;$$; 78 | CREATE EXTENSION IF NOT EXISTS pglogical; 79 | SELECT * FROM pglogical.create_node(node_name := 'test_subscriber', dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=super'); 80 | create_node 81 | ------------- 82 | 1755434425 83 | (1 row) 84 | 85 | BEGIN; 86 | SELECT * FROM pglogical.create_subscription( 87 | subscription_name := 'test_subscription', 88 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super', 89 | synchronize_structure := true, 90 | forward_origins := '{}'); 91 | create_subscription 92 | --------------------- 93 | 3848008564 94 | (1 row) 95 | 96 | /* 97 | * Remove the function we added in preseed because otherwise the restore of 98 | * schema will fail. We do this in same transaction as create_subscription() 99 | * because the subscription process will only start on commit. 100 | */ 101 | DROP FUNCTION IF EXISTS public.pglogical_regress_variables(); 102 | COMMIT; 103 | BEGIN; 104 | SET LOCAL statement_timeout = '30s'; 105 | SELECT pglogical.wait_for_subscription_sync_complete('test_subscription'); 106 | wait_for_subscription_sync_complete 107 | ------------------------------------- 108 | 109 | (1 row) 110 | 111 | COMMIT; 112 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 113 | sync_kind | sync_subid | sync_nspname | sync_relname | ?column? 114 | -----------+------------+--------------+--------------+---------- 115 | f | 3848008564 | | | t 116 | (1 row) 117 | 118 | -- Make sure we see the slot and active connection 119 | \c :provider_dsn 120 | SELECT plugin, slot_type, active FROM pg_replication_slots; 121 | plugin | slot_type | active 122 | ------------------+-----------+-------- 123 | pglogical_output | logical | t 124 | (1 row) 125 | 126 | SELECT count(*) FROM pg_stat_replication; 127 | count 128 | ------- 129 | 1 130 | (1 row) 131 | 132 | -------------------------------------------------------------------------------- /expected/init_fail.out: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | \c :provider_dsn 4 | SET client_min_messages = 'warning'; 5 | DROP ROLE IF EXISTS nonreplica; 6 | CREATE USER nonreplica; 7 | DO $$ 8 | BEGIN 9 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 10 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 11 | END IF; 12 | END;$$; 13 | CREATE EXTENSION IF NOT EXISTS pglogical; 14 | GRANT ALL ON SCHEMA pglogical TO nonreplica; 15 | GRANT ALL ON ALL TABLES IN SCHEMA pglogical TO nonreplica; 16 | \c :subscriber_dsn 17 | SET client_min_messages = 'warning'; 18 | \set VERBOSITY terse 19 | DO $$ 20 | BEGIN 21 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 22 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 23 | END IF; 24 | END;$$; 25 | DO $$ 26 | BEGIN 27 | IF version() ~ 'Postgres-XL' THEN 28 | CREATE EXTENSION IF NOT EXISTS pglogical; 29 | ELSE 30 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 31 | END IF; 32 | END; 33 | $$; 34 | ALTER EXTENSION pglogical UPDATE; 35 | -- fail (local node not existing) 36 | SELECT * FROM pglogical.create_subscription( 37 | subscription_name := 'test_subscription', 38 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 39 | forward_origins := '{}'); 40 | ERROR: local pglogical node not found 41 | -- succeed 42 | SELECT * FROM pglogical.create_node(node_name := 'test_subscriber', dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=nonreplica'); 43 | create_node 44 | ------------- 45 | 1755434425 46 | (1 row) 47 | 48 | -- fail (can't connect to remote) 49 | DO $$ 50 | BEGIN 51 | SELECT * FROM pglogical.create_subscription( 52 | subscription_name := 'test_subscription', 53 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonexisting', 54 | forward_origins := '{}'); 55 | EXCEPTION 56 | WHEN OTHERS THEN 57 | RAISE EXCEPTION '%:%', split_part(SQLERRM, ':', 1), (regexp_matches(SQLERRM, '^.*( FATAL:.*role.*)$'))[1]; 58 | END; 59 | $$; 60 | ERROR: could not connect to the postgresql server: FATAL: role "nonexisting" does not exist 61 | 62 | -- fail (remote node not existing) 63 | SELECT * FROM pglogical.create_subscription( 64 | subscription_name := 'test_subscription', 65 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 66 | forward_origins := '{}'); 67 | ERROR: could not fetch remote node info: ERROR: local pglogical node not found 68 | 69 | 70 | \c :provider_dsn 71 | -- succeed 72 | SELECT * FROM pglogical.create_node(node_name := 'test_provider', dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica'); 73 | create_node 74 | ------------- 75 | 2689511696 76 | (1 row) 77 | 78 | \c :subscriber_dsn 79 | \set VERBOSITY terse 80 | -- fail (can't connect with replication connection to remote) 81 | DO $$ 82 | BEGIN 83 | SELECT * FROM pglogical.create_subscription( 84 | subscription_name := 'test_subscription', 85 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 86 | forward_origins := '{}'); 87 | EXCEPTION 88 | WHEN OTHERS THEN 89 | RAISE EXCEPTION '%', split_part(SQLERRM, ':', 1); 90 | END; 91 | $$; 92 | ERROR: could not connect to the postgresql server in replication mode 93 | -- cleanup 94 | SELECT * FROM pglogical.drop_node('test_subscriber'); 95 | drop_node 96 | ----------- 97 | t 98 | (1 row) 99 | 100 | DROP EXTENSION pglogical; 101 | \c :provider_dsn 102 | SELECT * FROM pglogical.drop_node('test_provider'); 103 | drop_node 104 | ----------- 105 | t 106 | (1 row) 107 | 108 | SET client_min_messages = 'warning'; 109 | DROP OWNED BY nonreplica; 110 | DROP ROLE IF EXISTS nonreplica; 111 | DROP EXTENSION pglogical; 112 | -------------------------------------------------------------------------------- /expected/interfaces.out: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | \c :provider_dsn 4 | CREATE USER super2 SUPERUSER; 5 | \c :subscriber_dsn 6 | SELECT * FROM pglogical.alter_node_add_interface('test_provider', 'super2', (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super2'); 7 | alter_node_add_interface 8 | -------------------------- 9 | 3319308158 10 | (1 row) 11 | 12 | SELECT * FROM pglogical.alter_subscription_interface('test_subscription', 'super2'); 13 | alter_subscription_interface 14 | ------------------------------ 15 | t 16 | (1 row) 17 | 18 | DO $$ 19 | BEGIN 20 | FOR i IN 1..100 LOOP 21 | IF EXISTS (SELECT 1 FROM pglogical.show_subscription_status() WHERE status != 'down') THEN 22 | EXIT; 23 | END IF; 24 | PERFORM pg_sleep(0.1); 25 | END LOOP; 26 | END;$$; 27 | SELECT pg_sleep(0.1); 28 | pg_sleep 29 | ---------- 30 | 31 | (1 row) 32 | 33 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 34 | subscription_name | status | provider_node | replication_sets | forward_origins 35 | -------------------+-------------+---------------+---------------------------------------+----------------- 36 | test_subscription | replicating | test_provider | {default,default_insert_only,ddl_sql} | 37 | (1 row) 38 | 39 | \c :provider_dsn 40 | SELECT plugin, slot_type, active FROM pg_replication_slots; 41 | plugin | slot_type | active 42 | ------------------+-----------+-------- 43 | pglogical_output | logical | t 44 | (1 row) 45 | 46 | SELECT usename FROM pg_stat_replication WHERE application_name = 'test_subscription'; 47 | usename 48 | --------- 49 | super2 50 | (1 row) 51 | 52 | \c :subscriber_dsn 53 | SELECT * FROM pglogical.alter_subscription_interface('test_subscription', 'test_provider'); 54 | alter_subscription_interface 55 | ------------------------------ 56 | t 57 | (1 row) 58 | 59 | DO $$ 60 | BEGIN 61 | FOR i IN 1..100 LOOP 62 | IF EXISTS (SELECT 1 FROM pglogical.show_subscription_status() WHERE status != 'down') THEN 63 | EXIT; 64 | END IF; 65 | PERFORM pg_sleep(0.1); 66 | END LOOP; 67 | END;$$; 68 | SELECT pg_sleep(0.1); 69 | pg_sleep 70 | ---------- 71 | 72 | (1 row) 73 | 74 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 75 | subscription_name | status | provider_node | replication_sets | forward_origins 76 | -------------------+-------------+---------------+---------------------------------------+----------------- 77 | test_subscription | replicating | test_provider | {default,default_insert_only,ddl_sql} | 78 | (1 row) 79 | 80 | \c :provider_dsn 81 | DROP USER super2; 82 | SELECT plugin, slot_type, active FROM pg_replication_slots; 83 | plugin | slot_type | active 84 | ------------------+-----------+-------- 85 | pglogical_output | logical | t 86 | (1 row) 87 | 88 | SELECT usename FROM pg_stat_replication WHERE application_name = 'test_subscription'; 89 | usename 90 | --------- 91 | super 92 | (1 row) 93 | 94 | -------------------------------------------------------------------------------- /expected/matview.out: -------------------------------------------------------------------------------- 1 | /* First test whether a table's replication set can be properly manipulated */ 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | SELECT pglogical.replicate_ddl_command($$ 6 | CREATE TABLE public.test_tbl(id serial primary key, data text); 7 | CREATE MATERIALIZED VIEW public.test_mv AS (SELECT * FROM public.test_tbl); 8 | $$); 9 | replicate_ddl_command 10 | ----------------------- 11 | t 12 | (1 row) 13 | 14 | SELECT * FROM pglogical.replication_set_add_all_tables('default', '{public}'); 15 | replication_set_add_all_tables 16 | -------------------------------- 17 | t 18 | (1 row) 19 | 20 | INSERT INTO test_tbl VALUES (1, 'a'); 21 | REFRESH MATERIALIZED VIEW test_mv; 22 | INSERT INTO test_tbl VALUES (2, 'b'); 23 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 24 | wait_slot_confirm_lsn 25 | ----------------------- 26 | 27 | (1 row) 28 | 29 | SELECT * FROM test_tbl ORDER BY id; 30 | id | data 31 | ----+------ 32 | 1 | a 33 | 2 | b 34 | (2 rows) 35 | 36 | SELECT * FROM test_mv ORDER BY id; 37 | id | data 38 | ----+------ 39 | 1 | a 40 | (1 row) 41 | 42 | \c :subscriber_dsn 43 | SELECT * FROM test_tbl ORDER BY id; 44 | id | data 45 | ----+------ 46 | 1 | a 47 | 2 | b 48 | (2 rows) 49 | 50 | SELECT * FROM test_mv ORDER BY id; 51 | id | data 52 | ----+------ 53 | (0 rows) 54 | 55 | \c :provider_dsn 56 | SELECT pglogical.replicate_ddl_command($$ 57 | CREATE UNIQUE INDEX ON public.test_mv(id); 58 | $$); 59 | replicate_ddl_command 60 | ----------------------- 61 | t 62 | (1 row) 63 | 64 | INSERT INTO test_tbl VALUES (3, 'c'); 65 | REFRESH MATERIALIZED VIEW CONCURRENTLY test_mv; 66 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 67 | wait_slot_confirm_lsn 68 | ----------------------- 69 | 70 | (1 row) 71 | 72 | INSERT INTO test_tbl VALUES (4, 'd'); 73 | SELECT pglogical.replicate_ddl_command($$ 74 | REFRESH MATERIALIZED VIEW public.test_mv; 75 | $$); 76 | replicate_ddl_command 77 | ----------------------- 78 | t 79 | (1 row) 80 | 81 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 82 | wait_slot_confirm_lsn 83 | ----------------------- 84 | 85 | (1 row) 86 | 87 | INSERT INTO test_tbl VALUES (5, 'e'); 88 | SELECT pglogical.replicate_ddl_command($$ 89 | REFRESH MATERIALIZED VIEW CONCURRENTLY public.test_mv; 90 | $$); 91 | replicate_ddl_command 92 | ----------------------- 93 | t 94 | (1 row) 95 | 96 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 97 | wait_slot_confirm_lsn 98 | ----------------------- 99 | 100 | (1 row) 101 | 102 | SELECT * FROM test_tbl ORDER BY id; 103 | id | data 104 | ----+------ 105 | 1 | a 106 | 2 | b 107 | 3 | c 108 | 4 | d 109 | 5 | e 110 | (5 rows) 111 | 112 | SELECT * FROM test_mv ORDER BY id; 113 | id | data 114 | ----+------ 115 | 1 | a 116 | 2 | b 117 | 3 | c 118 | 4 | d 119 | 5 | e 120 | (5 rows) 121 | 122 | \c :subscriber_dsn 123 | SELECT * FROM test_tbl ORDER BY id; 124 | id | data 125 | ----+------ 126 | 1 | a 127 | 2 | b 128 | 3 | c 129 | 4 | d 130 | 5 | e 131 | (5 rows) 132 | 133 | SELECT * FROM test_mv ORDER BY id; 134 | id | data 135 | ----+------ 136 | 1 | a 137 | 2 | b 138 | 3 | c 139 | 4 | d 140 | 5 | e 141 | (5 rows) 142 | 143 | \c :provider_dsn 144 | \set VERBOSITY terse 145 | SELECT pglogical.replicate_ddl_command($$ 146 | DROP TABLE public.test_tbl CASCADE; 147 | $$); 148 | NOTICE: drop cascades to materialized view public.test_mv 149 | NOTICE: drop cascades to table public.test_tbl membership in replication set default 150 | replicate_ddl_command 151 | ----------------------- 152 | t 153 | (1 row) 154 | 155 | -------------------------------------------------------------------------------- /expected/preseed.out: -------------------------------------------------------------------------------- 1 | -- Indirection for connection strings 2 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 3 | OUT orig_provider_dsn text, 4 | OUT provider_dsn text, 5 | OUT provider1_dsn text, 6 | OUT subscriber_dsn text 7 | ) RETURNS record LANGUAGE SQL AS $f$ 8 | SELECT 9 | current_setting('pglogical.orig_provider_dsn'), 10 | current_setting('pglogical.provider_dsn'), 11 | current_setting('pglogical.provider1_dsn'), 12 | current_setting('pglogical.subscriber_dsn') 13 | $f$; 14 | SELECT * FROM pglogical_regress_variables() 15 | \gset 16 | /* 17 | * Tests to ensure that objects/data that exists pre-clone is successfully 18 | * cloned. The results are checked, after the clone, in preseed_check.sql. 19 | */ 20 | \c :provider_dsn 21 | CREATE SEQUENCE some_local_seq; 22 | CREATE TABLE some_local_tbl(id serial primary key, key text unique not null, data text); 23 | INSERT INTO some_local_tbl(key, data) VALUES('key1', 'data1'); 24 | INSERT INTO some_local_tbl(key, data) VALUES('key2', NULL); 25 | INSERT INTO some_local_tbl(key, data) VALUES('key3', 'data3'); 26 | CREATE TABLE some_local_tbl1(id serial, key text unique not null, data text); 27 | INSERT INTO some_local_tbl1(key, data) VALUES('key1', 'data1'); 28 | INSERT INTO some_local_tbl1(key, data) VALUES('key2', NULL); 29 | INSERT INTO some_local_tbl1(key, data) VALUES('key3', 'data3'); 30 | CREATE TABLE some_local_tbl2(id serial, key text, data text); 31 | INSERT INTO some_local_tbl2(key, data) VALUES('key1', 'data1'); 32 | INSERT INTO some_local_tbl2(key, data) VALUES('key2', NULL); 33 | INSERT INTO some_local_tbl2(key, data) VALUES('key3', 'data3'); 34 | CREATE TABLE some_local_tbl3(id integer, key text, data text); 35 | INSERT INTO some_local_tbl3(key, data) VALUES('key1', 'data1'); 36 | INSERT INTO some_local_tbl3(key, data) VALUES('key2', NULL); 37 | INSERT INTO some_local_tbl3(key, data) VALUES('key3', 'data3'); 38 | /* 39 | * Make sure that the pglogical_regress_variables function exists both on 40 | * provider and subscriber since the original connection might have been 41 | * to completely different database. 42 | */ 43 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 44 | OUT orig_provider_dsn text, 45 | OUT provider_dsn text, 46 | OUT provider1_dsn text, 47 | OUT subscriber_dsn text 48 | ) RETURNS record LANGUAGE SQL AS $f$ 49 | SELECT 50 | current_setting('pglogical.orig_provider_dsn'), 51 | current_setting('pglogical.provider_dsn'), 52 | current_setting('pglogical.provider1_dsn'), 53 | current_setting('pglogical.subscriber_dsn') 54 | $f$; 55 | CREATE DATABASE regression1; 56 | CREATE DATABASE sourcedb; 57 | \c :orig_provider_dsn 58 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 59 | OUT orig_provider_dsn text, 60 | OUT provider_dsn text, 61 | OUT provider1_dsn text, 62 | OUT subscriber_dsn text 63 | ) RETURNS record LANGUAGE SQL AS $f$ 64 | SELECT 65 | current_setting('pglogical.orig_provider_dsn'), 66 | current_setting('pglogical.provider_dsn'), 67 | current_setting('pglogical.provider1_dsn'), 68 | current_setting('pglogical.subscriber_dsn') 69 | $f$; 70 | \c :provider1_dsn 71 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 72 | OUT orig_provider_dsn text, 73 | OUT provider_dsn text, 74 | OUT provider1_dsn text, 75 | OUT subscriber_dsn text 76 | ) RETURNS record LANGUAGE SQL AS $f$ 77 | SELECT 78 | current_setting('pglogical.orig_provider_dsn'), 79 | current_setting('pglogical.provider_dsn'), 80 | current_setting('pglogical.provider1_dsn'), 81 | current_setting('pglogical.subscriber_dsn') 82 | $f$; 83 | \c :subscriber_dsn 84 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 85 | OUT orig_provider_dsn text, 86 | OUT provider_dsn text, 87 | OUT provider1_dsn text, 88 | OUT subscriber_dsn text 89 | ) RETURNS record LANGUAGE SQL AS $f$ 90 | SELECT 91 | current_setting('pglogical.orig_provider_dsn'), 92 | current_setting('pglogical.provider_dsn'), 93 | current_setting('pglogical.provider1_dsn'), 94 | current_setting('pglogical.subscriber_dsn') 95 | $f$; 96 | -------------------------------------------------------------------------------- /expected/preseed_check.out: -------------------------------------------------------------------------------- 1 | -- Verify data from preseed.sql has correctly been cloned 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | \c :provider_dsn 5 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl'::regclass and attnum > 0 order by attnum; 6 | attname | attnotnull | attisdropped 7 | ---------+------------+-------------- 8 | id | t | f 9 | key | t | f 10 | data | f | f 11 | (3 rows) 12 | 13 | SELECT * FROM some_local_tbl ORDER BY id; 14 | id | key | data 15 | ----+------+------- 16 | 1 | key1 | data1 17 | 2 | key2 | 18 | 3 | key3 | data3 19 | (3 rows) 20 | 21 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl1'::regclass and attnum > 0 order by attnum; 22 | attname | attnotnull | attisdropped 23 | ---------+------------+-------------- 24 | id | t | f 25 | key | t | f 26 | data | f | f 27 | (3 rows) 28 | 29 | SELECT * FROM some_local_tbl1 ORDER BY id; 30 | id | key | data 31 | ----+------+------- 32 | 1 | key1 | data1 33 | 2 | key2 | 34 | 3 | key3 | data3 35 | (3 rows) 36 | 37 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl2'::regclass and attnum > 0 order by attnum; 38 | attname | attnotnull | attisdropped 39 | ---------+------------+-------------- 40 | id | t | f 41 | key | f | f 42 | data | f | f 43 | (3 rows) 44 | 45 | SELECT * FROM some_local_tbl2 ORDER BY id; 46 | id | key | data 47 | ----+------+------- 48 | 1 | key1 | data1 49 | 2 | key2 | 50 | 3 | key3 | data3 51 | (3 rows) 52 | 53 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl3'::regclass and attnum > 0 order by attnum; 54 | attname | attnotnull | attisdropped 55 | ---------+------------+-------------- 56 | id | f | f 57 | key | f | f 58 | data | f | f 59 | (3 rows) 60 | 61 | SELECT * FROM some_local_tbl3 ORDER BY id; 62 | id | key | data 63 | ----+------+------- 64 | | key1 | data1 65 | | key2 | 66 | | key3 | data3 67 | (3 rows) 68 | 69 | \c :subscriber_dsn 70 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl'::regclass and attnum > 0 order by attnum; 71 | attname | attnotnull | attisdropped 72 | ---------+------------+-------------- 73 | id | t | f 74 | key | t | f 75 | data | f | f 76 | (3 rows) 77 | 78 | SELECT * FROM some_local_tbl ORDER BY id; 79 | id | key | data 80 | ----+-----+------ 81 | (0 rows) 82 | 83 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl1'::regclass and attnum > 0 order by attnum; 84 | attname | attnotnull | attisdropped 85 | ---------+------------+-------------- 86 | id | t | f 87 | key | t | f 88 | data | f | f 89 | (3 rows) 90 | 91 | SELECT * FROM some_local_tbl1 ORDER BY id; 92 | id | key | data 93 | ----+-----+------ 94 | (0 rows) 95 | 96 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl2'::regclass and attnum > 0 order by attnum; 97 | attname | attnotnull | attisdropped 98 | ---------+------------+-------------- 99 | id | t | f 100 | key | f | f 101 | data | f | f 102 | (3 rows) 103 | 104 | SELECT * FROM some_local_tbl2 ORDER BY id; 105 | id | key | data 106 | ----+-----+------ 107 | (0 rows) 108 | 109 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl3'::regclass and attnum > 0 order by attnum; 110 | attname | attnotnull | attisdropped 111 | ---------+------------+-------------- 112 | id | f | f 113 | key | f | f 114 | data | f | f 115 | (3 rows) 116 | 117 | SELECT * FROM some_local_tbl3 ORDER BY id; 118 | id | key | data 119 | ----+-----+------ 120 | (0 rows) 121 | 122 | \c :provider_dsn 123 | \set VERBOSITY terse 124 | SELECT pglogical.replicate_ddl_command($$ 125 | DROP SEQUENCE public.some_local_seq; 126 | DROP TABLE public.some_local_tbl; 127 | DROP TABLE public.some_local_tbl1; 128 | DROP TABLE public.some_local_tbl2; 129 | DROP TABLE public.some_local_tbl3; 130 | $$); 131 | replicate_ddl_command 132 | ----------------------- 133 | t 134 | (1 row) 135 | 136 | -------------------------------------------------------------------------------- /expected/sequence.out: -------------------------------------------------------------------------------- 1 | -- like bt_index_check('pglogical.sequence_state', true) 2 | CREATE FUNCTION heapallindexed() RETURNS void AS $$ 3 | DECLARE 4 | count_seqscan int; 5 | count_idxscan int; 6 | BEGIN 7 | count_seqscan := (SELECT count(*) FROM pglogical.sequence_state); 8 | SET enable_seqscan = off; 9 | count_idxscan := (SELECT count(*) FROM pglogical.sequence_state); 10 | RESET enable_seqscan; 11 | IF count_seqscan <> count_idxscan THEN 12 | RAISE 'seqscan found % rows, but idxscan found % rows', 13 | count_seqscan, count_idxscan; 14 | END IF; 15 | END 16 | $$ LANGUAGE plpgsql; 17 | -- Replicate one sequence. 18 | CREATE SEQUENCE stress; 19 | SELECT * FROM pglogical.create_replication_set('stress_seq'); 20 | create_replication_set 21 | ------------------------ 22 | 2261733486 23 | (1 row) 24 | 25 | SELECT * FROM pglogical.replication_set_add_sequence('stress_seq', 'stress'); 26 | replication_set_add_sequence 27 | ------------------------------ 28 | t 29 | (1 row) 30 | 31 | SELECT pglogical.synchronize_sequence('stress'); 32 | synchronize_sequence 33 | ---------------------- 34 | t 35 | (1 row) 36 | 37 | SELECT heapallindexed(); 38 | heapallindexed 39 | ---------------- 40 | 41 | (1 row) 42 | 43 | -- Sync it 400 times in one transaction, to cross a pglogical.sequence_state 44 | -- page boundary and get a non-HOT update. 45 | DO $$ 46 | BEGIN 47 | FOR i IN 1..400 LOOP 48 | PERFORM pglogical.synchronize_sequence('stress'); 49 | END LOOP; 50 | END; 51 | $$; 52 | SELECT heapallindexed(); 53 | heapallindexed 54 | ---------------- 55 | 56 | (1 row) 57 | 58 | -------------------------------------------------------------------------------- /internals-doc/.gitignore: -------------------------------------------------------------------------------- 1 | protocol.html 2 | -------------------------------------------------------------------------------- /pglogical--1.0.0--1.0.1.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 2 | replication_sets text[] = '{default,default_insert_only,ddl_sql}', synchronize_structure boolean = true, 3 | synchronize_data boolean = true, forward_origins text[] = '{all}') 4 | RETURNS oid STRICT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_create_subscription'; 5 | 6 | DO $$ 7 | BEGIN 8 | IF (SELECT count(1) FROM pglogical.node) > 0 THEN 9 | SELECT * FROM pglogical.create_replication_set('ddl_sql', true, false, false, false); 10 | END IF; 11 | END; $$; 12 | 13 | UPDATE pglogical.subscription SET sub_replication_sets = array_append(sub_replication_sets, 'ddl_sql'); 14 | 15 | WITH applys AS ( 16 | SELECT sub_name FROM pglogical.subscription WHERE sub_enabled 17 | ), 18 | disable AS ( 19 | SELECT pglogical.alter_subscription_disable(sub_name, true) FROM applys 20 | ) 21 | SELECT pglogical.alter_subscription_enable(sub_name, true) FROM applys; 22 | -------------------------------------------------------------------------------- /pglogical--1.1.0--1.1.1.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--1.1.0--1.1.1.sql -------------------------------------------------------------------------------- /pglogical--1.1.1--1.1.2.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--1.1.1--1.1.2.sql -------------------------------------------------------------------------------- /pglogical--1.1.2--1.2.0.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--1.1.2--1.2.0.sql -------------------------------------------------------------------------------- /pglogical--1.2.0--1.2.1.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX local_node_onlyone; 2 | -------------------------------------------------------------------------------- /pglogical--1.2.1--1.2.2.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS local_node_onlyone; 2 | -------------------------------------------------------------------------------- /pglogical--1.2.2--2.0.0.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE pglogical.subscription ADD COLUMN sub_apply_delay interval NOT NULL DEFAULT '0'; 2 | 3 | CREATE TABLE pglogical.replication_set_seq ( 4 | set_id oid NOT NULL, 5 | set_seqoid regclass NOT NULL, 6 | PRIMARY KEY(set_id, set_seqoid) 7 | ) WITH (user_catalog_table=true); 8 | 9 | WITH seqs AS ( 10 | SELECT r.set_id, r.set_reloid 11 | FROM pg_class c 12 | JOIN replication_set_relation r ON (r.set_reloid = c.oid) 13 | WHERE c.relkind = 'S' 14 | ), inserted AS ( 15 | INSERT INTO replication_set_seq SELECT set_id, set_reloid FROM seqs 16 | ) 17 | DELETE FROM replication_set_relation r USING seqs s WHERE r.set_reloid = s.set_reloid; 18 | 19 | ALTER TABLE pglogical.replication_set_relation RENAME TO replication_set_table; 20 | ALTER TABLE pglogical.replication_set_table 21 | ADD COLUMN set_att_list text[], 22 | ADD COLUMN set_row_filter pg_node_tree; 23 | 24 | DROP FUNCTION pglogical.replication_set_add_table(set_name name, relation regclass, synchronize_data boolean); 25 | CREATE FUNCTION pglogical.replication_set_add_table(set_name name, relation regclass, synchronize_data boolean DEFAULT false, columns text[] DEFAULT NULL, row_filter text DEFAULT NULL) 26 | RETURNS boolean CALLED ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_replication_set_add_table'; 27 | 28 | DROP FUNCTION pglogical.alter_subscription_resynchronize_table(subscription_name name, relation regclass); 29 | CREATE FUNCTION pglogical.alter_subscription_resynchronize_table(subscription_name name, relation regclass, 30 | truncate boolean DEFAULT true) 31 | RETURNS boolean STRICT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_alter_subscription_resynchronize_table'; 32 | 33 | DROP FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 34 | replication_sets text[], synchronize_structure boolean, 35 | synchronize_data boolean, forward_origins text[]); 36 | CREATE FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 37 | replication_sets text[] = '{default,default_insert_only,ddl_sql}', synchronize_structure boolean = false, 38 | synchronize_data boolean = true, forward_origins text[] = '{all}', apply_delay interval DEFAULT '0') 39 | RETURNS oid STRICT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_create_subscription'; 40 | 41 | DROP VIEW pglogical.TABLES; 42 | CREATE VIEW pglogical.TABLES AS 43 | WITH set_relations AS ( 44 | SELECT s.set_name, r.set_reloid 45 | FROM pglogical.replication_set_table r, 46 | pglogical.replication_set s, 47 | pglogical.local_node n 48 | WHERE s.set_nodeid = n.node_id 49 | AND s.set_id = r.set_id 50 | ), 51 | user_tables AS ( 52 | SELECT r.oid, n.nspname, r.relname, r.relreplident 53 | FROM pg_catalog.pg_class r, 54 | pg_catalog.pg_namespace n 55 | WHERE r.relkind = 'r' 56 | AND r.relpersistence = 'p' 57 | AND n.oid = r.relnamespace 58 | AND n.nspname !~ '^pg_' 59 | AND n.nspname != 'information_schema' 60 | AND n.nspname != 'pglogical' 61 | ) 62 | SELECT r.oid AS relid, n.nspname, r.relname, s.set_name 63 | FROM pg_catalog.pg_namespace n, 64 | pg_catalog.pg_class r, 65 | set_relations s 66 | WHERE r.relkind = 'r' 67 | AND n.oid = r.relnamespace 68 | AND r.oid = s.set_reloid 69 | UNION 70 | SELECT t.oid AS relid, t.nspname, t.relname, NULL 71 | FROM user_tables t 72 | WHERE t.oid NOT IN (SELECT set_reloid FROM set_relations); 73 | 74 | CREATE FUNCTION pglogical.show_repset_table_info(relation regclass, repsets text[], OUT relid oid, OUT nspname text, 75 | OUT relname text, OUT att_list text[], OUT has_row_filter boolean) 76 | RETURNS record STRICT STABLE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_show_repset_table_info'; 77 | 78 | CREATE FUNCTION pglogical.table_data_filtered(reltyp anyelement, relation regclass, repsets text[]) 79 | RETURNS SETOF anyelement CALLED ON NULL INPUT STABLE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_table_data_filtered'; 80 | 81 | CREATE TABLE pglogical.depend ( 82 | classid oid NOT NULL, 83 | objid oid NOT NULL, 84 | objsubid integer NOT NULL, 85 | 86 | refclassid oid NOT NULL, 87 | refobjid oid NOT NULL, 88 | refobjsubid integer NOT NULL, 89 | 90 | deptype "char" NOT NULL 91 | ) WITH (user_catalog_table=true); 92 | 93 | DROP EVENT TRIGGER IF EXISTS pglogical_truncate_trigger_add; 94 | DROP EVENT TRIGGER IF EXISTS pglogical_dependency_check_trigger; 95 | DROP FUNCTION IF EXISTS pglogical.truncate_trigger_add(); 96 | DROP FUNCTION IF EXISTS pglogical.dependency_check_trigger(); 97 | DROP FUNCTION IF EXISTS pglogical_hooks_setup(internal); 98 | -------------------------------------------------------------------------------- /pglogical--2.0.0--2.0.1.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.0.0--2.0.1.sql -------------------------------------------------------------------------------- /pglogical--2.0.0--2.1.0.sql: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION 2 | pglogical.wait_slot_confirm_lsn(slotname name, target pg_lsn) 3 | RETURNS void LANGUAGE c AS 'pglogical','pglogical_wait_slot_confirm_lsn'; 4 | 5 | -------------------------------------------------------------------------------- /pglogical--2.0.1--2.1.0.sql: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION 2 | pglogical.wait_slot_confirm_lsn(slotname name, target pg_lsn) 3 | RETURNS void LANGUAGE c AS 'pglogical','pglogical_wait_slot_confirm_lsn'; 4 | 5 | -------------------------------------------------------------------------------- /pglogical--2.1.0--2.1.1.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE pglogical.local_sync_status ADD COLUMN sync_statuslsn pg_lsn NULL; 2 | UPDATE pglogical.local_sync_status SET sync_statuslsn = '0/0'; 3 | ALTER TABLE pglogical.local_sync_status ALTER COLUMN sync_statuslsn SET NOT NULL; 4 | -------------------------------------------------------------------------------- /pglogical--2.1.1--2.2.0.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.1.1--2.2.0.sql -------------------------------------------------------------------------------- /pglogical--2.2.0--2.2.1.sql: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION pglogical.wait_for_subscription_sync_complete(subscription_name name) 2 | RETURNS void RETURNS NULL ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_wait_for_subscription_sync_complete'; 3 | 4 | CREATE FUNCTION pglogical.wait_for_table_sync_complete(subscription_name name, relation regclass) 5 | RETURNS void RETURNS NULL ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_wait_for_table_sync_complete'; 6 | 7 | CREATE FUNCTION pglogical.xact_commit_timestamp_origin("xid" xid, OUT "timestamp" timestamptz, OUT "roident" oid) 8 | RETURNS record RETURNS NULL ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_xact_commit_timestamp_origin'; 9 | -------------------------------------------------------------------------------- /pglogical--2.2.1--2.2.2.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.2.1--2.2.2.sql -------------------------------------------------------------------------------- /pglogical--2.2.2--2.3.0.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE pglogical.subscription ADD COLUMN sub_force_text_transfer boolean NOT NULL DEFAULT 'f'; 2 | 3 | CREATE FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 4 | replication_sets text[] = '{default,default_insert_only,ddl_sql}', synchronize_structure text = 'none', 5 | synchronize_data boolean = true, forward_origins text[] = '{all}', apply_delay interval DEFAULT '0', 6 | force_text_transfer boolean = false) 7 | RETURNS oid STRICT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_create_subscription'; 8 | 9 | DROP FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 10 | replication_sets text[], synchronize_structure boolean, 11 | synchronize_data boolean, forward_origins text[], apply_delay interval); 12 | 13 | ALTER TABLE pglogical.replication_set_table 14 | ADD COLUMN set_nsptarget name NOT NULL 15 | , ADD COLUMN set_reltarget name NOT NULL; 16 | ALTER TABLE pglogical.replication_set_seq 17 | ADD COLUMN set_nsptarget name NOT NULL 18 | , ADD COLUMN set_seqtarget name NOT NULL; 19 | DROP FUNCTION pglogical.show_repset_table_info(regclass, text[]); 20 | CREATE FUNCTION pglogical.show_repset_table_info(relation regclass, repsets text[], OUT relid oid, OUT nspname text, 21 | OUT relname text, OUT att_list text[], OUT has_row_filter boolean, OUT nsptarget text, OUT reltarget text) 22 | RETURNS record STRICT STABLE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_show_repset_table_info'; 23 | 24 | CREATE FUNCTION pglogical.show_repset_table_info_by_target(nsptarget name, reltarget name, repsets text[], OUT relid oid, OUT nspname text, 25 | OUT relname text, OUT att_list text[], OUT has_row_filter boolean, OUT nsptarget text, OUT reltarget text) 26 | RETURNS SETOF record STRICT STABLE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_show_repset_table_info_by_target'; 27 | 28 | UPDATE pglogical.replication_set_table 29 | SET set_nsptarget = n.nspname 30 | , set_reltarget = c.relname 31 | FROM pg_class c 32 | JOIN pg_namespace n ON n.oid = c.relnamespace 33 | WHERE c.oid = set_reloid; 34 | 35 | UPDATE pglogical.replication_set_seq 36 | SET set_nsptarget = n.nspname 37 | , set_seqtarget = c.relname 38 | FROM pg_class c 39 | JOIN pg_namespace n ON n.oid = c.relnamespace 40 | WHERE c.oid = set_seqoid; 41 | 42 | -- a VACUUM FULL of the table above would be nice here. 43 | 44 | DROP FUNCTION pglogical.replication_set_add_table(name, regclass, boolean, 45 | text[], text); 46 | CREATE FUNCTION pglogical.replication_set_add_table(set_name name, relation regclass, synchronize_data boolean DEFAULT false, 47 | columns text[] DEFAULT NULL, row_filter text DEFAULT NULL, nsptarget name DEFAULT NULL, reltarget name DEFAULT NULL) 48 | RETURNS boolean CALLED ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_replication_set_add_table'; 49 | 50 | DROP FUNCTION pglogical.replication_set_add_sequence(name, regclass, boolean); 51 | CREATE FUNCTION pglogical.replication_set_add_sequence(set_name name, relation regclass, synchronize_data boolean DEFAULT false, nsptarget name DEFAULT NULL, reltarget name DEFAULT NULL) 52 | RETURNS boolean VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_replication_set_add_sequence'; 53 | -------------------------------------------------------------------------------- /pglogical--2.2.2--2.3.1.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE pglogical.subscription ADD COLUMN sub_force_text_transfer boolean NOT NULL DEFAULT 'f'; 2 | -------------------------------------------------------------------------------- /pglogical--2.3.0--2.3.1.sql: -------------------------------------------------------------------------------- 1 | DROP FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 2 | replication_sets text[], synchronize_structure text, synchronize_data boolean, 3 | forward_origins text[], apply_delay interval, force_text_transfer boolean); 4 | 5 | CREATE FUNCTION pglogical.create_subscription(subscription_name name, provider_dsn text, 6 | replication_sets text[] = '{default,default_insert_only,ddl_sql}', synchronize_structure boolean = false, 7 | synchronize_data boolean = true, forward_origins text[] = '{all}', apply_delay interval DEFAULT '0', 8 | force_text_transfer boolean = false) 9 | RETURNS oid STRICT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_create_subscription'; 10 | 11 | DROP FUNCTION pglogical.show_repset_table_info(regclass, text[]); 12 | CREATE FUNCTION pglogical.show_repset_table_info(relation regclass, repsets text[], OUT relid oid, OUT nspname text, 13 | OUT relname text, OUT att_list text[], OUT has_row_filter boolean) 14 | RETURNS record STRICT STABLE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_show_repset_table_info'; 15 | 16 | DROP FUNCTION pglogical.show_repset_table_info_by_target(name, name, text[]); 17 | 18 | DROP FUNCTION pglogical.replication_set_add_table(name, regclass, boolean, text[], text, name, name); 19 | CREATE FUNCTION pglogical.replication_set_add_table(set_name name, relation regclass, synchronize_data boolean DEFAULT false, 20 | columns text[] DEFAULT NULL, row_filter text DEFAULT NULL) 21 | RETURNS boolean CALLED ON NULL INPUT VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_replication_set_add_table'; 22 | 23 | DROP FUNCTION pglogical.replication_set_add_sequence(name, regclass, boolean, name, name); 24 | CREATE FUNCTION pglogical.replication_set_add_sequence(set_name name, relation regclass, synchronize_data boolean DEFAULT false) 25 | RETURNS boolean VOLATILE LANGUAGE c AS 'MODULE_PATHNAME', 'pglogical_replication_set_add_sequence'; 26 | 27 | 28 | ALTER TABLE pglogical.replication_set_table RENAME TO replication_set_table_old; 29 | 30 | CREATE TABLE pglogical.replication_set_table ( 31 | set_id oid NOT NULL, 32 | set_reloid regclass NOT NULL, 33 | set_att_list text[], 34 | set_row_filter pg_node_tree, 35 | PRIMARY KEY(set_id, set_reloid) 36 | ) WITH (user_catalog_table=true); 37 | 38 | INSERT INTO pglogical.replication_set_table 39 | SELECT set_id, set_reloid, set_att_list, set_row_filter FROM pglogical.replication_set_table_old; 40 | 41 | DROP VIEW pglogical.tables; 42 | DROP TABLE pglogical.replication_set_table_old; 43 | 44 | ALTER TABLE pglogical.replication_set_seq RENAME TO replication_set_seq_old; 45 | 46 | CREATE TABLE pglogical.replication_set_seq ( 47 | set_id oid NOT NULL, 48 | set_seqoid regclass NOT NULL, 49 | PRIMARY KEY(set_id, set_seqoid) 50 | ) WITH (user_catalog_table=true); 51 | 52 | INSERT INTO pglogical.replication_set_seq 53 | SELECT set_id, set_seqoid FROM pglogical.replication_set_seq_old; 54 | 55 | DROP TABLE pglogical.replication_set_seq_old; 56 | 57 | 58 | -- must recreate on top of new replication_set_table 59 | CREATE VIEW pglogical.TABLES AS 60 | WITH set_relations AS ( 61 | SELECT s.set_name, r.set_reloid 62 | FROM pglogical.replication_set_table r, 63 | pglogical.replication_set s, 64 | pglogical.local_node n 65 | WHERE s.set_nodeid = n.node_id 66 | AND s.set_id = r.set_id 67 | ), 68 | user_tables AS ( 69 | SELECT r.oid, n.nspname, r.relname, r.relreplident 70 | FROM pg_catalog.pg_class r, 71 | pg_catalog.pg_namespace n 72 | WHERE r.relkind = 'r' 73 | AND r.relpersistence = 'p' 74 | AND n.oid = r.relnamespace 75 | AND n.nspname !~ '^pg_' 76 | AND n.nspname != 'information_schema' 77 | AND n.nspname != 'pglogical' 78 | ) 79 | SELECT r.oid AS relid, n.nspname, r.relname, s.set_name 80 | FROM pg_catalog.pg_namespace n, 81 | pg_catalog.pg_class r, 82 | set_relations s 83 | WHERE r.relkind = 'r' 84 | AND n.oid = r.relnamespace 85 | AND r.oid = s.set_reloid 86 | UNION 87 | SELECT t.oid AS relid, t.nspname, t.relname, NULL 88 | FROM user_tables t 89 | WHERE t.oid NOT IN (SELECT set_reloid FROM set_relations); 90 | -------------------------------------------------------------------------------- /pglogical--2.3.1--2.3.2.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.3.1--2.3.2.sql -------------------------------------------------------------------------------- /pglogical--2.3.2--2.3.3.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.3.2--2.3.3.sql -------------------------------------------------------------------------------- /pglogical--2.3.3--2.3.4.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.3.3--2.3.4.sql -------------------------------------------------------------------------------- /pglogical--2.3.4--2.4.0.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.3.4--2.4.0.sql -------------------------------------------------------------------------------- /pglogical--2.4.0--2.4.1.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.4.0--2.4.1.sql -------------------------------------------------------------------------------- /pglogical--2.4.1--2.4.2.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.4.1--2.4.2.sql -------------------------------------------------------------------------------- /pglogical--2.4.2--2.4.3.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.4.2--2.4.3.sql -------------------------------------------------------------------------------- /pglogical--2.4.3--2.4.4.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.4.3--2.4.4.sql -------------------------------------------------------------------------------- /pglogical--2.4.4--2.4.5.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2ndQuadrant/pglogical/ba943bdf49b465d2d41b9c6438b6d47f0a3f4fbc/pglogical--2.4.4--2.4.5.sql -------------------------------------------------------------------------------- /pglogical.control.in: -------------------------------------------------------------------------------- 1 | # pglogical extension 2 | comment = 'PostgreSQL Logical Replication' 3 | default_version = '__PGLOGICAL_VERSION__' 4 | module_pathname = '$libdir/pglogical' 5 | relocatable = false 6 | __REQUIRES__ 7 | schema = pglogical 8 | -------------------------------------------------------------------------------- /pglogical.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical.h 4 | * pglogical replication plugin 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_H 14 | #define PGLOGICAL_H 15 | 16 | #include "storage/s_lock.h" 17 | #include "postmaster/bgworker.h" 18 | #include "utils/array.h" 19 | #include "access/xlogdefs.h" 20 | #include "executor/executor.h" 21 | #include "miscadmin.h" 22 | 23 | #include "libpq-fe.h" 24 | 25 | #include "pglogical_fe.h" 26 | #include "pglogical_node.h" 27 | 28 | #include "pglogical_compat.h" 29 | 30 | #define PGLOGICAL_VERSION "2.4.5" 31 | #define PGLOGICAL_VERSION_NUM 20405 32 | 33 | #define PGLOGICAL_MIN_PROTO_VERSION_NUM 1 34 | #define PGLOGICAL_MAX_PROTO_VERSION_NUM 1 35 | 36 | #define EXTENSION_NAME "pglogical" 37 | 38 | #define REPLICATION_ORIGIN_ALL "all" 39 | 40 | #if PG_VERSION_NUM >= 90500 41 | #define HAVE_REPLICATION_ORIGINS 42 | #endif 43 | 44 | extern bool pglogical_synchronous_commit; 45 | extern char *pglogical_temp_directory; 46 | extern bool pglogical_use_spi; 47 | extern bool pglogical_batch_inserts; 48 | extern char *pglogical_extra_connection_options; 49 | 50 | #if PG_VERSION_NUM >= 150000 51 | extern shmem_request_hook_type prev_shmem_request_hook; 52 | #endif 53 | 54 | extern char *shorten_hash(const char *str, int maxlen); 55 | 56 | extern List *textarray_to_list(ArrayType *textarray); 57 | extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); 58 | 59 | extern Oid get_pglogical_table_oid(const char *table); 60 | 61 | extern void pglogical_execute_sql_command(char *cmdstr, char *role, 62 | bool isTopLevel); 63 | 64 | extern PGconn *pglogical_connect(const char *connstring, const char *connname, 65 | const char *suffix); 66 | extern PGconn *pglogical_connect_replica(const char *connstring, 67 | const char *connname, 68 | const char *suffix); 69 | extern void pglogical_identify_system(PGconn *streamConn, uint64* sysid, 70 | TimeLineID *timeline, XLogRecPtr *xlogpos, 71 | Name *dbname); 72 | extern void pglogical_start_replication(PGconn *streamConn, 73 | const char *slot_name, 74 | XLogRecPtr start_pos, 75 | const char *forward_origins, 76 | const char *replication_sets, 77 | const char *replicate_only_table, 78 | bool force_text_transfer); 79 | 80 | extern void pglogical_manage_extension(void); 81 | 82 | extern void apply_work(PGconn *streamConn); 83 | 84 | extern bool synchronize_sequences(void); 85 | extern void synchronize_sequence(Oid seqoid); 86 | extern void pglogical_create_sequence_state_record(Oid seqoid); 87 | extern void pglogical_drop_sequence_state_record(Oid seqoid); 88 | extern int64 sequence_get_last_value(Oid seqoid); 89 | 90 | extern bool in_pglogical_replicate_ddl_command; 91 | 92 | #include "utils/memdebug.h" 93 | 94 | /* 95 | * PostgreSQL exposes stubs for some Valgrind macros, but there are some 96 | * others we use that aren't supported by Pg proper yet. 97 | */ 98 | #ifndef USE_VALGRIND 99 | #define VALGRIND_CHECK_VALUE_IS_DEFINED(v) do{} while(0) 100 | #define VALGRIND_DO_LEAK_CHECK do{} while(0) 101 | #define VALGRIND_DO_ADDED_LEAK_CHECK do{} while(0) 102 | #define VALGRIND_DO_CHANGED_LEAK_CHECK do{} while(0) 103 | #define VALGRIND_DO_QUICK_LEAK_CHECK do{} while(0) 104 | #define VALGRIND_DISABLE_ERROR_REPORTING do {} while (0) 105 | #define VALGRIND_ENABLE_ERROR_REPORTING do {} while (0) 106 | 107 | /* 108 | * Gives us some error checking when no-op'd. pglogical uses this to report 109 | * the worker type, etc, prefixed by PGLOGICAL:, in the Valgrind logs. We 110 | * need to stub it out if we aren't using valgrind. 111 | */ 112 | pg_attribute_printf(1, 2) pg_attribute_unused() static inline void VALGRIND_PRINTF(const char *format, ...) {} 113 | 114 | #endif 115 | 116 | #endif /* PGLOGICAL_H */ 117 | -------------------------------------------------------------------------------- /pglogical_apply.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_apply.h 4 | * pglogical apply functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_apply.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_APPLY_H 14 | #define PGLOGICAL_APPLY_H 15 | 16 | #include "pglogical_relcache.h" 17 | #include "pglogical_proto_native.h" 18 | 19 | typedef void (*pglogical_apply_begin_fn) (void); 20 | typedef void (*pglogical_apply_commit_fn) (void); 21 | 22 | typedef void (*pglogical_apply_insert_fn) (PGLogicalRelation *rel, 23 | PGLogicalTupleData *newtup); 24 | typedef void (*pglogical_apply_update_fn) (PGLogicalRelation *rel, 25 | PGLogicalTupleData *oldtup, 26 | PGLogicalTupleData *newtup); 27 | typedef void (*pglogical_apply_delete_fn) (PGLogicalRelation *rel, 28 | PGLogicalTupleData *oldtup); 29 | 30 | typedef bool (*pglogical_apply_can_mi_fn) (PGLogicalRelation *rel); 31 | typedef void (*pglogical_apply_mi_add_tuple_fn) (PGLogicalRelation *rel, 32 | PGLogicalTupleData *tup); 33 | typedef void (*pglogical_apply_mi_finish_fn) (PGLogicalRelation *rel); 34 | 35 | #endif /* PGLOGICAL_APPLY_H */ 36 | -------------------------------------------------------------------------------- /pglogical_apply_heap.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_apply_heap.h 4 | * pglogical apply functions using heap api 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_apply_heap.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_APPLY_HEAP_H 14 | #define PGLOGICAL_APPLY_HEAP_H 15 | 16 | #include "pglogical_relcache.h" 17 | #include "pglogical_proto_native.h" 18 | 19 | extern void pglogical_apply_heap_begin(void); 20 | extern void pglogical_apply_heap_commit(void); 21 | 22 | extern void pglogical_apply_heap_insert(PGLogicalRelation *rel, 23 | PGLogicalTupleData *newtup); 24 | extern void pglogical_apply_heap_update(PGLogicalRelation *rel, 25 | PGLogicalTupleData *oldtup, 26 | PGLogicalTupleData *newtup); 27 | extern void pglogical_apply_heap_delete(PGLogicalRelation *rel, 28 | PGLogicalTupleData *oldtup); 29 | 30 | bool pglogical_apply_heap_can_mi(PGLogicalRelation *rel); 31 | void pglogical_apply_heap_mi_add_tuple(PGLogicalRelation *rel, 32 | PGLogicalTupleData *tup); 33 | void pglogical_apply_heap_mi_finish(PGLogicalRelation *rel); 34 | 35 | #endif /* PGLOGICAL_APPLY_HEAP_H */ 36 | -------------------------------------------------------------------------------- /pglogical_apply_spi.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_apply_spi.h 4 | * pglogical apply functions using SPI 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_apply_spi.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_APPLY_SPI_H 14 | #define PGLOGICAL_APPLY_SPI_H 15 | 16 | #include "pglogical_relcache.h" 17 | #include "pglogical_proto_native.h" 18 | 19 | extern void pglogical_apply_spi_begin(void); 20 | extern void pglogical_apply_spi_commit(void); 21 | 22 | extern void pglogical_apply_spi_insert(PGLogicalRelation *rel, 23 | PGLogicalTupleData *newtup); 24 | extern void pglogical_apply_spi_update(PGLogicalRelation *rel, 25 | PGLogicalTupleData *oldtup, 26 | PGLogicalTupleData *newtup); 27 | extern void pglogical_apply_spi_delete(PGLogicalRelation *rel, 28 | PGLogicalTupleData *oldtup); 29 | 30 | extern bool pglogical_apply_spi_can_mi(PGLogicalRelation *rel); 31 | extern void pglogical_apply_spi_mi_add_tuple(PGLogicalRelation *rel, 32 | PGLogicalTupleData *tup); 33 | extern void pglogical_apply_spi_mi_finish(PGLogicalRelation *rel); 34 | 35 | #endif /* PGLOGICAL_APPLY_SPI_H */ 36 | -------------------------------------------------------------------------------- /pglogical_conflict.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_conflict.h 4 | * pglogical conflict detection and resolution 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_conflict.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_CONGLICT_H 14 | #define PGLOGICAL_CONGLICT_H 15 | 16 | #include "nodes/execnodes.h" 17 | 18 | #include "replication/origin.h" 19 | 20 | #include "utils/guc.h" 21 | 22 | #include "pglogical_proto_native.h" 23 | 24 | typedef enum PGLogicalConflictResolution 25 | { 26 | PGLogicalResolution_ApplyRemote, 27 | PGLogicalResolution_KeepLocal, 28 | PGLogicalResolution_Skip 29 | } PGLogicalConflictResolution; 30 | 31 | typedef enum 32 | { 33 | PGLOGICAL_RESOLVE_ERROR, 34 | PGLOGICAL_RESOLVE_APPLY_REMOTE, 35 | PGLOGICAL_RESOLVE_KEEP_LOCAL, 36 | PGLOGICAL_RESOLVE_LAST_UPDATE_WINS, 37 | PGLOGICAL_RESOLVE_FIRST_UPDATE_WINS 38 | } PGLogicalResolveOption; 39 | 40 | extern int pglogical_conflict_resolver; 41 | extern int pglogical_conflict_log_level; 42 | 43 | typedef enum PGLogicalConflictType 44 | { 45 | CONFLICT_INSERT_INSERT, 46 | CONFLICT_UPDATE_UPDATE, 47 | CONFLICT_UPDATE_DELETE, 48 | CONFLICT_DELETE_DELETE 49 | } PGLogicalConflictType; 50 | 51 | extern bool pglogical_tuple_find_replidx(ResultRelInfo *relinfo, 52 | PGLogicalTupleData *tuple, 53 | TupleTableSlot *oldslot, 54 | Oid *idxrelid); 55 | 56 | extern Oid pglogical_tuple_find_conflict(ResultRelInfo *relinfo, 57 | PGLogicalTupleData *tuple, 58 | TupleTableSlot *oldslot); 59 | 60 | extern bool get_tuple_origin(HeapTuple local_tuple, TransactionId *xmin, 61 | RepOriginId *local_origin, TimestampTz *local_ts); 62 | 63 | extern bool try_resolve_conflict(Relation rel, HeapTuple localtuple, 64 | HeapTuple remotetuple, HeapTuple *resulttuple, 65 | PGLogicalConflictResolution *resolution); 66 | 67 | 68 | extern void pglogical_report_conflict(PGLogicalConflictType conflict_type, 69 | PGLogicalRelation *rel, 70 | HeapTuple localtuple, 71 | PGLogicalTupleData *oldkey, 72 | HeapTuple remotetuple, 73 | HeapTuple applytuple, 74 | PGLogicalConflictResolution resolution, 75 | TransactionId local_tuple_xid, 76 | bool found_local_origin, 77 | RepOriginId local_tuple_origin, 78 | TimestampTz local_tuple_timestamp, 79 | Oid conflict_idx_id, 80 | bool has_before_triggers); 81 | 82 | extern bool pglogical_conflict_resolver_check_hook(int *newval, void **extra, 83 | GucSource source); 84 | 85 | #endif /* PGLOGICAL_CONGLICT_H */ 86 | -------------------------------------------------------------------------------- /pglogical_dependency.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_dependency.h 4 | * Dependency handling 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_dependency.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_DEPENDENCY_H 14 | #define PGLOGICAL_DEPENDENCY_H 15 | 16 | extern void pglogical_recordDependencyOn(const ObjectAddress *depender, 17 | const ObjectAddress *referenced, 18 | DependencyType behavior); 19 | 20 | extern void pglogical_recordMultipleDependencies(const ObjectAddress *depender, 21 | const ObjectAddress *referenced, 22 | int nreferenced, 23 | DependencyType behavior); 24 | 25 | extern void pglogical_recordDependencyOnSingleRelExpr(const ObjectAddress *depender, 26 | Node *expr, Oid relId, 27 | DependencyType behavior, 28 | DependencyType self_behavior); 29 | 30 | extern void pglogical_tryDropDependencies(const ObjectAddress *object, 31 | DropBehavior behavior); 32 | 33 | extern void pglogical_checkDependency(const ObjectAddress *object, 34 | DropBehavior behavior); 35 | 36 | #endif /* PGLOGICAL_DEPENDENCY_H */ 37 | -------------------------------------------------------------------------------- /pglogical_executor.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_executor.h 4 | * pglogical replication plugin 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_executor.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_EXECUTOR_H 14 | #define PGLOGICAL_EXECUTOR_H 15 | 16 | #include "executor/executor.h" 17 | 18 | extern List *pglogical_truncated_tables; 19 | 20 | extern EState *create_estate_for_relation(Relation rel, bool forwrite); 21 | extern ExprContext *prepare_per_tuple_econtext(EState *estate, TupleDesc tupdesc); 22 | extern ExprState *pglogical_prepare_row_filter(Node *row_filter); 23 | 24 | extern void pglogical_executor_init(void); 25 | 26 | #endif /* PGLOGICAL_EXECUTOR_H */ 27 | -------------------------------------------------------------------------------- /pglogical_fe.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_fe.h 4 | * pglogical replication plugin 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_fe.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_FE_H 14 | #define PGLOGICAL_FE_H 15 | 16 | extern int find_other_exec_version(const char *argv0, const char *target, 17 | uint32 *version, char *retpath); 18 | 19 | extern char *pgl_get_connstr(char *connstr, char *dbname, char *options, char **errmsg); 20 | 21 | #endif /* PGLOGICAL_FE_H */ 22 | -------------------------------------------------------------------------------- /pglogical_monitoring.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_monitoring.c 4 | * support for monitoring and progress tracking 5 | * 6 | * Copyright (c) 2017, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_monitoring.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #include "postgres.h" 14 | 15 | #include "fmgr.h" 16 | #include "miscadmin.h" 17 | 18 | #include "replication/slot.h" 19 | 20 | #include "utils/pg_lsn.h" 21 | 22 | #include "storage/ipc.h" 23 | #include "storage/proc.h" 24 | 25 | #include "pgstat.h" 26 | 27 | #include "pglogical.h" 28 | 29 | PG_FUNCTION_INFO_V1(pglogical_wait_slot_confirm_lsn); 30 | 31 | /* 32 | * Wait for the confirmed_flush_lsn of the specified slot, or all logical slots 33 | * if none given, to pass the supplied value. If no position is supplied the 34 | * write position is used. 35 | * 36 | * No timeout is offered, use a statement_timeout. 37 | */ 38 | Datum 39 | pglogical_wait_slot_confirm_lsn(PG_FUNCTION_ARGS) 40 | { 41 | XLogRecPtr target_lsn; 42 | Name slot_name; 43 | int i; 44 | 45 | if (PG_ARGISNULL(0)) 46 | slot_name = NULL; 47 | else 48 | slot_name = PG_GETARG_NAME(0); 49 | 50 | if (PG_ARGISNULL(1)) 51 | { 52 | if (XLogRecPtrIsInvalid(XactLastCommitEnd)) 53 | target_lsn = GetXLogInsertRecPtr(); 54 | else 55 | target_lsn = XactLastCommitEnd; 56 | } 57 | else 58 | target_lsn = PG_GETARG_LSN(1); 59 | 60 | elog(DEBUG1, "waiting for %s to pass confirmed_flush position %X/%X", 61 | slot_name == NULL ? "all local slots" : NameStr(*slot_name), 62 | (uint32)(target_lsn>>32), (uint32)target_lsn); 63 | 64 | do 65 | { 66 | XLogRecPtr oldest_confirmed_lsn = InvalidXLogRecPtr; 67 | int oldest_slot_pos = -1; 68 | int rc; 69 | 70 | LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); 71 | for (i = 0; i < max_replication_slots; i++) 72 | { 73 | ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; 74 | 75 | if (!s->in_use) 76 | continue; 77 | 78 | if (slot_name != NULL && strncmp(NameStr(*slot_name), NameStr(s->data.name), NAMEDATALEN) != 0) 79 | continue; 80 | 81 | if (oldest_confirmed_lsn == InvalidXLogRecPtr 82 | || (s->data.confirmed_flush != InvalidXLogRecPtr && s->data.confirmed_flush < oldest_confirmed_lsn)) 83 | { 84 | oldest_confirmed_lsn = s->data.confirmed_flush; 85 | oldest_slot_pos = i; 86 | } 87 | } 88 | 89 | if (oldest_slot_pos >= 0) 90 | elog(DEBUG2, "oldest confirmed lsn is %X/%X on slot '%s', %u bytes left until %X/%X", 91 | (uint32)(oldest_confirmed_lsn>>32), (uint32)oldest_confirmed_lsn, 92 | NameStr(ReplicationSlotCtl->replication_slots[oldest_slot_pos].data.name), 93 | (uint32)(target_lsn - oldest_confirmed_lsn), 94 | (uint32)(target_lsn>>32), (uint32)target_lsn); 95 | 96 | LWLockRelease(ReplicationSlotControlLock); 97 | 98 | if (oldest_confirmed_lsn >= target_lsn) 99 | break; 100 | 101 | rc = WaitLatch(&MyProc->procLatch, 102 | WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, 103 | 1000); 104 | 105 | ResetLatch(&MyProc->procLatch); 106 | 107 | if (rc & WL_POSTMASTER_DEATH) 108 | proc_exit(1); 109 | 110 | CHECK_FOR_INTERRUPTS(); 111 | 112 | } while(1); 113 | 114 | PG_RETURN_VOID(); 115 | } 116 | -------------------------------------------------------------------------------- /pglogical_node.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_node.h 4 | * pglogical node and connection catalog manipulation functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_node.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_NODE_H 14 | #define PGLOGICAL_NODE_H 15 | 16 | #include "datatype/timestamp.h" 17 | 18 | typedef struct PGLogicalNode 19 | { 20 | Oid id; 21 | char *name; 22 | } PGLogicalNode; 23 | 24 | typedef struct PGlogicalInterface 25 | { 26 | Oid id; 27 | const char *name; 28 | Oid nodeid; 29 | const char *dsn; 30 | } PGlogicalInterface; 31 | 32 | typedef struct PGLogicalLocalNode 33 | { 34 | PGLogicalNode *node; 35 | PGlogicalInterface *node_if; 36 | } PGLogicalLocalNode; 37 | 38 | typedef struct PGLogicalSubscription 39 | { 40 | Oid id; 41 | char *name; 42 | PGLogicalNode *origin; 43 | PGLogicalNode *target; 44 | PGlogicalInterface *origin_if; 45 | PGlogicalInterface *target_if; 46 | bool enabled; 47 | Interval *apply_delay; 48 | char *slot_name; 49 | List *replication_sets; 50 | List *forward_origins; 51 | bool force_text_transfer; 52 | } PGLogicalSubscription; 53 | 54 | extern void create_node(PGLogicalNode *node); 55 | extern void drop_node(Oid nodeid); 56 | 57 | extern PGLogicalNode *get_node(Oid nodeid); 58 | extern PGLogicalNode *get_node_by_name(const char *name, bool missing_ok); 59 | 60 | extern void create_node_interface(PGlogicalInterface *node); 61 | extern void drop_node_interface(Oid ifid); 62 | extern void drop_node_interfaces(Oid nodeid); 63 | extern PGlogicalInterface *get_node_interface(Oid ifid); 64 | extern PGlogicalInterface *get_node_interface_by_name(Oid nodeid, 65 | const char *name, 66 | bool missing_ok); 67 | 68 | extern void create_local_node(Oid nodeid, Oid ifid); 69 | extern void drop_local_node(void); 70 | extern PGLogicalLocalNode *get_local_node(bool for_update, bool missing_ok); 71 | 72 | extern void create_subscription(PGLogicalSubscription *sub); 73 | extern void alter_subscription(PGLogicalSubscription *sub); 74 | extern void drop_subscription(Oid subid); 75 | 76 | extern PGLogicalSubscription *get_subscription(Oid subid); 77 | extern PGLogicalSubscription *get_subscription_by_name(const char *name, 78 | bool missing_ok); 79 | extern List *get_node_subscriptions(Oid nodeid, bool origin); 80 | 81 | #endif /* PGLOGICAL_NODE_H */ 82 | -------------------------------------------------------------------------------- /pglogical_origin--1.0.0.sql: -------------------------------------------------------------------------------- 1 | \echo Use "CREATE EXTENSION pglogical_origin" to load this file. \quit 2 | -------------------------------------------------------------------------------- /pglogical_origin.control: -------------------------------------------------------------------------------- 1 | # pglogical_origin extension 2 | comment = 'Dummy extension for compatibility when upgrading from Postgres 9.4' 3 | default_version = '1.0.0' 4 | module_pathname = '$libdir/pglogical' 5 | relocatable = false 6 | schema = pglogical_origin 7 | -------------------------------------------------------------------------------- /pglogical_output.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_output.c 4 | * Logical Replication output plugin which just loads and forwards 5 | * the call to the pglogical. 6 | * 7 | * This exists for backwards compatibility. 8 | * 9 | * Copyright (c) 2012-2015, PostgreSQL Global Development Group 10 | * 11 | * IDENTIFICATION 12 | * pglogical_output.c 13 | * 14 | *------------------------------------------------------------------------- 15 | */ 16 | #include "postgres.h" 17 | 18 | #include "replication/logical.h" 19 | 20 | PG_MODULE_MAGIC; 21 | 22 | extern void _PG_output_plugin_init(OutputPluginCallbacks *cb); 23 | 24 | void 25 | _PG_output_plugin_init(OutputPluginCallbacks *cb) 26 | { 27 | LogicalOutputPluginInit plugin_init; 28 | 29 | AssertVariableIsOfType(&_PG_output_plugin_init, LogicalOutputPluginInit); 30 | 31 | plugin_init = (LogicalOutputPluginInit) 32 | load_external_function("pglogical", "_PG_output_plugin_init", false, NULL); 33 | 34 | if (plugin_init == NULL) 35 | elog(ERROR, "could not load pglogical output plugin"); 36 | 37 | plugin_init(cb); 38 | } 39 | -------------------------------------------------------------------------------- /pglogical_output_config.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_LOGICAL_OUTPUT_CONFIG_H 2 | #define PG_LOGICAL_OUTPUT_CONFIG_H 3 | 4 | #include "nodes/pg_list.h" 5 | #include "pglogical_output_plugin.h" 6 | 7 | inline static bool 8 | server_float4_byval(void) 9 | { 10 | #ifdef USE_FLOAT4_BYVAL 11 | return true; 12 | #else 13 | return false; 14 | #endif 15 | } 16 | 17 | inline static bool 18 | server_float8_byval(void) 19 | { 20 | #ifdef USE_FLOAT8_BYVAL 21 | return true; 22 | #else 23 | return false; 24 | #endif 25 | } 26 | 27 | inline static bool 28 | server_integer_datetimes(void) 29 | { 30 | #ifdef USE_INTEGER_DATETIMES 31 | return true; 32 | #else 33 | return false; 34 | #endif 35 | } 36 | 37 | inline static bool 38 | server_bigendian(void) 39 | { 40 | #ifdef WORDS_BIGENDIAN 41 | return true; 42 | #else 43 | return false; 44 | #endif 45 | } 46 | 47 | extern int process_parameters(List *options, PGLogicalOutputData *data); 48 | 49 | extern List *prepare_startup_message(PGLogicalOutputData *data); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /pglogical_output_plugin.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_output_plugin.h 4 | * pglogical output plugin 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_output_plugin.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PG_LOGICAL_OUTPUT_PLUGIN_H 14 | #define PG_LOGICAL_OUTPUT_PLUGIN_H 15 | 16 | #include "nodes/pg_list.h" 17 | #include "nodes/primnodes.h" 18 | 19 | /* summon cross-PG-version compatibility voodoo */ 20 | #include "pglogical_compat.h" 21 | 22 | /* typedef appears in pglogical_output_plugin.h */ 23 | typedef struct PGLogicalOutputData 24 | { 25 | MemoryContext context; 26 | 27 | struct PGLogicalProtoAPI *api; 28 | 29 | /* Cached node id */ 30 | Oid local_node_id; 31 | 32 | /* protocol */ 33 | bool allow_internal_basetypes; 34 | bool allow_binary_basetypes; 35 | bool forward_changeset_origins; 36 | int field_datum_encoding; 37 | 38 | /* 39 | * client info 40 | * 41 | * Lots of this should move to a separate shorter-lived struct used only 42 | * during parameter reading, since it contains what the client asked for. 43 | * Once we've processed this during startup we don't refer to it again. 44 | */ 45 | uint32 client_pg_version; 46 | uint32 client_max_proto_version; 47 | uint32 client_min_proto_version; 48 | const char *client_expected_encoding; 49 | const char *client_protocol_format; 50 | uint32 client_binary_basetypes_major_version; 51 | bool client_want_internal_basetypes_set; 52 | bool client_want_internal_basetypes; 53 | bool client_want_binary_basetypes_set; 54 | bool client_want_binary_basetypes; 55 | bool client_binary_bigendian_set; 56 | bool client_binary_bigendian; 57 | uint32 client_binary_sizeofdatum; 58 | uint32 client_binary_sizeofint; 59 | uint32 client_binary_sizeoflong; 60 | bool client_binary_float4byval_set; 61 | bool client_binary_float4byval; 62 | bool client_binary_float8byval_set; 63 | bool client_binary_float8byval; 64 | bool client_binary_intdatetimes_set; 65 | bool client_binary_intdatetimes; 66 | bool client_no_txinfo; 67 | 68 | /* List of origin names */ 69 | List *forward_origins; 70 | /* List of PGLogicalRepSet */ 71 | List *replication_sets; 72 | RangeVar *replicate_only_table; 73 | } PGLogicalOutputData; 74 | 75 | #endif /* PG_LOGICAL_OUTPUT_PLUGIN_H */ 76 | -------------------------------------------------------------------------------- /pglogical_output_proto.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_proto.c 4 | * pglogical protocol functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_proto.c 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #include "postgres.h" 14 | #include "replication/reorderbuffer.h" 15 | #include "pglogical_output_plugin.h" 16 | 17 | #include "pglogical_output_proto.h" 18 | #include "pglogical_proto_native.h" 19 | #include "pglogical_proto_json.h" 20 | 21 | PGLogicalProtoAPI * 22 | pglogical_init_api(PGLogicalProtoType typ) 23 | { 24 | PGLogicalProtoAPI *res = palloc0(sizeof(PGLogicalProtoAPI)); 25 | 26 | if (typ == PGLogicalProtoJson) 27 | { 28 | res->write_rel = NULL; 29 | res->write_begin = pglogical_json_write_begin; 30 | res->write_commit = pglogical_json_write_commit; 31 | res->write_origin = NULL; 32 | res->write_insert = pglogical_json_write_insert; 33 | res->write_update = pglogical_json_write_update; 34 | res->write_delete = pglogical_json_write_delete; 35 | res->write_startup_message = json_write_startup_message; 36 | } 37 | else 38 | { 39 | res->write_rel = pglogical_write_rel; 40 | res->write_begin = pglogical_write_begin; 41 | res->write_commit = pglogical_write_commit; 42 | res->write_origin = pglogical_write_origin; 43 | res->write_insert = pglogical_write_insert; 44 | res->write_update = pglogical_write_update; 45 | res->write_delete = pglogical_write_delete; 46 | res->write_startup_message = write_startup_message; 47 | } 48 | 49 | return res; 50 | } 51 | -------------------------------------------------------------------------------- /pglogical_output_proto.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_output_proto.h 4 | * pglogical protocol 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_output_proto.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PG_LOGICAL_OUTPUT_PROTO_H 14 | #define PG_LOGICAL_OUTPUT_PROTO_H 15 | 16 | #include "lib/stringinfo.h" 17 | #include "replication/reorderbuffer.h" 18 | #include "utils/relcache.h" 19 | 20 | #include "pglogical_output_plugin.h" 21 | 22 | /* 23 | * Protocol capabilities 24 | * 25 | * PGLOGICAL_PROTO_VERSION_NUM is our native protocol and the greatest version 26 | * we can support. PGLOGICAL_PROTO_MIN_VERSION_NUM is the oldest version we 27 | * have backwards compatibility for. We negotiate protocol versions during the 28 | * startup handshake. See the protocol documentation for details. 29 | */ 30 | #define PGLOGICAL_PROTO_VERSION_NUM 1 31 | #define PGLOGICAL_PROTO_MIN_VERSION_NUM 1 32 | 33 | /* 34 | * The startup parameter format is versioned separately to the rest of the wire 35 | * protocol because we negotiate the wire protocol version using the startup 36 | * parameters sent to us. It hopefully won't ever need to change, but this 37 | * field is present in case we do need to change it, e.g. to a structured json 38 | * object. We can look at the startup params version to see whether we can 39 | * understand the startup params sent by the client and to fall back to 40 | * reading an older format if needed. 41 | */ 42 | #define PGLOGICAL_STARTUP_PARAM_FORMAT_FLAT 1 43 | 44 | /* 45 | * For similar reasons to the startup params 46 | * (PGLOGICAL_STARTUP_PARAM_FORMAT_FLAT) the startup reply message format is 47 | * versioned separately to the rest of the protocol. The client has to be able 48 | * to read it to find out what protocol version was selected by the upstream 49 | * when using the native protocol. 50 | */ 51 | #define PGLOGICAL_STARTUP_MSG_FORMAT_FLAT 1 52 | 53 | typedef enum PGLogicalProtoType 54 | { 55 | PGLogicalProtoNative, 56 | PGLogicalProtoJson 57 | } PGLogicalProtoType; 58 | 59 | typedef void (*pglogical_write_rel_fn) (StringInfo out, PGLogicalOutputData * data, 60 | Relation rel, Bitmapset *att_list); 61 | 62 | typedef void (*pglogical_write_begin_fn) (StringInfo out, PGLogicalOutputData * data, 63 | ReorderBufferTXN *txn); 64 | typedef void (*pglogical_write_commit_fn) (StringInfo out, PGLogicalOutputData * data, 65 | ReorderBufferTXN *txn, XLogRecPtr commit_lsn); 66 | 67 | typedef void (*pglogical_write_origin_fn) (StringInfo out, const char *origin, 68 | XLogRecPtr origin_lsn); 69 | 70 | typedef void (*pglogical_write_insert_fn) (StringInfo out, PGLogicalOutputData * data, 71 | Relation rel, HeapTuple newtuple, 72 | Bitmapset *att_list); 73 | typedef void (*pglogical_write_update_fn) (StringInfo out, PGLogicalOutputData * data, 74 | Relation rel, HeapTuple oldtuple, 75 | HeapTuple newtuple, 76 | Bitmapset *att_list); 77 | typedef void (*pglogical_write_delete_fn) (StringInfo out, PGLogicalOutputData * data, 78 | Relation rel, HeapTuple oldtuple, 79 | Bitmapset *att_list); 80 | 81 | typedef void (*write_startup_message_fn) (StringInfo out, List *msg); 82 | 83 | typedef struct PGLogicalProtoAPI 84 | { 85 | pglogical_write_rel_fn write_rel; 86 | pglogical_write_begin_fn write_begin; 87 | pglogical_write_commit_fn write_commit; 88 | pglogical_write_origin_fn write_origin; 89 | pglogical_write_insert_fn write_insert; 90 | pglogical_write_update_fn write_update; 91 | pglogical_write_delete_fn write_delete; 92 | write_startup_message_fn write_startup_message; 93 | } PGLogicalProtoAPI; 94 | 95 | extern PGLogicalProtoAPI *pglogical_init_api(PGLogicalProtoType typ); 96 | 97 | #endif /* PG_LOGICAL_OUTPUT_PROTO_H */ 98 | -------------------------------------------------------------------------------- /pglogical_proto_json.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_proto_json.h 4 | * pglogical protocol, json implementation 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_proto_json.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PG_LOGICAL_PROTO_JSON_H 14 | #define PG_LOGICAL_PROTO_JSON_H 15 | 16 | #include "pglogical_output_plugin.h" 17 | 18 | #include "lib/stringinfo.h" 19 | #include "nodes/pg_list.h" 20 | 21 | #include "pglogical_output_proto.h" 22 | 23 | extern void pglogical_json_write_begin(StringInfo out, PGLogicalOutputData *data, 24 | ReorderBufferTXN *txn); 25 | extern void pglogical_json_write_commit(StringInfo out, PGLogicalOutputData *data, 26 | ReorderBufferTXN *txn, XLogRecPtr commit_lsn); 27 | extern void pglogical_json_write_insert(StringInfo out, PGLogicalOutputData *data, 28 | Relation rel, HeapTuple newtuple, 29 | Bitmapset *att_list); 30 | extern void pglogical_json_write_update(StringInfo out, PGLogicalOutputData *data, 31 | Relation rel, HeapTuple oldtuple, 32 | HeapTuple newtuple, Bitmapset *att_list); 33 | extern void pglogical_json_write_delete(StringInfo out, PGLogicalOutputData *data, 34 | Relation rel, HeapTuple oldtuple, 35 | Bitmapset *att_list); 36 | extern void json_write_startup_message(StringInfo out, List *msg); 37 | 38 | #endif /* PG_LOGICAL_PROTO_JSON_H */ 39 | -------------------------------------------------------------------------------- /pglogical_proto_native.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_proto_native.h 4 | * pglogical protocol, native implementation 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_proto_native.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PG_LOGICAL_PROTO_NATIVE_H 14 | #define PG_LOGICAL_PROTO_NATIVE_H 15 | 16 | #include "lib/stringinfo.h" 17 | 18 | #include "utils/timestamp.h" 19 | 20 | #include "pglogical_output_plugin.h" 21 | #include "pglogical_output_proto.h" 22 | #include "pglogical_relcache.h" 23 | 24 | typedef struct PGLogicalTupleData 25 | { 26 | Datum values[MaxTupleAttributeNumber]; 27 | bool nulls[MaxTupleAttributeNumber]; 28 | bool changed[MaxTupleAttributeNumber]; 29 | } PGLogicalTupleData; 30 | 31 | extern void pglogical_write_rel(StringInfo out, PGLogicalOutputData *data, 32 | Relation rel, Bitmapset *att_list); 33 | extern void pglogical_write_begin(StringInfo out, PGLogicalOutputData *data, 34 | ReorderBufferTXN *txn); 35 | extern void pglogical_write_commit(StringInfo out, PGLogicalOutputData *data, 36 | ReorderBufferTXN *txn, XLogRecPtr commit_lsn); 37 | extern void pglogical_write_origin(StringInfo out, const char *origin, 38 | XLogRecPtr origin_lsn); 39 | extern void pglogical_write_insert(StringInfo out, PGLogicalOutputData *data, 40 | Relation rel, HeapTuple newtuple, Bitmapset *att_list); 41 | extern void pglogical_write_update(StringInfo out, PGLogicalOutputData *data, 42 | Relation rel, HeapTuple oldtuple, HeapTuple newtuple, 43 | Bitmapset *att_list); 44 | extern void pglogical_write_delete(StringInfo out, PGLogicalOutputData *data, 45 | Relation rel, HeapTuple oldtuple, Bitmapset *att_list); 46 | extern void write_startup_message(StringInfo out, List *msg); 47 | 48 | extern void pglogical_read_begin(StringInfo in, XLogRecPtr *remote_lsn, 49 | TimestampTz *committime, TransactionId *remote_xid); 50 | extern void pglogical_read_commit(StringInfo in, XLogRecPtr *commit_lsn, 51 | XLogRecPtr *end_lsn, TimestampTz *committime); 52 | extern char *pglogical_read_origin(StringInfo in, XLogRecPtr *origin_lsn); 53 | extern uint32 pglogical_read_rel(StringInfo in); 54 | extern PGLogicalRelation *pglogical_read_insert(StringInfo in, LOCKMODE lockmode, 55 | PGLogicalTupleData *newtup); 56 | extern PGLogicalRelation *pglogical_read_update(StringInfo in, LOCKMODE lockmode, bool *hasoldtup, 57 | PGLogicalTupleData *oldtup, PGLogicalTupleData *newtup); 58 | extern PGLogicalRelation *pglogical_read_delete(StringInfo in, LOCKMODE lockmode, 59 | PGLogicalTupleData *oldtup); 60 | #endif /* PG_LOGICAL_PROTO_NATIVE_H */ 61 | -------------------------------------------------------------------------------- /pglogical_queue.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_node.h 4 | * pglogical node and connection catalog manipulation functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_node.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_QUEUE_H 14 | #define PGLOGICAL_QUEUE_H 15 | 16 | #include "utils/jsonb.h" 17 | 18 | #define QUEUE_COMMAND_TYPE_SQL 'Q' 19 | #define QUEUE_COMMAND_TYPE_TRUNCATE 'T' 20 | #define QUEUE_COMMAND_TYPE_TABLESYNC 'A' 21 | #define QUEUE_COMMAND_TYPE_SEQUENCE 'S' 22 | 23 | typedef struct QueuedMessage 24 | { 25 | TimestampTz queued_at; 26 | List *replication_sets; 27 | char *role; 28 | char message_type; 29 | Jsonb *message; 30 | } QueuedMessage; 31 | 32 | extern void queue_message(List *replication_sets, Oid roleoid, 33 | char message_type, char *message); 34 | 35 | extern QueuedMessage *queued_message_from_tuple(HeapTuple queue_tup); 36 | 37 | extern Oid get_queue_table_oid(void); 38 | 39 | extern void create_truncate_trigger(Relation rel); 40 | 41 | #endif /* PGLOGICAL_NODE_H */ 42 | -------------------------------------------------------------------------------- /pglogical_relcache.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_relcache.h 4 | * pglogical relation cache 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_relcache.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_RELCACHE_H 14 | #define PGLOGICAL_RELCACHE_H 15 | 16 | #include "storage/lock.h" 17 | 18 | typedef struct PGLogicalRemoteRel 19 | { 20 | uint32 relid; 21 | char *nspname; 22 | char *relname; 23 | int natts; 24 | char **attnames; 25 | 26 | /* Only returned by info function, not protocol. */ 27 | bool hasRowFilter; 28 | } PGLogicalRemoteRel; 29 | 30 | typedef struct PGLogicalRelation 31 | { 32 | /* Info coming from the remote side. */ 33 | uint32 remoteid; 34 | char *nspname; 35 | char *relname; 36 | int natts; 37 | char **attnames; 38 | 39 | /* Mapping to local relation, filled as needed. */ 40 | Oid reloid; 41 | Relation rel; 42 | int *attmap; 43 | 44 | /* Additional cache, only valid as long as relation mapping is. */ 45 | bool hasTriggers; 46 | } PGLogicalRelation; 47 | 48 | extern void pglogical_relation_cache_update(uint32 remoteid, 49 | char *schemaname, char *relname, 50 | int natts, char **attnames); 51 | extern void pglogical_relation_cache_updater(PGLogicalRemoteRel *remoterel); 52 | 53 | extern PGLogicalRelation *pglogical_relation_open(uint32 remoteid, 54 | LOCKMODE lockmode); 55 | extern void pglogical_relation_close(PGLogicalRelation * rel, 56 | LOCKMODE lockmode); 57 | extern void pglogical_relation_invalidate_cb(Datum arg, Oid reloid); 58 | 59 | struct PGLogicalTupleData; 60 | 61 | #endif /* PGLOGICAL_RELCACHE_H */ 62 | -------------------------------------------------------------------------------- /pglogical_repset.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_repset.h 4 | * pglogical replication set manipulation functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_repset.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_REPSET_H 14 | #define PGLOGICAL_REPSET_H 15 | 16 | #include "replication/reorderbuffer.h" 17 | 18 | typedef struct PGLogicalRepSet 19 | { 20 | Oid id; 21 | Oid nodeid; 22 | char *name; 23 | bool replicate_insert; 24 | bool replicate_update; 25 | bool replicate_delete; 26 | bool replicate_truncate; 27 | } PGLogicalRepSet; 28 | 29 | #define DEFAULT_REPSET_NAME "default" 30 | #define DEFAULT_INSONLY_REPSET_NAME "default_insert_only" 31 | #define DDL_SQL_REPSET_NAME "ddl_sql" 32 | 33 | /* This is only valid within one output plugin instance/walsender. */ 34 | typedef struct PGLogicalTableRepInfo 35 | { 36 | Oid reloid; /* key */ 37 | 38 | bool isvalid; /* is this entry valid? */ 39 | 40 | bool replicate_insert; /* should insert be replicated? */ 41 | bool replicate_update; /* should update be replicated? */ 42 | bool replicate_delete; /* should delete be replicated? */ 43 | 44 | Bitmapset *att_list; /* column filter 45 | NULL if everything is replicated 46 | otherwise each replicated column 47 | is a member */ 48 | List *row_filter; /* compiled row_filter nodes */ 49 | } PGLogicalTableRepInfo; 50 | 51 | extern PGLogicalRepSet *get_replication_set(Oid setid); 52 | extern PGLogicalRepSet *get_replication_set_by_name(Oid nodeid, 53 | const char *setname, 54 | bool missing_ok); 55 | 56 | extern List *get_node_replication_sets(Oid nodeid); 57 | extern List *get_replication_sets(Oid nodeid, List *replication_set_names, 58 | bool missing_ok); 59 | 60 | extern PGLogicalTableRepInfo *get_table_replication_info(Oid nodeid, 61 | Relation table, List *subs_replication_sets); 62 | 63 | extern void create_replication_set(PGLogicalRepSet *repset); 64 | extern void alter_replication_set(PGLogicalRepSet *repset); 65 | extern void drop_replication_set(Oid setid); 66 | extern void drop_node_replication_sets(Oid nodeid); 67 | 68 | extern void replication_set_add_table(Oid setid, Oid reloid, 69 | List *att_list, Node *row_filter); 70 | extern void replication_set_add_seq(Oid setid, Oid seqoid); 71 | extern List *replication_set_get_tables(Oid setid); 72 | extern List *replication_set_get_seqs(Oid setid); 73 | extern PGDLLEXPORT void replication_set_remove_table(Oid setid, Oid reloid, 74 | bool from_drop); 75 | extern PGDLLEXPORT void replication_set_remove_seq(Oid setid, Oid reloid, 76 | bool from_drop); 77 | 78 | extern List *get_table_replication_sets(Oid nodeid, Oid reloid); 79 | extern List *get_seq_replication_sets(Oid nodeid, Oid seqoid); 80 | 81 | extern PGLogicalRepSet *replication_set_from_tuple(HeapTuple tuple); 82 | 83 | extern Oid get_replication_set_rel_oid(void); 84 | extern Oid get_replication_set_table_rel_oid(void); 85 | extern Oid get_replication_set_seq_rel_oid(void); 86 | 87 | extern char *stringlist_to_identifierstr(List *repsets); 88 | extern int get_att_num_by_name(TupleDesc desc, const char *attname); 89 | 90 | #endif /* PGLOGICAL_REPSET_H */ 91 | -------------------------------------------------------------------------------- /pglogical_rpc.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_rpc.h 4 | * Remote calls 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_rpc.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_RPC_H 14 | #define PGLOGICAL_RPC_H 15 | 16 | #include "libpq-fe.h" 17 | 18 | extern List *pg_logical_get_remote_repset_tables(PGconn *conn, 19 | List *replication_sets); 20 | extern PGLogicalRemoteRel *pg_logical_get_remote_repset_table(PGconn *conn, 21 | RangeVar *rv, List *replication_sets); 22 | 23 | extern bool pglogical_remote_slot_active(PGconn *conn, const char *slot_name); 24 | extern void pglogical_drop_remote_slot(PGconn *conn, const char *slot_name); 25 | extern void pglogical_remote_node_info(PGconn *conn, Oid *nodeid, 26 | char **node_name, char **sysid, char **dbname, 27 | char **replication_sets); 28 | extern bool pglogical_remote_function_exists(PGconn *conn, const char *nspname, 29 | const char *proname, int nargs, char *argname); 30 | 31 | #endif /* PGLOGICAL_RPC_H */ 32 | -------------------------------------------------------------------------------- /pglogical_sync.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_sync.h 4 | * table synchronization functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_sync.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_SYNC_H 14 | #define PGLOGICAL_SYNC_H 15 | 16 | #include "libpq-fe.h" 17 | 18 | #include "nodes/primnodes.h" 19 | #include "pglogical_node.h" 20 | 21 | typedef struct PGLogicalSyncStatus 22 | { 23 | char kind; 24 | Oid subid; 25 | NameData nspname; 26 | NameData relname; 27 | char status; 28 | XLogRecPtr statuslsn; /* remote lsn of the state change used for 29 | * synchronization coordination */ 30 | } PGLogicalSyncStatus; 31 | 32 | #define SYNC_KIND_INIT 'i' 33 | #define SYNC_KIND_FULL 'f' 34 | #define SYNC_KIND_STRUCTURE 's' 35 | #define SYNC_KIND_DATA 'd' 36 | 37 | #define SyncKindData(kind) \ 38 | (kind == SYNC_KIND_FULL || kind == SYNC_KIND_DATA) 39 | 40 | #define SyncKindStructure(kind) \ 41 | (kind == SYNC_KIND_FULL || kind == SYNC_KIND_STRUCTURE) 42 | 43 | #define SYNC_STATUS_NONE '\0' /* No sync. */ 44 | #define SYNC_STATUS_INIT 'i' /* Ask for sync. */ 45 | #define SYNC_STATUS_STRUCTURE 's' /* Sync structure */ 46 | #define SYNC_STATUS_DATA 'd' /* Data sync. */ 47 | #define SYNC_STATUS_CONSTRAINTS 'c' /* Constraint sync (post-data structure). */ 48 | #define SYNC_STATUS_SYNCWAIT 'w' /* Table sync is waiting to get OK from main thread. */ 49 | #define SYNC_STATUS_CATCHUP 'u' /* Catching up. */ 50 | #define SYNC_STATUS_SYNCDONE 'y' /* Synchronization finished (at lsn). */ 51 | #define SYNC_STATUS_READY 'r' /* Done. */ 52 | 53 | extern void pglogical_sync_worker_finish(void); 54 | 55 | extern void pglogical_sync_subscription(PGLogicalSubscription *sub); 56 | extern char pglogical_sync_table(PGLogicalSubscription *sub, RangeVar *table, XLogRecPtr *status_lsn); 57 | 58 | extern void create_local_sync_status(PGLogicalSyncStatus *sync); 59 | extern void drop_subscription_sync_status(Oid subid); 60 | 61 | extern PGLogicalSyncStatus *get_subscription_sync_status(Oid subid, 62 | bool missing_ok); 63 | extern void set_subscription_sync_status(Oid subid, char status); 64 | 65 | extern void drop_table_sync_status(const char *nspname, const char *relname); 66 | extern void drop_table_sync_status_for_sub(Oid subid, const char *nspname, 67 | const char *relname); 68 | 69 | extern PGLogicalSyncStatus *get_table_sync_status(Oid subid, 70 | const char *schemaname, 71 | const char *relname, 72 | bool missing_ok); 73 | extern void set_table_sync_status(Oid subid, const char *schemaname, 74 | const char *relname, char status, 75 | XLogRecPtr status_lsn); 76 | extern List *get_unsynced_tables(Oid subid); 77 | 78 | /* For interface compat with pgl3 */ 79 | inline static void free_sync_status(PGLogicalSyncStatus *sync) 80 | { 81 | pfree(sync); 82 | } 83 | 84 | extern bool wait_for_sync_status_change(Oid subid, const char *nspname, 85 | const char *relname, char desired_state, 86 | XLogRecPtr *status_lsn); 87 | 88 | extern void truncate_table(char *nspname, char *relname); 89 | extern List *get_subscription_tables(Oid subid); 90 | 91 | #ifdef WIN32 92 | extern void QuoteWindowsArgv(StringInfo cmdline, const char * argv[]); 93 | #endif 94 | 95 | #endif /* PGLOGICAL_SYNC_H */ 96 | 97 | -------------------------------------------------------------------------------- /pglogical_worker.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pglogical_worker.h 4 | * pglogical worker helper functions 5 | * 6 | * Copyright (c) 2015, PostgreSQL Global Development Group 7 | * 8 | * IDENTIFICATION 9 | * pglogical_worker.h 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef PGLOGICAL_WORKER_H 14 | #define PGLOGICAL_WORKER_H 15 | 16 | #include "storage/lock.h" 17 | 18 | #include "pglogical.h" 19 | 20 | typedef enum { 21 | PGLOGICAL_WORKER_NONE, /* Unused slot. */ 22 | PGLOGICAL_WORKER_MANAGER, /* Manager. */ 23 | PGLOGICAL_WORKER_APPLY, /* Apply. */ 24 | PGLOGICAL_WORKER_SYNC /* Special type of Apply that synchronizes 25 | * one table. */ 26 | } PGLogicalWorkerType; 27 | 28 | typedef struct PGLogicalApplyWorker 29 | { 30 | Oid subid; /* Subscription id for apply worker. */ 31 | bool sync_pending; /* Is there new synchronization info pending?. */ 32 | XLogRecPtr replay_stop_lsn; /* Replay should stop here if defined. */ 33 | } PGLogicalApplyWorker; 34 | 35 | typedef struct PGLogicalSyncWorker 36 | { 37 | PGLogicalApplyWorker apply; /* Apply worker info, must be first. */ 38 | NameData nspname; /* Name of the schema of table to copy if any. */ 39 | NameData relname; /* Name of the table to copy if any. */ 40 | } PGLogicalSyncWorker; 41 | 42 | typedef struct PGLogicalWorker { 43 | PGLogicalWorkerType worker_type; 44 | 45 | /* Generation counter incremented at each registration */ 46 | uint16 generation; 47 | 48 | /* Pointer to proc array. NULL if not running. */ 49 | PGPROC *proc; 50 | 51 | /* Time at which worker crashed (normally 0). */ 52 | TimestampTz crashed_at; 53 | 54 | /* Database id to connect to. */ 55 | Oid dboid; 56 | 57 | /* Type-specific worker info */ 58 | union 59 | { 60 | PGLogicalApplyWorker apply; 61 | PGLogicalSyncWorker sync; 62 | } worker; 63 | 64 | } PGLogicalWorker; 65 | 66 | typedef struct PGLogicalContext { 67 | /* Write lock. */ 68 | LWLock *lock; 69 | 70 | /* Supervisor process. */ 71 | PGPROC *supervisor; 72 | 73 | /* Signal that subscription info have changed. */ 74 | bool subscriptions_changed; 75 | 76 | /* Background workers. */ 77 | int total_workers; 78 | PGLogicalWorker workers[FLEXIBLE_ARRAY_MEMBER]; 79 | } PGLogicalContext; 80 | 81 | extern PGLogicalContext *PGLogicalCtx; 82 | extern PGLogicalWorker *MyPGLogicalWorker; 83 | extern PGLogicalApplyWorker *MyApplyWorker; 84 | extern PGLogicalSubscription *MySubscription; 85 | 86 | extern volatile sig_atomic_t got_SIGTERM; 87 | 88 | extern void handle_sigterm(SIGNAL_ARGS); 89 | 90 | extern void pglogical_subscription_changed(Oid subid, bool kill); 91 | 92 | extern void pglogical_worker_shmem_init(void); 93 | 94 | extern int pglogical_worker_register(PGLogicalWorker *worker); 95 | extern void pglogical_worker_attach(int slot, PGLogicalWorkerType type); 96 | 97 | extern PGLogicalWorker *pglogical_manager_find(Oid dboid); 98 | extern PGLogicalWorker *pglogical_apply_find(Oid dboid, Oid subscriberid); 99 | extern List *pglogical_apply_find_all(Oid dboid); 100 | 101 | extern PGLogicalWorker *pglogical_sync_find(Oid dboid, Oid subid, 102 | const char *nspname, const char *relname); 103 | extern List *pglogical_sync_find_all(Oid dboid, Oid subscriberid); 104 | 105 | extern PGLogicalWorker *pglogical_get_worker(int slot); 106 | extern bool pglogical_worker_running(PGLogicalWorker *w); 107 | extern void pglogical_worker_kill(PGLogicalWorker *worker); 108 | 109 | extern const char * pglogical_worker_type_name(PGLogicalWorkerType type); 110 | 111 | #endif /* PGLOGICAL_WORKER_H */ 112 | -------------------------------------------------------------------------------- /regress-postgresql.conf: -------------------------------------------------------------------------------- 1 | # Configuration that affects behaviour being tested: 2 | shared_preload_libraries = 'pglogical' 3 | wal_level = logical 4 | max_wal_senders = 20 5 | max_replication_slots = 20 6 | max_worker_processes = 20 7 | track_commit_timestamp = on 8 | 9 | # Purely testing related: 10 | hba_file = './regress-pg_hba.conf' 11 | DateStyle = 'ISO, DMY' 12 | log_line_prefix='[%m] [%p] [%d] ' 13 | fsync=off 14 | 15 | # Handy things to turn on when debugging 16 | #log_min_messages = debug2 17 | #log_error_verbosity = verbose 18 | #log_statement = 'all' 19 | 20 | pglogical.synchronous_commit = true 21 | 22 | # Indirection of dsns for testing 23 | pglogical.orig_provider_dsn = 'dbname=sourcedb' 24 | pglogical.provider_dsn = 'dbname=regression' 25 | pglogical.provider1_dsn = 'dbname=regression1' 26 | pglogical.subscriber_dsn = 'dbname=postgres' 27 | 28 | # Uncomment to test SPI and multi-insert 29 | #pglogical.use_spi = true 30 | #pglogical.conflict_resolution = error 31 | -------------------------------------------------------------------------------- /sql/apply_delay.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | 4 | \c :subscriber_dsn 5 | GRANT ALL ON SCHEMA public TO nonsuper; 6 | SELECT E'\'' || current_database() || E'\'' AS subdb; 7 | \gset 8 | 9 | \c :provider_dsn 10 | 11 | SELECT * FROM pglogical.create_replication_set('delay'); 12 | 13 | \c :subscriber_dsn 14 | 15 | CREATE or REPLACE function int2interval (x integer) returns interval as 16 | $$ select $1*'1 sec'::interval $$ 17 | language sql; 18 | 19 | SELECT * FROM pglogical.create_subscription( 20 | subscription_name := 'test_subscription_delay', 21 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super', 22 | replication_sets := '{delay}', 23 | forward_origins := '{}', 24 | synchronize_structure := false, 25 | synchronize_data := false, 26 | apply_delay := int2interval(2) -- 2 seconds 27 | ); 28 | 29 | BEGIN; 30 | SET LOCAL statement_timeout = '30s'; 31 | SELECT pglogical.wait_for_subscription_sync_complete('test_subscription_delay'); 32 | COMMIT; 33 | 34 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 35 | 36 | SELECT status FROM pglogical.show_subscription_status() WHERE subscription_name = 'test_subscription_delay'; 37 | 38 | -- Make sure we see the slot and active connection 39 | \c :provider_dsn 40 | SELECT plugin, slot_type, database, active FROM pg_replication_slots; 41 | SELECT count(*) FROM pg_stat_replication; 42 | 43 | CREATE TABLE public.timestamps ( 44 | id text primary key, 45 | ts timestamptz 46 | ); 47 | 48 | SELECT pglogical.replicate_ddl_command($$ 49 | CREATE TABLE public.basic_dml1 ( 50 | id serial primary key, 51 | other integer, 52 | data text, 53 | something interval 54 | ); 55 | $$); 56 | -- clear old applies, from any previous tests etc. 57 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 58 | 59 | INSERT INTO timestamps VALUES ('ts1', CURRENT_TIMESTAMP); 60 | 61 | SELECT * FROM pglogical.replication_set_add_table('delay', 'basic_dml1'); 62 | 63 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 64 | 65 | INSERT INTO timestamps VALUES ('ts2', CURRENT_TIMESTAMP); 66 | 67 | INSERT INTO basic_dml1(other, data, something) 68 | VALUES (5, 'foo', '1 minute'::interval), 69 | (4, 'bar', '12 weeks'::interval), 70 | (3, 'baz', '2 years 1 hour'::interval), 71 | (2, 'qux', '8 months 2 days'::interval), 72 | (1, NULL, NULL); 73 | 74 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 75 | 76 | INSERT INTO timestamps VALUES ('ts3', CURRENT_TIMESTAMP); 77 | 78 | SELECT round (EXTRACT(EPOCH FROM (SELECT ts from timestamps where id = 'ts2')) - 79 | EXTRACT(EPOCH FROM (SELECT ts from timestamps where id = 'ts1'))) :: integer >= 2 as ddl_replication_delayed; 80 | SELECT round (EXTRACT(EPOCH FROM (SELECT ts from timestamps where id = 'ts3')) - 81 | EXTRACT(EPOCH FROM (SELECT ts from timestamps where id = 'ts2'))) :: integer >= 2 as inserts_replication_delayed; 82 | 83 | \c :subscriber_dsn 84 | 85 | SELECT * FROM basic_dml1; 86 | 87 | SELECT pglogical.drop_subscription('test_subscription_delay'); 88 | 89 | \c :provider_dsn 90 | \set VERBOSITY terse 91 | SELECT * FROM pglogical.drop_replication_set('delay'); 92 | DROP TABLE public.timestamps CASCADE; 93 | SELECT pglogical.replicate_ddl_command($$ 94 | DROP TABLE public.basic_dml1 CASCADE; 95 | $$); 96 | -------------------------------------------------------------------------------- /sql/basic.sql: -------------------------------------------------------------------------------- 1 | -- basic builtin datatypes 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | SELECT pglogical.replicate_ddl_command($$ 7 | CREATE TABLE public.basic_dml ( 8 | id serial primary key, 9 | other integer, 10 | data text, 11 | something interval 12 | ); 13 | $$); 14 | 15 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 16 | 17 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 18 | 19 | \c :subscriber_dsn 20 | 21 | ALTER TABLE public.basic_dml ADD COLUMN subonly integer; 22 | ALTER TABLE public.basic_dml ADD COLUMN subonly_def integer DEFAULT 99; 23 | 24 | \c :provider_dsn 25 | 26 | -- check basic insert replication 27 | INSERT INTO basic_dml(other, data, something) 28 | VALUES (5, 'foo', '1 minute'::interval), 29 | (4, 'bar', '12 weeks'::interval), 30 | (3, 'baz', '2 years 1 hour'::interval), 31 | (2, 'qux', '8 months 2 days'::interval), 32 | (1, NULL, NULL); 33 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 34 | \c :subscriber_dsn 35 | SELECT id, other, data, something, subonly, subonly_def FROM basic_dml ORDER BY id; 36 | 37 | -- update one row 38 | \c :provider_dsn 39 | UPDATE basic_dml SET other = '4', data = NULL, something = '3 days'::interval WHERE id = 4; 40 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 41 | \c :subscriber_dsn 42 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 43 | 44 | -- update multiple rows 45 | \c :provider_dsn 46 | UPDATE basic_dml SET other = id, data = data || id::text; 47 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 48 | \c :subscriber_dsn 49 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 50 | 51 | \c :provider_dsn 52 | UPDATE basic_dml SET other = id, something = something - '10 seconds'::interval WHERE id < 3; 53 | UPDATE basic_dml SET other = id, something = something + '10 seconds'::interval WHERE id > 3; 54 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 55 | \c :subscriber_dsn 56 | SELECT id, other, data, something, subonly, subonly_def FROM basic_dml ORDER BY id; 57 | 58 | -- delete one row 59 | \c :provider_dsn 60 | DELETE FROM basic_dml WHERE id = 2; 61 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 62 | \c :subscriber_dsn 63 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 64 | 65 | -- delete multiple rows 66 | \c :provider_dsn 67 | DELETE FROM basic_dml WHERE id < 4; 68 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 69 | \c :subscriber_dsn 70 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 71 | 72 | -- truncate 73 | \c :provider_dsn 74 | TRUNCATE basic_dml; 75 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 76 | \c :subscriber_dsn 77 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 78 | 79 | -- copy 80 | \c :provider_dsn 81 | \COPY basic_dml FROM STDIN WITH CSV 82 | 9000,1,aaa,1 hour 83 | 9001,2,bbb,2 years 84 | 9002,3,ccc,3 minutes 85 | 9003,4,ddd,4 days 86 | \. 87 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 88 | \c :subscriber_dsn 89 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 90 | 91 | \c :provider_dsn 92 | \set VERBOSITY terse 93 | SELECT pglogical.replicate_ddl_command($$ 94 | DROP TABLE public.basic_dml CASCADE; 95 | $$); 96 | -------------------------------------------------------------------------------- /sql/bidirectional.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | SELECT E'\'' || current_database() || E'\'' AS pubdb; 7 | \gset 8 | 9 | \c :provider_dsn 10 | DO $$ 11 | BEGIN 12 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 13 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 14 | END IF; 15 | END;$$; 16 | 17 | SELECT * FROM pglogical.create_subscription( 18 | subscription_name := 'test_bidirectional', 19 | provider_dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=super', 20 | synchronize_structure := false, 21 | synchronize_data := false, 22 | forward_origins := '{}'); 23 | 24 | BEGIN; 25 | SET LOCAL statement_timeout = '10s'; 26 | SELECT pglogical.wait_for_subscription_sync_complete('test_bidirectional'); 27 | COMMIT; 28 | 29 | \c :subscriber_dsn 30 | SELECT pglogical.replicate_ddl_command($$ 31 | CREATE TABLE public.basic_dml ( 32 | id serial primary key, 33 | other integer, 34 | data text, 35 | something interval 36 | ); 37 | $$); 38 | 39 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 40 | 41 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 42 | 43 | \c :provider_dsn 44 | 45 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 46 | 47 | -- check basic insert replication 48 | INSERT INTO basic_dml(other, data, something) 49 | VALUES (5, 'foo', '1 minute'::interval), 50 | (4, 'bar', '12 weeks'::interval), 51 | (3, 'baz', '2 years 1 hour'::interval), 52 | (2, 'qux', '8 months 2 days'::interval), 53 | (1, NULL, NULL); 54 | 55 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 56 | 57 | \c :subscriber_dsn 58 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 59 | 60 | UPDATE basic_dml SET other = id, something = something - '10 seconds'::interval WHERE id < 3; 61 | UPDATE basic_dml SET other = id, something = something + '10 seconds'::interval WHERE id > 3; 62 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 63 | 64 | \c :provider_dsn 65 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 66 | 67 | \c :provider_dsn 68 | \set VERBOSITY terse 69 | SELECT pglogical.replicate_ddl_command($$ 70 | DROP TABLE public.basic_dml CASCADE; 71 | $$); 72 | 73 | SELECT pglogical.drop_subscription('test_bidirectional'); 74 | 75 | SET client_min_messages = 'warning'; 76 | DROP EXTENSION IF EXISTS pglogical_origin; 77 | 78 | \c :subscriber_dsn 79 | \a 80 | SELECT slot_name FROM pg_replication_slots WHERE database = current_database(); 81 | SELECT count(*) FROM pg_stat_replication WHERE application_name = 'test_bidirectional'; 82 | -------------------------------------------------------------------------------- /sql/conflict_secondary_unique.sql: -------------------------------------------------------------------------------- 1 | --PRIMARY KEY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | 7 | -- Test conflicts where a secondary unique constraint with a predicate exits, 8 | -- ensuring we don't generate false conflicts. 9 | SELECT pglogical.replicate_ddl_command($$ 10 | CREATE TABLE public.secondary_unique_pred ( 11 | a integer PRIMARY KEY, 12 | b integer NOT NULL, 13 | check_unique boolean NOT NULL 14 | ); 15 | 16 | CREATE UNIQUE INDEX ON public.secondary_unique_pred (b) WHERE (check_unique); 17 | $$); 18 | 19 | SELECT * FROM pglogical.replication_set_add_table('default', 'secondary_unique_pred'); 20 | 21 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (1, 1, false); 22 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (2, 1, false); 23 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (3, 2, true); 24 | -- must fail 25 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (5, 2, true); 26 | 27 | SELECT * FROM secondary_unique_pred ORDER BY a; 28 | 29 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 30 | 31 | \c :subscriber_dsn 32 | 33 | SELECT * FROM secondary_unique_pred ORDER BY a; 34 | 35 | \c :provider_dsn 36 | 37 | -- This line doesn't conflict on the provider. On the subscriber 38 | -- we must not detect a conflict on (b), since the existing local 39 | -- row matches (check_unique) but the new remote row doesn't. So 40 | -- this must get applied. 41 | INSERT INTO secondary_unique_pred (a, b, check_unique) VALUES (4, 2, false); 42 | 43 | SELECT * FROM secondary_unique_pred ORDER BY a; 44 | 45 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 46 | 47 | \c :subscriber_dsn 48 | 49 | SELECT * FROM secondary_unique_pred ORDER BY a; 50 | 51 | \c :provider_dsn 52 | \set VERBOSITY terse 53 | SELECT pglogical.replicate_ddl_command($$ 54 | DROP TABLE public.secondary_unique_pred CASCADE; 55 | $$); 56 | -------------------------------------------------------------------------------- /sql/copy.sql: -------------------------------------------------------------------------------- 1 | --test COPY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | 7 | SELECT pglogical.replicate_ddl_command($$ 8 | CREATE TABLE public.x ( 9 | a serial primary key, 10 | b int, 11 | c text not null default 'stuff', 12 | d text, 13 | e text 14 | ); 15 | $$); 16 | 17 | SELECT * FROM pglogical.replication_set_add_table('default', 'x'); 18 | 19 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 20 | 21 | COPY x (a, b, c, d, e) from stdin; 22 | 9999 \N \\N \NN \N 23 | 10000 21 31 41 51 24 | \. 25 | 26 | COPY x (b, d) from stdin; 27 | 1 test_1 28 | \. 29 | 30 | COPY x (b, d) from stdin; 31 | 2 test_2 32 | 3 test_3 33 | 4 test_4 34 | 5 test_5 35 | 6 test_6 36 | 7 test_7 37 | 8 test_8 38 | 9 test_9 39 | 10 test_10 40 | 11 test_11 41 | 12 test_12 42 | 13 test_13 43 | 14 test_14 44 | 15 test_15 45 | \. 46 | 47 | COPY x (a, b, c, d, e) from stdin; 48 | 10001 22 32 42 52 49 | 10002 23 33 43 53 50 | 10003 24 34 44 54 51 | 10004 25 35 45 55 52 | 10005 26 36 46 56 53 | \. 54 | 55 | SELECT * FROM x ORDER BY a; 56 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 57 | 58 | \c :subscriber_dsn 59 | SELECT * FROM x ORDER BY a; 60 | 61 | \c :provider_dsn 62 | 63 | \set VERBOSITY terse 64 | SELECT pglogical.replicate_ddl_command($$ 65 | DROP TABLE public.x CASCADE; 66 | $$); 67 | -------------------------------------------------------------------------------- /sql/drop.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | 4 | \c :provider_dsn 5 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider'); 6 | 7 | SELECT plugin, slot_type, active FROM pg_replication_slots; 8 | SELECT count(*) FROM pg_stat_replication; 9 | 10 | \c :subscriber_dsn 11 | SELECT * FROM pglogical.drop_subscription('test_subscription'); 12 | SELECT * FROM pglogical.drop_node(node_name := 'test_subscriber'); 13 | 14 | \c :provider_dsn 15 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider'); 16 | 17 | \c :subscriber_dsn 18 | DROP OWNED BY nonsuper, super CASCADE; 19 | 20 | \c :provider_dsn 21 | DROP OWNED BY nonsuper, super CASCADE; 22 | 23 | \c :provider1_dsn 24 | DROP OWNED BY nonsuper, super CASCADE; 25 | 26 | \c :orig_provider_dsn 27 | DROP OWNED BY nonsuper, super CASCADE; 28 | 29 | \c :subscriber_dsn 30 | SET client_min_messages = 'warning'; 31 | DROP ROLE IF EXISTS nonsuper, super; 32 | 33 | \c :provider_dsn 34 | SET client_min_messages = 'warning'; 35 | DROP ROLE IF EXISTS nonsuper, super; 36 | 37 | \c :provider1_dsn 38 | SET client_min_messages = 'warning'; 39 | DROP ROLE IF EXISTS nonsuper, super; 40 | 41 | \c :orig_provider_dsn 42 | SET client_min_messages = 'warning'; 43 | DROP ROLE IF EXISTS nonsuper, super; 44 | -------------------------------------------------------------------------------- /sql/foreign_key.sql: -------------------------------------------------------------------------------- 1 | --FOREIGN KEY 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | 6 | \c :provider_dsn 7 | 8 | SELECT pglogical.replicate_ddl_command($$ 9 | CREATE TABLE public.f1k_products ( 10 | product_no integer PRIMARY KEY, 11 | product_id integer, 12 | name text, 13 | price numeric 14 | ); 15 | 16 | CREATE TABLE public.f1k_orders ( 17 | order_id integer, 18 | product_no integer REFERENCES public.f1k_products (product_no), 19 | quantity integer 20 | ); 21 | --pass 22 | $$); 23 | 24 | SELECT * FROM pglogical.replication_set_add_table('default', 'f1k_products'); 25 | SELECT * FROM pglogical.replication_set_add_table('default_insert_only', 'f1k_orders'); 26 | 27 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 28 | 29 | INSERT into public.f1k_products VALUES (1, 1, 'product1', 1.20); 30 | INSERT into public.f1k_products VALUES (2, 2, 'product2', 2.40); 31 | 32 | INSERT into public.f1k_orders VALUES (300, 1, 4); 33 | INSERT into public.f1k_orders VALUES (22, 2, 14); 34 | INSERT into public.f1k_orders VALUES (23, 2, 24); 35 | INSERT into public.f1k_orders VALUES (24, 2, 40); 36 | 37 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 38 | 39 | \c :subscriber_dsn 40 | 41 | SELECT * FROM public.f1k_products; 42 | SELECT * FROM public.f1k_orders; 43 | 44 | 45 | \c :provider_dsn 46 | \set VERBOSITY terse 47 | SELECT pglogical.replicate_ddl_command($$ 48 | DROP TABLE public.f1k_orders CASCADE; 49 | DROP TABLE public.f1k_products CASCADE; 50 | $$); 51 | -------------------------------------------------------------------------------- /sql/huge_tx.sql: -------------------------------------------------------------------------------- 1 | -- test huge transactions 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | -- lots of small rows replication with DDL outside transaction 7 | SELECT pglogical.replicate_ddl_command($$ 8 | CREATE TABLE public.a_huge ( 9 | id integer primary key, 10 | id1 integer, 11 | data text default 'data', 12 | data1 text default 'data1' 13 | ); 14 | $$); 15 | SELECT * FROM pglogical.replication_set_add_table('default', 'a_huge'); 16 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 17 | 18 | BEGIN; 19 | 20 | INSERT INTO public.a_huge VALUES (generate_series(1, 20000000), generate_series(1, 20000000)); 21 | 22 | COMMIT; 23 | 24 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 25 | 26 | \c :subscriber_dsn 27 | SELECT count(*) FROM a_huge; 28 | \dtS+ a_huge; 29 | 30 | \c :provider_dsn 31 | -- lots of small rows replication with DDL within transaction 32 | BEGIN; 33 | SELECT pglogical.replicate_ddl_command($$ 34 | CREATE TABLE public.b_huge ( 35 | id integer primary key, 36 | id1 integer, 37 | data text default 'data', 38 | data1 text default 'data1' 39 | ); 40 | $$); 41 | 42 | SELECT * FROM pglogical.replication_set_add_table('default', 'b_huge'); 43 | 44 | INSERT INTO public.b_huge VALUES (generate_series(1,20000000), generate_series(1,20000000)); 45 | 46 | COMMIT; 47 | 48 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 49 | 50 | \c :subscriber_dsn 51 | SELECT count(*) FROM b_huge; 52 | \dtS+ b_huge; 53 | 54 | \c :provider_dsn 55 | \set VERBOSITY terse 56 | SELECT pglogical.replicate_ddl_command($$ 57 | DROP TABLE public.a_huge CASCADE; 58 | DROP TABLE public.b_huge CASCADE; 59 | $$); 60 | 61 | 62 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 63 | -------------------------------------------------------------------------------- /sql/huge_tx_100k_tables.sql: -------------------------------------------------------------------------------- 1 | -- test huge transactions 2 | -- Set 'max_locks_per_transaction' to 10000 to run test 3 | SELECT * FROM pglogical_regress_variables() 4 | \gset 5 | 6 | \c :provider_dsn 7 | -- medium number of rows in many different tables (100k): replication with DDL outside transaction 8 | 9 | create or replace function create_many_tables(int, int) returns void language plpgsql as $$ 10 | DECLARE 11 | i int; 12 | cr_command varchar; 13 | BEGIN 14 | FOR i IN $1 .. $2 LOOP 15 | cr_command := 'SELECT pglogical.replicate_ddl_command('' 16 | CREATE TABLE public.HUGE' || i || ' ( 17 | id integer primary key, 18 | id1 integer, 19 | data text default ''''data'''', 20 | data1 text default ''''data1'''' 21 | ); 22 | '')'; 23 | EXECUTE cr_command; 24 | END LOOP; 25 | END; 26 | $$; 27 | 28 | -- write multile version of this statement 29 | 30 | create or replace function add_many_tables_to_replication_set(int, int) returns void language plpgsql as $$ 31 | DECLARE 32 | i int; 33 | cr_command varchar; 34 | BEGIN 35 | FOR i IN $1 .. $2 LOOP 36 | cr_command := 'SELECT * FROM pglogical.replication_set_add_table( 37 | ''default'', ''HUGE' || i || ''' );'; 38 | EXECUTE cr_command; 39 | END LOOP; 40 | END; 41 | $$; 42 | 43 | create or replace function insert_into_many_tables(int, int) returns void language plpgsql as $$ 44 | DECLARE 45 | i int; 46 | cr_command varchar; 47 | BEGIN 48 | FOR i IN $1 .. $2 LOOP 49 | cr_command := 'INSERT INTO public.HUGE' || i || ' VALUES (generate_series(1, 200), generate_series(1, 200))'; 50 | 51 | EXECUTE cr_command; 52 | END LOOP; 53 | END; 54 | $$; 55 | 56 | create or replace function drop_many_tables(int, int) returns void language plpgsql as $$ 57 | DECLARE 58 | i int; 59 | cr_command varchar; 60 | BEGIN 61 | FOR i IN $1 .. $2 LOOP 62 | cr_command := 'SELECT pglogical.replicate_ddl_command('' 63 | DROP TABLE public.HUGE' || i ||' CASCADE; 64 | '')'; 65 | EXECUTE cr_command; 66 | END LOOP; 67 | END; 68 | $$; 69 | 70 | SELECT * FROM create_many_tables(1,100000); 71 | SELECT * FROM add_many_tables_to_replication_set(1,100000); 72 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 73 | BEGIN; 74 | SELECT * FROM insert_into_many_tables(1,100000); 75 | COMMIT; 76 | 77 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 78 | 79 | \c :subscriber_dsn 80 | 81 | SELECT count(*) FROM public.HUGE2; 82 | \dtS+ public.HUGE2; 83 | 84 | \c :provider_dsn 85 | 86 | \set VERBOSITY terse 87 | SELECT * FROM drop_many_tables(1,100000); 88 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 89 | 90 | -- medium number of rows in many different tables: replication with DDL inside transaction 91 | BEGIN; 92 | SELECT * FROM create_many_tables(1,100000); 93 | SELECT * FROM add_many_tables_to_replication_set(1,100000); 94 | SELECT * FROM insert_into_many_tables(1,100000); 95 | COMMIT; 96 | 97 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 98 | 99 | \c :subscriber_dsn 100 | 101 | SELECT count(*) FROM public.HUGE2; 102 | \dtS+ public.HUGE2; 103 | 104 | \c :provider_dsn 105 | 106 | \set VERBOSITY terse 107 | SELECT * FROM drop_many_tables(1,100000); 108 | 109 | DROP function create_many_tables(int, int); 110 | DROP function add_many_tables_to_replication_set(int,int); 111 | DROP function insert_into_many_tables(int, int); 112 | DROP function drop_many_tables(int, int); 113 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 114 | 115 | -------------------------------------------------------------------------------- /sql/huge_tx_many_tables.sql: -------------------------------------------------------------------------------- 1 | -- test huge transactions 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | -- medium number of rows in many different tables: replication with DDL outside transaction 7 | 8 | create or replace function create_many_tables(int, int) returns void language plpgsql as $$ 9 | DECLARE 10 | i int; 11 | cr_command varchar; 12 | BEGIN 13 | FOR i IN $1 .. $2 LOOP 14 | cr_command := 'SELECT pglogical.replicate_ddl_command('' 15 | CREATE TABLE public.HUGE' || i || ' ( 16 | id integer primary key, 17 | id1 integer, 18 | data text default ''''data'''', 19 | data1 text default ''''data1'''' 20 | ); 21 | '')'; 22 | EXECUTE cr_command; 23 | END LOOP; 24 | END; 25 | $$; 26 | --write multiple version of this. 27 | 28 | create or replace function add_many_tables_to_replication_set(int, int) returns void language plpgsql as $$ 29 | DECLARE 30 | i int; 31 | cr_command varchar; 32 | BEGIN 33 | FOR i IN $1 .. $2 LOOP 34 | cr_command := 'SELECT * FROM pglogical.replication_set_add_table( 35 | ''default'', ''HUGE' || i || ''' );'; 36 | EXECUTE cr_command; 37 | END LOOP; 38 | END; 39 | $$; 40 | 41 | create or replace function insert_into_many_tables(int, int) returns void language plpgsql as $$ 42 | DECLARE 43 | i int; 44 | cr_command varchar; 45 | BEGIN 46 | FOR i IN $1 .. $2 LOOP 47 | cr_command := 'INSERT INTO public.HUGE' || i || ' VALUES (generate_series(1, 100000), generate_series(1, 100000))'; 48 | 49 | EXECUTE cr_command; 50 | END LOOP; 51 | END; 52 | $$; 53 | 54 | create or replace function drop_many_tables(int, int) returns void language plpgsql as $$ 55 | DECLARE 56 | i int; 57 | cr_command varchar; 58 | BEGIN 59 | FOR i IN $1 .. $2 LOOP 60 | cr_command := 'SELECT pglogical.replicate_ddl_command('' 61 | DROP TABLE public.HUGE' || i ||' CASCADE; 62 | '')'; 63 | EXECUTE cr_command; 64 | END LOOP; 65 | END; 66 | $$; 67 | 68 | SELECT * FROM create_many_tables(1,200); 69 | SELECT * FROM add_many_tables_to_replication_set(1,200); 70 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 71 | BEGIN; 72 | SELECT * FROM insert_into_many_tables(1,200); 73 | COMMIT; 74 | 75 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 76 | 77 | \c :subscriber_dsn 78 | 79 | SELECT count(*) FROM public.HUGE2; 80 | \dtS+ public.HUGE2; 81 | 82 | \c :provider_dsn 83 | 84 | \set VERBOSITY terse 85 | SELECT * FROM drop_many_tables(1,200); 86 | 87 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 88 | 89 | -- medium number of rows in many different tables: replication with DDL inside transaction 90 | BEGIN; 91 | SELECT * FROM create_many_tables(1,200); 92 | SELECT * FROM add_many_tables_to_replication_set(1,200); 93 | SELECT * FROM insert_into_many_tables(1,200); 94 | COMMIT; 95 | 96 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 97 | 98 | \c :subscriber_dsn 99 | 100 | SELECT count(*) FROM public.HUGE2; 101 | \dtS+ public.HUGE2; 102 | 103 | \c :provider_dsn 104 | 105 | \set VERBOSITY terse 106 | SELECT * FROM drop_many_tables(1,200); 107 | DROP function create_many_tables(int, int); 108 | DROP function add_many_tables_to_replication_set(int, int); 109 | DROP function insert_into_many_tables(int, int); 110 | DROP function drop_many_tables(int, int); 111 | 112 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 113 | 114 | -------------------------------------------------------------------------------- /sql/infofuncs.sql: -------------------------------------------------------------------------------- 1 | DO $$ 2 | BEGIN 3 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 4 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 5 | END IF; 6 | END;$$; 7 | CREATE EXTENSION pglogical; 8 | 9 | SELECT pglogical.pglogical_max_proto_version(); 10 | 11 | SELECT pglogical.pglogical_min_proto_version(); 12 | 13 | -- test extension version 14 | SELECT pglogical.pglogical_version() = extversion 15 | FROM pg_extension 16 | WHERE extname = 'pglogical'; 17 | 18 | DROP EXTENSION pglogical; 19 | 20 | -- test upgrades 21 | DO $$ 22 | BEGIN 23 | IF version() ~ 'Postgres-XL' THEN 24 | CREATE EXTENSION IF NOT EXISTS pglogical; 25 | ELSE 26 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 27 | END IF; 28 | END; 29 | $$; 30 | ALTER EXTENSION pglogical UPDATE; 31 | 32 | SELECT pglogical.pglogical_version() = extversion 33 | FROM pg_extension 34 | WHERE extname = 'pglogical'; 35 | 36 | DROP EXTENSION pglogical; 37 | -------------------------------------------------------------------------------- /sql/init.sql: -------------------------------------------------------------------------------- 1 | -- This should be done with pg_regress's --create-role option 2 | -- but it's blocked by bug 37906 3 | SELECT * FROM pglogical_regress_variables() 4 | \gset 5 | 6 | \c :provider_dsn 7 | SET client_min_messages = 'warning'; 8 | DROP USER IF EXISTS nonsuper; 9 | DROP USER IF EXISTS super; 10 | 11 | CREATE USER nonsuper WITH replication; 12 | CREATE USER super SUPERUSER; 13 | 14 | \c :subscriber_dsn 15 | SET client_min_messages = 'warning'; 16 | DROP USER IF EXISTS nonsuper; 17 | DROP USER IF EXISTS super; 18 | 19 | CREATE USER nonsuper WITH replication; 20 | CREATE USER super SUPERUSER; 21 | 22 | -- Can't because of bug 37906 23 | --GRANT ALL ON DATABASE regress TO nonsuper; 24 | --GRANT ALL ON DATABASE regress TO nonsuper; 25 | 26 | \c :provider_dsn 27 | GRANT ALL ON SCHEMA public TO nonsuper; 28 | 29 | DO $$ 30 | BEGIN 31 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') >= 1000 THEN 32 | CREATE OR REPLACE FUNCTION public.pg_current_xlog_location() RETURNS pg_lsn 33 | LANGUAGE SQL AS 'SELECT pg_current_wal_lsn()'; 34 | ALTER FUNCTION public.pg_current_xlog_location() OWNER TO super; 35 | END IF; 36 | END; $$; 37 | 38 | \c :subscriber_dsn 39 | GRANT ALL ON SCHEMA public TO nonsuper; 40 | SELECT E'\'' || current_database() || E'\'' AS subdb; 41 | \gset 42 | 43 | \c :provider_dsn 44 | SET client_min_messages = 'warning'; 45 | 46 | DO $$ 47 | BEGIN 48 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 49 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 50 | END IF; 51 | END;$$; 52 | 53 | DO $$ 54 | BEGIN 55 | IF version() ~ 'Postgres-XL' THEN 56 | CREATE EXTENSION IF NOT EXISTS pglogical; 57 | ELSE 58 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 59 | END IF; 60 | END; 61 | $$; 62 | ALTER EXTENSION pglogical UPDATE; 63 | 64 | \dx pglogical 65 | 66 | SELECT * FROM pglogical.create_node(node_name := 'test_provider', dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super'); 67 | 68 | \c :subscriber_dsn 69 | SET client_min_messages = 'warning'; 70 | 71 | DO $$ 72 | BEGIN 73 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 74 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 75 | END IF; 76 | END;$$; 77 | 78 | CREATE EXTENSION IF NOT EXISTS pglogical; 79 | 80 | SELECT * FROM pglogical.create_node(node_name := 'test_subscriber', dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=super'); 81 | 82 | BEGIN; 83 | SELECT * FROM pglogical.create_subscription( 84 | subscription_name := 'test_subscription', 85 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super', 86 | synchronize_structure := true, 87 | forward_origins := '{}'); 88 | /* 89 | * Remove the function we added in preseed because otherwise the restore of 90 | * schema will fail. We do this in same transaction as create_subscription() 91 | * because the subscription process will only start on commit. 92 | */ 93 | DROP FUNCTION IF EXISTS public.pglogical_regress_variables(); 94 | COMMIT; 95 | 96 | BEGIN; 97 | SET LOCAL statement_timeout = '30s'; 98 | SELECT pglogical.wait_for_subscription_sync_complete('test_subscription'); 99 | COMMIT; 100 | 101 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 102 | 103 | -- Make sure we see the slot and active connection 104 | \c :provider_dsn 105 | SELECT plugin, slot_type, active FROM pg_replication_slots; 106 | SELECT count(*) FROM pg_stat_replication; 107 | -------------------------------------------------------------------------------- /sql/init_fail.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | SET client_min_messages = 'warning'; 7 | DROP ROLE IF EXISTS nonreplica; 8 | CREATE USER nonreplica; 9 | 10 | DO $$ 11 | BEGIN 12 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 13 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 14 | END IF; 15 | END;$$; 16 | CREATE EXTENSION IF NOT EXISTS pglogical; 17 | GRANT ALL ON SCHEMA pglogical TO nonreplica; 18 | GRANT ALL ON ALL TABLES IN SCHEMA pglogical TO nonreplica; 19 | 20 | \c :subscriber_dsn 21 | SET client_min_messages = 'warning'; 22 | \set VERBOSITY terse 23 | DO $$ 24 | BEGIN 25 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 26 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 27 | END IF; 28 | END;$$; 29 | 30 | DO $$ 31 | BEGIN 32 | IF version() ~ 'Postgres-XL' THEN 33 | CREATE EXTENSION IF NOT EXISTS pglogical; 34 | ELSE 35 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 36 | END IF; 37 | END; 38 | $$; 39 | ALTER EXTENSION pglogical UPDATE; 40 | 41 | -- fail (local node not existing) 42 | SELECT * FROM pglogical.create_subscription( 43 | subscription_name := 'test_subscription', 44 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 45 | forward_origins := '{}'); 46 | 47 | -- succeed 48 | SELECT * FROM pglogical.create_node(node_name := 'test_subscriber', dsn := (SELECT subscriber_dsn FROM pglogical_regress_variables()) || ' user=nonreplica'); 49 | 50 | -- fail (can't connect to remote) 51 | DO $$ 52 | BEGIN 53 | SELECT * FROM pglogical.create_subscription( 54 | subscription_name := 'test_subscription', 55 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonexisting', 56 | forward_origins := '{}'); 57 | EXCEPTION 58 | WHEN OTHERS THEN 59 | RAISE EXCEPTION '%:%', split_part(SQLERRM, ':', 1), (regexp_matches(SQLERRM, '^.*( FATAL:.*role.*)$'))[1]; 60 | END; 61 | $$; 62 | 63 | -- fail (remote node not existing) 64 | SELECT * FROM pglogical.create_subscription( 65 | subscription_name := 'test_subscription', 66 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 67 | forward_origins := '{}'); 68 | 69 | \c :provider_dsn 70 | -- succeed 71 | SELECT * FROM pglogical.create_node(node_name := 'test_provider', dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica'); 72 | 73 | \c :subscriber_dsn 74 | \set VERBOSITY terse 75 | 76 | -- fail (can't connect with replication connection to remote) 77 | DO $$ 78 | BEGIN 79 | SELECT * FROM pglogical.create_subscription( 80 | subscription_name := 'test_subscription', 81 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=nonreplica', 82 | forward_origins := '{}'); 83 | EXCEPTION 84 | WHEN OTHERS THEN 85 | RAISE EXCEPTION '%', split_part(SQLERRM, ':', 1); 86 | END; 87 | $$; 88 | -- cleanup 89 | 90 | SELECT * FROM pglogical.drop_node('test_subscriber'); 91 | DROP EXTENSION pglogical; 92 | 93 | \c :provider_dsn 94 | SELECT * FROM pglogical.drop_node('test_provider'); 95 | 96 | SET client_min_messages = 'warning'; 97 | DROP OWNED BY nonreplica; 98 | DROP ROLE IF EXISTS nonreplica; 99 | DROP EXTENSION pglogical; 100 | -------------------------------------------------------------------------------- /sql/interfaces.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | CREATE USER super2 SUPERUSER; 7 | 8 | \c :subscriber_dsn 9 | SELECT * FROM pglogical.alter_node_add_interface('test_provider', 'super2', (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super2'); 10 | 11 | SELECT * FROM pglogical.alter_subscription_interface('test_subscription', 'super2'); 12 | 13 | DO $$ 14 | BEGIN 15 | FOR i IN 1..100 LOOP 16 | IF EXISTS (SELECT 1 FROM pglogical.show_subscription_status() WHERE status != 'down') THEN 17 | EXIT; 18 | END IF; 19 | PERFORM pg_sleep(0.1); 20 | END LOOP; 21 | END;$$; 22 | 23 | SELECT pg_sleep(0.1); 24 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 25 | 26 | \c :provider_dsn 27 | SELECT plugin, slot_type, active FROM pg_replication_slots; 28 | SELECT usename FROM pg_stat_replication WHERE application_name = 'test_subscription'; 29 | 30 | \c :subscriber_dsn 31 | SELECT * FROM pglogical.alter_subscription_interface('test_subscription', 'test_provider'); 32 | 33 | DO $$ 34 | BEGIN 35 | FOR i IN 1..100 LOOP 36 | IF EXISTS (SELECT 1 FROM pglogical.show_subscription_status() WHERE status != 'down') THEN 37 | EXIT; 38 | END IF; 39 | PERFORM pg_sleep(0.1); 40 | END LOOP; 41 | END;$$; 42 | 43 | SELECT pg_sleep(0.1); 44 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 45 | 46 | \c :provider_dsn 47 | DROP USER super2; 48 | SELECT plugin, slot_type, active FROM pg_replication_slots; 49 | SELECT usename FROM pg_stat_replication WHERE application_name = 'test_subscription'; 50 | -------------------------------------------------------------------------------- /sql/matview.sql: -------------------------------------------------------------------------------- 1 | /* First test whether a table's replication set can be properly manipulated */ 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | 7 | SELECT pglogical.replicate_ddl_command($$ 8 | CREATE TABLE public.test_tbl(id serial primary key, data text); 9 | CREATE MATERIALIZED VIEW public.test_mv AS (SELECT * FROM public.test_tbl); 10 | $$); 11 | 12 | SELECT * FROM pglogical.replication_set_add_all_tables('default', '{public}'); 13 | 14 | INSERT INTO test_tbl VALUES (1, 'a'); 15 | 16 | REFRESH MATERIALIZED VIEW test_mv; 17 | 18 | INSERT INTO test_tbl VALUES (2, 'b'); 19 | 20 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 21 | 22 | SELECT * FROM test_tbl ORDER BY id; 23 | SELECT * FROM test_mv ORDER BY id; 24 | 25 | \c :subscriber_dsn 26 | 27 | SELECT * FROM test_tbl ORDER BY id; 28 | SELECT * FROM test_mv ORDER BY id; 29 | 30 | \c :provider_dsn 31 | SELECT pglogical.replicate_ddl_command($$ 32 | CREATE UNIQUE INDEX ON public.test_mv(id); 33 | $$); 34 | INSERT INTO test_tbl VALUES (3, 'c'); 35 | 36 | REFRESH MATERIALIZED VIEW CONCURRENTLY test_mv; 37 | 38 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 39 | 40 | INSERT INTO test_tbl VALUES (4, 'd'); 41 | 42 | SELECT pglogical.replicate_ddl_command($$ 43 | REFRESH MATERIALIZED VIEW public.test_mv; 44 | $$); 45 | 46 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 47 | 48 | INSERT INTO test_tbl VALUES (5, 'e'); 49 | 50 | SELECT pglogical.replicate_ddl_command($$ 51 | REFRESH MATERIALIZED VIEW CONCURRENTLY public.test_mv; 52 | $$); 53 | 54 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 55 | 56 | SELECT * FROM test_tbl ORDER BY id; 57 | SELECT * FROM test_mv ORDER BY id; 58 | 59 | \c :subscriber_dsn 60 | 61 | SELECT * FROM test_tbl ORDER BY id; 62 | SELECT * FROM test_mv ORDER BY id; 63 | 64 | \c :provider_dsn 65 | \set VERBOSITY terse 66 | SELECT pglogical.replicate_ddl_command($$ 67 | DROP TABLE public.test_tbl CASCADE; 68 | $$); 69 | -------------------------------------------------------------------------------- /sql/multiple_upstreams.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | 4 | \c :subscriber_dsn 5 | GRANT ALL ON SCHEMA public TO nonsuper; 6 | SELECT E'\'' || current_database() || E'\'' AS subdb; 7 | \gset 8 | 9 | \c :provider1_dsn 10 | SET client_min_messages = 'warning'; 11 | 12 | GRANT ALL ON SCHEMA public TO nonsuper; 13 | 14 | SET client_min_messages = 'warning'; 15 | 16 | DO $$ 17 | BEGIN 18 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 19 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 20 | END IF; 21 | END;$$; 22 | 23 | CREATE EXTENSION IF NOT EXISTS pglogical; 24 | 25 | SELECT * FROM pglogical.create_node(node_name := 'test_provider1', dsn := (SELECT provider1_dsn FROM pglogical_regress_variables()) || ' user=super'); 26 | 27 | \c :provider_dsn 28 | -- add these entries to provider 29 | SELECT pglogical.replicate_ddl_command($$ 30 | CREATE TABLE public.multi_ups_tbl(id integer primary key, key text unique not null, data text); 31 | $$); 32 | 33 | INSERT INTO multi_ups_tbl VALUES(1, 'key1', 'data1'); 34 | INSERT INTO multi_ups_tbl VALUES(2, 'key2', 'data2'); 35 | INSERT INTO multi_ups_tbl VALUES(3, 'key3', 'data3'); 36 | 37 | SELECT * FROM pglogical.replication_set_add_table('default', 'multi_ups_tbl', true); 38 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 39 | 40 | \c :provider1_dsn 41 | 42 | -- add these entries to provider1 43 | CREATE TABLE multi_ups_tbl(id integer primary key, key text unique not null, data text); 44 | INSERT INTO multi_ups_tbl VALUES(4, 'key4', 'data4'); 45 | INSERT INTO multi_ups_tbl VALUES(5, 'key5', 'data5'); 46 | INSERT INTO multi_ups_tbl VALUES(6, 'key6', 'data6'); 47 | 48 | SELECT * FROM pglogical.replication_set_add_table('default', 'multi_ups_tbl'); 49 | 50 | \c :subscriber_dsn 51 | 52 | -- We'll use the already existing pglogical node 53 | -- notice synchronize_structure as false when table definition already exists 54 | SELECT * FROM pglogical.create_subscription( 55 | subscription_name := 'test_subscription1', 56 | provider_dsn := (SELECT provider1_dsn FROM pglogical_regress_variables()) || ' user=super', 57 | synchronize_structure := false, 58 | forward_origins := '{}'); 59 | 60 | BEGIN; 61 | SET LOCAL statement_timeout = '10s'; 62 | SELECT pglogical.wait_for_subscription_sync_complete('test_subscription1'); 63 | COMMIT; 64 | 65 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 66 | 67 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 68 | 69 | SELECT * from multi_ups_tbl ORDER BY id; 70 | 71 | -- Make sure we see the slot and active connection 72 | \c :provider1_dsn 73 | SELECT plugin, slot_type, active FROM pg_replication_slots; 74 | SELECT count(*) FROM pg_stat_replication; 75 | 76 | -- cleanup 77 | \c :provider_dsn 78 | \set VERBOSITY terse 79 | SELECT pglogical.replicate_ddl_command($$ 80 | DROP TABLE public.multi_ups_tbl CASCADE; 81 | $$); 82 | 83 | \c :provider1_dsn 84 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider1'); 85 | \set VERBOSITY terse 86 | DROP TABLE public.multi_ups_tbl CASCADE; 87 | 88 | \c :subscriber_dsn 89 | SELECT * FROM pglogical.drop_subscription('test_subscription1'); 90 | 91 | \c :provider1_dsn 92 | SELECT * FROM pglogical.drop_node(node_name := 'test_provider1'); 93 | SELECT plugin, slot_type, active FROM pg_replication_slots; 94 | SELECT count(*) FROM pg_stat_replication; 95 | -------------------------------------------------------------------------------- /sql/node_origin_cascade.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | 4 | \c :provider_dsn 5 | SELECT E'\'' || current_database() || E'\'' AS pubdb; 6 | \gset 7 | 8 | \c :orig_provider_dsn 9 | SET client_min_messages = 'warning'; 10 | 11 | GRANT ALL ON SCHEMA public TO nonsuper; 12 | 13 | SET client_min_messages = 'warning'; 14 | 15 | DO $$ 16 | BEGIN 17 | IF (SELECT setting::integer/100 FROM pg_settings WHERE name = 'server_version_num') = 904 THEN 18 | CREATE EXTENSION IF NOT EXISTS pglogical_origin; 19 | END IF; 20 | END;$$; 21 | 22 | DO $$ 23 | BEGIN 24 | IF version() ~ 'Postgres-XL' THEN 25 | CREATE EXTENSION IF NOT EXISTS pglogical; 26 | ELSE 27 | CREATE EXTENSION IF NOT EXISTS pglogical VERSION '1.0.0'; 28 | END IF; 29 | END; 30 | $$; 31 | ALTER EXTENSION pglogical UPDATE; 32 | 33 | SELECT * FROM pglogical.create_node(node_name := 'test_orig_provider', dsn := (SELECT orig_provider_dsn FROM pglogical_regress_variables()) || ' user=super'); 34 | 35 | \c :provider_dsn 36 | SET client_min_messages = 'warning'; 37 | -- test_provider pglogical node already exists here. 38 | 39 | BEGIN; 40 | SELECT * FROM pglogical.create_subscription( 41 | subscription_name := 'test_orig_subscription', 42 | provider_dsn := (SELECT orig_provider_dsn FROM pglogical_regress_variables()) || ' user=super', 43 | synchronize_structure := false, 44 | forward_origins := '{}'); 45 | COMMIT; 46 | 47 | BEGIN; 48 | SET LOCAL statement_timeout = '10s'; 49 | SELECT pglogical.wait_for_subscription_sync_complete('test_orig_subscription'); 50 | COMMIT; 51 | 52 | SELECT subscription_name, status, provider_node, replication_sets, forward_origins FROM pglogical.show_subscription_status(); 53 | 54 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 55 | 56 | -- Make sure we see the slot and active connection 57 | \c :orig_provider_dsn 58 | SELECT plugin, slot_type, active FROM pg_replication_slots; 59 | SELECT count(*) FROM pg_stat_replication; 60 | 61 | -- Table that replicates from top level provider to mid-level pglogical node. 62 | 63 | \c :orig_provider_dsn 64 | 65 | SELECT pglogical.replicate_ddl_command($$ 66 | CREATE TABLE public.top_level_tbl ( 67 | id serial primary key, 68 | other integer, 69 | data text, 70 | something interval 71 | ); 72 | $$); 73 | 74 | SELECT * FROM pglogical.replication_set_add_table('default', 'top_level_tbl'); 75 | INSERT INTO top_level_tbl(other, data, something) 76 | VALUES (5, 'foo', '1 minute'::interval), 77 | (4, 'bar', '12 weeks'::interval), 78 | (3, 'baz', '2 years 1 hour'::interval), 79 | (2, 'qux', '8 months 2 days'::interval), 80 | (1, NULL, NULL); 81 | 82 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 83 | 84 | \c :provider_dsn 85 | SELECT id, other, data, something FROM top_level_tbl ORDER BY id; 86 | 87 | -- Table that replicates from top level provider to mid-level pglogical node. 88 | SELECT pglogical.replicate_ddl_command($$ 89 | CREATE TABLE public.mid_level_tbl ( 90 | id serial primary key, 91 | other integer, 92 | data text, 93 | something interval 94 | ); 95 | $$); 96 | 97 | SELECT * FROM pglogical.replication_set_add_table('default', 'mid_level_tbl'); 98 | INSERT INTO mid_level_tbl(other, data, something) 99 | VALUES (5, 'foo', '1 minute'::interval), 100 | (4, 'bar', '12 weeks'::interval), 101 | (3, 'baz', '2 years 1 hour'::interval), 102 | (2, 'qux', '8 months 2 days'::interval), 103 | (1, NULL, NULL); 104 | 105 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 106 | 107 | \c :subscriber_dsn 108 | SELECT id, other, data, something FROM mid_level_tbl ORDER BY id; 109 | 110 | -- drop the tables 111 | \c :orig_provider_dsn 112 | \set VERBOSITY terse 113 | SELECT pglogical.replicate_ddl_command($$ 114 | DROP TABLE public.top_level_tbl CASCADE; 115 | $$); 116 | 117 | \c :provider_dsn 118 | \set VERBOSITY terse 119 | SELECT pglogical.replicate_ddl_command($$ 120 | DROP TABLE public.mid_level_tbl CASCADE; 121 | $$); 122 | 123 | \c :provider_dsn 124 | SELECT * FROM pglogical.drop_subscription('test_orig_subscription'); 125 | 126 | \c :orig_provider_dsn 127 | SELECT * FROM pglogical.drop_node(node_name := 'test_orig_provider'); 128 | 129 | SELECT plugin, slot_type, active FROM pg_replication_slots; 130 | SELECT count(*) FROM pg_stat_replication; 131 | -------------------------------------------------------------------------------- /sql/parallel.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM pglogical_regress_variables() 2 | \gset 3 | 4 | \c :provider_dsn 5 | 6 | SELECT * FROM pglogical.create_replication_set('parallel'); 7 | 8 | \c :subscriber_dsn 9 | 10 | SELECT * FROM pglogical.create_subscription( 11 | subscription_name := 'test_subscription_parallel', 12 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super', 13 | replication_sets := '{parallel,default}', 14 | forward_origins := '{}', 15 | synchronize_structure := false, 16 | synchronize_data := false 17 | ); 18 | 19 | SELECT * FROM pglogical.create_subscription( 20 | subscription_name := 'test_subscription_parallel', 21 | provider_dsn := (SELECT provider_dsn FROM pglogical_regress_variables()) || ' user=super', 22 | replication_sets := '{parallel}', 23 | forward_origins := '{}', 24 | synchronize_structure := false, 25 | synchronize_data := false 26 | ); 27 | 28 | BEGIN; 29 | SET LOCAL statement_timeout = '10s'; 30 | SELECT pglogical.wait_for_subscription_sync_complete('test_subscription_parallel'); 31 | COMMIT; 32 | 33 | SELECT sync_kind, sync_subid, sync_nspname, sync_relname, sync_status IN ('y', 'r') FROM pglogical.local_sync_status ORDER BY 2,3,4; 34 | 35 | SELECT * FROM pglogical.show_subscription_status(); 36 | 37 | -- Make sure we see the slot and active connection 38 | \c :provider_dsn 39 | SELECT plugin, slot_type, database, active FROM pg_replication_slots; 40 | SELECT count(*) FROM pg_stat_replication; 41 | 42 | SELECT pglogical.replicate_ddl_command($$ 43 | CREATE TABLE public.basic_dml1 ( 44 | id serial primary key, 45 | other integer, 46 | data text, 47 | something interval 48 | ); 49 | CREATE TABLE public.basic_dml2 ( 50 | id serial primary key, 51 | other integer, 52 | data text, 53 | something interval 54 | ); 55 | $$); 56 | 57 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml1'); 58 | SELECT * FROM pglogical.replication_set_add_table('parallel', 'basic_dml2'); 59 | 60 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 61 | 62 | WITH one AS ( 63 | INSERT INTO basic_dml1(other, data, something) 64 | VALUES (5, 'foo', '1 minute'::interval), 65 | (4, 'bar', '12 weeks'::interval), 66 | (3, 'baz', '2 years 1 hour'::interval), 67 | (2, 'qux', '8 months 2 days'::interval), 68 | (1, NULL, NULL) 69 | RETURNING * 70 | ) 71 | INSERT INTO basic_dml2 SELECT * FROM one; 72 | 73 | BEGIN; 74 | UPDATE basic_dml1 SET other = id, something = something - '10 seconds'::interval WHERE id < 3; 75 | DELETE FROM basic_dml2 WHERE id < 3; 76 | COMMIT; 77 | 78 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 79 | 80 | SELECT * FROM basic_dml1; 81 | SELECT * FROM basic_dml2; 82 | 83 | \c :subscriber_dsn 84 | 85 | SELECT * FROM basic_dml1; 86 | SELECT * FROM basic_dml2; 87 | 88 | SELECT pglogical.drop_subscription('test_subscription_parallel'); 89 | 90 | \c :provider_dsn 91 | \set VERBOSITY terse 92 | SELECT * FROM pglogical.drop_replication_set('parallel'); 93 | 94 | SELECT pglogical.replicate_ddl_command($$ 95 | DROP TABLE public.basic_dml1 CASCADE; 96 | DROP TABLE public.basic_dml2 CASCADE; 97 | $$); 98 | -------------------------------------------------------------------------------- /sql/preseed.sql: -------------------------------------------------------------------------------- 1 | -- Indirection for connection strings 2 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 3 | OUT orig_provider_dsn text, 4 | OUT provider_dsn text, 5 | OUT provider1_dsn text, 6 | OUT subscriber_dsn text 7 | ) RETURNS record LANGUAGE SQL AS $f$ 8 | SELECT 9 | current_setting('pglogical.orig_provider_dsn'), 10 | current_setting('pglogical.provider_dsn'), 11 | current_setting('pglogical.provider1_dsn'), 12 | current_setting('pglogical.subscriber_dsn') 13 | $f$; 14 | 15 | SELECT * FROM pglogical_regress_variables() 16 | \gset 17 | 18 | /* 19 | * Tests to ensure that objects/data that exists pre-clone is successfully 20 | * cloned. The results are checked, after the clone, in preseed_check.sql. 21 | */ 22 | \c :provider_dsn 23 | CREATE SEQUENCE some_local_seq; 24 | CREATE TABLE some_local_tbl(id serial primary key, key text unique not null, data text); 25 | INSERT INTO some_local_tbl(key, data) VALUES('key1', 'data1'); 26 | INSERT INTO some_local_tbl(key, data) VALUES('key2', NULL); 27 | INSERT INTO some_local_tbl(key, data) VALUES('key3', 'data3'); 28 | CREATE TABLE some_local_tbl1(id serial, key text unique not null, data text); 29 | INSERT INTO some_local_tbl1(key, data) VALUES('key1', 'data1'); 30 | INSERT INTO some_local_tbl1(key, data) VALUES('key2', NULL); 31 | INSERT INTO some_local_tbl1(key, data) VALUES('key3', 'data3'); 32 | CREATE TABLE some_local_tbl2(id serial, key text, data text); 33 | INSERT INTO some_local_tbl2(key, data) VALUES('key1', 'data1'); 34 | INSERT INTO some_local_tbl2(key, data) VALUES('key2', NULL); 35 | INSERT INTO some_local_tbl2(key, data) VALUES('key3', 'data3'); 36 | CREATE TABLE some_local_tbl3(id integer, key text, data text); 37 | INSERT INTO some_local_tbl3(key, data) VALUES('key1', 'data1'); 38 | INSERT INTO some_local_tbl3(key, data) VALUES('key2', NULL); 39 | INSERT INTO some_local_tbl3(key, data) VALUES('key3', 'data3'); 40 | 41 | /* 42 | * Make sure that the pglogical_regress_variables function exists both on 43 | * provider and subscriber since the original connection might have been 44 | * to completely different database. 45 | */ 46 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 47 | OUT orig_provider_dsn text, 48 | OUT provider_dsn text, 49 | OUT provider1_dsn text, 50 | OUT subscriber_dsn text 51 | ) RETURNS record LANGUAGE SQL AS $f$ 52 | SELECT 53 | current_setting('pglogical.orig_provider_dsn'), 54 | current_setting('pglogical.provider_dsn'), 55 | current_setting('pglogical.provider1_dsn'), 56 | current_setting('pglogical.subscriber_dsn') 57 | $f$; 58 | 59 | CREATE DATABASE regression1; 60 | CREATE DATABASE sourcedb; 61 | 62 | \c :orig_provider_dsn 63 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 64 | OUT orig_provider_dsn text, 65 | OUT provider_dsn text, 66 | OUT provider1_dsn text, 67 | OUT subscriber_dsn text 68 | ) RETURNS record LANGUAGE SQL AS $f$ 69 | SELECT 70 | current_setting('pglogical.orig_provider_dsn'), 71 | current_setting('pglogical.provider_dsn'), 72 | current_setting('pglogical.provider1_dsn'), 73 | current_setting('pglogical.subscriber_dsn') 74 | $f$; 75 | 76 | \c :provider1_dsn 77 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 78 | OUT orig_provider_dsn text, 79 | OUT provider_dsn text, 80 | OUT provider1_dsn text, 81 | OUT subscriber_dsn text 82 | ) RETURNS record LANGUAGE SQL AS $f$ 83 | SELECT 84 | current_setting('pglogical.orig_provider_dsn'), 85 | current_setting('pglogical.provider_dsn'), 86 | current_setting('pglogical.provider1_dsn'), 87 | current_setting('pglogical.subscriber_dsn') 88 | $f$; 89 | 90 | \c :subscriber_dsn 91 | CREATE OR REPLACE FUNCTION public.pglogical_regress_variables( 92 | OUT orig_provider_dsn text, 93 | OUT provider_dsn text, 94 | OUT provider1_dsn text, 95 | OUT subscriber_dsn text 96 | ) RETURNS record LANGUAGE SQL AS $f$ 97 | SELECT 98 | current_setting('pglogical.orig_provider_dsn'), 99 | current_setting('pglogical.provider_dsn'), 100 | current_setting('pglogical.provider1_dsn'), 101 | current_setting('pglogical.subscriber_dsn') 102 | $f$; 103 | -------------------------------------------------------------------------------- /sql/preseed_check.sql: -------------------------------------------------------------------------------- 1 | -- Verify data from preseed.sql has correctly been cloned 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl'::regclass and attnum > 0 order by attnum; 7 | SELECT * FROM some_local_tbl ORDER BY id; 8 | 9 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl1'::regclass and attnum > 0 order by attnum; 10 | SELECT * FROM some_local_tbl1 ORDER BY id; 11 | 12 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl2'::regclass and attnum > 0 order by attnum; 13 | SELECT * FROM some_local_tbl2 ORDER BY id; 14 | 15 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl3'::regclass and attnum > 0 order by attnum; 16 | SELECT * FROM some_local_tbl3 ORDER BY id; 17 | 18 | \c :subscriber_dsn 19 | 20 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl'::regclass and attnum > 0 order by attnum; 21 | SELECT * FROM some_local_tbl ORDER BY id; 22 | 23 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl1'::regclass and attnum > 0 order by attnum; 24 | SELECT * FROM some_local_tbl1 ORDER BY id; 25 | 26 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl2'::regclass and attnum > 0 order by attnum; 27 | SELECT * FROM some_local_tbl2 ORDER BY id; 28 | 29 | SELECT attname, attnotnull, attisdropped from pg_attribute where attrelid = 'some_local_tbl3'::regclass and attnum > 0 order by attnum; 30 | SELECT * FROM some_local_tbl3 ORDER BY id; 31 | 32 | \c :provider_dsn 33 | \set VERBOSITY terse 34 | SELECT pglogical.replicate_ddl_command($$ 35 | DROP SEQUENCE public.some_local_seq; 36 | DROP TABLE public.some_local_tbl; 37 | DROP TABLE public.some_local_tbl1; 38 | DROP TABLE public.some_local_tbl2; 39 | DROP TABLE public.some_local_tbl3; 40 | $$); 41 | -------------------------------------------------------------------------------- /sql/row_filter_sampling.sql: -------------------------------------------------------------------------------- 1 | -- row based filtering 2 | SELECT * FROM pglogical_regress_variables() 3 | \gset 4 | 5 | \c :provider_dsn 6 | -- testing volatile sampling function in row_filter 7 | SELECT pglogical.replicate_ddl_command($$ 8 | CREATE TABLE public.test_tablesample (id int primary key, name text) WITH (fillfactor=10); 9 | $$); 10 | -- use fillfactor so we don't have to load too much data to get multiple pages 11 | INSERT INTO test_tablesample 12 | SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i); 13 | 14 | create or replace function funcn_get_system_sample_count(integer, integer) returns bigint as 15 | $$ (SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM ($1) REPEATABLE ($2)); $$ 16 | language sql volatile; 17 | 18 | create or replace function funcn_get_bernoulli_sample_count(integer, integer) returns bigint as 19 | $$ (SELECT count(*) FROM test_tablesample TABLESAMPLE BERNOULLI ($1) REPEATABLE ($2)); $$ 20 | language sql volatile; 21 | 22 | SELECT * FROM pglogical.replication_set_add_table('default', 'test_tablesample', false, row_filter := $rf$id > funcn_get_system_sample_count(100, 3) $rf$); 23 | SELECT * FROM pglogical.replication_set_remove_table('default', 'test_tablesample'); 24 | SELECT * FROM pglogical.replication_set_add_table('default', 'test_tablesample', true, row_filter := $rf$id > funcn_get_bernoulli_sample_count(10, 0) $rf$); 25 | 26 | SELECT * FROM test_tablesample ORDER BY id limit 5; 27 | SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL); 28 | 29 | \c :subscriber_dsn 30 | 31 | BEGIN; 32 | SET LOCAL statement_timeout = '10s'; 33 | SELECT pglogical.wait_for_table_sync_complete('test_subscription', 'test_tablesample'); 34 | COMMIT; 35 | 36 | SELECT sync_kind, sync_nspname, sync_relname, sync_status FROM pglogical.local_sync_status WHERE sync_relname = 'test_tablesample'; 37 | 38 | SELECT * FROM test_tablesample ORDER BY id limit 5; 39 | 40 | \c :provider_dsn 41 | \set VERBOSITY terse 42 | DROP FUNCTION funcn_get_system_sample_count(integer, integer); 43 | DROP FUNCTION funcn_get_bernoulli_sample_count(integer, integer); 44 | SELECT pglogical.replicate_ddl_command($$ 45 | DROP TABLE public.test_tablesample CASCADE; 46 | $$); 47 | -------------------------------------------------------------------------------- /sql/sequence.sql: -------------------------------------------------------------------------------- 1 | -- like bt_index_check('pglogical.sequence_state', true) 2 | CREATE FUNCTION heapallindexed() RETURNS void AS $$ 3 | DECLARE 4 | count_seqscan int; 5 | count_idxscan int; 6 | BEGIN 7 | count_seqscan := (SELECT count(*) FROM pglogical.sequence_state); 8 | SET enable_seqscan = off; 9 | count_idxscan := (SELECT count(*) FROM pglogical.sequence_state); 10 | RESET enable_seqscan; 11 | IF count_seqscan <> count_idxscan THEN 12 | RAISE 'seqscan found % rows, but idxscan found % rows', 13 | count_seqscan, count_idxscan; 14 | END IF; 15 | END 16 | $$ LANGUAGE plpgsql; 17 | 18 | -- Replicate one sequence. 19 | CREATE SEQUENCE stress; 20 | SELECT * FROM pglogical.create_replication_set('stress_seq'); 21 | SELECT * FROM pglogical.replication_set_add_sequence('stress_seq', 'stress'); 22 | SELECT pglogical.synchronize_sequence('stress'); 23 | SELECT heapallindexed(); 24 | 25 | -- Sync it 400 times in one transaction, to cross a pglogical.sequence_state 26 | -- page boundary and get a non-HOT update. 27 | DO $$ 28 | BEGIN 29 | FOR i IN 1..400 LOOP 30 | PERFORM pglogical.synchronize_sequence('stress'); 31 | END LOOP; 32 | END; 33 | $$; 34 | SELECT heapallindexed(); 35 | -------------------------------------------------------------------------------- /t/basic.sql: -------------------------------------------------------------------------------- 1 | -- basic builtin datatypes 2 | CREATE OR REPLACE FUNCTION public.pg_xlog_wait_remote_apply(i_pos pg_lsn, i_pid integer) RETURNS VOID 3 | AS $FUNC$ 4 | BEGIN 5 | WHILE EXISTS(SELECT true FROM pg_stat_get_wal_senders() s WHERE s.replay_location < i_pos AND (i_pid = 0 OR s.pid = i_pid)) LOOP 6 | PERFORM pg_sleep(0.01); 7 | END LOOP; 8 | END;$FUNC$ LANGUAGE plpgsql; 9 | 10 | SELECT pglogical.replicate_ddl_command($$ 11 | CREATE TABLE public.basic_dml ( 12 | id serial primary key, 13 | other integer, 14 | data text, 15 | something interval 16 | ); 17 | $$); 18 | 19 | SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml'); 20 | 21 | SELECT pg_xlog_wait_remote_apply(pg_current_xlog_location(), 0); 22 | 23 | -- check basic insert replication 24 | INSERT INTO basic_dml(other, data, something) 25 | VALUES (5, 'foo', '1 minute'::interval), 26 | (4, 'bar', '12 weeks'::interval), 27 | (3, 'baz', '2 years 1 hour'::interval), 28 | (2, 'qux', '8 months 2 days'::interval), 29 | (1, NULL, NULL); 30 | SELECT pg_xlog_wait_remote_apply(pg_current_xlog_location(), 0); 31 | 32 | SELECT id, other, data, something FROM basic_dml ORDER BY id; 33 | 34 | -------------------------------------------------------------------------------- /t/perl-94-postgresql.conf: -------------------------------------------------------------------------------- 1 | # Configuration that affects behaviour being tested: 2 | shared_preload_libraries = 'pglogical' 3 | wal_level = logical 4 | max_wal_senders = 10 5 | max_replication_slots = 10 6 | 7 | # Purely testing related: 8 | DateStyle = 'ISO, DMY' 9 | log_line_prefix='[%m] [%p] [%d] ' 10 | fsync=off 11 | 12 | pglogical.synchronous_commit = true 13 | 14 | -------------------------------------------------------------------------------- /t/perl-95-postgresql.conf: -------------------------------------------------------------------------------- 1 | # Configuration that affects behaviour being tested: 2 | shared_preload_libraries = 'pglogical' 3 | wal_level = logical 4 | max_wal_senders = 10 5 | max_replication_slots = 10 6 | track_commit_timestamp = on 7 | 8 | # Purely testing related: 9 | DateStyle = 'ISO, DMY' 10 | log_line_prefix='[%m] [%p] [%d] ' 11 | fsync=off 12 | log_statement = 'all' 13 | 14 | pglogical.synchronous_commit = true 15 | 16 | --------------------------------------------------------------------------------