Add decoding of sequences to built-in replication
authorTomas Vondra <tomas.vondra@postgresql.org>
Thu, 24 Mar 2022 17:20:21 +0000 (18:20 +0100)
committerTomas Vondra <tomas.vondra@postgresql.org>
Thu, 24 Mar 2022 17:49:27 +0000 (18:49 +0100)
This commit adds support for decoding of sequences to the built-in
replication (the infrastructure was added by commit 0da92dc530).

The syntax and behavior mostly mimics handling of tables, i.e. a
publication may be defined as FOR ALL SEQUENCES (replicating all
sequences in a database), FOR ALL SEQUENCES IN SCHEMA (replicating
all sequences in a particular schema) or individual sequences.

To publish sequence modifications, the publication has to include
'sequence' action. The protocol is extended with a new message,
describing sequence increments.

A new system view pg_publication_sequences lists all the sequences
added to a publication, both directly and indirectly. Various psql
commands (\d and \dRp) are improved to also display publications
including a given sequence, or sequences included in a publication.

Author: Tomas Vondra, Cary Huang
Reviewed-by: Peter Eisentraut, Amit Kapila, Hannu Krosing, Andres
             Freund, Petr Jelinek
Discussion: https://postgr.es/m/d045f3c2-6cfb-06d3-5540-e63c320df8bc@enterprisedb.com
Discussion: https://postgr.es/m/1710ed7e13b.cd7177461430746.3372264562543607781@highgo.ca

40 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/protocol.sgml
doc/src/sgml/ref/alter_publication.sgml
doc/src/sgml/ref/alter_subscription.sgml
doc/src/sgml/ref/create_publication.sgml
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_publication.c
src/backend/catalog/system_views.sql
src/backend/commands/publicationcmds.c
src/backend/commands/sequence.c
src/backend/commands/subscriptioncmds.c
src/backend/commands/tablecmds.c
src/backend/executor/execReplication.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/replication/logical/proto.c
src/backend/replication/logical/tablesync.c
src/backend/replication/logical/worker.c
src/backend/replication/pgoutput/pgoutput.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/syscache.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/t/002_pg_dump.pl
src/bin/psql/describe.c
src/bin/psql/tab-complete.c
src/include/catalog/pg_proc.dat
src/include/catalog/pg_publication.h
src/include/catalog/pg_publication_namespace.h
src/include/commands/sequence.h
src/include/nodes/parsenodes.h
src/include/replication/logicalproto.h
src/include/replication/pgoutput.h
src/test/regress/expected/object_address.out
src/test/regress/expected/publication.out
src/test/regress/expected/rules.out
src/test/regress/sql/object_address.sql
src/test/regress/sql/publication.sql
src/test/subscription/t/030_sequences.pl [new file with mode: 0644]

index 2a8cd0266491b84bba9e19db174e290dbee1bb61..b8c954a5547148433d4eb002744e489b82fae295 100644 (file)
@@ -6281,6 +6281,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        Reference to schema
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>pntype</structfield> <type>char</type>
+       Determines which object type is included from this schema.
+      </para>
+      <para>
+       Reference to schema
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -9598,6 +9608,11 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry>prepared transactions</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-publication-sequences"><structname>pg_publication_sequences</structname></link></entry>
+      <entry>publications and their associated sequences</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-publication-tables"><structname>pg_publication_tables</structname></link></entry>
       <entry>publications and their associated tables</entry>
@@ -11433,6 +11448,72 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
 
  </sect1>
 
+ <sect1 id="view-pg-publication-sequences">
+  <title><structname>pg_publication_sequences</structname></title>
+
+  <indexterm zone="view-pg-publication-sequences">
+   <primary>pg_publication_sequences</primary>
+  </indexterm>
+
+  <para>
+   The view <structname>pg_publication_sequences</structname> provides
+   information about the mapping between publications and the sequences they
+   contain.  Unlike the underlying catalog
+   <link linkend="catalog-pg-publication-rel"><structname>pg_publication_rel</structname></link>,
+   this view expands
+   publications defined as <literal>FOR ALL SEQUENCES</literal>, so for such
+   publications there will be a row for each eligible sequence.
+  </para>
+
+  <table>
+   <title><structname>pg_publication_sequences</structname> Columns</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>pubname</structfield> <type>name</type>
+       (references <link linkend="catalog-pg-publication"><structname>pg_publication</structname></link>.<structfield>pubname</structfield>)
+      </para>
+      <para>
+       Name of publication
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+       (references <link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.<structfield>nspname</structfield>)
+      </para>
+      <para>
+       Name of schema containing sequence
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>sequencename</structfield> <type>name</type>
+       (references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>relname</structfield>)
+      </para>
+      <para>
+       Name of sequence
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
  <sect1 id="view-pg-publication-tables">
   <title><structname>pg_publication_tables</structname></title>
 
index 719b947ef4ea015429462b79c7c7b6cccdf3573f..c61c310e176528559f33693bc9b42c844514e42e 100644 (file)
@@ -7065,6 +7065,125 @@ Relation
 </listitem>
 </varlistentry>
 
+<varlistentry id="protocol-logicalrep-message-formats-Sequence">
+<term>
+Sequence
+</term>
+<listitem>
+<para>
+
+<variablelist>
+<varlistentry>
+<term>
+        Byte1('X')
+</term>
+<listitem>
+<para>
+                Identifies the message as a sequence message.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int32 (TransactionId)
+</term>
+<listitem>
+<para>
+               Xid of the transaction (only present for streamed transactions).
+               This field is available since protocol version 2.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int8(0)
+</term>
+<listitem>
+<para>
+                Flags; currently unused.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int64 (XLogRecPtr)
+</term>
+<listitem>
+<para>
+                The LSN of the sequence increment.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        String
+</term>
+<listitem>
+<para>
+                Namespace (empty string for <literal>pg_catalog</literal>).
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        String
+</term>
+<listitem>
+<para>
+                Relation name.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+        Int8
+</term>
+<listitem>
+<para>
+                1 if the sequence update is transactions, 0 otherwise.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+        Int64
+</term>
+<listitem>
+<para>
+                <structfield>last_value</structfield> value of the sequence.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+        Int64
+</term>
+<listitem>
+<para>
+                <structfield>log_cnt</structfield> value of the sequence.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+        Int8
+</term>
+<listitem>
+<para>
+                <structfield>is_called</structfield> value of the sequence.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry id="protocol-logicalrep-message-formats-Type">
 <term>
 Type
index 32b75f6c78e59e4b838dcce8d853b0a52b4fc09d..a8cc8f3dc25909e9bb5980e47ed14213f962759f 100644 (file)
@@ -31,7 +31,9 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
 <phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
 
     TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
+    SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [, ... ]
     ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
+    ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -44,13 +46,13 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
   </para>
 
   <para>
-   The first three variants change which tables/schemas are part of the
-   publication.  The <literal>SET</literal> clause will replace the list of
-   tables/schemas in the publication with the specified list; the existing
-   tables/schemas that were present in the publication will be removed.  The
-   <literal>ADD</literal> and <literal>DROP</literal> clauses will add and
-   remove one or more tables/schemas from the publication.  Note that adding
-   tables/schemas to a publication that is already subscribed to will require an
+   The first three variants change which objects (tables, sequences or schemas)
+   are part of the publication.  The <literal>SET</literal> clause will replace
+   the list of objects in the publication with the specified list; the existing
+   objects that were present in the publication will be removed.
+   The <literal>ADD</literal> and <literal>DROP</literal> clauses will add and
+   remove one or more objects from the publication.  Note that adding objects
+   to a publication that is already subscribed to will require an
    <literal>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</literal> action on the
    subscribing side in order to become effective. Note also that the combination
    of <literal>DROP</literal> with a <literal>WHERE</literal> clause is not
@@ -122,6 +124,15 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><replaceable class="parameter">sequence_name</replaceable></term>
+    <listitem>
+     <para>
+      Name of an existing sequence.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">schema_name</replaceable></term>
     <listitem>
index ac2db249cbbc027802b1890b42435bf93270cb1b..3e46bbdb0460466607924373f9922cfc9d99def5 100644 (file)
@@ -148,8 +148,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
     <listitem>
      <para>
       Fetch missing table information from publisher.  This will start
-      replication of tables that were added to the subscribed-to publications
-      since <command>CREATE SUBSCRIPTION</command> or
+      replication of tables and sequences that were added to the subscribed-to
+       publications since <command>CREATE SUBSCRIPTION</command> or
       the last invocation of <command>REFRESH PUBLICATION</command>.
      </para>
 
@@ -167,8 +167,8 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
           The default is <literal>true</literal>.
          </para>
          <para>
-          Previously subscribed tables are not copied, even if a table's row
-          filter <literal>WHERE</literal> clause has since been modified.
+          Previously subscribed tables and sequences are not copied, even if a
+          table's row filter <literal>WHERE</literal> clause has since been modified.
          </para>
         </listitem>
        </varlistentry>
index 4979b9b646d0127fe89ac30725e19a48289c2193..e5081eb50eaccf928ec021b940cf37dbe8245a12 100644 (file)
@@ -22,14 +22,21 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
-    [ FOR ALL TABLES
+    [ FOR ALL <replaceable class="parameter">object_type</replaceable> [, ...]
       | FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
     [ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
 
+<phrase>where <replaceable class="parameter">object type</replaceable> is one of:</phrase>
+
+    TABLES
+    SEQUENCES
+
 <phrase>where <replaceable class="parameter">publication_object</replaceable> is one of:</phrase>
 
     TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
+    SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [ * ] [, ... ]
     ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
+    ALL SEQUENCES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -107,27 +114,43 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>FOR SEQUENCE</literal></term>
+    <listitem>
+     <para>
+      Specifies a list of sequences to add to the publication.
+     </para>
+
+     <para>
+      Specifying a sequence that is part of a schema specified by <literal>FOR
+      ALL SEQUENCES IN SCHEMA</literal> is not supported.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>FOR ALL TABLES</literal></term>
+    <term><literal>FOR ALL SEQUENCES</literal></term>
     <listitem>
      <para>
-      Marks the publication as one that replicates changes for all tables in
-      the database, including tables created in the future.
+      Marks the publication as one that replicates changes for all tables/sequences in
+      the database, including tables/sequences created in the future.
      </para>
     </listitem>
    </varlistentry>
 
    <varlistentry>
     <term><literal>FOR ALL TABLES IN SCHEMA</literal></term>
+    <term><literal>FOR ALL SEQUENCES IN SCHEMA</literal></term>
     <listitem>
      <para>
-      Marks the publication as one that replicates changes for all tables in
-      the specified list of schemas, including tables created in the future.
+      Marks the publication as one that replicates changes for all sequences/tables in
+      the specified list of schemas, including sequences/tables created in the future.
      </para>
 
      <para>
-      Specifying a schema along with a table which belongs to the specified
-      schema using <literal>FOR TABLE</literal> is not supported.
+      Specifying a schema along with a sequence/table which belongs to the specified
+      schema using <literal>FOR SEQUENCE</literal>/<literal>FOR TABLE</literal> is not supported.
      </para>
 
      <para>
@@ -202,10 +225,9 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
   <title>Notes</title>
 
   <para>
-   If <literal>FOR TABLE</literal>, <literal>FOR ALL TABLES</literal> or
-   <literal>FOR ALL TABLES IN SCHEMA</literal> are not specified, then the
-   publication starts out with an empty set of tables.  That is useful if
-   tables or schemas are to be added later.
+   If <literal>FOR TABLE</literal>, <literal>FOR SEQUENCE</literal>, etc. is
+   not specified, then the publication starts out with an empty set of tables
+   and sequences.  That is useful if objects are to be added later.
   </para>
 
   <para>
@@ -220,10 +242,9 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
   </para>
 
   <para>
-   To add a table to a publication, the invoking user must have ownership
-   rights on the table.  The <command>FOR ALL TABLES</command> and
-   <command>FOR ALL TABLES IN SCHEMA</command> clauses require the invoking
-   user to be a superuser.
+   To add a table or a sequence to a publication, the invoking user must
+   have ownership rights on the object.  The <command>FOR ALL ...</command>
+   clauses require the invoking user to be a superuser.
   </para>
 
   <para>
index d7ce063997a098be50dabb7378ef6d10d7f6009f..3fd17ea64f040231d9b4c2fc73aba6915f9a291f 100644 (file)
@@ -1930,12 +1930,14 @@ get_object_address_publication_schema(List *object, bool missing_ok)
    char       *pubname;
    char       *schemaname;
    Oid         schemaid;
+   char       *objtype;
 
    ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
 
    /* Fetch schema name and publication name from input list */
    schemaname = strVal(linitial(object));
    pubname = strVal(lsecond(object));
+   objtype = strVal(lthird(object));
 
    schemaid = get_namespace_oid(schemaname, missing_ok);
    if (!OidIsValid(schemaid))
@@ -1948,10 +1950,12 @@ get_object_address_publication_schema(List *object, bool missing_ok)
 
    /* Find the publication schema mapping in syscache */
    address.objectId =
-       GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
+       GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
                        Anum_pg_publication_namespace_oid,
                        ObjectIdGetDatum(schemaid),
-                       ObjectIdGetDatum(pub->oid));
+                       ObjectIdGetDatum(pub->oid),
+                       CharGetDatum(objtype[0]));
+
    if (!OidIsValid(address.objectId) && !missing_ok)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -2232,7 +2236,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
        case OBJECT_DOMCONSTRAINT:
        case OBJECT_CAST:
        case OBJECT_USER_MAPPING:
-       case OBJECT_PUBLICATION_NAMESPACE:
        case OBJECT_PUBLICATION_REL:
        case OBJECT_DEFACL:
        case OBJECT_TRANSFORM:
@@ -2257,6 +2260,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
            /* fall through to check args length */
            /* FALLTHROUGH */
        case OBJECT_OPERATOR:
+       case OBJECT_PUBLICATION_NAMESPACE:
            if (list_length(args) != 2)
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2327,6 +2331,8 @@ pg_get_object_address(PG_FUNCTION_ARGS)
            objnode = (Node *) list_make2(name, linitial(args));
            break;
        case OBJECT_PUBLICATION_NAMESPACE:
+           objnode = (Node *) list_make3(linitial(name), linitial(args), lsecond(args));
+           break;
        case OBJECT_USER_MAPPING:
            objnode = (Node *) list_make2(linitial(name), linitial(args));
            break;
@@ -2881,11 +2887,12 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
  *
  * Get publication name and schema name from the object address into pubname and
  * nspname. Both pubname and nspname are palloc'd strings which will be freed by
- * the caller.
+ * the caller. The last parameter specifies which object type is included from
+ * the schema.
  */
 static bool
 getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
-                        char **pubname, char **nspname)
+                        char **pubname, char **nspname, char **objtype)
 {
    HeapTuple   tup;
    Form_pg_publication_namespace pnform;
@@ -2921,6 +2928,13 @@ getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
        return false;
    }
 
+   /*
+    * The type is always a single character, but we need to pass it as a string,
+    * so allocate two charaters and set the first one. The second one is \0.
+    */
+   *objtype = palloc0(2);
+   *objtype[0] = pnform->pntype;
+
    ReleaseSysCache(tup);
    return true;
 }
@@ -3926,15 +3940,17 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
            {
                char       *pubname;
                char       *nspname;
+               char       *objtype;
 
                if (!getPublicationSchemaInfo(object, missing_ok,
-                                             &pubname, &nspname))
+                                             &pubname, &nspname, &objtype))
                    break;
 
-               appendStringInfo(&buffer, _("publication of schema %s in publication %s"),
-                                nspname, pubname);
+               appendStringInfo(&buffer, _("publication of schema %s in publication %s type %s"),
+                                nspname, pubname, objtype);
                pfree(pubname);
                pfree(nspname);
+               pfree(objtype);
                break;
            }
 
@@ -5729,18 +5745,24 @@ getObjectIdentityParts(const ObjectAddress *object,
            {
                char       *pubname;
                char       *nspname;
+               char       *objtype;
 
                if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
-                                             &nspname))
+                                             &nspname, &objtype))
                    break;
-               appendStringInfo(&buffer, "%s in publication %s",
-                                nspname, pubname);
+               appendStringInfo(&buffer, "%s in publication %s type %s",
+                                nspname, pubname, objtype);
 
                if (objargs)
                    *objargs = list_make1(pubname);
                else
                    pfree(pubname);
 
+               if (objargs)
+                   *objargs = lappend(*objargs, objtype);
+               else
+                   pfree(objtype);
+
                if (objname)
                    *objname = list_make1(nspname);
                else
index 789b895db89fe1503121dec2c5438c5410c30e0c..5bcfc94e2bab1be7a58c3d865c34e5247c3c3ed9 100644 (file)
 static void
 check_publication_add_relation(Relation targetrel)
 {
-   /* Must be a regular or partitioned table */
+   /* Must be a regular or partitioned table, or a sequence */
    if (RelationGetForm(targetrel)->relkind != RELKIND_RELATION &&
-       RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE)
+       RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE &&
+       RelationGetForm(targetrel)->relkind != RELKIND_SEQUENCE)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("cannot add relation \"%s\" to publication",
@@ -131,7 +132,8 @@ static bool
 is_publishable_class(Oid relid, Form_pg_class reltuple)
 {
    return (reltuple->relkind == RELKIND_RELATION ||
-           reltuple->relkind == RELKIND_PARTITIONED_TABLE) &&
+           reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
+           reltuple->relkind == RELKIND_SEQUENCE) &&
        !IsCatalogRelationOid(relid) &&
        reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
        relid >= FirstNormalObjectId;
@@ -176,6 +178,52 @@ filter_partitions(List *relids)
    return result;
 }
 
+/*
+ * Check the character is a valid object type for schema publication.
+ *
+ * This recognizes either 't' for tables or 's' for sequences. Places that
+ * need to handle 'u' for unsupported relkinds need to do that explicitlyl
+ */
+static void
+AssertObjectTypeValid(char objectType)
+{
+#ifdef USE_ASSERT_CHECKING
+   Assert(objectType == PUB_OBJTYPE_SEQUENCE || objectType == PUB_OBJTYPE_TABLE);
+#endif
+}
+
+/*
+ * Determine object type given the object type set for a schema.
+ */
+char
+pub_get_object_type_for_relkind(char relkind)
+{
+   /* sequence maps directly to sequence relkind */
+   if (relkind == RELKIND_SEQUENCE)
+       return PUB_OBJTYPE_SEQUENCE;
+
+   /* for table, we match either regular or partitioned table */
+   if (relkind == RELKIND_RELATION ||
+       relkind == RELKIND_PARTITIONED_TABLE)
+       return PUB_OBJTYPE_TABLE;
+
+   return PUB_OBJTYPE_UNSUPPORTED;
+}
+
+/*
+ * Determine if publication object type matches the relkind.
+ *
+ * Returns true if the relation matches object type replicated by this schema,
+ * false otherwise.
+ */
+static bool
+pub_object_type_matches_relkind(char objectType, char relkind)
+{
+   AssertObjectTypeValid(objectType);
+
+   return (pub_get_object_type_for_relkind(relkind) == objectType);
+}
+
 /*
  * Another variant of this, taking a Relation.
  */
@@ -205,7 +253,7 @@ is_schema_publication(Oid pubid)
                ObjectIdGetDatum(pubid));
 
    scan = systable_beginscan(pubschsrel,
-                             PublicationNamespacePnnspidPnpubidIndexId,
+                             PublicationNamespacePnnspidPnpubidPntypeIndexId,
                              true, NULL, 1, &scankey);
    tup = systable_getnext(scan);
    result = HeapTupleIsValid(tup);
@@ -313,7 +361,9 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
        }
        else
        {
-           aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
+           /* we only search for ancestors of tables, so PUB_OBJTYPE_TABLE */
+           aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor),
+                                                 PUB_OBJTYPE_TABLE);
            if (list_member_oid(aschemaPubids, puboid))
            {
                topmost_relid = ancestor;
@@ -436,7 +486,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
  * Insert new publication / schema mapping.
  */
 ObjectAddress
-publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
+publication_add_schema(Oid pubid, Oid schemaid, char objectType, bool if_not_exists)
 {
    Relation    rel;
    HeapTuple   tup;
@@ -448,6 +498,8 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
    ObjectAddress myself,
                referenced;
 
+   AssertObjectTypeValid(objectType);
+
    rel = table_open(PublicationNamespaceRelationId, RowExclusiveLock);
 
    /*
@@ -455,9 +507,10 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
     * duplicates, it's here just to provide nicer error message in common
     * case. The real protection is the unique key on the catalog.
     */
-   if (SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP,
+   if (SearchSysCacheExists3(PUBLICATIONNAMESPACEMAP,
                              ObjectIdGetDatum(schemaid),
-                             ObjectIdGetDatum(pubid)))
+                             ObjectIdGetDatum(pubid),
+                             CharGetDatum(objectType)))
    {
        table_close(rel, RowExclusiveLock);
 
@@ -483,6 +536,8 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
        ObjectIdGetDatum(pubid);
    values[Anum_pg_publication_namespace_pnnspid - 1] =
        ObjectIdGetDatum(schemaid);
+   values[Anum_pg_publication_namespace_pntype - 1] =
+       CharGetDatum(objectType);
 
    tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
@@ -508,7 +563,7 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
     * publication_add_relation for why we need to consider all the
     * partitions.
     */
-   schemaRels = GetSchemaPublicationRelations(schemaid,
+   schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
                                               PUBLICATION_PART_ALL);
    InvalidatePublicationRels(schemaRels);
 
@@ -542,11 +597,14 @@ GetRelationPublications(Oid relid)
 /*
  * Gets list of relation oids for a publication.
  *
- * This should only be used FOR TABLE publications, the FOR ALL TABLES
- * should use GetAllTablesPublicationRelations().
+ * This should only be used FOR TABLE / FOR SEQUENCE publications, the FOR
+ * ALL TABLES / SEQUENCES should use GetAllTablesPublicationRelations()
+ * and GetAllSequencesPublicationRelations().
+ *
+ * XXX pub_partopt only matters for tables, not sequences.
  */
 List *
-GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
+GetPublicationRelations(Oid pubid, char objectType, PublicationPartOpt pub_partopt)
 {
    List       *result;
    Relation    pubrelsrel;
@@ -554,6 +612,8 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
    SysScanDesc scan;
    HeapTuple   tup;
 
+   AssertObjectTypeValid(objectType);
+
    /* Find all publications associated with the relation. */
    pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
 
@@ -568,11 +628,29 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
    result = NIL;
    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
+       char    relkind;
        Form_pg_publication_rel pubrel;
 
        pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
-       result = GetPubPartitionOptionRelations(result, pub_partopt,
-                                               pubrel->prrelid);
+       relkind = get_rel_relkind(pubrel->prrelid);
+
+       /*
+        * If the relkind does not match the requested object type, ignore the
+        * relation. For example we might be interested only in sequences, so
+        * we ignore tables.
+        */
+       if (!pub_object_type_matches_relkind(objectType, relkind))
+           continue;
+
+       /*
+        * We don't have partitioned sequences, so just add them to the list.
+        * Otherwise consider adding all child relations, if requested.
+        */
+       if (relkind == RELKIND_SEQUENCE)
+           result = lappend_oid(result, pubrel->prrelid);
+       else
+           result = GetPubPartitionOptionRelations(result, pub_partopt,
+                                                   pubrel->prrelid);
    }
 
    systable_endscan(scan);
@@ -622,6 +700,43 @@ GetAllTablesPublications(void)
    return result;
 }
 
+/*
+ * Gets list of publication oids for publications marked as FOR ALL SEQUENCES.
+ */
+List *
+GetAllSequencesPublications(void)
+{
+   List       *result;
+   Relation    rel;
+   ScanKeyData scankey;
+   SysScanDesc scan;
+   HeapTuple   tup;
+
+   /* Find all publications that are marked as for all sequences. */
+   rel = table_open(PublicationRelationId, AccessShareLock);
+
+   ScanKeyInit(&scankey,
+               Anum_pg_publication_puballsequences,
+               BTEqualStrategyNumber, F_BOOLEQ,
+               BoolGetDatum(true));
+
+   scan = systable_beginscan(rel, InvalidOid, false,
+                             NULL, 1, &scankey);
+
+   result = NIL;
+   while (HeapTupleIsValid(tup = systable_getnext(scan)))
+   {
+       Oid         oid = ((Form_pg_publication) GETSTRUCT(tup))->oid;
+
+       result = lappend_oid(result, oid);
+   }
+
+   systable_endscan(scan);
+   table_close(rel, AccessShareLock);
+
+   return result;
+}
+
 /*
  * Gets list of all relation published by FOR ALL TABLES publication(s).
  *
@@ -688,28 +803,38 @@ GetAllTablesPublicationRelations(bool pubviaroot)
 /*
  * Gets the list of schema oids for a publication.
  *
- * This should only be used FOR ALL TABLES IN SCHEMA publications.
+ * This should only be used FOR ALL TABLES IN SCHEMA and FOR ALL SEQUENCES
+ * publications.
+ *
+ * 'objectType' determines whether to get FOR TABLE or FOR SEQUENCES schemas
  */
 List *
-GetPublicationSchemas(Oid pubid)
+GetPublicationSchemas(Oid pubid, char objectType)
 {
    List       *result = NIL;
    Relation    pubschsrel;
-   ScanKeyData scankey;
+   ScanKeyData scankey[2];
    SysScanDesc scan;
    HeapTuple   tup;
 
+   AssertObjectTypeValid(objectType);
+
    /* Find all schemas associated with the publication */
    pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
 
-   ScanKeyInit(&scankey,
+   ScanKeyInit(&scankey[0],
                Anum_pg_publication_namespace_pnpubid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(pubid));
 
+   ScanKeyInit(&scankey[1],
+               Anum_pg_publication_namespace_pntype,
+               BTEqualStrategyNumber, F_CHAREQ,
+               CharGetDatum(objectType));
+
    scan = systable_beginscan(pubschsrel,
-                             PublicationNamespacePnnspidPnpubidIndexId,
-                             true, NULL, 1, &scankey);
+                             PublicationNamespacePnnspidPnpubidPntypeIndexId,
+                             true, NULL, 2, scankey);
    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
        Form_pg_publication_namespace pubsch;
@@ -727,14 +852,26 @@ GetPublicationSchemas(Oid pubid)
 
 /*
  * Gets the list of publication oids associated with a specified schema.
+ *
+ * objectType specifies whether we're looking for schemas including tables or
+ * sequences.
+ *
+ * Note: relcache calls this for all object types, not just tables and sequences.
+ * Which is why we handle the PUB_OBJTYPE_UNSUPPORTED object type too.
  */
 List *
-GetSchemaPublications(Oid schemaid)
+GetSchemaPublications(Oid schemaid, char objectType)
 {
    List       *result = NIL;
    CatCList   *pubschlist;
    int         i;
 
+   /* unsupported object type */
+   if (objectType == PUB_OBJTYPE_UNSUPPORTED)
+       return result;
+
+   AssertObjectTypeValid(objectType);
+
    /* Find all publications associated with the schema */
    pubschlist = SearchSysCacheList1(PUBLICATIONNAMESPACEMAP,
                                     ObjectIdGetDatum(schemaid));
@@ -742,6 +879,11 @@ GetSchemaPublications(Oid schemaid)
    {
        HeapTuple   tup = &pubschlist->members[i]->tuple;
        Oid         pubid = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pnpubid;
+       char        pntype = ((Form_pg_publication_namespace) GETSTRUCT(tup))->pntype;
+
+       /* Skip schemas publishing a different object type. */
+       if (pntype != objectType)
+           continue;
 
        result = lappend_oid(result, pubid);
    }
@@ -753,9 +895,13 @@ GetSchemaPublications(Oid schemaid)
 
 /*
  * Get the list of publishable relation oids for a specified schema.
+ *
+ * objectType specifies whether this is FOR ALL TABLES IN SCHEMA or FOR ALL
+ * SEQUENCES IN SCHEMA
  */
 List *
-GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
+GetSchemaPublicationRelations(Oid schemaid, char objectType,
+                             PublicationPartOpt pub_partopt)
 {
    Relation    classRel;
    ScanKeyData key[1];
@@ -764,6 +910,7 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
    List       *result = NIL;
 
    Assert(OidIsValid(schemaid));
+   AssertObjectTypeValid(objectType);
 
    classRel = table_open(RelationRelationId, AccessShareLock);
 
@@ -784,9 +931,16 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
            continue;
 
        relkind = get_rel_relkind(relid);
-       if (relkind == RELKIND_RELATION)
-           result = lappend_oid(result, relid);
-       else if (relkind == RELKIND_PARTITIONED_TABLE)
+
+       /* Skip if the relkind does not match FOR ALL TABLES / SEQUENCES. */
+       if (!pub_object_type_matches_relkind(objectType, relkind))
+           continue;
+
+       /*
+        * If the object is a partitioned table, lookup all the child relations
+        * (if requested). Otherwise just add the object to the list.
+        */
+       if (relkind == RELKIND_PARTITIONED_TABLE)
        {
            List       *partitionrels = NIL;
 
@@ -799,7 +953,11 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
                                                           pub_partopt,
                                                           relForm->oid);
            result = list_concat_unique_oid(result, partitionrels);
+           continue;
        }
+
+       /* non-partitioned tables and sequences */
+       result = lappend_oid(result, relid);
    }
 
    table_endscan(scan);
@@ -809,27 +967,67 @@ GetSchemaPublicationRelations(Oid schemaid, PublicationPartOpt pub_partopt)
 
 /*
  * Gets the list of all relations published by FOR ALL TABLES IN SCHEMA
- * publication.
+ * or FOR ALL SEQUENCES IN SCHEMA publication.
  */
 List *
-GetAllSchemaPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
+GetAllSchemaPublicationRelations(Oid pubid, char objectType,
+                                PublicationPartOpt pub_partopt)
 {
    List       *result = NIL;
-   List       *pubschemalist = GetPublicationSchemas(pubid);
+   List       *pubschemalist = GetPublicationSchemas(pubid, objectType);
    ListCell   *cell;
 
+   AssertObjectTypeValid(objectType);
+
    foreach(cell, pubschemalist)
    {
        Oid         schemaid = lfirst_oid(cell);
        List       *schemaRels = NIL;
 
-       schemaRels = GetSchemaPublicationRelations(schemaid, pub_partopt);
+       schemaRels = GetSchemaPublicationRelations(schemaid, objectType,
+                                                  pub_partopt);
        result = list_concat(result, schemaRels);
    }
 
    return result;
 }
 
+/*
+ * Gets list of all relation published by FOR ALL SEQUENCES publication(s).
+ */
+List *
+GetAllSequencesPublicationRelations(void)
+{
+   Relation    classRel;
+   ScanKeyData key[1];
+   TableScanDesc scan;
+   HeapTuple   tuple;
+   List       *result = NIL;
+
+   classRel = table_open(RelationRelationId, AccessShareLock);
+
+   ScanKeyInit(&key[0],
+               Anum_pg_class_relkind,
+               BTEqualStrategyNumber, F_CHAREQ,
+               CharGetDatum(RELKIND_SEQUENCE));
+
+   scan = table_beginscan_catalog(classRel, 1, key);
+
+   while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+   {
+       Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
+       Oid         relid = relForm->oid;
+
+       if (is_publishable_class(relid, relForm))
+           result = lappend_oid(result, relid);
+   }
+
+   table_endscan(scan);
+
+   table_close(classRel, AccessShareLock);
+   return result;
+}
+
 /*
  * Get publication using oid
  *
@@ -852,10 +1050,12 @@ GetPublication(Oid pubid)
    pub->oid = pubid;
    pub->name = pstrdup(NameStr(pubform->pubname));
    pub->alltables = pubform->puballtables;
+   pub->allsequences = pubform->puballsequences;
    pub->pubactions.pubinsert = pubform->pubinsert;
    pub->pubactions.pubupdate = pubform->pubupdate;
    pub->pubactions.pubdelete = pubform->pubdelete;
    pub->pubactions.pubtruncate = pubform->pubtruncate;
+   pub->pubactions.pubsequence = pubform->pubsequence;
    pub->pubviaroot = pubform->pubviaroot;
 
    ReleaseSysCache(tup);
@@ -966,10 +1166,12 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
                       *schemarelids;
 
            relids = GetPublicationRelations(publication->oid,
+                                            PUB_OBJTYPE_TABLE,
                                             publication->pubviaroot ?
                                             PUBLICATION_PART_ROOT :
                                             PUBLICATION_PART_LEAF);
            schemarelids = GetAllSchemaPublicationRelations(publication->oid,
+                                                           PUB_OBJTYPE_TABLE,
                                                            publication->pubviaroot ?
                                                            PUBLICATION_PART_ROOT :
                                                            PUBLICATION_PART_LEAF);
@@ -1005,3 +1207,71 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
 
    SRF_RETURN_DONE(funcctx);
 }
+
+/*
+ * Returns Oids of sequences in a publication.
+ */
+Datum
+pg_get_publication_sequences(PG_FUNCTION_ARGS)
+{
+   FuncCallContext *funcctx;
+   char       *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
+   Publication *publication;
+   List       *sequences;
+
+   /* stuff done only on the first call of the function */
+   if (SRF_IS_FIRSTCALL())
+   {
+       MemoryContext oldcontext;
+
+       /* create a function context for cross-call persistence */
+       funcctx = SRF_FIRSTCALL_INIT();
+
+       /* switch to memory context appropriate for multiple function calls */
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+       publication = GetPublicationByName(pubname, false);
+
+       /*
+        * Publications support partitioned tables, although all changes are
+        * replicated using leaf partition identity and schema, so we only
+        * need those.
+        */
+       if (publication->allsequences)
+           sequences = GetAllSequencesPublicationRelations();
+       else
+       {
+           List       *relids,
+                      *schemarelids;
+
+           relids = GetPublicationRelations(publication->oid,
+                                            PUB_OBJTYPE_SEQUENCE,
+                                            publication->pubviaroot ?
+                                            PUBLICATION_PART_ROOT :
+                                            PUBLICATION_PART_LEAF);
+           schemarelids = GetAllSchemaPublicationRelations(publication->oid,
+                                                           PUB_OBJTYPE_SEQUENCE,
+                                                           publication->pubviaroot ?
+                                                           PUBLICATION_PART_ROOT :
+                                                           PUBLICATION_PART_LEAF);
+           sequences = list_concat_unique_oid(relids, schemarelids);
+       }
+
+       funcctx->user_fctx = (void *) sequences;
+
+       MemoryContextSwitchTo(oldcontext);
+   }
+
+   /* stuff done on every call of the function */
+   funcctx = SRF_PERCALL_SETUP();
+   sequences = (List *) funcctx->user_fctx;
+
+   if (funcctx->call_cntr < list_length(sequences))
+   {
+       Oid         relid = list_nth_oid(sequences, funcctx->call_cntr);
+
+       SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
+   }
+
+   SRF_RETURN_DONE(funcctx);
+}
index bd48ee7bd255e649ddaa2696c8dcd6fd844776f8..9ac8e9a2998b9b37de85837ff20e15a86e144975 100644 (file)
@@ -374,6 +374,16 @@ CREATE VIEW pg_publication_tables AS
          pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
     WHERE C.oid = GPT.relid;
 
+CREATE VIEW pg_publication_sequences AS
+    SELECT
+        P.pubname AS pubname,
+        N.nspname AS schemaname,
+        C.relname AS sequencename
+    FROM pg_publication P,
+         LATERAL pg_get_publication_sequences(P.pubname) GPS,
+         pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)
+    WHERE C.oid = GPS.relid;
+
 CREATE VIEW pg_locks AS
     SELECT * FROM pg_lock_status() AS L;
 
index 1aad2e769cb8d85e0559be8e02fb9dfdc8a34d79..f890d3f0baadabafe62d7982ca66f6e2ef9a98fb 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "access/genam.h"
 #include "access/htup_details.h"
+#include "access/relation.h"
 #include "access/table.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
@@ -67,15 +68,17 @@ typedef struct rf_context
 } rf_context;
 
 static List *OpenRelIdList(List *relids);
-static List *OpenTableList(List *tables);
-static void CloseTableList(List *rels);
+static List *OpenRelationList(List *rels, char objectType);
+static void CloseRelationList(List *rels);
 static void LockSchemaList(List *schemalist);
-static void PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
+static void PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
                                 AlterPublicationStmt *stmt);
-static void PublicationDropTables(Oid pubid, List *rels, bool missing_ok);
-static void PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
-                                 AlterPublicationStmt *stmt);
-static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok);
+static void PublicationDropRelations(Oid pubid, List *rels, bool missing_ok);
+static void PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
+                                 bool if_not_exists, AlterPublicationStmt *stmt);
+static void PublicationDropSchemas(Oid pubid, List *schemas, char objectType,
+                                  bool missing_ok);
+
 
 static void
 parse_publication_options(ParseState *pstate,
@@ -95,6 +98,7 @@ parse_publication_options(ParseState *pstate,
    pubactions->pubupdate = true;
    pubactions->pubdelete = true;
    pubactions->pubtruncate = true;
+   pubactions->pubsequence = true;
    *publish_via_partition_root = false;
 
    /* Parse options */
@@ -119,6 +123,7 @@ parse_publication_options(ParseState *pstate,
            pubactions->pubupdate = false;
            pubactions->pubdelete = false;
            pubactions->pubtruncate = false;
+           pubactions->pubsequence = false;
 
            *publish_given = true;
            publish = defGetString(defel);
@@ -141,6 +146,8 @@ parse_publication_options(ParseState *pstate,
                    pubactions->pubdelete = true;
                else if (strcmp(publish_opt, "truncate") == 0)
                    pubactions->pubtruncate = true;
+               else if (strcmp(publish_opt, "sequence") == 0)
+                   pubactions->pubsequence = true;
                else
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
@@ -167,7 +174,9 @@ parse_publication_options(ParseState *pstate,
  */
 static void
 ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
-                          List **rels, List **schemas)
+                          List **tables, List **sequences,
+                          List **tables_schemas, List **sequences_schemas,
+                          List **schemas)
 {
    ListCell   *cell;
    PublicationObjSpec *pubobj;
@@ -185,12 +194,23 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
        switch (pubobj->pubobjtype)
        {
            case PUBLICATIONOBJ_TABLE:
-               *rels = lappend(*rels, pubobj->pubtable);
+               *tables = lappend(*tables, pubobj->pubtable);
+               break;
+           case PUBLICATIONOBJ_SEQUENCE:
+               *sequences = lappend(*sequences, pubobj->pubtable);
                break;
            case PUBLICATIONOBJ_TABLES_IN_SCHEMA:
                schemaid = get_namespace_oid(pubobj->name, false);
 
                /* Filter out duplicates if user specifies "sch1, sch1" */
+               *tables_schemas = list_append_unique_oid(*tables_schemas, schemaid);
+               *schemas = list_append_unique_oid(*schemas, schemaid);
+               break;
+           case PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA:
+               schemaid = get_namespace_oid(pubobj->name, false);
+
+               /* Filter out duplicates if user specifies "sch1, sch1" */
+               *sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
                *schemas = list_append_unique_oid(*schemas, schemaid);
                break;
            case PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA:
@@ -204,6 +224,21 @@ ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
                list_free(search_path);
 
                /* Filter out duplicates if user specifies "sch1, sch1" */
+               *tables_schemas = list_append_unique_oid(*tables_schemas, schemaid);
+               *schemas = list_append_unique_oid(*schemas, schemaid);
+               break;
+           case PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA:
+               search_path = fetch_search_path(false);
+               if (search_path == NIL) /* nothing valid in search_path? */
+                   ereport(ERROR,
+                           errcode(ERRCODE_UNDEFINED_SCHEMA),
+                           errmsg("no schema has been selected for CURRENT_SCHEMA"));
+
+               schemaid = linitial_oid(search_path);
+               list_free(search_path);
+
+               /* Filter out duplicates if user specifies "sch1, sch1" */
+               *sequences_schemas = list_append_unique_oid(*sequences_schemas, schemaid);
                *schemas = list_append_unique_oid(*schemas, schemaid);
                break;
            default:
@@ -240,6 +275,14 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
                        errdetail("Table \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
                                  RelationGetRelationName(rel),
                                  get_namespace_name(relSchemaId)));
+           else if (checkobjtype == PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA)
+               ereport(ERROR,
+                       errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                       errmsg("cannot add schema \"%s\" to publication",
+                              get_namespace_name(relSchemaId)),
+                       errdetail("Sequence \"%s\" in schema \"%s\" is already part of the publication, adding the same schema is not supported.",
+                                 RelationGetRelationName(rel),
+                                 get_namespace_name(relSchemaId)));
            else if (checkobjtype == PUBLICATIONOBJ_TABLE)
                ereport(ERROR,
                        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -248,6 +291,14 @@ CheckObjSchemaNotAlreadyInPublication(List *rels, List *schemaidlist,
                               RelationGetRelationName(rel)),
                        errdetail("Table's schema \"%s\" is already part of the publication or part of the specified schema list.",
                                  get_namespace_name(relSchemaId)));
+           else if (checkobjtype == PUBLICATIONOBJ_SEQUENCE)
+               ereport(ERROR,
+                       errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                       errmsg("cannot add relation \"%s.%s\" to publication",
+                              get_namespace_name(relSchemaId),
+                              RelationGetRelationName(rel)),
+                       errdetail("Sequence's schema \"%s\" is already part of the publication or part of the specified schema list.",
+                                 get_namespace_name(relSchemaId)));
        }
    }
 }
@@ -615,6 +666,7 @@ TransformPubWhereClauses(List *tables, const char *queryString,
 ObjectAddress
 CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 {
+   ListCell   *lc;
    Relation    rel;
    ObjectAddress myself;
    Oid         puboid;
@@ -626,9 +678,25 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
    bool        publish_via_partition_root_given;
    bool        publish_via_partition_root;
    AclResult   aclresult;
-   List       *relations = NIL;
+   List       *tables = NIL;
+   List       *sequences = NIL;
+   List       *tables_schemaidlist = NIL;
+   List       *sequences_schemaidlist = NIL;
    List       *schemaidlist = NIL;
 
+   bool        for_all_tables = false;
+   bool        for_all_sequences = false;
+
+   /* Translate the list of object types (represented by strings) to bool flags. */
+   foreach (lc, stmt->for_all_objects)
+   {
+       char   *val = strVal(lfirst(lc));
+       if (strcmp(val, "tables") == 0)
+           for_all_tables = true;
+       else if (strcmp(val, "sequences") == 0)
+           for_all_sequences = true;
+   }
+
    /* must have CREATE privilege on database */
    aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
    if (aclresult != ACLCHECK_OK)
@@ -636,7 +704,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
                       get_database_name(MyDatabaseId));
 
    /* FOR ALL TABLES requires superuser */
-   if (stmt->for_all_tables && !superuser())
+   if (for_all_tables && !superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("must be superuser to create FOR ALL TABLES publication")));
@@ -672,7 +740,9 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
                                Anum_pg_publication_oid);
    values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
    values[Anum_pg_publication_puballtables - 1] =
-       BoolGetDatum(stmt->for_all_tables);
+       BoolGetDatum(for_all_tables);
+   values[Anum_pg_publication_puballsequences - 1] =
+       BoolGetDatum(for_all_sequences);
    values[Anum_pg_publication_pubinsert - 1] =
        BoolGetDatum(pubactions.pubinsert);
    values[Anum_pg_publication_pubupdate - 1] =
@@ -681,6 +751,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
        BoolGetDatum(pubactions.pubdelete);
    values[Anum_pg_publication_pubtruncate - 1] =
        BoolGetDatum(pubactions.pubtruncate);
+   values[Anum_pg_publication_pubsequence - 1] =
+       BoolGetDatum(pubactions.pubsequence);
    values[Anum_pg_publication_pubviaroot - 1] =
        BoolGetDatum(publish_via_partition_root);
 
@@ -698,45 +770,88 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
    CommandCounterIncrement();
 
    /* Associate objects with the publication. */
-   if (stmt->for_all_tables)
+   if (for_all_tables || for_all_sequences)
    {
        /* Invalidate relcache so that publication info is rebuilt. */
        CacheInvalidateRelcacheAll();
    }
-   else
+
+   /*
+    * If the publication might have either tables or sequences (directly or
+    * through a schema), process that.
+    */
+   if (!for_all_tables || !for_all_sequences)
    {
-       ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
+       ObjectsInPublicationToOids(stmt->pubobjects, pstate,
+                                  &tables, &sequences,
+                                  &tables_schemaidlist,
+                                  &sequences_schemaidlist,
                                   &schemaidlist);
 
        /* FOR ALL TABLES IN SCHEMA requires superuser */
-       if (list_length(schemaidlist) > 0 && !superuser())
+       if (list_length(tables_schemaidlist) > 0 && !superuser())
            ereport(ERROR,
                    errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                    errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
 
-       if (list_length(relations) > 0)
+       /* FOR ALL SEQUENCES IN SCHEMA requires superuser */
+       if (list_length(sequences_schemaidlist) > 0 && !superuser())
+           ereport(ERROR,
+                   errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                   errmsg("must be superuser to create FOR ALL SEQUENCES IN SCHEMA publication"));
+
+       /* tables added directly */
+       if (list_length(tables) > 0)
        {
            List       *rels;
 
-           rels = OpenTableList(relations);
-           CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
+           rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
+           CheckObjSchemaNotAlreadyInPublication(rels, tables_schemaidlist,
                                                  PUBLICATIONOBJ_TABLE);
 
            TransformPubWhereClauses(rels, pstate->p_sourcetext,
                                     publish_via_partition_root);
 
-           PublicationAddTables(puboid, rels, true, NULL);
-           CloseTableList(rels);
+           PublicationAddRelations(puboid, rels, true, NULL);
+           CloseRelationList(rels);
+       }
+
+       /* sequences added directly */
+       if (list_length(sequences) > 0)
+       {
+           List       *rels;
+
+           rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
+           CheckObjSchemaNotAlreadyInPublication(rels, sequences_schemaidlist,
+                                                 PUBLICATIONOBJ_SEQUENCE);
+           PublicationAddRelations(puboid, rels, true, NULL);
+           CloseRelationList(rels);
+       }
+
+       /* tables added through a schema */
+       if (list_length(tables_schemaidlist) > 0)
+       {
+           /*
+            * Schema lock is held until the publication is created to prevent
+            * concurrent schema deletion.
+            */
+           LockSchemaList(tables_schemaidlist);
+           PublicationAddSchemas(puboid,
+                                 tables_schemaidlist, PUB_OBJTYPE_TABLE,
+                                 true, NULL);
        }
 
-       if (list_length(schemaidlist) > 0)
+       /* sequences added through a schema */
+       if (list_length(sequences_schemaidlist) > 0)
        {
            /*
             * Schema lock is held until the publication is created to prevent
             * concurrent schema deletion.
             */
-           LockSchemaList(schemaidlist);
-           PublicationAddSchemas(puboid, schemaidlist, true, NULL);
+           LockSchemaList(sequences_schemaidlist);
+           PublicationAddSchemas(puboid,
+                                 sequences_schemaidlist, PUB_OBJTYPE_SEQUENCE,
+                                 true, NULL);
        }
    }
 
@@ -799,6 +914,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
                           AccessShareLock);
 
        root_relids = GetPublicationRelations(pubform->oid,
+                                             PUB_OBJTYPE_TABLE,
                                              PUBLICATION_PART_ROOT);
 
        foreach(lc, root_relids)
@@ -857,6 +973,9 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 
        values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
        replaces[Anum_pg_publication_pubtruncate - 1] = true;
+
+       values[Anum_pg_publication_pubsequence - 1] = BoolGetDatum(pubactions.pubsequence);
+       replaces[Anum_pg_publication_pubsequence - 1] = true;
    }
 
    if (publish_via_partition_root_given)
@@ -876,7 +995,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
    pubform = (Form_pg_publication) GETSTRUCT(tup);
 
    /* Invalidate the relcache. */
-   if (pubform->puballtables)
+   if (pubform->puballtables || pubform->puballsequences)
    {
        CacheInvalidateRelcacheAll();
    }
@@ -892,6 +1011,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
         */
        if (root_relids == NIL)
            relids = GetPublicationRelations(pubform->oid,
+                                            PUB_OBJTYPE_TABLE,
                                             PUBLICATION_PART_ALL);
        else
        {
@@ -905,7 +1025,20 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
                                                        lfirst_oid(lc));
        }
 
+       /* tables */
+       schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
+                                                       PUB_OBJTYPE_TABLE,
+                                                       PUBLICATION_PART_ALL);
+       relids = list_concat_unique_oid(relids, schemarelids);
+
+       /* sequences */
+       relids = list_concat_unique_oid(relids,
+                                       GetPublicationRelations(pubform->oid,
+                                           PUB_OBJTYPE_SEQUENCE,
+                                           PUBLICATION_PART_ALL));
+
        schemarelids = GetAllSchemaPublicationRelations(pubform->oid,
+                                                       PUB_OBJTYPE_SEQUENCE,
                                                        PUBLICATION_PART_ALL);
        relids = list_concat_unique_oid(relids, schemarelids);
 
@@ -960,7 +1093,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
    if (!tables && stmt->action != AP_SetObjects)
        return;
 
-   rels = OpenTableList(tables);
+   rels = OpenRelationList(tables, PUB_OBJTYPE_TABLE);
 
    if (stmt->action == AP_AddObjects)
    {
@@ -970,19 +1103,22 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
         * Check if the relation is member of the existing schema in the
         * publication or member of the schema list specified.
         */
-       schemas = list_concat_copy(schemaidlist, GetPublicationSchemas(pubid));
+       schemas = list_concat_copy(schemaidlist,
+                                  GetPublicationSchemas(pubid,
+                                                        PUB_OBJTYPE_TABLE));
        CheckObjSchemaNotAlreadyInPublication(rels, schemas,
                                              PUBLICATIONOBJ_TABLE);
 
        TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
 
-       PublicationAddTables(pubid, rels, false, stmt);
+       PublicationAddRelations(pubid, rels, false, stmt);
    }
    else if (stmt->action == AP_DropObjects)
-       PublicationDropTables(pubid, rels, false);
+       PublicationDropRelations(pubid, rels, false);
    else                        /* AP_SetObjects */
    {
        List       *oldrelids = GetPublicationRelations(pubid,
+                                                       PUB_OBJTYPE_TABLE,
                                                        PUBLICATION_PART_ROOT);
        List       *delrels = NIL;
        ListCell   *oldlc;
@@ -1064,18 +1200,18 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
        }
 
        /* And drop them. */
-       PublicationDropTables(pubid, delrels, true);
+       PublicationDropRelations(pubid, delrels, true);
 
        /*
         * Don't bother calculating the difference for adding, we'll catch and
         * skip existing ones when doing catalog update.
         */
-       PublicationAddTables(pubid, rels, true, stmt);
+       PublicationAddRelations(pubid, rels, true, stmt);
 
-       CloseTableList(delrels);
+       CloseRelationList(delrels);
    }
 
-   CloseTableList(rels);
+   CloseRelationList(rels);
 }
 
 /*
@@ -1085,7 +1221,8 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
  */
 static void
 AlterPublicationSchemas(AlterPublicationStmt *stmt,
-                       HeapTuple tup, List *schemaidlist)
+                       HeapTuple tup, List *schemaidlist,
+                       char objectType)
 {
    Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
 
@@ -1107,20 +1244,20 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
        List       *rels;
        List       *reloids;
 
-       reloids = GetPublicationRelations(pubform->oid, PUBLICATION_PART_ROOT);
+       reloids = GetPublicationRelations(pubform->oid, objectType, PUBLICATION_PART_ROOT);
        rels = OpenRelIdList(reloids);
 
        CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
                                              PUBLICATIONOBJ_TABLES_IN_SCHEMA);
 
-       CloseTableList(rels);
-       PublicationAddSchemas(pubform->oid, schemaidlist, false, stmt);
+       CloseRelationList(rels);
+       PublicationAddSchemas(pubform->oid, schemaidlist, objectType, false, stmt);
    }
    else if (stmt->action == AP_DropObjects)
-       PublicationDropSchemas(pubform->oid, schemaidlist, false);
+       PublicationDropSchemas(pubform->oid, schemaidlist, objectType, false);
    else                        /* AP_SetObjects */
    {
-       List       *oldschemaids = GetPublicationSchemas(pubform->oid);
+       List       *oldschemaids = GetPublicationSchemas(pubform->oid, objectType);
        List       *delschemas = NIL;
 
        /* Identify which schemas should be dropped */
@@ -1133,13 +1270,13 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
        LockSchemaList(delschemas);
 
        /* And drop them */
-       PublicationDropSchemas(pubform->oid, delschemas, true);
+       PublicationDropSchemas(pubform->oid, delschemas, objectType, true);
 
        /*
         * Don't bother calculating the difference for adding, we'll catch and
         * skip existing ones when doing catalog update.
         */
-       PublicationAddSchemas(pubform->oid, schemaidlist, true, stmt);
+       PublicationAddSchemas(pubform->oid, schemaidlist, objectType, true, stmt);
    }
 }
 
@@ -1149,12 +1286,13 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt,
  */
 static void
 CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
-                     List *tables, List *schemaidlist)
+                     List *tables, List *tables_schemaidlist,
+                     List *sequences, List *sequences_schemaidlist)
 {
    Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
 
    if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
-       schemaidlist && !superuser())
+       (tables_schemaidlist || sequences_schemaidlist) && !superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("must be superuser to add or set schemas")));
@@ -1163,13 +1301,24 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
     * Check that user is allowed to manipulate the publication tables in
     * schema
     */
-   if (schemaidlist && pubform->puballtables)
+   if (tables_schemaidlist && pubform->puballtables)
        ereport(ERROR,
                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                 errmsg("publication \"%s\" is defined as FOR ALL TABLES",
                        NameStr(pubform->pubname)),
                 errdetail("Tables from schema cannot be added to, dropped from, or set on FOR ALL TABLES publications.")));
 
+   /*
+    * Check that user is allowed to manipulate the publication sequences in
+    * schema
+    */
+   if (sequences_schemaidlist && pubform->puballsequences)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
+                       NameStr(pubform->pubname)),
+                errdetail("Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.")));
+
    /* Check that user is allowed to manipulate the publication tables. */
    if (tables && pubform->puballtables)
        ereport(ERROR,
@@ -1177,6 +1326,107 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup,
                 errmsg("publication \"%s\" is defined as FOR ALL TABLES",
                        NameStr(pubform->pubname)),
                 errdetail("Tables cannot be added to or dropped from FOR ALL TABLES publications.")));
+
+   /* Check that user is allowed to manipulate the publication tables. */
+   if (sequences && pubform->puballsequences)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("publication \"%s\" is defined as FOR ALL SEQUENCES",
+                       NameStr(pubform->pubname)),
+                errdetail("Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.")));
+}
+
+/*
+ * Add or remove sequence to/from publication.
+ */
+static void
+AlterPublicationSequences(AlterPublicationStmt *stmt, HeapTuple tup,
+                         List *sequences, List *schemaidlist)
+{
+   List       *rels = NIL;
+   Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
+   Oid         pubid = pubform->oid;
+
+   /*
+    * It is quite possible that for the SET case user has not specified any
+    * tables in which case we need to remove all the existing tables.
+    */
+   if (!sequences && stmt->action != AP_SetObjects)
+       return;
+
+   rels = OpenRelationList(sequences, PUB_OBJTYPE_SEQUENCE);
+
+   if (stmt->action == AP_AddObjects)
+   {
+       List       *schemas = NIL;
+
+       /*
+        * Check if the relation is member of the existing schema in the
+        * publication or member of the schema list specified.
+        */
+       schemas = list_concat_copy(schemaidlist,
+                                  GetPublicationSchemas(pubid,
+                                                        PUB_OBJTYPE_SEQUENCE));
+       CheckObjSchemaNotAlreadyInPublication(rels, schemas,
+                                             PUBLICATIONOBJ_SEQUENCE);
+       PublicationAddRelations(pubid, rels, false, stmt);
+   }
+   else if (stmt->action == AP_DropObjects)
+       PublicationDropRelations(pubid, rels, false);
+   else                        /* DEFELEM_SET */
+   {
+       List       *oldrelids = GetPublicationRelations(pubid,
+                                                       PUB_OBJTYPE_SEQUENCE,
+                                                       PUBLICATION_PART_ROOT);
+       List       *delrels = NIL;
+       ListCell   *oldlc;
+
+       CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
+                                             PUBLICATIONOBJ_SEQUENCE);
+
+       /* Calculate which relations to drop. */
+       foreach(oldlc, oldrelids)
+       {
+           Oid         oldrelid = lfirst_oid(oldlc);
+           ListCell   *newlc;
+           PublicationRelInfo *oldrel;
+           bool        found = false;
+
+           foreach(newlc, rels)
+           {
+               PublicationRelInfo *newpubrel;
+
+               newpubrel = (PublicationRelInfo *) lfirst(newlc);
+               if (RelationGetRelid(newpubrel->relation) == oldrelid)
+               {
+                   found = true;
+                   break;
+               }
+           }
+           /* Not yet in the list, open it and add to the list */
+           if (!found)
+           {
+               oldrel = palloc(sizeof(PublicationRelInfo));
+               oldrel->whereClause = NULL;
+               oldrel->relation = table_open(oldrelid,
+                                             ShareUpdateExclusiveLock);
+               delrels = lappend(delrels, oldrel);
+           }
+       }
+
+       /* And drop them. */
+       PublicationDropRelations(pubid, delrels, true);
+
+       /*
+        * Don't bother calculating the difference for adding, we'll catch and
+        * skip existing ones when doing catalog update.
+        */
+       PublicationAddRelations(pubid, rels, true, stmt);
+
+       CloseRelationList(delrels);
+   }
+
+   CloseRelationList(rels);
 }
 
 /*
@@ -1214,14 +1464,22 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
        AlterPublicationOptions(pstate, stmt, rel, tup);
    else
    {
-       List       *relations = NIL;
+       List       *tables = NIL;
+       List       *sequences = NIL;
+       List       *tables_schemaidlist = NIL;
+       List       *sequences_schemaidlist = NIL;
        List       *schemaidlist = NIL;
        Oid         pubid = pubform->oid;
 
-       ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
+       ObjectsInPublicationToOids(stmt->pubobjects, pstate,
+                                  &tables, &sequences,
+                                  &tables_schemaidlist,
+                                  &sequences_schemaidlist,
                                   &schemaidlist);
 
-       CheckAlterPublication(stmt, tup, relations, schemaidlist);
+       CheckAlterPublication(stmt, tup,
+                             tables, tables_schemaidlist,
+                             sequences, sequences_schemaidlist);
 
        heap_freetuple(tup);
 
@@ -1249,9 +1507,16 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
                    errmsg("publication \"%s\" does not exist",
                           stmt->pubname));
 
-       AlterPublicationTables(stmt, tup, relations, schemaidlist,
+       AlterPublicationTables(stmt, tup, tables, tables_schemaidlist,
                               pstate->p_sourcetext);
-       AlterPublicationSchemas(stmt, tup, schemaidlist);
+
+       AlterPublicationSequences(stmt, tup, sequences, sequences_schemaidlist);
+
+       AlterPublicationSchemas(stmt, tup, tables_schemaidlist,
+                               PUB_OBJTYPE_TABLE);
+
+       AlterPublicationSchemas(stmt, tup, sequences_schemaidlist,
+                               PUB_OBJTYPE_SEQUENCE);
    }
 
    /* Cleanup. */
@@ -1319,7 +1584,7 @@ RemovePublicationById(Oid pubid)
    pubform = (Form_pg_publication) GETSTRUCT(tup);
 
    /* Invalidate relcache so that publication info is rebuilt. */
-   if (pubform->puballtables)
+   if (pubform->puballtables || pubform->puballsequences)
        CacheInvalidateRelcacheAll();
 
    CatalogTupleDelete(rel, &tup->t_self);
@@ -1355,6 +1620,7 @@ RemovePublicationSchemaById(Oid psoid)
     * partitions.
     */
    schemaRels = GetSchemaPublicationRelations(pubsch->pnnspid,
+                                              pubsch->pntype,
                                               PUBLICATION_PART_ALL);
    InvalidatePublicationRels(schemaRels);
 
@@ -1397,29 +1663,45 @@ OpenRelIdList(List *relids)
  * add them to a publication.
  */
 static List *
-OpenTableList(List *tables)
+OpenRelationList(List *rels, char objectType)
 {
    List       *relids = NIL;
-   List       *rels = NIL;
+   List       *result = NIL;
    ListCell   *lc;
    List       *relids_with_rf = NIL;
 
    /*
     * Open, share-lock, and check all the explicitly-specified relations
     */
-   foreach(lc, tables)
+   foreach(lc, rels)
    {
        PublicationTable *t = lfirst_node(PublicationTable, lc);
        bool        recurse = t->relation->inh;
        Relation    rel;
        Oid         myrelid;
        PublicationRelInfo *pub_rel;
+       char        myrelkind;
 
        /* Allow query cancel in case this takes a long time */
        CHECK_FOR_INTERRUPTS();
 
        rel = table_openrv(t->relation, ShareUpdateExclusiveLock);
        myrelid = RelationGetRelid(rel);
+       myrelkind = get_rel_relkind(myrelid);
+
+       /*
+        * Make sure the relkind matches the expected object type. This may
+        * happen e.g. when adding a sequence using ADD TABLE or a table
+        * using ADD SEQUENCE).
+        *
+        * XXX We let through unsupported object types (views etc.). Those
+        * will be caught later in check_publication_add_relation.
+        */
+       if (pub_get_object_type_for_relkind(myrelkind) != PUB_OBJTYPE_UNSUPPORTED &&
+           pub_get_object_type_for_relkind(myrelkind) != objectType)
+           ereport(ERROR,
+                   errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                   errmsg("object type does not match type expected by command"));
 
        /*
         * Filter out duplicates if user specifies "foo, foo".
@@ -1444,7 +1726,7 @@ OpenTableList(List *tables)
        pub_rel = palloc(sizeof(PublicationRelInfo));
        pub_rel->relation = rel;
        pub_rel->whereClause = t->whereClause;
-       rels = lappend(rels, pub_rel);
+       result = lappend(result, pub_rel);
        relids = lappend_oid(relids, myrelid);
 
        if (t->whereClause)
@@ -1498,7 +1780,7 @@ OpenTableList(List *tables)
                pub_rel->relation = rel;
                /* child inherits WHERE clause from parent */
                pub_rel->whereClause = t->whereClause;
-               rels = lappend(rels, pub_rel);
+               result = lappend(result, pub_rel);
                relids = lappend_oid(relids, childrelid);
 
                if (t->whereClause)
@@ -1510,14 +1792,14 @@ OpenTableList(List *tables)
    list_free(relids);
    list_free(relids_with_rf);
 
-   return rels;
+   return result;
 }
 
 /*
  * Close all relations in the list.
  */
 static void
-CloseTableList(List *rels)
+CloseRelationList(List *rels)
 {
    ListCell   *lc;
 
@@ -1565,12 +1847,12 @@ LockSchemaList(List *schemalist)
  * Add listed tables to the publication.
  */
 static void
-PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
+PublicationAddRelations(Oid pubid, List *rels, bool if_not_exists,
                     AlterPublicationStmt *stmt)
 {
    ListCell   *lc;
 
-   Assert(!stmt || !stmt->for_all_tables);
+   Assert(!stmt || !stmt->for_all_objects);
 
    foreach(lc, rels)
    {
@@ -1599,7 +1881,7 @@ PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
  * Remove listed tables from the publication.
  */
 static void
-PublicationDropTables(Oid pubid, List *rels, bool missing_ok)
+PublicationDropRelations(Oid pubid, List *rels, bool missing_ok)
 {
    ObjectAddress obj;
    ListCell   *lc;
@@ -1639,19 +1921,19 @@ PublicationDropTables(Oid pubid, List *rels, bool missing_ok)
  * Add listed schemas to the publication.
  */
 static void
-PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
-                     AlterPublicationStmt *stmt)
+PublicationAddSchemas(Oid pubid, List *schemas, char objectType,
+                     bool if_not_exists, AlterPublicationStmt *stmt)
 {
    ListCell   *lc;
 
-   Assert(!stmt || !stmt->for_all_tables);
+   Assert(!stmt || !stmt->for_all_objects);
 
    foreach(lc, schemas)
    {
        Oid         schemaid = lfirst_oid(lc);
        ObjectAddress obj;
 
-       obj = publication_add_schema(pubid, schemaid, if_not_exists);
+       obj = publication_add_schema(pubid, schemaid, objectType, if_not_exists);
        if (stmt)
        {
            EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress,
@@ -1667,7 +1949,7 @@ PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists,
  * Remove listed schemas from the publication.
  */
 static void
-PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok)
+PublicationDropSchemas(Oid pubid, List *schemas, char objectType, bool missing_ok)
 {
    ObjectAddress obj;
    ListCell   *lc;
@@ -1677,10 +1959,11 @@ PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok)
    {
        Oid         schemaid = lfirst_oid(lc);
 
-       psid = GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
+       psid = GetSysCacheOid3(PUBLICATIONNAMESPACEMAP,
                               Anum_pg_publication_namespace_oid,
                               ObjectIdGetDatum(schemaid),
-                              ObjectIdGetDatum(pubid));
+                              ObjectIdGetDatum(pubid),
+                              CharGetDatum(objectType));
        if (!OidIsValid(psid))
        {
            if (missing_ok)
@@ -1735,6 +2018,13 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
                            NameStr(form->pubname)),
                     errhint("The owner of a FOR ALL TABLES publication must be a superuser.")));
 
+       if (form->puballsequences && !superuser_arg(newOwnerId))
+           ereport(ERROR,
+                   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                    errmsg("permission denied to change owner of publication \"%s\"",
+                           NameStr(form->pubname)),
+                    errhint("The owner of a FOR ALL SEQUENCES publication must be a superuser.")));
+
        if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid))
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
index c13cada3bf122954ae2bd837fb6d476732dabe32..717bb0b2aa9d39fb58ac7d92d00b31f9cf98d9f1 100644 (file)
@@ -336,6 +336,160 @@ ResetSequence(Oid seq_relid)
    relation_close(seq_rel, NoLock);
 }
 
+/*
+ * Update the sequence state by modifying the existing sequence data row.
+ *
+ * This keeps the same relfilenode, so the behavior is non-transactional.
+ */
+static void
+SetSequence_non_transactional(Oid seqrelid, int64 last_value, int64 log_cnt, bool is_called)
+{
+   SeqTable    elm;
+   Relation    seqrel;
+   Buffer      buf;
+   HeapTupleData seqdatatuple;
+   Form_pg_sequence_data seq;
+
+   /* open and lock sequence */
+   init_sequence(seqrelid, &elm, &seqrel);
+
+   /* lock page' buffer and read tuple */
+   seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
+
+   /* check the comment above nextval_internal()'s equivalent call. */
+   if (RelationNeedsWAL(seqrel))
+   {
+       GetTopTransactionId();
+
+       if (XLogLogicalInfoActive())
+           GetCurrentTransactionId();
+   }
+
+   /* ready to change the on-disk (or really, in-buffer) tuple */
+   START_CRIT_SECTION();
+
+   seq->last_value = last_value;
+   seq->is_called = is_called;
+   seq->log_cnt = log_cnt;
+
+   MarkBufferDirty(buf);
+
+   /* XLOG stuff */
+   if (RelationNeedsWAL(seqrel))
+   {
+       xl_seq_rec  xlrec;
+       XLogRecPtr  recptr;
+       Page        page = BufferGetPage(buf);
+
+       XLogBeginInsert();
+       XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+       xlrec.node = seqrel->rd_node;
+       xlrec.created = false;
+
+       XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+       XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
+
+       recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+       PageSetLSN(page, recptr);
+   }
+
+   END_CRIT_SECTION();
+
+   UnlockReleaseBuffer(buf);
+
+   /* Clear local cache so that we don't think we have cached numbers */
+   /* Note that we do not change the currval() state */
+   elm->cached = elm->last;
+
+   relation_close(seqrel, NoLock);
+}
+
+/*
+ * Update the sequence state by creating a new relfilenode.
+ *
+ * This creates a new relfilenode, to allow transactional behavior.
+ */
+static void
+SetSequence_transactional(Oid seq_relid, int64 last_value, int64 log_cnt, bool is_called)
+{
+   SeqTable    elm;
+   Relation    seqrel;
+   Buffer      buf;
+   HeapTupleData seqdatatuple;
+   Form_pg_sequence_data seq;
+   HeapTuple   tuple;
+
+   /* open and lock sequence */
+   init_sequence(seq_relid, &elm, &seqrel);
+
+   /* lock page' buffer and read tuple */
+   seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
+
+   /* Copy the existing sequence tuple. */
+   tuple = heap_copytuple(&seqdatatuple);
+
+   /* Now we're done with the old page */
+   UnlockReleaseBuffer(buf);
+
+   /*
+    * Modify the copied tuple to update the sequence state (similar to what
+    * ResetSequence does).
+    */
+   seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
+   seq->last_value = last_value;
+   seq->is_called = is_called;
+   seq->log_cnt = log_cnt;
+
+   /*
+    * Create a new storage file for the sequence - this is needed for the
+    * transactional behavior.
+    */
+   RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence);
+
+   /*
+    * Ensure sequence's relfrozenxid is at 0, since it won't contain any
+    * unfrozen XIDs.  Same with relminmxid, since a sequence will never
+    * contain multixacts.
+    */
+   Assert(seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
+   Assert(seqrel->rd_rel->relminmxid == InvalidMultiXactId);
+
+   /*
+    * Insert the modified tuple into the new storage file. This does all the
+    * necessary WAL-logging etc.
+    */
+   fill_seq_with_data(seqrel, tuple);
+
+   /* Clear local cache so that we don't think we have cached numbers */
+   /* Note that we do not change the currval() state */
+   elm->cached = elm->last;
+
+   relation_close(seqrel, NoLock);
+}
+
+/*
+ * Set a sequence to a specified internal state.
+ *
+ * The change is made transactionally, so that on failure of the current
+ * transaction, the sequence will be restored to its previous state.
+ * We do that by creating a whole new relfilenode for the sequence; so this
+ * works much like the rewriting forms of ALTER TABLE.
+ *
+ * Caller is assumed to have acquired AccessExclusiveLock on the sequence,
+ * which must not be released until end of transaction.  Caller is also
+ * responsible for permissions checking.
+ */
+void
+SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called)
+{
+   if (transactional)
+       SetSequence_transactional(seq_relid, last_value, log_cnt, is_called);
+   else
+       SetSequence_non_transactional(seq_relid, last_value, log_cnt, is_called);
+}
+
 /*
  * Initialize a sequence's relation with the specified tuple as content
  */
index e16f04626dec60b2cb2c3156ef5d9625b6893599..abebffdf3bb582c5d0ede44d9889f7db36016a07 100644 (file)
@@ -90,6 +90,7 @@ typedef struct SubOpts
 } SubOpts;
 
 static List *fetch_table_list(WalReceiverConn *wrconn, List *publications);
+static List *fetch_sequence_list(WalReceiverConn *wrconn, List *publications);
 static void check_duplicates_in_publist(List *publist, Datum *datums);
 static List *merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname);
 static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err);
@@ -541,9 +542,9 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
    {
        char       *err;
        WalReceiverConn *wrconn;
-       List       *tables;
+       List       *relations;
        ListCell   *lc;
-       char        table_state;
+       char        sync_state;
 
        /* Try to connect to the publisher. */
        wrconn = walrcv_connect(conninfo, true, stmt->subname, &err);
@@ -558,14 +559,17 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
             * Set sync state based on if we were asked to do data copy or
             * not.
             */
-           table_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
+           sync_state = opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
 
            /*
-            * Get the table list from publisher and build local table status
-            * info.
+            * Get the table and sequence list from publisher and build
+            * local relation sync status info.
             */
-           tables = fetch_table_list(wrconn, publications);
-           foreach(lc, tables)
+           relations = fetch_table_list(wrconn, publications);
+           relations = list_concat(relations,
+                                   fetch_sequence_list(wrconn, publications));
+
+           foreach(lc, relations)
            {
                RangeVar   *rv = (RangeVar *) lfirst(lc);
                Oid         relid;
@@ -576,7 +580,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
                CheckSubscriptionRelkind(get_rel_relkind(relid),
                                         rv->schemaname, rv->relname);
 
-               AddSubscriptionRelState(subid, relid, table_state,
+               AddSubscriptionRelState(subid, relid, sync_state,
                                        InvalidXLogRecPtr);
            }
 
@@ -602,12 +606,12 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
                 *
                 * Note that if tables were specified but copy_data is false
                 * then it is safe to enable two_phase up-front because those
-                * tables are already initially in READY state. When the
-                * subscription has no tables, we leave the twophase state as
-                * PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
+                * relations are already initially in READY state. When the
+                * subscription has no relations, we leave the twophase state
+                * as PENDING, to allow ALTER SUBSCRIPTION ... REFRESH
                 * PUBLICATION to work.
                 */
-               if (opts.twophase && !opts.copy_data && tables != NIL)
+               if (opts.twophase && !opts.copy_data && relations != NIL)
                    twophase_enabled = true;
 
                walrcv_create_slot(wrconn, opts.slot_name, false, twophase_enabled,
@@ -677,8 +681,10 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data)
 
    PG_TRY();
    {
-       /* Get the table list from publisher. */
+       /* Get the list of relations from publisher. */
        pubrel_names = fetch_table_list(wrconn, sub->publications);
+       pubrel_names = list_concat(pubrel_names,
+                                  fetch_sequence_list(wrconn, sub->publications));
 
        /* Get local table list. */
        subrel_states = GetSubscriptionRelations(sub->oid);
@@ -1712,6 +1718,75 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications)
    return tablelist;
 }
 
+/*
+ * Get the list of sequences which belong to specified publications on the
+ * publisher connection.
+ */
+static List *
+fetch_sequence_list(WalReceiverConn *wrconn, List *publications)
+{
+   WalRcvExecResult *res;
+   StringInfoData cmd;
+   TupleTableSlot *slot;
+   Oid         tableRow[2] = {TEXTOID, TEXTOID};
+   ListCell   *lc;
+   bool        first;
+   List       *tablelist = NIL;
+
+   Assert(list_length(publications) > 0);
+
+   initStringInfo(&cmd);
+   appendStringInfoString(&cmd, "SELECT DISTINCT s.schemaname, s.sequencename\n"
+                          "  FROM pg_catalog.pg_publication_sequences s\n"
+                          " WHERE s.pubname IN (");
+   first = true;
+   foreach(lc, publications)
+   {
+       char       *pubname = strVal(lfirst(lc));
+
+       if (first)
+           first = false;
+       else
+           appendStringInfoString(&cmd, ", ");
+
+       appendStringInfoString(&cmd, quote_literal_cstr(pubname));
+   }
+   appendStringInfoChar(&cmd, ')');
+
+   res = walrcv_exec(wrconn, cmd.data, 2, tableRow);
+   pfree(cmd.data);
+
+   if (res->status != WALRCV_OK_TUPLES)
+       ereport(ERROR,
+               (errmsg("could not receive list of replicated sequences from the publisher: %s",
+                       res->err)));
+
+   /* Process sequences. */
+   slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
+   while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
+   {
+       char       *nspname;
+       char       *relname;
+       bool        isnull;
+       RangeVar   *rv;
+
+       nspname = TextDatumGetCString(slot_getattr(slot, 1, &isnull));
+       Assert(!isnull);
+       relname = TextDatumGetCString(slot_getattr(slot, 2, &isnull));
+       Assert(!isnull);
+
+       rv = makeRangeVar(nspname, relname, -1);
+       tablelist = lappend(tablelist, rv);
+
+       ExecClearTuple(slot);
+   }
+   ExecDropSingleTupleTableSlot(slot);
+
+   walrcv_clear_result(res);
+
+   return tablelist;
+}
+
 /*
  * This is to report the connection failure while dropping replication slots.
  * Here, we report the WARNING for all tablesync slots so that user can drop
index 80faae985e924ba0ae61ef136d424952003995e4..124b9961dc98781be7dbf4928a8adb8936d8d5f2 100644 (file)
@@ -42,6 +42,7 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_publication_namespace.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
@@ -16381,11 +16382,14 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
     * Check that setting the relation to a different schema won't result in a
     * publication having both a schema and the same schema's table, as this
     * is not supported.
+    *
+    * XXX We do this for tables and sequences, but it's better to keep the two
+    * blocks separate, to make the strings easier to translate.
     */
    if (stmt->objectType == OBJECT_TABLE)
    {
        ListCell   *lc;
-       List       *schemaPubids = GetSchemaPublications(nspOid);
+       List       *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_TABLE);
        List       *relPubids = GetRelationPublications(RelationGetRelid(rel));
 
        foreach(lc, relPubids)
@@ -16403,6 +16407,27 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
                                  get_publication_name(pubid, false)));
        }
    }
+   else if (stmt->objectType == OBJECT_SEQUENCE)
+   {
+       ListCell   *lc;
+       List       *schemaPubids = GetSchemaPublications(nspOid, PUB_OBJTYPE_SEQUENCE);
+       List       *relPubids = GetRelationPublications(RelationGetRelid(rel));
+
+       foreach(lc, relPubids)
+       {
+           Oid         pubid = lfirst_oid(lc);
+
+           if (list_member_oid(schemaPubids, pubid))
+               ereport(ERROR,
+                       errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                       errmsg("cannot move sequence \"%s\" to schema \"%s\"",
+                              RelationGetRelationName(rel), stmt->newschema),
+                       errdetail("The schema \"%s\" and same schema's sequence \"%s\" cannot be part of the same publication \"%s\".",
+                                 stmt->newschema,
+                                 RelationGetRelationName(rel),
+                                 get_publication_name(pubid, false)));
+       }
+   }
 
    /* common checks on switching namespaces */
    CheckSetNamespace(oldNspOid, nspOid);
index 13328141e2308654d27b4b5f753aa9446a103938..0df7cf587471f7bad0a53ba7bc9937aafd87dc70 100644 (file)
@@ -636,7 +636,9 @@ void
 CheckSubscriptionRelkind(char relkind, const char *nspname,
                         const char *relname)
 {
-   if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
+   if (relkind != RELKIND_RELATION &&
+       relkind != RELKIND_PARTITIONED_TABLE &&
+       relkind != RELKIND_SEQUENCE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot use relation \"%s.%s\" as logical replication target",
index d4f8455a2bdb048814557d363ac64c5bd7e25491..55f720a88f40ab77988f0a5f1fbf9383d33c2dc1 100644 (file)
@@ -4862,7 +4862,7 @@ _copyCreatePublicationStmt(const CreatePublicationStmt *from)
    COPY_STRING_FIELD(pubname);
    COPY_NODE_FIELD(options);
    COPY_NODE_FIELD(pubobjects);
-   COPY_SCALAR_FIELD(for_all_tables);
+   COPY_NODE_FIELD(for_all_objects);
 
    return newnode;
 }
@@ -4875,7 +4875,7 @@ _copyAlterPublicationStmt(const AlterPublicationStmt *from)
    COPY_STRING_FIELD(pubname);
    COPY_NODE_FIELD(options);
    COPY_NODE_FIELD(pubobjects);
-   COPY_SCALAR_FIELD(for_all_tables);
+   COPY_NODE_FIELD(for_all_objects);
    COPY_SCALAR_FIELD(action);
 
    return newnode;
index f1002afe7a0db90d653607ad812c82c384622003..82562eb9b872de5a3f28d35e433f6b29bbfa1f05 100644 (file)
@@ -2333,7 +2333,7 @@ _equalCreatePublicationStmt(const CreatePublicationStmt *a,
    COMPARE_STRING_FIELD(pubname);
    COMPARE_NODE_FIELD(options);
    COMPARE_NODE_FIELD(pubobjects);
-   COMPARE_SCALAR_FIELD(for_all_tables);
+   COMPARE_NODE_FIELD(for_all_objects);
 
    return true;
 }
@@ -2345,7 +2345,7 @@ _equalAlterPublicationStmt(const AlterPublicationStmt *a,
    COMPARE_STRING_FIELD(pubname);
    COMPARE_NODE_FIELD(options);
    COMPARE_NODE_FIELD(pubobjects);
-   COMPARE_SCALAR_FIELD(for_all_tables);
+   COMPARE_NODE_FIELD(for_all_objects);
    COMPARE_SCALAR_FIELD(action);
 
    return true;
index 0036c2f9e2d6ade3040ddf4844784bec362ade96..e327bc735fbc3ce1e24cd1c5527bad0a8c1d17de 100644 (file)
@@ -446,7 +446,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                transform_element_list transform_type_list
                TriggerTransitions TriggerReferencing
                vacuum_relation_list opt_vacuum_relation_list
-               drop_option_list pub_obj_list
+               drop_option_list pub_obj_list pub_obj_type_list
 
 %type <node>   opt_routine_body
 %type <groupclause> group_clause
@@ -575,6 +575,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <node>   var_value zone_value
 %type <rolespec> auth_ident RoleSpec opt_granted_by
 %type <publicationobjectspec> PublicationObjSpec
+%type <node>   pub_obj_type
 
 %type <keyword> unreserved_keyword type_func_name_keyword
 %type <keyword> col_name_keyword reserved_keyword
@@ -9701,12 +9702,9 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
  *
  * CREATE PUBLICATION FOR ALL TABLES [WITH options]
  *
- * CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
- *
- * pub_obj is one of:
+ * CREATE PUBLICATION FOR ALL SEQUENCES [WITH options]
  *
- *     TABLE table [, ...]
- *     ALL TABLES IN SCHEMA schema [, ...]
+ * CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
  *
  *****************************************************************************/
 
@@ -9718,12 +9716,12 @@ CreatePublicationStmt:
                    n->options = $4;
                    $$ = (Node *)n;
                }
-           | CREATE PUBLICATION name FOR ALL TABLES opt_definition
+           | CREATE PUBLICATION name FOR ALL pub_obj_type_list opt_definition
                {
                    CreatePublicationStmt *n = makeNode(CreatePublicationStmt);
                    n->pubname = $3;
                    n->options = $7;
-                   n->for_all_tables = true;
+                   n->for_all_objects = $6;
                    $$ = (Node *)n;
                }
            | CREATE PUBLICATION name FOR pub_obj_list opt_definition
@@ -9772,6 +9770,26 @@ PublicationObjSpec:
                    $$->pubobjtype = PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA;
                    $$->location = @5;
                }
+           | SEQUENCE relation_expr
+               {
+                   $$ = makeNode(PublicationObjSpec);
+                   $$->pubobjtype = PUBLICATIONOBJ_SEQUENCE;
+                   $$->pubtable = makeNode(PublicationTable);
+                   $$->pubtable->relation = $2;
+               }
+           | ALL SEQUENCES IN_P SCHEMA ColId
+               {
+                   $$ = makeNode(PublicationObjSpec);
+                   $$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA;
+                   $$->name = $5;
+                   $$->location = @5;
+               }
+           | ALL SEQUENCES IN_P SCHEMA CURRENT_SCHEMA
+               {
+                   $$ = makeNode(PublicationObjSpec);
+                   $$->pubobjtype = PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA;
+                   $$->location = @5;
+               }
            | ColId OptWhereClause
                {
                    $$ = makeNode(PublicationObjSpec);
@@ -9826,6 +9844,19 @@ pub_obj_list:    PublicationObjSpec
                    { $$ = lappend($1, $3); }
    ;
 
+pub_obj_type:  TABLES
+                   { $$ = (Node *) makeString("tables"); }
+               | SEQUENCES
+                   { $$ = (Node *) makeString("sequences"); }
+   ;
+
+pub_obj_type_list: pub_obj_type
+                   { $$ = list_make1($1); }
+               | pub_obj_type_list ',' pub_obj_type
+                   { $$ = lappend($1, $3); }
+   ;
+
+
 /*****************************************************************************
  *
  * ALTER PUBLICATION name SET ( options )
@@ -9836,11 +9867,6 @@ pub_obj_list:    PublicationObjSpec
  *
  * ALTER PUBLICATION name SET pub_obj [, ...]
  *
- * pub_obj is one of:
- *
- *     TABLE table_name [, ...]
- *     ALL TABLES IN SCHEMA schema_name [, ...]
- *
  *****************************************************************************/
 
 AlterPublicationStmt:
index c9b0eeefd7ee9c2b9ed9800a7b3226acfce57439..3dbe85d61a2ec1944144ab2b613040dea7703932 100644 (file)
@@ -648,6 +648,56 @@ logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
    pq_sendbytes(out, message, sz);
 }
 
+/*
+ * Write SEQUENCE to stream
+ */
+void
+logicalrep_write_sequence(StringInfo out, Relation rel, TransactionId xid,
+                         XLogRecPtr lsn, bool transactional,
+                         int64 last_value, int64 log_cnt, bool is_called)
+{
+   uint8       flags = 0;
+   char       *relname;
+
+   pq_sendbyte(out, LOGICAL_REP_MSG_SEQUENCE);
+
+   /* transaction ID (if not valid, we're not streaming) */
+   if (TransactionIdIsValid(xid))
+       pq_sendint32(out, xid);
+
+   pq_sendint8(out, flags);
+   pq_sendint64(out, lsn);
+
+   logicalrep_write_namespace(out, RelationGetNamespace(rel));
+   relname = RelationGetRelationName(rel);
+   pq_sendstring(out, relname);
+
+   pq_sendint8(out, transactional);
+   pq_sendint64(out, last_value);
+   pq_sendint64(out, log_cnt);
+   pq_sendint8(out, is_called);
+}
+
+/*
+ * Read SEQUENCE from the stream.
+ */
+void
+logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata)
+{
+   /* XXX skipping flags and lsn */
+   pq_getmsgint(in, 1);
+   pq_getmsgint64(in);
+
+   /* Read relation name from stream */
+   seqdata->nspname = pstrdup(logicalrep_read_namespace(in));
+   seqdata->seqname = pstrdup(pq_getmsgstring(in));
+
+   seqdata->transactional = pq_getmsgint(in, 1);
+   seqdata->last_value = pq_getmsgint64(in);
+   seqdata->log_cnt = pq_getmsgint64(in);
+   seqdata->is_called = pq_getmsgint(in, 1);
+}
+
 /*
  * Write relation description to the output stream.
  */
@@ -1203,6 +1253,8 @@ logicalrep_message_type(LogicalRepMsgType action)
            return "STREAM ABORT";
        case LOGICAL_REP_MSG_STREAM_PREPARE:
            return "STREAM PREPARE";
+       case LOGICAL_REP_MSG_SEQUENCE:
+           return "SEQUENCE";
    }
 
    elog(ERROR, "invalid logical replication message type \"%c\"", action);
index 1659964571c0f0c141cf2dab61701ce4e99421a0..d8b12d94bc3fd953531cd281a2927acc73459826 100644 (file)
 #include "catalog/pg_subscription_rel.h"
 #include "catalog/pg_type.h"
 #include "commands/copy.h"
+#include "commands/sequence.h"
 #include "miscadmin.h"
 #include "parser/parse_relation.h"
 #include "pgstat.h"
@@ -999,6 +1000,95 @@ copy_table(Relation rel)
    logicalrep_rel_close(relmapentry, NoLock);
 }
 
+/*
+ * Fetch sequence data (current state) from the remote node.
+ */
+static void
+fetch_sequence_data(char *nspname, char *relname,
+                   int64 *last_value, int64 *log_cnt, bool *is_called)
+{
+   WalRcvExecResult *res;
+   StringInfoData cmd;
+   TupleTableSlot *slot;
+   Oid         tableRow[3] = {INT8OID, INT8OID, BOOLOID};
+
+   initStringInfo(&cmd);
+   appendStringInfo(&cmd, "SELECT last_value, log_cnt, is_called\n"
+                      "  FROM %s", quote_qualified_identifier(nspname, relname));
+
+   res = walrcv_exec(LogRepWorkerWalRcvConn, cmd.data, 3, tableRow);
+   pfree(cmd.data);
+
+   if (res->status != WALRCV_OK_TUPLES)
+       ereport(ERROR,
+               (errmsg("could not receive list of replicated tables from the publisher: %s",
+                       res->err)));
+
+   /* Process the sequence. */
+   slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
+   while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
+   {
+       bool        isnull;
+
+       *last_value = DatumGetInt64(slot_getattr(slot, 1, &isnull));
+       Assert(!isnull);
+
+       *log_cnt = DatumGetInt64(slot_getattr(slot, 2, &isnull));
+       Assert(!isnull);
+
+       *is_called = DatumGetBool(slot_getattr(slot, 3, &isnull));
+       Assert(!isnull);
+
+       ExecClearTuple(slot);
+   }
+   ExecDropSingleTupleTableSlot(slot);
+
+   walrcv_clear_result(res);
+}
+
+/*
+ * Copy existing data of a sequence from publisher.
+ *
+ * Caller is responsible for locking the local relation.
+ */
+static void
+copy_sequence(Relation rel)
+{
+   LogicalRepRelMapEntry *relmapentry;
+   LogicalRepRelation lrel;
+   List       *qual = NIL;
+   StringInfoData cmd;
+   int64       last_value = 0,
+               log_cnt = 0;
+   bool        is_called = 0;
+
+   /* Get the publisher relation info. */
+   fetch_remote_table_info(get_namespace_name(RelationGetNamespace(rel)),
+                           RelationGetRelationName(rel), &lrel, &qual);
+
+   /* sequences don't have row filters */
+   Assert(!qual);
+
+   /* Put the relation into relmap. */
+   logicalrep_relmap_update(&lrel);
+
+   /* Map the publisher relation to local one. */
+   relmapentry = logicalrep_rel_open(lrel.remoteid, NoLock);
+   Assert(rel == relmapentry->localrel);
+
+   /* Start copy on the publisher. */
+   initStringInfo(&cmd);
+
+   Assert(lrel.relkind == RELKIND_SEQUENCE);
+
+   fetch_sequence_data(lrel.nspname, lrel.relname, &last_value, &log_cnt, &is_called);
+
+   /* tablesync sets the sequences in non-transactional way */
+   SetSequence(RelationGetRelid(rel), false, last_value, log_cnt, is_called);
+
+   logicalrep_rel_close(relmapentry, NoLock);
+}
+
 /*
  * Determine the tablesync slot name.
  *
@@ -1260,10 +1350,21 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
                        originname)));
    }
 
-   /* Now do the initial data copy */
-   PushActiveSnapshot(GetTransactionSnapshot());
-   copy_table(rel);
-   PopActiveSnapshot();
+   /* Do the right action depending on the relation kind. */
+   if (get_rel_relkind(RelationGetRelid(rel)) == RELKIND_SEQUENCE)
+   {
+       /* Now do the initial sequence copy */
+       PushActiveSnapshot(GetTransactionSnapshot());
+       copy_sequence(rel);
+       PopActiveSnapshot();
+   }
+   else
+   {
+       /* Now do the initial data copy */
+       PushActiveSnapshot(GetTransactionSnapshot());
+       copy_table(rel);
+       PopActiveSnapshot();
+   }
 
    res = walrcv_exec(LogRepWorkerWalRcvConn, "COMMIT", 0, NULL);
    if (res->status != WALRCV_OK_COMMAND)
index 82dcffc2db8b640565d3abd522dcc3c38e3d3e64..f3868b3e1f83b7107e352f9d697fb051495eafbb 100644 (file)
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_subscription_rel.h"
 #include "catalog/pg_tablespace.h"
+#include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -1143,6 +1144,57 @@ apply_handle_origin(StringInfo s)
                 errmsg_internal("ORIGIN message sent out of order")));
 }
 
+/*
+ * Handle SEQUENCE message.
+ */
+static void
+apply_handle_sequence(StringInfo s)
+{
+   LogicalRepSequence  seq;
+   Oid                 relid;
+
+   if (handle_streamed_transaction(LOGICAL_REP_MSG_SEQUENCE, s))
+       return;
+
+   logicalrep_read_sequence(s, &seq);
+
+   /*
+    * Non-transactional sequence updates should not be part of a remote
+    * transaction. There should not be any running transaction.
+    */
+   Assert((!seq.transactional) || in_remote_transaction);
+   Assert(!(!seq.transactional && in_remote_transaction));
+   Assert(!(!seq.transactional && IsTransactionState()));
+
+   /*
+    * Make sure we're in a transaction (needed by SetSequence). For
+    * non-transactional updates we're guaranteed to start a new one,
+    * and we'll commit it at the end.
+    */
+   if (!IsTransactionState())
+   {
+       StartTransactionCommand();
+       maybe_reread_subscription();
+   }
+
+   relid = RangeVarGetRelid(makeRangeVar(seq.nspname,
+                                         seq.seqname, -1),
+                            RowExclusiveLock, false);
+
+   /* lock the sequence in AccessExclusiveLock, as expected by SetSequence */
+   LockRelationOid(relid, AccessExclusiveLock);
+
+   /* apply the sequence change */
+   SetSequence(relid, seq.transactional, seq.last_value, seq.log_cnt, seq.is_called);
+
+   /*
+    * Commit the per-stream transaction (we only do this when not in
+    * remote transaction, i.e. for non-transactional sequence updates.
+    */
+   if (!in_remote_transaction)
+       CommitTransactionCommand();
+}
+
 /*
  * Handle STREAM START message.
  */
@@ -2511,6 +2563,10 @@ apply_dispatch(StringInfo s)
             */
            break;
 
+       case LOGICAL_REP_MSG_SEQUENCE:
+           apply_handle_sequence(s);
+           return;
+
        case LOGICAL_REP_MSG_STREAM_START:
            apply_handle_stream_start(s);
            break;
index 5fddab3a3d4aa39e6c6b786d686f41af923ade3d..4cdc698cbb3e3e5c706824b5855020aadadc3906 100644 (file)
@@ -15,6 +15,7 @@
 #include "access/tupconvert.h"
 #include "catalog/partition.h"
 #include "catalog/pg_publication.h"
+#include "catalog/pg_publication_namespace.h"
 #include "catalog/pg_publication_rel.h"
 #include "commands/defrem.h"
 #include "executor/executor.h"
@@ -53,6 +54,10 @@ static void pgoutput_message(LogicalDecodingContext *ctx,
                             ReorderBufferTXN *txn, XLogRecPtr message_lsn,
                             bool transactional, const char *prefix,
                             Size sz, const char *message);
+static void pgoutput_sequence(LogicalDecodingContext *ctx,
+                             ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
+                             Relation relation, bool transactional,
+                             int64 last_value, int64 log_cnt, bool is_called);
 static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
                                   RepOriginId origin_id);
 static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
@@ -208,6 +213,7 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
    cb->change_cb = pgoutput_change;
    cb->truncate_cb = pgoutput_truncate;
    cb->message_cb = pgoutput_message;
+   cb->sequence_cb = pgoutput_sequence;
    cb->commit_cb = pgoutput_commit_txn;
 
    cb->begin_prepare_cb = pgoutput_begin_prepare_txn;
@@ -224,6 +230,7 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
    cb->stream_commit_cb = pgoutput_stream_commit;
    cb->stream_change_cb = pgoutput_change;
    cb->stream_message_cb = pgoutput_message;
+   cb->stream_sequence_cb = pgoutput_sequence;
    cb->stream_truncate_cb = pgoutput_truncate;
    /* transaction streaming - two-phase commit */
    cb->stream_prepare_cb = pgoutput_stream_prepare_txn;
@@ -237,6 +244,7 @@ parse_output_parameters(List *options, PGOutputData *data)
    bool        publication_names_given = false;
    bool        binary_option_given = false;
    bool        messages_option_given = false;
+   bool        sequences_option_given = false;
    bool        streaming_given = false;
    bool        two_phase_option_given = false;
 
@@ -244,6 +252,7 @@ parse_output_parameters(List *options, PGOutputData *data)
    data->streaming = false;
    data->messages = false;
    data->two_phase = false;
+   data->sequences = true;
 
    foreach(lc, options)
    {
@@ -312,6 +321,16 @@ parse_output_parameters(List *options, PGOutputData *data)
 
            data->messages = defGetBoolean(defel);
        }
+       else if (strcmp(defel->defname, "sequences") == 0)
+       {
+           if (sequences_option_given)
+               ereport(ERROR,
+                       (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("conflicting or redundant options")));
+           sequences_option_given = true;
+
+           data->sequences = defGetBoolean(defel);
+       }
        else if (strcmp(defel->defname, "streaming") == 0)
        {
            if (streaming_given)
@@ -1440,6 +1459,51 @@ pgoutput_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
    OutputPluginWrite(ctx, true);
 }
 
+static void
+pgoutput_sequence(LogicalDecodingContext *ctx,
+                 ReorderBufferTXN *txn, XLogRecPtr sequence_lsn,
+                 Relation relation, bool transactional,
+                 int64 last_value, int64 log_cnt, bool is_called)
+{
+   PGOutputData *data = (PGOutputData *) ctx->output_plugin_private;
+   TransactionId xid = InvalidTransactionId;
+   RelationSyncEntry *relentry;
+
+   if (!data->sequences)
+       return;
+
+   if (!is_publishable_relation(relation))
+       return;
+
+   /*
+    * Remember the xid for the message in streaming mode. See
+    * pgoutput_change.
+    */
+   if (in_streaming)
+       xid = txn->xid;
+
+   relentry = get_rel_sync_entry(data, relation);
+
+   /*
+    * First check the sequence filter.
+    *
+    * We handle just REORDER_BUFFER_CHANGE_SEQUENCE here.
+    */
+   if (!relentry->pubactions.pubsequence)
+       return;
+
+   OutputPluginPrepareWrite(ctx, true);
+   logicalrep_write_sequence(ctx->out,
+                             relation,
+                             xid,
+                             sequence_lsn,
+                             transactional,
+                             last_value,
+                             log_cnt,
+                             is_called);
+   OutputPluginWrite(ctx, true);
+}
+
 /*
  * Currently we always forward.
  */
@@ -1725,7 +1789,8 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
        entry->schema_sent = false;
        entry->streamed_txns = NIL;
        entry->pubactions.pubinsert = entry->pubactions.pubupdate =
-           entry->pubactions.pubdelete = entry->pubactions.pubtruncate = false;
+           entry->pubactions.pubdelete = entry->pubactions.pubtruncate =
+           entry->pubactions.pubsequence = false;
        entry->new_slot = NULL;
        entry->old_slot = NULL;
        memset(entry->exprstate, 0, sizeof(entry->exprstate));
@@ -1739,13 +1804,13 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
    {
        Oid         schemaId = get_rel_namespace(relid);
        List       *pubids = GetRelationPublications(relid);
-
+       char        objectType = pub_get_object_type_for_relkind(get_rel_relkind(relid));
        /*
         * We don't acquire a lock on the namespace system table as we build
         * the cache entry using a historic snapshot and all the later changes
         * are absorbed while decoding WAL.
         */
-       List       *schemaPubids = GetSchemaPublications(schemaId);
+       List       *schemaPubids = GetSchemaPublications(schemaId, objectType);
        ListCell   *lc;
        Oid         publish_as_relid = relid;
        int         publish_ancestor_level = 0;
@@ -1780,6 +1845,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
        entry->pubactions.pubupdate = false;
        entry->pubactions.pubdelete = false;
        entry->pubactions.pubtruncate = false;
+       entry->pubactions.pubsequence = false;
 
        /*
         * Tuple slots cleanups. (Will be rebuilt later if needed).
@@ -1826,9 +1892,11 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
 
            /*
             * If this is a FOR ALL TABLES publication, pick the partition root
-            * and set the ancestor level accordingly.
+            * and set the ancestor level accordingly. If this is a FOR ALL
+            * SEQUENCES publication, we publish it too but we don't need to
+            * pick the partition root etc.
             */
-           if (pub->alltables)
+           if (pub->alltables || pub->allsequences)
            {
                publish = true;
                if (pub->pubviaroot && am_partition)
@@ -1889,6 +1957,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
                entry->pubactions.pubupdate |= pub->pubactions.pubupdate;
                entry->pubactions.pubdelete |= pub->pubactions.pubdelete;
                entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
+               entry->pubactions.pubsequence |= pub->pubactions.pubsequence;
 
                /*
                 * We want to publish the changes as the top-most ancestor
index 3d05297b0d9093d0bdda0bc729240226975f5d84..4f3fe1118a287337ee1e82b357e8ffb1941e19e3 100644 (file)
@@ -56,6 +56,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_publication.h"
+#include "catalog/pg_publication_namespace.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_shseclabel.h"
 #include "catalog/pg_statistic_ext.h"
@@ -5562,6 +5563,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
    Oid         schemaid;
    List       *ancestors = NIL;
    Oid         relid = RelationGetRelid(relation);
+   char        relkind = relation->rd_rel->relkind;
+   char        objType;
 
    /*
     * If not publishable, it publishes no actions.  (pgoutput_change() will
@@ -5588,8 +5591,15 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
    /* Fetch the publication membership info. */
    puboids = GetRelationPublications(relid);
    schemaid = RelationGetNamespace(relation);
-   puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
+   objType = pub_get_object_type_for_relkind(relkind);
 
+   puboids = list_concat_unique_oid(puboids,
+                                    GetSchemaPublications(schemaid, objType));
+
+   /*
+    * If this is a partion (and thus a table), lookup all ancestors and track
+    * all publications them too.
+    */
    if (relation->rd_rel->relispartition)
    {
        /* Add publications that the ancestors are in too. */
@@ -5601,12 +5611,23 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
 
            puboids = list_concat_unique_oid(puboids,
                                             GetRelationPublications(ancestor));
+
+           /* include all publications publishing schema of all ancestors */
            schemaid = get_rel_namespace(ancestor);
            puboids = list_concat_unique_oid(puboids,
-                                            GetSchemaPublications(schemaid));
+                                            GetSchemaPublications(schemaid,
+                                                                  PUB_OBJTYPE_TABLE));
        }
    }
-   puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
+
+   /*
+    * Consider also FOR ALL TABLES and FOR ALL SEQUENCES publications,
+    * depending on the relkind of the relation.
+    */
+   if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
+       puboids = list_concat_unique_oid(puboids, GetAllSequencesPublications());
+   else
+       puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
 
    foreach(lc, puboids)
    {
@@ -5625,6 +5646,7 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
        pubdesc->pubactions.pubupdate |= pubform->pubupdate;
        pubdesc->pubactions.pubdelete |= pubform->pubdelete;
        pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
+       pubdesc->pubactions.pubsequence |= pubform->pubsequence;
 
        /*
         * Check if all columns referenced in the filter expression are part of
index f4e7819f1e2d8726178b90fd6dc6af5f22476d1e..a675877d1950cc85c289614434da29259709a7ce 100644 (file)
@@ -630,12 +630,12 @@ static const struct cachedesc cacheinfo[] = {
        64
    },
    {PublicationNamespaceRelationId,    /* PUBLICATIONNAMESPACEMAP */
-       PublicationNamespacePnnspidPnpubidIndexId,
-       2,
+       PublicationNamespacePnnspidPnpubidPntypeIndexId,
+       3,
        {
            Anum_pg_publication_namespace_pnnspid,
            Anum_pg_publication_namespace_pnpubid,
-           0,
+           Anum_pg_publication_namespace_pntype,
            0
        },
        64
index e5816c4ccea61083df2b0a7e165b601f27199b3d..00629f08362f5620fe601e4d1f9456bf896425bf 100644 (file)
@@ -3814,10 +3814,12 @@ getPublications(Archive *fout, int *numPublications)
    int         i_pubname;
    int         i_pubowner;
    int         i_puballtables;
+   int         i_puballsequences;
    int         i_pubinsert;
    int         i_pubupdate;
    int         i_pubdelete;
    int         i_pubtruncate;
+   int         i_pubsequence;
    int         i_pubviaroot;
    int         i,
                ntups;
@@ -3833,23 +3835,29 @@ getPublications(Archive *fout, int *numPublications)
    resetPQExpBuffer(query);
 
    /* Get the publications. */
-   if (fout->remoteVersion >= 130000)
+   if (fout->remoteVersion >= 150000)
+       appendPQExpBuffer(query,
+                         "SELECT p.tableoid, p.oid, p.pubname, "
+                         "p.pubowner, "
+                         "p.puballtables, p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubsequence, p.pubviaroot "
+                         "FROM pg_publication p");
+   else if (fout->remoteVersion >= 130000)
        appendPQExpBuffer(query,
                          "SELECT p.tableoid, p.oid, p.pubname, "
                          "p.pubowner, "
-                         "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
+                         "p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, p.pubviaroot "
                          "FROM pg_publication p");
    else if (fout->remoteVersion >= 110000)
        appendPQExpBuffer(query,
                          "SELECT p.tableoid, p.oid, p.pubname, "
                          "p.pubowner, "
-                         "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
+                         "p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubsequence, false AS pubviaroot "
                          "FROM pg_publication p");
    else
        appendPQExpBuffer(query,
                          "SELECT p.tableoid, p.oid, p.pubname, "
                          "p.pubowner, "
-                         "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
+                         "p.puballtables, false AS puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubsequence, false AS pubviaroot "
                          "FROM pg_publication p");
 
    res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -3861,10 +3869,12 @@ getPublications(Archive *fout, int *numPublications)
    i_pubname = PQfnumber(res, "pubname");
    i_pubowner = PQfnumber(res, "pubowner");
    i_puballtables = PQfnumber(res, "puballtables");
+   i_puballsequences = PQfnumber(res, "puballsequences");
    i_pubinsert = PQfnumber(res, "pubinsert");
    i_pubupdate = PQfnumber(res, "pubupdate");
    i_pubdelete = PQfnumber(res, "pubdelete");
    i_pubtruncate = PQfnumber(res, "pubtruncate");
+   i_pubsequence = PQfnumber(res, "pubsequence");
    i_pubviaroot = PQfnumber(res, "pubviaroot");
 
    pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
@@ -3880,6 +3890,8 @@ getPublications(Archive *fout, int *numPublications)
        pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
        pubinfo[i].puballtables =
            (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
+       pubinfo[i].puballsequences =
+           (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
        pubinfo[i].pubinsert =
            (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
        pubinfo[i].pubupdate =
@@ -3888,6 +3900,8 @@ getPublications(Archive *fout, int *numPublications)
            (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
        pubinfo[i].pubtruncate =
            (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
+       pubinfo[i].pubsequence =
+           (strcmp(PQgetvalue(res, i, i_pubsequence), "t") == 0);
        pubinfo[i].pubviaroot =
            (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
 
@@ -3933,6 +3947,9 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
    if (pubinfo->puballtables)
        appendPQExpBufferStr(query, " FOR ALL TABLES");
 
+   if (pubinfo->puballsequences)
+       appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
+
    appendPQExpBufferStr(query, " WITH (publish = '");
    if (pubinfo->pubinsert)
    {
@@ -3967,6 +3984,15 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
        first = false;
    }
 
+   if (pubinfo->pubsequence)
+   {
+       if (!first)
+           appendPQExpBufferStr(query, ", ");
+
+       appendPQExpBufferStr(query, "sequence");
+       first = false;
+   }
+
    appendPQExpBufferStr(query, "'");
 
    if (pubinfo->pubviaroot)
@@ -4013,6 +4039,7 @@ getPublicationNamespaces(Archive *fout)
    int         i_oid;
    int         i_pnpubid;
    int         i_pnnspid;
+   int         i_pntype;
    int         i,
                j,
                ntups;
@@ -4024,7 +4051,7 @@ getPublicationNamespaces(Archive *fout)
 
    /* Collect all publication membership info. */
    appendPQExpBufferStr(query,
-                        "SELECT tableoid, oid, pnpubid, pnnspid "
+                        "SELECT tableoid, oid, pnpubid, pnnspid, pntype "
                         "FROM pg_catalog.pg_publication_namespace");
    res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -4034,6 +4061,7 @@ getPublicationNamespaces(Archive *fout)
    i_oid = PQfnumber(res, "oid");
    i_pnpubid = PQfnumber(res, "pnpubid");
    i_pnnspid = PQfnumber(res, "pnnspid");
+   i_pntype = PQfnumber(res, "pntype");
 
    /* this allocation may be more than we need */
    pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
@@ -4043,6 +4071,7 @@ getPublicationNamespaces(Archive *fout)
    {
        Oid         pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
        Oid         pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
+       char        pntype = PQgetvalue(res, i, i_pntype)[0];
        PublicationInfo *pubinfo;
        NamespaceInfo *nspinfo;
 
@@ -4074,6 +4103,7 @@ getPublicationNamespaces(Archive *fout)
        pubsinfo[j].dobj.name = nspinfo->dobj.name;
        pubsinfo[j].publication = pubinfo;
        pubsinfo[j].pubschema = nspinfo;
+       pubsinfo[j].pubtype = pntype;
 
        /* Decide whether we want to dump it */
        selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
@@ -4207,7 +4237,11 @@ dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
    query = createPQExpBuffer();
 
    appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
-   appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
+
+   if (pubsinfo->pubtype == 't')
+       appendPQExpBuffer(query, "ADD ALL TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
+   else
+       appendPQExpBuffer(query, "ADD ALL SEQUENCES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
 
    /*
     * There is no point in creating drop query as the drop is done by schema
@@ -4240,6 +4274,7 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
    TableInfo  *tbinfo = pubrinfo->pubtable;
    PQExpBuffer query;
    char       *tag;
+   char       *description;
 
    /* Do nothing in data-only dump */
    if (dopt->dataOnly)
@@ -4249,10 +4284,22 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
 
    query = createPQExpBuffer();
 
-   appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
-                     fmtId(pubinfo->dobj.name));
+   if (tbinfo->relkind == RELKIND_SEQUENCE)
+   {
+       appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD SEQUENCE",
+                         fmtId(pubinfo->dobj.name));
+       description = "PUBLICATION SEQUENCE";
+   }
+   else
+   {
+       appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
+                         fmtId(pubinfo->dobj.name));
+       description = "PUBLICATION TABLE";
+   }
+
    appendPQExpBuffer(query, " %s",
                      fmtQualifiedDumpable(tbinfo));
+
    if (pubrinfo->pubrelqual)
    {
        /*
@@ -4275,7 +4322,7 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
                     ARCHIVE_OPTS(.tag = tag,
                                  .namespace = tbinfo->dobj.namespace->dobj.name,
                                  .owner = pubinfo->rolname,
-                                 .description = "PUBLICATION TABLE",
+                                 .description = description,
                                  .section = SECTION_POST_DATA,
                                  .createStmt = query->data));
 
index 772dc0cf7a206bdbeaddbbc35596893a1f9ddb88..893725d121fd4ab18da32b33afdfe019924767cc 100644 (file)
@@ -615,10 +615,12 @@ typedef struct _PublicationInfo
    DumpableObject dobj;
    const char *rolname;
    bool        puballtables;
+   bool        puballsequences;
    bool        pubinsert;
    bool        pubupdate;
    bool        pubdelete;
    bool        pubtruncate;
+   bool        pubsequence;
    bool        pubviaroot;
 } PublicationInfo;
 
@@ -643,6 +645,7 @@ typedef struct _PublicationSchemaInfo
    DumpableObject dobj;
    PublicationInfo *publication;
    NamespaceInfo *pubschema;
+   char pubtype;
 } PublicationSchemaInfo;
 
 /*
index fd1052e5db857b2700280e7221930801e3a6eb01..19f908f6006d6cfee74035c5d88d08a355b8c15e 100644 (file)
@@ -2358,7 +2358,7 @@ my %tests = (
        create_order => 50,
        create_sql   => 'CREATE PUBLICATION pub1;',
        regexp       => qr/^
-           \QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate');\E
+           \QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate, sequence');\E
            /xm,
        like => { %full_runs, section_post_data => 1, },
    },
@@ -2378,16 +2378,27 @@ my %tests = (
        create_order => 50,
        create_sql   => 'CREATE PUBLICATION pub3;',
        regexp => qr/^
-           \QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate');\E
+           \QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate, sequence');\E
            /xm,
        like => { %full_runs, section_post_data => 1, },
    },
 
    'CREATE PUBLICATION pub4' => {
        create_order => 50,
-       create_sql   => 'CREATE PUBLICATION pub4;',
+       create_sql   => 'CREATE PUBLICATION pub4
+                        FOR ALL SEQUENCES
+                        WITH (publish = \'\');',
        regexp => qr/^
-           \QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate');\E
+           \QCREATE PUBLICATION pub4 FOR ALL SEQUENCES WITH (publish = '');\E
+           /xm,
+       like => { %full_runs, section_post_data => 1, },
+   },
+
+   'CREATE PUBLICATION pub5' => {
+       create_order => 50,
+       create_sql   => 'CREATE PUBLICATION pub5;',
+       regexp => qr/^
+           \QCREATE PUBLICATION pub5 WITH (publish = 'insert, update, delete, truncate, sequence');\E
            /xm,
        like => { %full_runs, section_post_data => 1, },
    },
@@ -2474,6 +2485,27 @@ my %tests = (
        unlike => { exclude_dump_test_schema => 1, },
    },
 
+   'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test' => {
+       create_order => 51,
+       create_sql =>
+         'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;',
+       regexp => qr/^
+           \QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA dump_test;\E
+           /xm,
+       like   => { %full_runs, section_post_data => 1, },
+       unlike => { exclude_dump_test_schema => 1, },
+   },
+
+   'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public' => {
+       create_order => 52,
+       create_sql =>
+         'ALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;',
+       regexp => qr/^
+           \QALTER PUBLICATION pub3 ADD ALL SEQUENCES IN SCHEMA public;\E
+           /xm,
+       like => { %full_runs, section_post_data => 1, },
+   },
+
    'CREATE SCHEMA public' => {
        regexp => qr/^CREATE SCHEMA public;/m,
 
index 714097cad1bcbb57bedb570c86589cb8d029b36b..b8a532a45f7e6ad4e738f856f02da4b325529816 100644 (file)
@@ -1633,28 +1633,19 @@ describeOneTableDetails(const char *schemaname,
    if (tableinfo.relkind == RELKIND_SEQUENCE)
    {
        PGresult   *result = NULL;
-       printQueryOpt myopt = pset.popt;
-       char       *footers[2] = {NULL, NULL};
 
        if (pset.sversion >= 100000)
        {
            printfPQExpBuffer(&buf,
-                             "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
-                             "       seqstart AS \"%s\",\n"
-                             "       seqmin AS \"%s\",\n"
-                             "       seqmax AS \"%s\",\n"
-                             "       seqincrement AS \"%s\",\n"
-                             "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
-                             "       seqcache AS \"%s\"\n",
-                             gettext_noop("Type"),
-                             gettext_noop("Start"),
-                             gettext_noop("Minimum"),
-                             gettext_noop("Maximum"),
-                             gettext_noop("Increment"),
+                             "SELECT pg_catalog.format_type(seqtypid, NULL),\n"
+                             "       seqstart,\n"
+                             "       seqmin,\n"
+                             "       seqmax,\n"
+                             "       seqincrement,\n"
+                             "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END,\n"
+                             "       seqcache\n",
                              gettext_noop("yes"),
-                             gettext_noop("no"),
-                             gettext_noop("Cycles?"),
-                             gettext_noop("Cache"));
+                             gettext_noop("no"));
            appendPQExpBuffer(&buf,
                              "FROM pg_catalog.pg_sequence\n"
                              "WHERE seqrelid = '%s';",
@@ -1663,22 +1654,15 @@ describeOneTableDetails(const char *schemaname,
        else
        {
            printfPQExpBuffer(&buf,
-                             "SELECT 'bigint' AS \"%s\",\n"
-                             "       start_value AS \"%s\",\n"
-                             "       min_value AS \"%s\",\n"
-                             "       max_value AS \"%s\",\n"
-                             "       increment_by AS \"%s\",\n"
-                             "       CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
-                             "       cache_value AS \"%s\"\n",
-                             gettext_noop("Type"),
-                             gettext_noop("Start"),
-                             gettext_noop("Minimum"),
-                             gettext_noop("Maximum"),
-                             gettext_noop("Increment"),
+                             "SELECT 'bigint',\n"
+                             "       start_value,\n"
+                             "       min_value,\n"
+                             "       max_value,\n"
+                             "       increment_by,\n"
+                             "       CASE WHEN is_cycled THEN '%s' ELSE '%s' END,\n"
+                             "       cache_value\n",
                              gettext_noop("yes"),
-                             gettext_noop("no"),
-                             gettext_noop("Cycles?"),
-                             gettext_noop("Cache"));
+                             gettext_noop("no"));
            appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
            /* must be separate because fmtId isn't reentrant */
            appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
@@ -1688,6 +1672,51 @@ describeOneTableDetails(const char *schemaname,
        if (!res)
            goto error_return;
 
+       numrows = PQntuples(res);
+
+       /* XXX reset to use expanded output for sequences (maybe we should
+        * keep this disabled, just like for tables?) */
+       myopt.expanded = pset.popt.topt.expanded;
+
+       printTableInit(&cont, &myopt, title.data, 7, numrows);
+       printTableInitialized = true;
+
+       printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
+                         schemaname, relationname);
+
+       printTableAddHeader(&cont, gettext_noop("Type"), true, 'l');
+       printTableAddHeader(&cont, gettext_noop("Start"), true, 'r');
+       printTableAddHeader(&cont, gettext_noop("Minimum"), true, 'r');
+       printTableAddHeader(&cont, gettext_noop("Maximum"), true, 'r');
+       printTableAddHeader(&cont, gettext_noop("Increment"), true, 'r');
+       printTableAddHeader(&cont, gettext_noop("Cycles?"), true, 'l');
+       printTableAddHeader(&cont, gettext_noop("Cache"), true, 'r');
+
+       /* Generate table cells to be printed */
+       for (i = 0; i < numrows; i++)
+       {
+           /* Type */
+           printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
+
+           /* Start */
+           printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
+
+           /* Minimum */
+           printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
+
+           /* Maximum */
+           printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
+
+           /* Increment */
+           printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
+
+           /* Cycles? */
+           printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
+
+           /* Cache */
+           printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
+       }
+
        /* Footer information about a sequence */
 
        /* Get the column that owns this sequence */
@@ -1721,29 +1750,63 @@ describeOneTableDetails(const char *schemaname,
            switch (PQgetvalue(result, 0, 1)[0])
            {
                case 'a':
-                   footers[0] = psprintf(_("Owned by: %s"),
-                                         PQgetvalue(result, 0, 0));
+                   printTableAddFooter(&cont,
+                                       psprintf(_("Owned by: %s"),
+                                                PQgetvalue(result, 0, 0)));
                    break;
                case 'i':
-                   footers[0] = psprintf(_("Sequence for identity column: %s"),
-                                         PQgetvalue(result, 0, 0));
+                   printTableAddFooter(&cont,
+                                       psprintf(_("Sequence for identity column: %s"),
+                                                PQgetvalue(result, 0, 0)));
                    break;
            }
        }
        PQclear(result);
 
-       printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
-                         schemaname, relationname);
+       /* print any publications */
+       if (pset.sversion >= 150000)
+       {
+           int         tuples = 0;
 
-       myopt.footers = footers;
-       myopt.topt.default_footer = false;
-       myopt.title = title.data;
-       myopt.translate_header = true;
+           printfPQExpBuffer(&buf,
+                             "SELECT pubname\n"
+                             "FROM pg_catalog.pg_publication p\n"
+                             "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
+                             "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
+                             "WHERE pc.oid ='%s' and pn.pntype = 's' and pg_catalog.pg_relation_is_publishable('%s')\n"
+                             "UNION\n"
+                             "SELECT pubname\n"
+                             "FROM pg_catalog.pg_publication p\n"
+                             "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
+                             "WHERE pr.prrelid = '%s'\n"
+                             "UNION\n"
+                             "SELECT pubname\n"
+                             "FROM pg_catalog.pg_publication p\n"
+                             "WHERE p.puballsequences AND pg_catalog.pg_relation_is_publishable('%s')\n"
+                             "ORDER BY 1;",
+                             oid, oid, oid, oid);
 
-       printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+           result = PSQLexec(buf.data);
+           if (!result)
+               goto error_return;
+           else
+               tuples = PQntuples(result);
+
+           if (tuples > 0)
+               printTableAddFooter(&cont, _("Publications:"));
+
+           /* Might be an empty set - that's ok */
+           for (i = 0; i < tuples; i++)
+           {
+               printfPQExpBuffer(&buf, "    \"%s\"",
+                                 PQgetvalue(result, i, 0));
+
+               printTableAddFooter(&cont, buf.data);
+           }
+           PQclear(result);
+       }
 
-       if (footers[0])
-           free(footers[0]);
+       printTable(&cont, pset.queryFout, false, pset.logfile);
 
        retval = true;
        goto error_return;      /* not an error, just return early */
@@ -1970,6 +2033,11 @@ describeOneTableDetails(const char *schemaname,
    for (i = 0; i < cols; i++)
        printTableAddHeader(&cont, headers[i], true, 'l');
 
+   res = PSQLexec(buf.data);
+   if (!res)
+       goto error_return;
+   numrows = PQntuples(res);
+
    /* Generate table cells to be printed */
    for (i = 0; i < numrows; i++)
    {
@@ -2895,7 +2963,7 @@ describeOneTableDetails(const char *schemaname,
                                  "FROM pg_catalog.pg_publication p\n"
                                  "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
                                  "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
-                                 "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
+                                 "WHERE pc.oid ='%s' and pn.pntype = 't' and pg_catalog.pg_relation_is_publishable('%s')\n"
                                  "UNION\n"
                                  "SELECT pubname\n"
                                  "     , pg_get_expr(pr.prqual, c.oid)\n"
@@ -4785,7 +4853,7 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
        int         i;
 
        printfPQExpBuffer(&buf,
-                         "SELECT pubname \n"
+                         "SELECT pubname, (CASE WHEN pntype = 't' THEN 'tables' ELSE 'sequences' END) AS pubtype\n"
                          "FROM pg_catalog.pg_publication p\n"
                          "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
                          "     JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
@@ -4814,8 +4882,9 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
            /* Might be an empty set - that's ok */
            for (i = 0; i < pub_schema_tuples; i++)
            {
-               printfPQExpBuffer(&buf, "    \"%s\"",
-                                 PQgetvalue(result, i, 0));
+               printfPQExpBuffer(&buf, "    \"%s\" (%s)",
+                                 PQgetvalue(result, i, 0),
+                                 PQgetvalue(result, i, 1));
 
                footers[i + 1] = pg_strdup(buf.data);
            }
@@ -5820,7 +5889,7 @@ listPublications(const char *pattern)
    PQExpBufferData buf;
    PGresult   *res;
    printQueryOpt myopt = pset.popt;
-   static const bool translate_columns[] = {false, false, false, false, false, false, false, false};
+   static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
 
    if (pset.sversion < 100000)
    {
@@ -5834,23 +5903,45 @@ listPublications(const char *pattern)
 
    initPQExpBuffer(&buf);
 
-   printfPQExpBuffer(&buf,
-                     "SELECT pubname AS \"%s\",\n"
-                     "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
-                     "  puballtables AS \"%s\",\n"
-                     "  pubinsert AS \"%s\",\n"
-                     "  pubupdate AS \"%s\",\n"
-                     "  pubdelete AS \"%s\"",
-                     gettext_noop("Name"),
-                     gettext_noop("Owner"),
-                     gettext_noop("All tables"),
-                     gettext_noop("Inserts"),
-                     gettext_noop("Updates"),
-                     gettext_noop("Deletes"));
+   if (pset.sversion >= 150000)
+       printfPQExpBuffer(&buf,
+                         "SELECT pubname AS \"%s\",\n"
+                         "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
+                         "  puballtables AS \"%s\",\n"
+                         "  puballsequences AS \"%s\",\n"
+                         "  pubinsert AS \"%s\",\n"
+                         "  pubupdate AS \"%s\",\n"
+                         "  pubdelete AS \"%s\"",
+                         gettext_noop("Name"),
+                         gettext_noop("Owner"),
+                         gettext_noop("All tables"),
+                         gettext_noop("All sequences"),
+                         gettext_noop("Inserts"),
+                         gettext_noop("Updates"),
+                         gettext_noop("Deletes"));
+   else
+       printfPQExpBuffer(&buf,
+                         "SELECT pubname AS \"%s\",\n"
+                         "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
+                         "  puballtables AS \"%s\",\n"
+                         "  pubinsert AS \"%s\",\n"
+                         "  pubupdate AS \"%s\",\n"
+                         "  pubdelete AS \"%s\"",
+                         gettext_noop("Name"),
+                         gettext_noop("Owner"),
+                         gettext_noop("All tables"),
+                         gettext_noop("Inserts"),
+                         gettext_noop("Updates"),
+                         gettext_noop("Deletes"));
+
    if (pset.sversion >= 110000)
        appendPQExpBuffer(&buf,
                          ",\n  pubtruncate AS \"%s\"",
                          gettext_noop("Truncates"));
+   if (pset.sversion >= 150000)
+       appendPQExpBuffer(&buf,
+                         ",\n  pubsequence AS \"%s\"",
+                         gettext_noop("Sequences"));
    if (pset.sversion >= 130000)
        appendPQExpBuffer(&buf,
                          ",\n  pubviaroot AS \"%s\"",
@@ -5936,6 +6027,7 @@ describePublications(const char *pattern)
    PGresult   *res;
    bool        has_pubtruncate;
    bool        has_pubviaroot;
+   bool        has_pubsequence;
 
    PQExpBufferData title;
    printTableContent cont;
@@ -5952,6 +6044,7 @@ describePublications(const char *pattern)
 
    has_pubtruncate = (pset.sversion >= 110000);
    has_pubviaroot = (pset.sversion >= 130000);
+   has_pubsequence = (pset.sversion >= 150000);
 
    initPQExpBuffer(&buf);
 
@@ -5959,12 +6052,17 @@ describePublications(const char *pattern)
                      "SELECT oid, pubname,\n"
                      "  pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
                      "  puballtables, pubinsert, pubupdate, pubdelete");
+
    if (has_pubtruncate)
        appendPQExpBufferStr(&buf,
                             ", pubtruncate");
    if (has_pubviaroot)
        appendPQExpBufferStr(&buf,
                             ", pubviaroot");
+   if (has_pubsequence)
+       appendPQExpBufferStr(&buf,
+                            ", puballsequences, pubsequence");
+
    appendPQExpBufferStr(&buf,
                         "\nFROM pg_catalog.pg_publication\n");
 
@@ -6005,6 +6103,7 @@ describePublications(const char *pattern)
        char       *pubid = PQgetvalue(res, i, 0);
        char       *pubname = PQgetvalue(res, i, 1);
        bool        puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
+       bool        puballsequences = strcmp(PQgetvalue(res, i, 9), "t") == 0;
        printTableOpt myopt = pset.popt.topt;
 
        if (has_pubtruncate)
@@ -6012,29 +6111,43 @@ describePublications(const char *pattern)
        if (has_pubviaroot)
            ncols++;
 
+       /* sequences have two extra columns (puballsequences, pubsequences) */
+       if (has_pubsequence)
+           ncols += 2;
+
        initPQExpBuffer(&title);
        printfPQExpBuffer(&title, _("Publication %s"), pubname);
        printTableInit(&cont, &myopt, title.data, ncols, nrows);
 
        printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
        printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
+       if (has_pubsequence)
+           printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
        printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
        printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
        printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
        if (has_pubtruncate)
            printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
+       if (has_pubsequence)
+           printTableAddHeader(&cont, gettext_noop("Sequences"), true, align);
        if (has_pubviaroot)
            printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
 
-       printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
-       printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
-       printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
-       printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
-       printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
+       printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false); /* owner */
+       printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false); /* all tables */
+
+       if (has_pubsequence)
+           printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); /* all sequences */
+
+       printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false); /* insert */
+       printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false); /* update */
+       printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false); /* delete */
        if (has_pubtruncate)
-           printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
+           printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); /* truncate */
+       if (has_pubsequence)
+           printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false); /* sequence */
        if (has_pubviaroot)
-           printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
+           printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false); /* via root */
 
        if (!puballtables)
        {
@@ -6054,6 +6167,7 @@ describePublications(const char *pattern)
                              "WHERE c.relnamespace = n.oid\n"
                              "  AND c.oid = pr.prrelid\n"
                              "  AND pr.prpubid = '%s'\n"
+                             "  AND c.relkind != 'S'\n" /* exclude sequences */
                              "ORDER BY 1,2", pubid);
            if (!addFooterToPublicationDesc(&buf, "Tables:", false, &cont))
                goto error_return;
@@ -6065,7 +6179,7 @@ describePublications(const char *pattern)
                                  "SELECT n.nspname\n"
                                  "FROM pg_catalog.pg_namespace n\n"
                                  "     JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
-                                 "WHERE pn.pnpubid = '%s'\n"
+                                 "WHERE pn.pnpubid = '%s' AND pn.pntype = 't'\n"
                                  "ORDER BY 1", pubid);
                if (!addFooterToPublicationDesc(&buf, "Tables from schemas:",
                                                true, &cont))
@@ -6073,6 +6187,37 @@ describePublications(const char *pattern)
            }
        }
 
+       if (!puballsequences)
+       {
+           /* Get the tables for the specified publication */
+           printfPQExpBuffer(&buf,
+                             "SELECT n.nspname, c.relname, NULL\n"
+                             "FROM pg_catalog.pg_class c,\n"
+                             "     pg_catalog.pg_namespace n,\n"
+                             "     pg_catalog.pg_publication_rel pr\n"
+                             "WHERE c.relnamespace = n.oid\n"
+                             "  AND c.oid = pr.prrelid\n"
+                             "  AND pr.prpubid = '%s'\n"
+                             "  AND c.relkind = 'S'\n" /* only sequences */
+                             "ORDER BY 1,2", pubid);
+           if (!addFooterToPublicationDesc(&buf, "Sequences:", false, &cont))
+               goto error_return;
+
+           if (pset.sversion >= 150000)
+           {
+               /* Get the schemas for the specified publication */
+               printfPQExpBuffer(&buf,
+                                 "SELECT n.nspname\n"
+                                 "FROM pg_catalog.pg_namespace n\n"
+                                 "     JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
+                                 "WHERE pn.pnpubid = '%s' AND pn.pntype = 's'\n"
+                                 "ORDER BY 1", pubid);
+               if (!addFooterToPublicationDesc(&buf, "Sequences from schemas:",
+                                               true, &cont))
+                   goto error_return;
+           }
+       }
+
        printTable(&cont, pset.queryFout, false, pset.logfile);
        printTableCleanup(&cont);
 
index 5c064595a97d4a5dd39ed0c894579c808a488fd1..e59bd8302d65f457d17ac38327d7de998fdb1fae 100644 (file)
@@ -1815,11 +1815,15 @@ psql_completion(const char *text, int start, int end)
        COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
    /* ALTER PUBLICATION <name> ADD */
    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
-       COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
+       COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
             (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
              ends_with(prev_wd, ',')))
        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+   else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") ||
+            (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "SEQUENCE") &&
+             ends_with(prev_wd, ',')))
+       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
    /*
     * "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
     * table attributes
@@ -1838,11 +1842,11 @@ psql_completion(const char *text, int start, int end)
        COMPLETE_WITH(",");
    /* ALTER PUBLICATION <name> DROP */
    else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
-       COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
+       COMPLETE_WITH("ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
    /* ALTER PUBLICATION <name> SET */
    else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
-       COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
-   else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
+       COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "ALL SEQUENCES IN SCHEMA", "TABLE", "SEQUENCE");
+   else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES|SEQUENCES", "IN", "SCHEMA"))
        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
                                 " AND nspname NOT LIKE E'pg\\\\_%%'",
                                 "CURRENT_SCHEMA");
index d8e8715ed1c00068a4c4350353d6382ccae8cd3b..699bd0aa3e335afaf5bf76d15c4c99e638036db4 100644 (file)
   provolatile => 's', prorettype => 'oid', proargtypes => 'text',
   proallargtypes => '{text,oid}', proargmodes => '{i,o}',
   proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_tables' },
+{ oid => '8000', descr => 'get OIDs of sequences in a publication',
+  proname => 'pg_get_publication_sequences', prorows => '1000', proretset => 't',
+  provolatile => 's', prorettype => 'oid', proargtypes => 'text',
+  proallargtypes => '{text,oid}', proargmodes => '{i,o}',
+  proargnames => '{pubname,relid}', prosrc => 'pg_get_publication_sequences' },
 { oid => '6121',
   descr => 'returns whether a relation can be part of a publication',
   proname => 'pg_relation_is_publishable', provolatile => 's',
index fe773cf9b7d59133b7a51e0edb91f66f3dd32c37..97f26208e1d6ec62d40163501150a3d7835e4b8f 100644 (file)
@@ -40,6 +40,12 @@ CATALOG(pg_publication,6104,PublicationRelationId)
     */
    bool        puballtables;
 
+   /*
+    * indicates that this is special publication which should encompass all
+    * sequences in the database (except for the unlogged and temp ones)
+    */
+   bool        puballsequences;
+
    /* true if inserts are published */
    bool        pubinsert;
 
@@ -52,6 +58,9 @@ CATALOG(pg_publication,6104,PublicationRelationId)
    /* true if truncates are published */
    bool        pubtruncate;
 
+   /* true if sequences are published */
+   bool        pubsequence;
+
    /* true if partition changes are published using root schema */
    bool        pubviaroot;
 } FormData_pg_publication;
@@ -72,6 +81,7 @@ typedef struct PublicationActions
    bool        pubupdate;
    bool        pubdelete;
    bool        pubtruncate;
+   bool        pubsequence;
 } PublicationActions;
 
 typedef struct PublicationDesc
@@ -92,6 +102,7 @@ typedef struct Publication
    Oid         oid;
    char       *name;
    bool        alltables;
+   bool        allsequences;
    bool        pubviaroot;
    PublicationActions pubactions;
 } Publication;
@@ -122,14 +133,15 @@ typedef enum PublicationPartOpt
    PUBLICATION_PART_ALL,
 } PublicationPartOpt;
 
-extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt);
+extern List *GetPublicationRelations(Oid pubid, char objectType,
+                                    PublicationPartOpt pub_partopt);
 extern List *GetAllTablesPublications(void);
 extern List *GetAllTablesPublicationRelations(bool pubviaroot);
-extern List *GetPublicationSchemas(Oid pubid);
-extern List *GetSchemaPublications(Oid schemaid);
-extern List *GetSchemaPublicationRelations(Oid schemaid,
+extern List *GetPublicationSchemas(Oid pubid, char objectType);
+extern List *GetSchemaPublications(Oid schemaid, char objectType);
+extern List *GetSchemaPublicationRelations(Oid schemaid, char objectType,
                                           PublicationPartOpt pub_partopt);
-extern List *GetAllSchemaPublicationRelations(Oid puboid,
+extern List *GetAllSchemaPublicationRelations(Oid puboid, char objectType,
                                              PublicationPartOpt pub_partopt);
 extern List *GetPubPartitionOptionRelations(List *result,
                                            PublicationPartOpt pub_partopt,
@@ -137,11 +149,15 @@ extern List *GetPubPartitionOptionRelations(List *result,
 extern Oid GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
                                            int *ancestor_level);
 
+extern List *GetAllSequencesPublications(void);
+extern List *GetAllSequencesPublicationRelations(void);
+
 extern bool is_publishable_relation(Relation rel);
 extern bool is_schema_publication(Oid pubid);
 extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
                                              bool if_not_exists);
 extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
+                                           char objectType,
                                            bool if_not_exists);
 
 extern Oid get_publication_oid(const char *pubname, bool missing_ok);
index e4306da02e7f1a2d42117f50809189eebce3d8d4..7340a1ec64612c390fc14a34585f5131d9444af0 100644 (file)
@@ -32,6 +32,7 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
    Oid         oid;            /* oid */
    Oid         pnpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
    Oid         pnnspid BKI_LOOKUP(pg_namespace);   /* Oid of the schema */
+   char        pntype;                             /* object type to include */
 } FormData_pg_publication_namespace;
 
 /* ----------------
@@ -42,6 +43,13 @@ CATALOG(pg_publication_namespace,8901,PublicationNamespaceRelationId)
 typedef FormData_pg_publication_namespace *Form_pg_publication_namespace;
 
 DECLARE_UNIQUE_INDEX_PKEY(pg_publication_namespace_oid_index, 8902, PublicationNamespaceObjectIndexId, on pg_publication_namespace using btree(oid oid_ops));
-DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_index, 8903, PublicationNamespacePnnspidPnpubidIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_publication_namespace_pnnspid_pnpubid_pntype_index, 8903, PublicationNamespacePnnspidPnpubidPntypeIndexId, on pg_publication_namespace using btree(pnnspid oid_ops, pnpubid oid_ops, pntype char_ops));
+
+/* object type to include from a schema, maps to relkind */
+#define        PUB_OBJTYPE_TABLE           't' /* table (regular or partitioned) */
+#define        PUB_OBJTYPE_SEQUENCE        's' /* sequence object */
+#define        PUB_OBJTYPE_UNSUPPORTED     'u' /* used for non-replicated types */
+
+extern char    pub_get_object_type_for_relkind(char relkind);
 
 #endif                         /* PG_PUBLICATION_NAMESPACE_H */
index 9fecc41954e6ae32d83315bcc209c6a3f18d04a9..5bab90db8e0159bc657697dea1c0b148bbb230ec 100644 (file)
@@ -60,6 +60,7 @@ extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
 extern void DeleteSequenceTuple(Oid relid);
 extern void ResetSequence(Oid seq_relid);
+extern void SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called);
 extern void ResetSequenceCaches(void);
 
 extern void seq_redo(XLogReaderState *rptr);
index 2f618cb22926831b3544233128cff16bd5ea13b5..cb1fcc0ee31cae68c9b740e1a32fe2db079ea2d9 100644 (file)
@@ -3665,6 +3665,10 @@ typedef enum PublicationObjSpecType
    PUBLICATIONOBJ_TABLES_IN_SCHEMA,    /* All tables in schema */
    PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA,    /* All tables in first element of
                                             * search_path */
+   PUBLICATIONOBJ_SEQUENCE,        /* Sequence type */
+   PUBLICATIONOBJ_SEQUENCES_IN_SCHEMA, /* Sequences in schema type */
+   PUBLICATIONOBJ_SEQUENCES_IN_CUR_SCHEMA, /* Get the first element of
+                                            * search_path */
    PUBLICATIONOBJ_CONTINUATION /* Continuation of previous type */
 } PublicationObjSpecType;
 
@@ -3683,7 +3687,7 @@ typedef struct CreatePublicationStmt
    char       *pubname;        /* Name of the publication */
    List       *options;        /* List of DefElem nodes */
    List       *pubobjects;     /* Optional list of publication objects */
-   bool        for_all_tables; /* Special publication for all tables in db */
+   List       *for_all_objects; /* Special publication for all objects in db */
 } CreatePublicationStmt;
 
 typedef enum AlterPublicationAction
@@ -3706,7 +3710,7 @@ typedef struct AlterPublicationStmt
     * objects.
     */
    List       *pubobjects;     /* Optional list of publication objects */
-   bool        for_all_tables; /* Special publication for all tables in db */
+   List       *for_all_objects; /* Special publication for all objects in db */
    AlterPublicationAction action;  /* What action to perform with the given
                                     * objects */
 } AlterPublicationStmt;
index 4d2c881644a9f637d478c9522b24c2d0ad6d12f2..fb86ca022d2adcbc0a58d5cc3613f3f4c7c39214 100644 (file)
@@ -61,6 +61,7 @@ typedef enum LogicalRepMsgType
    LOGICAL_REP_MSG_RELATION = 'R',
    LOGICAL_REP_MSG_TYPE = 'Y',
    LOGICAL_REP_MSG_MESSAGE = 'M',
+   LOGICAL_REP_MSG_SEQUENCE = 'Q',
    LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
    LOGICAL_REP_MSG_PREPARE = 'P',
    LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
@@ -118,6 +119,18 @@ typedef struct LogicalRepTyp
    char       *typname;        /* name of the remote type */
 } LogicalRepTyp;
 
+/* Sequence info */
+typedef struct LogicalRepSequence
+{
+   Oid         remoteid;       /* unique id of the remote sequence */
+   char       *nspname;        /* schema name of remote sequence */
+   char       *seqname;        /* name of the remote sequence */
+   bool        transactional;
+   int64       last_value;
+   int64       log_cnt;
+   bool        is_called;
+} LogicalRepSequence;
+
 /* Transaction info */
 typedef struct LogicalRepBeginData
 {
@@ -230,6 +243,12 @@ extern List *logicalrep_read_truncate(StringInfo in,
                                      bool *cascade, bool *restart_seqs);
 extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
                                     bool transactional, const char *prefix, Size sz, const char *message);
+extern void logicalrep_write_sequence(StringInfo out, Relation rel,
+                                     TransactionId xid, XLogRecPtr lsn,
+                                     bool transactional,
+                                     int64 last_value, int64 log_cnt,
+                                     bool is_called);
+extern void logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata);
 extern void logicalrep_write_rel(StringInfo out, TransactionId xid,
                                 Relation rel);
 extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
index eafedd610a50b2096fd1a74e18317cb3b6a9880c..f4e9f35d09d1e173ce827853901c6cb34593a596 100644 (file)
@@ -29,6 +29,7 @@ typedef struct PGOutputData
    bool        streaming;
    bool        messages;
    bool        two_phase;
+   bool        sequences;
 } PGOutputData;
 
 #endif                         /* PGOUTPUT_H */
index 4117fc27c9a5bc104556df8feff178a1af3cc57a..c95d44b3db929d219ab47bbe17b93cdd07d69f75 100644 (file)
@@ -46,6 +46,7 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
 CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
+CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
 RESET client_min_messages;
 CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
 WARNING:  tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables
@@ -428,7 +429,8 @@ WITH objects (type, name, args) AS (VALUES
                ('transform', '{int}', '{sql}'),
                ('access method', '{btree}', '{}'),
                ('publication', '{addr_pub}', '{}'),
-               ('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
+               ('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'),
+               ('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
                ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
                ('subscription', '{regress_addr_sub}', '{}'),
                ('statistics object', '{addr_nsp, gentable_stat}', '{}')
@@ -492,8 +494,9 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
  subscription              |            | regress_addr_sub  | regress_addr_sub                                                     | t
  publication               |            | addr_pub          | addr_pub                                                             | t
  publication relation      |            |                   | addr_nsp.gentable in publication addr_pub                            | t
- publication namespace     |            |                   | addr_nsp in publication addr_pub_schema                              | t
-(50 rows)
+ publication namespace     |            |                   | addr_nsp in publication addr_pub_schema type t                       | t
+ publication namespace     |            |                   | addr_nsp in publication addr_pub_schema2 type s                      | t
+(51 rows)
 
 ---
 --- Cleanup resources
@@ -506,6 +509,7 @@ drop cascades to server integer
 drop cascades to user mapping for regress_addr_user on server integer
 DROP PUBLICATION addr_pub;
 DROP PUBLICATION addr_pub_schema;
+DROP PUBLICATION addr_pub_schema2;
 DROP SUBSCRIPTION regress_addr_sub;
 DROP SCHEMA addr_nsp CASCADE;
 NOTICE:  drop cascades to 14 other objects
index 4e191c120ace0c113d4a7da9b568376a57bc263b..92f6122d40993e81903eda57a0644f2e535cffee 100644 (file)
@@ -30,20 +30,20 @@ ERROR:  conflicting or redundant options
 LINE 1: ...ub_xxx WITH (publish_via_partition_root = 'true', publish_vi...
                                                              ^
 \dRp
-                                              List of publications
-        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f         | f
- testpub_default    | regress_publication_user | f          | f       | t       | f       | f         | f
+                                                            List of publications
+        Name        |          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
+ testpub_default    | regress_publication_user | f          | f             | f       | t       | f       | f         | f         | f
 (2 rows)
 
-ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
+ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete, sequence');
 \dRp
-                                              List of publications
-        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f         | f
- testpub_default    | regress_publication_user | f          | t       | t       | t       | f         | f
+                                                            List of publications
+        Name        |          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
+ testpub_default    | regress_publication_user | f          | f             | t       | t       | t       | f         | t         | f
 (2 rows)
 
 --- adding tables
@@ -61,6 +61,9 @@ CREATE TABLE testpub_tbl2 (id serial primary key, data text);
 ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2;
 ERROR:  publication "testpub_foralltables" is defined as FOR ALL TABLES
 DETAIL:  Tables cannot be added to or dropped from FOR ALL TABLES publications.
+-- fail - can't add a table using ADD SEQUENCE command
+ALTER PUBLICATION testpub_foralltables ADD SEQUENCE testpub_tbl2;
+ERROR:  object type does not match type expected by command
 -- fail - can't drop from all tables publication
 ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
 ERROR:  publication "testpub_foralltables" is defined as FOR ALL TABLES
@@ -87,10 +90,10 @@ RESET client_min_messages;
 -- should be able to add schema to 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable ADD ALL TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                              Publication testpub_fortable
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "public.testpub_tbl1"
 Tables from schemas:
@@ -99,20 +102,20 @@ Tables from schemas:
 -- should be able to drop schema from 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable DROP ALL TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                              Publication testpub_fortable
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "public.testpub_tbl1"
 
 -- should be able to set schema to 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable SET ALL TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                              Publication testpub_fortable
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test"
 
@@ -134,10 +137,10 @@ ERROR:  relation "testpub_nopk" is not part of the publication
 -- should be able to set table to schema publication
 ALTER PUBLICATION testpub_forschema SET TABLE pub_test.testpub_nopk;
 \dRp+ testpub_forschema
-                               Publication testpub_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "pub_test.testpub_nopk"
 
@@ -159,10 +162,10 @@ Publications:
     "testpub_foralltables"
 
 \dRp+ testpub_foralltables
-                              Publication testpub_foralltables
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | t          | t       | t       | f       | f         | f
+                                            Publication testpub_foralltables
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | t          | f             | t       | t       | f       | f         | f         | f
 (1 row)
 
 DROP TABLE testpub_tbl2;
@@ -174,24 +177,527 @@ CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3;
 CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3;
 RESET client_min_messages;
 \dRp+ testpub3
-                                    Publication testpub3
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                                  Publication testpub3
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "public.testpub_tbl3"
     "public.testpub_tbl3a"
 
 \dRp+ testpub4
-                                    Publication testpub4
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                                  Publication testpub4
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "public.testpub_tbl3"
 
 DROP TABLE testpub_tbl3, testpub_tbl3a;
 DROP PUBLICATION testpub3, testpub4;
+--- adding sequences
+CREATE SEQUENCE testpub_seq0;
+CREATE SEQUENCE pub_test.testpub_seq1;
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES WITH (publish = 'sequence');
+RESET client_min_messages;
+ALTER PUBLICATION testpub_forallsequences SET (publish = 'insert, sequence');
+CREATE SEQUENCE testpub_seq2;
+-- fail - can't add to for all sequences publication
+ALTER PUBLICATION testpub_forallsequences ADD SEQUENCE testpub_seq2;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.
+-- fail - can't drop from all sequences publication
+ALTER PUBLICATION testpub_forallsequences DROP SEQUENCE testpub_seq2;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.
+-- fail - can't add to for all sequences publication
+ALTER PUBLICATION testpub_forallsequences SET SEQUENCE pub_test.testpub_seq1;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences cannot be added to or dropped from FOR ALL SEQUENCES publications.
+-- fail - can't add schema to 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences ADD ALL SEQUENCES IN SCHEMA pub_test;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.
+-- fail - can't drop schema from 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences DROP ALL SEQUENCES IN SCHEMA pub_test;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.
+-- fail - can't set schema to 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences SET ALL SEQUENCES IN SCHEMA pub_test;
+ERROR:  publication "testpub_forallsequences" is defined as FOR ALL SEQUENCES
+DETAIL:  Sequences from schema cannot be added to, dropped from, or set on FOR ALL SEQUENCES publications.
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forsequence FOR SEQUENCE testpub_seq0;
+RESET client_min_messages;
+-- should be able to add schema to 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence ADD ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+                                            Publication testpub_forsequence
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Sequences:
+    "public.testpub_seq0"
+Sequences from schemas:
+    "pub_test"
+
+-- fail - can't add sequence from the schema we already added
+ALTER PUBLICATION testpub_forsequence ADD SEQUENCE pub_test.testpub_seq1;
+ERROR:  cannot add relation "pub_test.testpub_seq1" to publication
+DETAIL:  Sequence's schema "pub_test" is already part of the publication or part of the specified schema list.
+-- fail - can't add sequence using ADD TABLE command
+ALTER PUBLICATION testpub_forsequence ADD TABLE pub_test.testpub_seq1;
+ERROR:  object type does not match type expected by command
+-- should be able to drop schema from 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence DROP ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+                                            Publication testpub_forsequence
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Sequences:
+    "public.testpub_seq0"
+
+-- should be able to set schema to 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence SET ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+                                            Publication testpub_forsequence
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Sequences from schemas:
+    "pub_test"
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forschema FOR ALL SEQUENCES IN SCHEMA pub_test;
+RESET client_min_messages;
+-- fail - can't create publication with schema and sequence of the same schema
+CREATE PUBLICATION testpub_for_seq_schema FOR ALL SEQUENCES IN SCHEMA pub_test, SEQUENCE pub_test.testpub_seq1;
+ERROR:  cannot add relation "pub_test.testpub_seq1" to publication
+DETAIL:  Sequence's schema "pub_test" is already part of the publication or part of the specified schema list.
+-- fail - can't add a sequence of the same schema to the schema publication
+ALTER PUBLICATION testpub_forschema ADD SEQUENCE pub_test.testpub_seq1;
+ERROR:  cannot add relation "pub_test.testpub_seq1" to publication
+DETAIL:  Sequence's schema "pub_test" is already part of the publication or part of the specified schema list.
+-- fail - can't drop a sequence from the schema publication which isn't in the
+-- publication
+ALTER PUBLICATION testpub_forschema DROP SEQUENCE pub_test.testpub_seq1;
+ERROR:  relation "testpub_seq1" is not part of the publication
+-- should be able to set sequence to schema publication
+ALTER PUBLICATION testpub_forschema SET SEQUENCE pub_test.testpub_seq1;
+\dRp+ testpub_forschema
+                                             Publication testpub_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Sequences:
+    "pub_test.testpub_seq1"
+
+SELECT pubname, puballtables, puballsequences FROM pg_publication WHERE pubname = 'testpub_forallsequences';
+         pubname         | puballtables | puballsequences 
+-------------------------+--------------+-----------------
+ testpub_forallsequences | f            | t
+(1 row)
+
+\d+ pub_test.testpub_seq1
+                       Sequence "pub_test.testpub_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_forallsequences"
+    "testpub_forschema"
+    "testpub_forsequence"
+
+\dRp+ testpub_forallsequences
+                                          Publication testpub_forallsequences
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | t             | t       | f       | f       | f         | t         | f
+(1 row)
+
+DROP SEQUENCE testpub_seq0, pub_test.testpub_seq1, testpub_seq2;
+DROP PUBLICATION testpub_forallsequences, testpub_forsequence, testpub_forschema;
+-- Publication mixing tables and sequences
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_mix;
+RESET client_min_messages;
+CREATE SEQUENCE testpub_seq1;
+CREATE SEQUENCE pub_test.testpub_seq2;
+ALTER PUBLICATION testpub_mix ADD SEQUENCE testpub_seq1, TABLE testpub_tbl1;
+\dRp+ testpub_mix
+                                                Publication testpub_mix
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables:
+    "public.testpub_tbl1"
+Sequences:
+    "public.testpub_seq1"
+
+ALTER PUBLICATION testpub_mix ADD ALL SEQUENCES IN SCHEMA pub_test, ALL TABLES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+                                                Publication testpub_mix
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables:
+    "public.testpub_tbl1"
+Tables from schemas:
+    "pub_test"
+Sequences:
+    "public.testpub_seq1"
+Sequences from schemas:
+    "pub_test"
+
+ALTER PUBLICATION testpub_mix DROP ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+                                                Publication testpub_mix
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables:
+    "public.testpub_tbl1"
+Tables from schemas:
+    "pub_test"
+Sequences:
+    "public.testpub_seq1"
+
+ALTER PUBLICATION testpub_mix DROP ALL TABLES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+                                                Publication testpub_mix
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables:
+    "public.testpub_tbl1"
+Sequences:
+    "public.testpub_seq1"
+
+DROP PUBLICATION testpub_mix;
+DROP SEQUENCE testpub_seq1;
+DROP SEQUENCE pub_test.testpub_seq2;
+-- make sure we replicate only the correct relation type
+CREATE SCHEMA pub_test1;
+CREATE SEQUENCE pub_test1.test_seq1;
+CREATE TABLE pub_test1.test_tbl1 (a int primary key, b int);
+CREATE SCHEMA pub_test2;
+CREATE SEQUENCE pub_test2.test_seq2;
+CREATE TABLE pub_test2.test_tbl2 (a int primary key, b int);
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_schemas;
+RESET client_min_messages;
+-- add tables from one schema, sequences from the other
+ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test2;
+ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test1;
+\dRp+ testpub_schemas
+                                              Publication testpub_schemas
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables from schemas:
+    "pub_test2"
+Sequences from schemas:
+    "pub_test1"
+
+\dn+ pub_test1
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test1 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (sequences)
+
+\dn+ pub_test2
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test2 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (tables)
+
+\d+ pub_test1.test_seq1;
+                        Sequence "pub_test1.test_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test1.test_tbl1;
+                               Table "pub_test1.test_tbl1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl1_pkey" PRIMARY KEY, btree (a)
+
+\d+ pub_test2.test_seq2;
+                        Sequence "pub_test2.test_seq2"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+
+\d+ pub_test2.test_tbl2;
+                               Table "pub_test2.test_tbl2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl2_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+-- add the other object type from each schema
+ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test1;
+ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test2;
+\dRp+ testpub_schemas
+                                              Publication testpub_schemas
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables from schemas:
+    "pub_test1"
+    "pub_test2"
+Sequences from schemas:
+    "pub_test1"
+    "pub_test2"
+
+\dn+ pub_test1
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test1 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (sequences)
+    "testpub_schemas" (tables)
+
+\dn+ pub_test2
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test2 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (tables)
+    "testpub_schemas" (sequences)
+
+\d+ pub_test1.test_seq1;
+                        Sequence "pub_test1.test_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test1.test_tbl1;
+                               Table "pub_test1.test_tbl1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl1_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_seq2;
+                        Sequence "pub_test2.test_seq2"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_tbl2;
+                               Table "pub_test2.test_tbl2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl2_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+-- now drop the object type added first
+ALTER PUBLICATION testpub_schemas DROP ALL TABLES IN SCHEMA pub_test2;
+ALTER PUBLICATION testpub_schemas DROP ALL SEQUENCES IN SCHEMA pub_test1;
+\dRp+ testpub_schemas
+                                              Publication testpub_schemas
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables from schemas:
+    "pub_test1"
+Sequences from schemas:
+    "pub_test2"
+
+\dn+ pub_test1
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test1 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (tables)
+
+\dn+ pub_test2
+                            List of schemas
+   Name    |          Owner           | Access privileges | Description 
+-----------+--------------------------+-------------------+-------------
+ pub_test2 | regress_publication_user |                   | 
+Publications:
+    "testpub_schemas" (sequences)
+
+\d+ pub_test1.test_seq1;
+                        Sequence "pub_test1.test_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+
+\d+ pub_test1.test_tbl1;
+                               Table "pub_test1.test_tbl1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl1_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_seq2;
+                        Sequence "pub_test2.test_seq2"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_tbl2;
+                               Table "pub_test2.test_tbl2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl2_pkey" PRIMARY KEY, btree (a)
+
+-- should fail (publication contains the whole schema)
+ALTER PUBLICATION testpub_schemas ADD TABLE pub_test1.test_tbl1;
+ERROR:  cannot add relation "pub_test1.test_tbl1" to publication
+DETAIL:  Table's schema "pub_test1" is already part of the publication or part of the specified schema list.
+ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test2.test_seq2;
+ERROR:  cannot add relation "pub_test2.test_seq2" to publication
+DETAIL:  Sequence's schema "pub_test2" is already part of the publication or part of the specified schema list.
+-- should work (different schema)
+ALTER PUBLICATION testpub_schemas ADD TABLE pub_test2.test_tbl2;
+ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test1.test_seq1;
+\dRp+ testpub_schemas
+                                              Publication testpub_schemas
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables:
+    "pub_test2.test_tbl2"
+Tables from schemas:
+    "pub_test1"
+Sequences:
+    "pub_test1.test_seq1"
+Sequences from schemas:
+    "pub_test2"
+
+\d+ pub_test1.test_seq1;
+                        Sequence "pub_test1.test_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test1.test_tbl1;
+                               Table "pub_test1.test_tbl1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl1_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_seq2;
+                        Sequence "pub_test2.test_seq2"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_tbl2;
+                               Table "pub_test2.test_tbl2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl2_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+-- now drop the explicitly added objects again
+ALTER PUBLICATION testpub_schemas DROP TABLE pub_test2.test_tbl2;
+ALTER PUBLICATION testpub_schemas DROP SEQUENCE pub_test1.test_seq1;
+\dRp+ testpub_schemas
+                                              Publication testpub_schemas
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
+Tables from schemas:
+    "pub_test1"
+Sequences from schemas:
+    "pub_test2"
+
+\d+ pub_test1.test_seq1;
+                        Sequence "pub_test1.test_seq1"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+
+\d+ pub_test1.test_tbl1;
+                               Table "pub_test1.test_tbl1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl1_pkey" PRIMARY KEY, btree (a)
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_seq2;
+                        Sequence "pub_test2.test_seq2"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+Publications:
+    "testpub_schemas"
+
+\d+ pub_test2.test_tbl2;
+                               Table "pub_test2.test_tbl2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           | not null |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+Indexes:
+    "test_tbl2_pkey" PRIMARY KEY, btree (a)
+
+DROP PUBLICATION testpub_schemas;
+DROP TABLE pub_test1.test_tbl1, pub_test2.test_tbl2;
+DROP SEQUENCE pub_test1.test_seq1, pub_test2.test_seq2;
+DROP SCHEMA pub_test1, pub_test2;
 -- Tests for partitioned tables
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_forparted;
@@ -207,10 +713,10 @@ UPDATE testpub_parted1 SET a = 1;
 -- only parent is listed as being in publication, not the partition
 ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
 \dRp+ testpub_forparted
-                               Publication testpub_forparted
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub_forparted
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "public.testpub_parted"
 
@@ -223,10 +729,10 @@ ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1;
 UPDATE testpub_parted1 SET a = 1;
 ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
 \dRp+ testpub_forparted
-                               Publication testpub_forparted
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | t
+                                             Publication testpub_forparted
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | t
 Tables:
     "public.testpub_parted"
 
@@ -255,10 +761,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub5 FOR TABLE testpub_rf_tbl1, testpub_rf_tbl2 WHERE (c <> 'test' AND d < 5) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                                  Publication testpub5
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -271,10 +777,10 @@ Tables:
 
 ALTER PUBLICATION testpub5 ADD TABLE testpub_rf_tbl3 WHERE (e > 1000 AND e < 2000);
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                                  Publication testpub5
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -290,10 +796,10 @@ Publications:
 
 ALTER PUBLICATION testpub5 DROP TABLE testpub_rf_tbl2;
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                                  Publication testpub5
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl3" WHERE ((e > 1000) AND (e < 2000))
@@ -301,10 +807,10 @@ Tables:
 -- remove testpub_rf_tbl1 and add testpub_rf_tbl3 again (another WHERE expression)
 ALTER PUBLICATION testpub5 SET TABLE testpub_rf_tbl3 WHERE (e > 300 AND e < 500);
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                                  Publication testpub5
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl3" WHERE ((e > 300) AND (e < 500))
 
@@ -337,10 +843,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_syntax1 FOR TABLE testpub_rf_tbl1, ONLY testpub_rf_tbl3 WHERE (e < 999) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub_syntax1
-                                Publication testpub_syntax1
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                              Publication testpub_syntax1
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl3" WHERE (e < 999)
@@ -350,10 +856,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_syntax2 FOR TABLE testpub_rf_tbl1, testpub_rf_schema1.testpub_rf_tbl5 WHERE (h < 999) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub_syntax2
-                                Publication testpub_syntax2
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                              Publication testpub_syntax2
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | f       | f       | f         | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "testpub_rf_schema1.testpub_rf_tbl5" WHERE (h < 999)
@@ -658,10 +1164,10 @@ ERROR:  relation "testpub_tbl1" is already member of publication "testpub_fortbl
 CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1;
 ERROR:  publication "testpub_fortbl" already exists
 \dRp+ testpub_fortbl
-                                 Publication testpub_fortbl
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                               Publication testpub_fortbl
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "pub_test.testpub_nopk"
     "public.testpub_tbl1"
@@ -699,10 +1205,10 @@ Publications:
     "testpub_fortbl"
 
 \dRp+ testpub_default
-                                Publication testpub_default
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | f         | f
+                                              Publication testpub_default
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | f         | t         | f
 Tables:
     "pub_test.testpub_nopk"
     "public.testpub_tbl1"
@@ -780,10 +1286,10 @@ REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
 DROP TABLE testpub_parted;
 DROP TABLE testpub_tbl1;
 \dRp+ testpub_default
-                                Publication testpub_default
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | f         | f
+                                              Publication testpub_default
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | f         | t         | f
 (1 row)
 
 -- fail - must be owner of publication
@@ -793,20 +1299,20 @@ ERROR:  must be owner of publication testpub_default
 RESET ROLE;
 ALTER PUBLICATION testpub_default RENAME TO testpub_foo;
 \dRp testpub_foo
-                                           List of publications
-    Name     |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
--------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpub_foo | regress_publication_user | f          | t       | t       | t       | f         | f
+                                                         List of publications
+    Name     |          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+-------------+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ testpub_foo | regress_publication_user | f          | f             | t       | t       | t       | f         | t         | f
 (1 row)
 
 -- rename back to keep the rest simple
 ALTER PUBLICATION testpub_foo RENAME TO testpub_default;
 ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2;
 \dRp testpub_default
-                                             List of publications
-      Name       |           Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
------------------+---------------------------+------------+---------+---------+---------+-----------+----------
- testpub_default | regress_publication_user2 | f          | t       | t       | t       | f         | f
+                                                           List of publications
+      Name       |           Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+-----------------+---------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ testpub_default | regress_publication_user2 | f          | f             | t       | t       | t       | f         | t         | f
 (1 row)
 
 -- adding schemas and tables
@@ -822,19 +1328,19 @@ CREATE TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA"(id int);
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub1_forschema FOR ALL TABLES IN SCHEMA pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
 CREATE PUBLICATION testpub2_forschema FOR ALL TABLES IN SCHEMA pub_test1, pub_test2, pub_test3;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub2_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -848,44 +1354,44 @@ CREATE PUBLICATION testpub6_forschema FOR ALL TABLES IN SCHEMA "CURRENT_SCHEMA",
 CREATE PUBLICATION testpub_fortable FOR TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA";
 RESET client_min_messages;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub3_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "public"
 
 \dRp+ testpub4_forschema
-                               Publication testpub4_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub4_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
 
 \dRp+ testpub5_forschema
-                               Publication testpub5_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub5_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
     "public"
 
 \dRp+ testpub6_forschema
-                               Publication testpub6_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub6_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
     "public"
 
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                              Publication testpub_fortable
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "CURRENT_SCHEMA.CURRENT_SCHEMA"
 
@@ -919,10 +1425,10 @@ ERROR:  schema "testpub_view" does not exist
 -- dropping the schema should reflect the change in publication
 DROP SCHEMA pub_test3;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub2_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -930,20 +1436,20 @@ Tables from schemas:
 -- renaming the schema should reflect the change in publication
 ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub2_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1_renamed"
     "pub_test2"
 
 ALTER SCHEMA pub_test1_renamed RENAME to pub_test1;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub2_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -951,10 +1457,10 @@ Tables from schemas:
 -- alter publication add schema
 ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -963,10 +1469,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -975,10 +1481,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA pub_test1;
 ERROR:  schema "pub_test1" is already member of publication "testpub1_forschema"
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -986,10 +1492,10 @@ Tables from schemas:
 -- alter publication drop schema
 ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -997,10 +1503,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test2;
 ERROR:  tables from schema "pub_test2" are not part of the publication
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1008,29 +1514,29 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
 -- drop all schemas
 ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 (1 row)
 
 -- alter publication set multiple schema
 ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA pub_test1, pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1039,10 +1545,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1051,10 +1557,10 @@ Tables from schemas:
 -- removing the duplicate schemas
 ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA pub_test1, pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub1_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1124,18 +1630,18 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub3_forschema;
 RESET client_min_messages;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub3_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 (1 row)
 
 ALTER PUBLICATION testpub3_forschema SET ALL TABLES IN SCHEMA pub_test1;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                             Publication testpub3_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1145,20 +1651,20 @@ CREATE PUBLICATION testpub_forschema_fortable FOR ALL TABLES IN SCHEMA pub_test1
 CREATE PUBLICATION testpub_fortable_forschema FOR TABLE pub_test2.tbl1, ALL TABLES IN SCHEMA pub_test1;
 RESET client_min_messages;
 \dRp+ testpub_forschema_fortable
-                           Publication testpub_forschema_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                         Publication testpub_forschema_fortable
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "pub_test2.tbl1"
 Tables from schemas:
     "pub_test1"
 
 \dRp+ testpub_fortable_forschema
-                           Publication testpub_fortable_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                         Publication testpub_fortable_forschema
+          Owner           | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Sequences | Via root 
+--------------------------+------------+---------------+---------+---------+---------+-----------+-----------+----------
+ regress_publication_user | f          | f             | t       | t       | t       | t         | t         | f
 Tables:
     "pub_test2.tbl1"
 Tables from schemas:
@@ -1202,40 +1708,85 @@ CREATE SCHEMA sch1;
 CREATE SCHEMA sch2;
 CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a);
 CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10);
+CREATE SEQUENCE sch1.seq1;
+CREATE SEQUENCE sch2.seq2;
 -- Schema publication that does not include the schema that has the parent table
 CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
 SELECT * FROM pg_publication_tables;
  pubname | schemaname | tablename  
 ---------+------------+------------
  pub     | sch2       | tbl1_part1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
 DROP PUBLICATION pub;
 -- Table publication that does not include the parent table
 CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
+ALTER PUBLICATION pub ADD SEQUENCE sch2.seq2;
 SELECT * FROM pg_publication_tables;
  pubname | schemaname | tablename  
 ---------+------------+------------
  pub     | sch2       | tbl1_part1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
 -- Table publication that includes both the parent table and the child table
 ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
 SELECT * FROM pg_publication_tables;
  pubname | schemaname | tablename 
 ---------+------------+-----------
  pub     | sch1       | tbl1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch1       | seq1
+ pub     | sch2       | seq2
+(2 rows)
+
 DROP PUBLICATION pub;
 -- Schema publication that does not include the schema that has the parent table
 CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
 SELECT * FROM pg_publication_tables;
  pubname | schemaname | tablename  
 ---------+------------+------------
  pub     | sch2       | tbl1_part1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch1       | seq1
+(1 row)
+
+DROP PUBLICATION pub;
+-- Sequence publication
+CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
 DROP PUBLICATION pub;
 -- Table publication that does not include the parent table
 CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
@@ -1245,14 +1796,26 @@ SELECT * FROM pg_publication_tables;
  pub     | sch2       | tbl1_part1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+(0 rows)
+
 -- Table publication that includes both the parent table and the child table
 ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
 SELECT * FROM pg_publication_tables;
  pubname | schemaname | tablename  
 ---------+------------+------------
  pub     | sch2       | tbl1_part1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
 DROP PUBLICATION pub;
 DROP TABLE sch2.tbl1_part1;
 DROP TABLE sch1.tbl1;
@@ -1268,9 +1831,81 @@ SELECT * FROM pg_publication_tables;
  pub     | sch1       | tbl1
 (1 row)
 
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+(0 rows)
+
+DROP PUBLICATION pub;
+-- Schema publication
+CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
+DROP PUBLICATION pub;
+-- Sequence publication
+CREATE PUBLICATION pub FOR ALL SEQUENCES IN SCHEMA sch2;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch1       | seq1
+ pub     | sch2       | seq2
+(2 rows)
+
+ALTER PUBLICATION pub DROP SEQUENCE sch1.seq1;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch2       | seq2
+(1 row)
+
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch1;
+SELECT * FROM pg_publication_tables;
+ pubname | schemaname | tablename 
+---------+------------+-----------
+(0 rows)
+
+SELECT * FROM pg_publication_sequences;
+ pubname | schemaname | sequencename 
+---------+------------+--------------
+ pub     | sch1       | seq1
+ pub     | sch2       | seq2
+(2 rows)
+
 RESET client_min_messages;
 DROP PUBLICATION pub;
 DROP TABLE sch1.tbl1;
+DROP SEQUENCE sch1.seq1, sch2.seq2;
 DROP SCHEMA sch1 cascade;
 DROP SCHEMA sch2 cascade;
 RESET SESSION AUTHORIZATION;
index 6cb6388880d019f57d2c5bedbe102e770a8a9725..8a5b20e62cb423fd34c310af0f00da42a13a50bb 100644 (file)
@@ -1429,6 +1429,14 @@ pg_prepared_xacts| SELECT p.transaction,
    FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid)
      LEFT JOIN pg_authid u ON ((p.ownerid = u.oid)))
      LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
+pg_publication_sequences| SELECT p.pubname,
+    n.nspname AS schemaname,
+    c.relname AS sequencename
+   FROM pg_publication p,
+    LATERAL pg_get_publication_sequences((p.pubname)::text) gps(relid),
+    (pg_class c
+     JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
+  WHERE (c.oid = gps.relid);
 pg_publication_tables| SELECT p.pubname,
     n.nspname AS schemaname,
     c.relname AS tablename
index acd0468a9d9f7a5615080e80d84c20a8453ae1e9..9d8323468d668e19c0c39ca64a3e3e72c6d123be 100644 (file)
@@ -49,6 +49,7 @@ CREATE TRANSFORM FOR int LANGUAGE SQL (
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
 CREATE PUBLICATION addr_pub_schema FOR ALL TABLES IN SCHEMA addr_nsp;
+CREATE PUBLICATION addr_pub_schema2 FOR ALL SEQUENCES IN SCHEMA addr_nsp;
 RESET client_min_messages;
 CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
 CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable;
@@ -198,7 +199,8 @@ WITH objects (type, name, args) AS (VALUES
                ('transform', '{int}', '{sql}'),
                ('access method', '{btree}', '{}'),
                ('publication', '{addr_pub}', '{}'),
-               ('publication namespace', '{addr_nsp}', '{addr_pub_schema}'),
+               ('publication namespace', '{addr_nsp}', '{addr_pub_schema,t}'),
+               ('publication namespace', '{addr_nsp}', '{addr_pub_schema2,s}'),
                ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'),
                ('subscription', '{regress_addr_sub}', '{}'),
                ('statistics object', '{addr_nsp, gentable_stat}', '{}')
@@ -218,6 +220,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*,
 DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 DROP PUBLICATION addr_pub;
 DROP PUBLICATION addr_pub_schema;
+DROP PUBLICATION addr_pub_schema2;
 DROP SUBSCRIPTION regress_addr_sub;
 
 DROP SCHEMA addr_nsp CASCADE;
index 5457c56b33f80f0906ea87e5a26fd9ca5ba139a3..c195e75c6f07c7d53a675099510d6c8d5ce3f0e0 100644 (file)
@@ -27,7 +27,7 @@ CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publis
 
 \dRp
 
-ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
+ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete, sequence');
 
 \dRp
 
@@ -46,6 +46,8 @@ ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update');
 CREATE TABLE testpub_tbl2 (id serial primary key, data text);
 -- fail - can't add to for all tables publication
 ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2;
+-- fail - can't add a table using ADD SEQUENCE command
+ALTER PUBLICATION testpub_foralltables ADD SEQUENCE testpub_tbl2;
 -- fail - can't drop from all tables publication
 ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2;
 -- fail - can't add to for all tables publication
@@ -104,6 +106,183 @@ RESET client_min_messages;
 DROP TABLE testpub_tbl3, testpub_tbl3a;
 DROP PUBLICATION testpub3, testpub4;
 
+--- adding sequences
+CREATE SEQUENCE testpub_seq0;
+CREATE SEQUENCE pub_test.testpub_seq1;
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES WITH (publish = 'sequence');
+RESET client_min_messages;
+ALTER PUBLICATION testpub_forallsequences SET (publish = 'insert, sequence');
+
+CREATE SEQUENCE testpub_seq2;
+-- fail - can't add to for all sequences publication
+ALTER PUBLICATION testpub_forallsequences ADD SEQUENCE testpub_seq2;
+-- fail - can't drop from all sequences publication
+ALTER PUBLICATION testpub_forallsequences DROP SEQUENCE testpub_seq2;
+-- fail - can't add to for all sequences publication
+ALTER PUBLICATION testpub_forallsequences SET SEQUENCE pub_test.testpub_seq1;
+
+-- fail - can't add schema to 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences ADD ALL SEQUENCES IN SCHEMA pub_test;
+-- fail - can't drop schema from 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences DROP ALL SEQUENCES IN SCHEMA pub_test;
+-- fail - can't set schema to 'FOR ALL SEQUENCES' publication
+ALTER PUBLICATION testpub_forallsequences SET ALL SEQUENCES IN SCHEMA pub_test;
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forsequence FOR SEQUENCE testpub_seq0;
+RESET client_min_messages;
+-- should be able to add schema to 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence ADD ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+-- fail - can't add sequence from the schema we already added
+ALTER PUBLICATION testpub_forsequence ADD SEQUENCE pub_test.testpub_seq1;
+-- fail - can't add sequence using ADD TABLE command
+ALTER PUBLICATION testpub_forsequence ADD TABLE pub_test.testpub_seq1;
+-- should be able to drop schema from 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence DROP ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+-- should be able to set schema to 'FOR SEQUENCE' publication
+ALTER PUBLICATION testpub_forsequence SET ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_forsequence
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forschema FOR ALL SEQUENCES IN SCHEMA pub_test;
+RESET client_min_messages;
+-- fail - can't create publication with schema and sequence of the same schema
+CREATE PUBLICATION testpub_for_seq_schema FOR ALL SEQUENCES IN SCHEMA pub_test, SEQUENCE pub_test.testpub_seq1;
+-- fail - can't add a sequence of the same schema to the schema publication
+ALTER PUBLICATION testpub_forschema ADD SEQUENCE pub_test.testpub_seq1;
+-- fail - can't drop a sequence from the schema publication which isn't in the
+-- publication
+ALTER PUBLICATION testpub_forschema DROP SEQUENCE pub_test.testpub_seq1;
+-- should be able to set sequence to schema publication
+ALTER PUBLICATION testpub_forschema SET SEQUENCE pub_test.testpub_seq1;
+\dRp+ testpub_forschema
+
+SELECT pubname, puballtables, puballsequences FROM pg_publication WHERE pubname = 'testpub_forallsequences';
+\d+ pub_test.testpub_seq1
+\dRp+ testpub_forallsequences
+DROP SEQUENCE testpub_seq0, pub_test.testpub_seq1, testpub_seq2;
+DROP PUBLICATION testpub_forallsequences, testpub_forsequence, testpub_forschema;
+
+-- Publication mixing tables and sequences
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_mix;
+RESET client_min_messages;
+
+CREATE SEQUENCE testpub_seq1;
+CREATE SEQUENCE pub_test.testpub_seq2;
+
+ALTER PUBLICATION testpub_mix ADD SEQUENCE testpub_seq1, TABLE testpub_tbl1;
+\dRp+ testpub_mix
+
+ALTER PUBLICATION testpub_mix ADD ALL SEQUENCES IN SCHEMA pub_test, ALL TABLES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+
+ALTER PUBLICATION testpub_mix DROP ALL SEQUENCES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+
+ALTER PUBLICATION testpub_mix DROP ALL TABLES IN SCHEMA pub_test;
+\dRp+ testpub_mix
+
+DROP PUBLICATION testpub_mix;
+DROP SEQUENCE testpub_seq1;
+DROP SEQUENCE pub_test.testpub_seq2;
+
+
+-- make sure we replicate only the correct relation type
+CREATE SCHEMA pub_test1;
+CREATE SEQUENCE pub_test1.test_seq1;
+CREATE TABLE pub_test1.test_tbl1 (a int primary key, b int);
+
+CREATE SCHEMA pub_test2;
+CREATE SEQUENCE pub_test2.test_seq2;
+CREATE TABLE pub_test2.test_tbl2 (a int primary key, b int);
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_schemas;
+RESET client_min_messages;
+
+-- add tables from one schema, sequences from the other
+ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test2;
+ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test1;
+
+\dRp+ testpub_schemas
+
+\dn+ pub_test1
+\dn+ pub_test2
+
+\d+ pub_test1.test_seq1;
+\d+ pub_test1.test_tbl1;
+
+\d+ pub_test2.test_seq2;
+\d+ pub_test2.test_tbl2;
+
+-- add the other object type from each schema
+ALTER PUBLICATION testpub_schemas ADD ALL TABLES IN SCHEMA pub_test1;
+ALTER PUBLICATION testpub_schemas ADD ALL SEQUENCES IN SCHEMA pub_test2;
+
+\dRp+ testpub_schemas
+
+\dn+ pub_test1
+\dn+ pub_test2
+
+\d+ pub_test1.test_seq1;
+\d+ pub_test1.test_tbl1;
+
+\d+ pub_test2.test_seq2;
+\d+ pub_test2.test_tbl2;
+
+-- now drop the object type added first
+ALTER PUBLICATION testpub_schemas DROP ALL TABLES IN SCHEMA pub_test2;
+ALTER PUBLICATION testpub_schemas DROP ALL SEQUENCES IN SCHEMA pub_test1;
+
+\dRp+ testpub_schemas
+
+\dn+ pub_test1
+\dn+ pub_test2
+
+\d+ pub_test1.test_seq1;
+\d+ pub_test1.test_tbl1;
+
+\d+ pub_test2.test_seq2;
+\d+ pub_test2.test_tbl2;
+
+-- should fail (publication contains the whole schema)
+ALTER PUBLICATION testpub_schemas ADD TABLE pub_test1.test_tbl1;
+ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test2.test_seq2;
+
+-- should work (different schema)
+ALTER PUBLICATION testpub_schemas ADD TABLE pub_test2.test_tbl2;
+ALTER PUBLICATION testpub_schemas ADD SEQUENCE pub_test1.test_seq1;
+
+\dRp+ testpub_schemas
+
+\d+ pub_test1.test_seq1;
+\d+ pub_test1.test_tbl1;
+
+\d+ pub_test2.test_seq2;
+\d+ pub_test2.test_tbl2;
+
+-- now drop the explicitly added objects again
+ALTER PUBLICATION testpub_schemas DROP TABLE pub_test2.test_tbl2;
+ALTER PUBLICATION testpub_schemas DROP SEQUENCE pub_test1.test_seq1;
+
+\dRp+ testpub_schemas
+
+\d+ pub_test1.test_seq1;
+\d+ pub_test1.test_tbl1;
+
+\d+ pub_test2.test_seq2;
+\d+ pub_test2.test_tbl2;
+
+DROP PUBLICATION testpub_schemas;
+DROP TABLE pub_test1.test_tbl1, pub_test2.test_tbl2;
+DROP SEQUENCE pub_test1.test_seq1, pub_test2.test_seq2;
+DROP SCHEMA pub_test1, pub_test2;
+
 -- Tests for partitioned tables
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_forparted;
@@ -717,32 +896,51 @@ CREATE SCHEMA sch1;
 CREATE SCHEMA sch2;
 CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a);
 CREATE TABLE sch2.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10);
+CREATE SEQUENCE sch1.seq1;
+CREATE SEQUENCE sch2.seq2;
 -- Schema publication that does not include the schema that has the parent table
 CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 DROP PUBLICATION pub;
 -- Table publication that does not include the parent table
 CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
+ALTER PUBLICATION pub ADD SEQUENCE sch2.seq2;
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 -- Table publication that includes both the parent table and the child table
 ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 DROP PUBLICATION pub;
 -- Schema publication that does not include the schema that has the parent table
 CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+DROP PUBLICATION pub;
+-- Sequence publication
+CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 DROP PUBLICATION pub;
 -- Table publication that does not include the parent table
 CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0);
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 -- Table publication that includes both the parent table and the child table
 ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch2;
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 DROP PUBLICATION pub;
 DROP TABLE sch2.tbl1_part1;
@@ -755,10 +953,36 @@ CREATE TABLE sch1.tbl1_part3 (a int) PARTITION BY RANGE(a);
 ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30);
 CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
 SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+DROP PUBLICATION pub;
+-- Schema publication
+CREATE PUBLICATION pub FOR SEQUENCE sch2.seq2;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+DROP PUBLICATION pub;
+-- Sequence publication
+CREATE PUBLICATION pub FOR ALL SEQUENCES IN SCHEMA sch2;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+ALTER PUBLICATION pub ADD SEQUENCE sch1.seq1;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+ALTER PUBLICATION pub DROP SEQUENCE sch1.seq1;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
+
+ALTER PUBLICATION pub ADD ALL SEQUENCES IN SCHEMA sch1;
+SELECT * FROM pg_publication_tables;
+SELECT * FROM pg_publication_sequences;
 
 RESET client_min_messages;
 DROP PUBLICATION pub;
 DROP TABLE sch1.tbl1;
+DROP SEQUENCE sch1.seq1, sch2.seq2;
 DROP SCHEMA sch1 cascade;
 DROP SCHEMA sch2 cascade;
 
diff --git a/src/test/subscription/t/030_sequences.pl b/src/test/subscription/t/030_sequences.pl
new file mode 100644 (file)
index 0000000..9ae3c03
--- /dev/null
@@ -0,0 +1,202 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# This tests that sequences are replicated correctly by logical replication
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Initialize publisher node
+my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
+$node_publisher->init(allows_streaming => 'logical');
+$node_publisher->start;
+
+# Create subscriber node
+my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$node_subscriber->init(allows_streaming => 'logical');
+$node_subscriber->start;
+
+# Create some preexisting content on publisher
+my $ddl = qq(
+   CREATE TABLE seq_test (v BIGINT);
+   CREATE SEQUENCE s;
+);
+
+# Setup structure on the publisher
+$node_publisher->safe_psql('postgres', $ddl);
+
+# Create some the same structure on subscriber, and an extra sequence that
+# we'll create on the publisher later
+$ddl = qq(
+   CREATE TABLE seq_test (v BIGINT);
+   CREATE SEQUENCE s;
+   CREATE SEQUENCE s2;
+);
+
+$node_subscriber->safe_psql('postgres', $ddl);
+
+# Setup logical replication
+my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
+$node_publisher->safe_psql('postgres',
+   "CREATE PUBLICATION seq_pub");
+
+$node_publisher->safe_psql('postgres',
+   "ALTER PUBLICATION seq_pub ADD SEQUENCE s");
+
+$node_subscriber->safe_psql('postgres',
+   "CREATE SUBSCRIPTION seq_sub CONNECTION '$publisher_connstr' PUBLICATION seq_pub"
+);
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Wait for initial sync to finish as well
+my $synced_query =
+  "SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('s', 'r');";
+$node_subscriber->poll_query_until('postgres', $synced_query)
+  or die "Timed out while waiting for subscriber to synchronize data";
+
+# Insert initial test data
+$node_publisher->safe_psql(
+   'postgres', qq(
+   -- generate a number of values using the sequence
+   INSERT INTO seq_test SELECT nextval('s') FROM generate_series(1,100);
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Check the data on subscriber
+my $result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s;
+));
+
+is( $result, '132|0|t',
+   'initial test data replicated');
+
+
+# advance the sequence in a rolled-back transaction - the rollback
+# does not wait for the replication, so we could see any intermediate state
+# so do something else after the test, to ensure we wait for everything
+$node_publisher->safe_psql(
+   'postgres', qq(
+   BEGIN;
+   INSERT INTO seq_test SELECT nextval('s') FROM generate_series(1,100);
+   ROLLBACK;
+   INSERT INTO seq_test VALUES (-1);
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s;
+));
+
+is( $result, '231|0|t',
+   'advance sequence in rolled-back transaction');
+
+
+# create a new sequence and roll it back - should not be replicated, due to
+# the transactional behavior
+$node_publisher->safe_psql(
+   'postgres', qq(
+   BEGIN;
+   CREATE SEQUENCE s2;
+   ALTER PUBLICATION seq_pub ADD SEQUENCE s2;
+   INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
+   ROLLBACK;
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s2;
+));
+
+is( $result, '1|0|f',
+   'create new sequence and roll it back');
+
+
+# create a new sequence, advance it in a rolled-back transaction, but commit
+# the create - the advance should be replicated nevertheless
+$node_publisher->safe_psql(
+   'postgres', qq(
+   BEGIN;
+   CREATE SEQUENCE s2;
+   ALTER PUBLICATION seq_pub ADD SEQUENCE s2;
+   SAVEPOINT sp1;
+   INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
+   ROLLBACK TO sp1;
+   COMMIT;
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Wait for sync of the second sequence we just added to finish
+$synced_query =
+  "SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('s', 'r');";
+$node_subscriber->poll_query_until('postgres', $synced_query)
+  or die "Timed out while waiting for subscriber to synchronize data";
+
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s2;
+));
+
+is( $result, '132|0|t',
+   'create sequence, advance it in rolled-back transaction, but commit the create');
+
+
+# advance the new sequence in a transaction, and roll it back - the rollback
+# does not wait for the replication, so we could see any intermediate state
+# so do something else after the test, to ensure we wait for everything
+$node_publisher->safe_psql(
+   'postgres', qq(
+   BEGIN;
+   INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
+   ROLLBACK;
+   INSERT INTO seq_test VALUES (-1);
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s2;
+));
+
+is( $result, '231|0|t',
+   'advance the new sequence in a transaction and roll it back');
+
+
+# advance the sequence in a subtransaction - the subtransaction gets rolled
+# back, but commit the main one - the changes should still be replicated
+$node_publisher->safe_psql(
+   'postgres', qq(
+   BEGIN;
+   SAVEPOINT s1;
+   INSERT INTO seq_test SELECT nextval('s2') FROM generate_series(1,100);
+   ROLLBACK TO s1;
+   COMMIT;
+));
+
+$node_publisher->wait_for_catchup('seq_sub');
+
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+   'postgres', qq(
+   SELECT * FROM s2;
+));
+
+is( $result, '330|0|t',
+   'advance sequence in a subtransaction');
+
+
+done_testing();