*** a/src/backend/commands/user.c --- b/src/backend/commands/user.c *************** *** 250,256 **** CreateRole(CreateRoleStmt *stmt) if (dcanlogin) canlogin = intVal(dcanlogin->arg) != 0; if (disreplication) ! isreplication = intVal(disreplication->arg) != 0; if (dconnlimit) { connlimit = intVal(dconnlimit->arg); --- 250,256 ---- if (dcanlogin) canlogin = intVal(dcanlogin->arg) != 0; if (disreplication) ! isreplication = intVal(disreplication->arg); if (dconnlimit) { connlimit = intVal(dconnlimit->arg); *************** *** 352,358 **** CreateRole(CreateRoleStmt *stmt) /* superuser gets catupdate right by default */ new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper); new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); ! new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication); new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); if (password) --- 352,358 ---- /* superuser gets catupdate right by default */ new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper); new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); ! new_record[Anum_pg_authid_rolreplication - 1] = Int32GetDatum(isreplication); new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); if (password) *************** *** 732,738 **** AlterRole(AlterRoleStmt *stmt) if (isreplication >= 0) { ! new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0); new_record_repl[Anum_pg_authid_rolreplication - 1] = true; } --- 732,738 ---- if (isreplication >= 0) { ! new_record[Anum_pg_authid_rolreplication - 1] = Int32GetDatum(isreplication); new_record_repl[Anum_pg_authid_rolreplication - 1] = true; } *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 555,561 **** static void processCASbits(int cas_bits, int location, const char *constrType, LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P ! MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF --- 555,561 ---- LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P ! MAPPING MASTER MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF *************** *** 571,577 **** static void processCASbits(int cas_bits, int location, const char *constrType, QUOTE RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX ! RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE --- 571,577 ---- QUOTE RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX ! RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA REPLICATION RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE *************** *** 890,895 **** AlterOptRoleElem: --- 890,907 ---- { $$ = makeDefElem("rolemembers", (Node *)$2); } + | REPLICATION + { + $$ = makeDefElem("isreplication", (Node *)makeInteger(1)); + } + | MASTER REPLICATION + { + $$ = makeDefElem("isreplication", (Node *)makeInteger(2)); + } + | CASCADE REPLICATION + { + $$ = makeDefElem("isreplication", (Node *)makeInteger(3)); + } | IDENT { /* *************** *** 915,924 **** AlterOptRoleElem: $$ = makeDefElem("createrole", (Node *)makeInteger(TRUE)); else if (strcmp($1, "nocreaterole") == 0) $$ = makeDefElem("createrole", (Node *)makeInteger(FALSE)); - else if (strcmp($1, "replication") == 0) - $$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE)); else if (strcmp($1, "noreplication") == 0) ! $$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE)); else if (strcmp($1, "createdb") == 0) $$ = makeDefElem("createdb", (Node *)makeInteger(TRUE)); else if (strcmp($1, "nocreatedb") == 0) --- 927,934 ---- $$ = makeDefElem("createrole", (Node *)makeInteger(TRUE)); else if (strcmp($1, "nocreaterole") == 0) $$ = makeDefElem("createrole", (Node *)makeInteger(FALSE)); else if (strcmp($1, "noreplication") == 0) ! $$ = makeDefElem("isreplication", (Node *)makeInteger(0)); else if (strcmp($1, "createdb") == 0) $$ = makeDefElem("createdb", (Node *)makeInteger(TRUE)); else if (strcmp($1, "nocreatedb") == 0) *************** *** 12561,12566 **** unreserved_keyword: --- 12571,12577 ---- | LOCATION | LOCK_P | MAPPING + | MASTER | MATCH | MAXVALUE | MINUTE_P *************** *** 12613,12618 **** unreserved_keyword: --- 12624,12630 ---- | REPEATABLE | REPLACE | REPLICA + | REPLICATION | RESET | RESTART | RESTRICT *** a/src/backend/utils/init/miscinit.c --- b/src/backend/utils/init/miscinit.c *************** *** 392,398 **** SetUserIdAndContext(Oid userid, bool sec_def_context) /* * Check if the authenticated user is a replication role */ ! bool is_authenticated_user_replication_role(void) { bool result = false; --- 392,398 ---- /* * Check if the authenticated user is a replication role */ ! int is_authenticated_user_replication_role(void) { bool result = false; *** a/src/backend/utils/init/postinit.c --- b/src/backend/utils/init/postinit.c *************** *** 724,733 **** InitPostgres(const char *in_dbname, Oid dboid, const char *username, { Assert(!bootstrap); ! if (!superuser() && !is_authenticated_user_replication_role()) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); /* process any options passed in the startup packet */ if (MyProcPort != NULL) --- 724,758 ---- { Assert(!bootstrap); ! int replication_privilege = 0; ! if (!superuser() && (replication_privilege = is_authenticated_user_replication_role()) == 0) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); + else + { + bool isRecovery = RecoveryInProgress(); + switch (replication_privilege) + { + case 1: + /* nothing to think about privilege */ + break; + case 2: + /* master only */ + if (isRecovery) + ereport(FATAL, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or cascade replication role to start walsender"))); + break; + case 3: + /* cascade only */ + if (!isRecovery) + ereport(FATAL, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or master replication role to start walsender"))); + break; + } + } /* process any options passed in the startup packet */ if (MyProcPort != NULL) *** a/src/bin/pg_dump/pg_dumpall.c --- b/src/bin/pg_dump/pg_dumpall.c *************** *** 659,670 **** dumpRoles(PGconn *conn) "rolname = current_user AS is_current_user " "FROM pg_authid " "ORDER BY 2"); else if (server_version >= 80200) printfPQExpBuffer(buf, "SELECT oid, rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, " "rolcanlogin, rolconnlimit, rolpassword, " ! "rolvaliduntil, false as rolreplication, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_authid " --- 659,691 ---- "rolname = current_user AS is_current_user " "FROM pg_authid " "ORDER BY 2"); + else if (server_version >= 90200) + printfPQExpBuffer(buf, + "SELECT oid, rolname, rolsuper, rolinherit, " + "rolcreaterole, rolcreatedb, " + "rolcanlogin, rolconnlimit, rolpassword, " + "rolvaliduntil, rolreplication::int, " + "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " + "rolname = current_user AS is_current_user " + "FROM pg_authid " + "ORDER BY 2"); + else if (server_version >= 90100) + printfPQExpBuffer(buf, + "SELECT oid, rolname, rolsuper, rolinherit, " + "rolcreaterole, rolcreatedb, " + "rolcanlogin, rolconnlimit, rolpassword, " + "rolvaliduntil, " + "CASE WHEN rolreplication THEN 2 ELSE 0 END, " + "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " + "rolname = current_user AS is_current_user " + "FROM pg_authid " + "ORDER BY 2"); else if (server_version >= 80200) printfPQExpBuffer(buf, "SELECT oid, rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, " "rolcanlogin, rolconnlimit, rolpassword, " ! "rolvaliduntil, 0 as rolreplication, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_authid " *************** *** 674,680 **** dumpRoles(PGconn *conn) "SELECT oid, rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, " "rolcanlogin, rolconnlimit, rolpassword, " ! "rolvaliduntil, false as rolreplication, " "null as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_authid " --- 695,701 ---- "SELECT oid, rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, " "rolcanlogin, rolconnlimit, rolpassword, " ! "rolvaliduntil, 0 as rolreplication, " "null as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_authid " *************** *** 690,696 **** dumpRoles(PGconn *conn) "-1 as rolconnlimit, " "passwd as rolpassword, " "valuntil as rolvaliduntil, " ! "false as rolreplication, " "null as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_shadow " --- 711,717 ---- "-1 as rolconnlimit, " "passwd as rolpassword, " "valuntil as rolvaliduntil, " ! "0 as rolreplication, " "null as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_shadow " *************** *** 786,795 **** dumpRoles(PGconn *conn) else appendPQExpBuffer(buf, " NOLOGIN"); ! if (strcmp(PQgetvalue(res, i, i_rolreplication), "t") == 0) ! appendPQExpBuffer(buf, " REPLICATION"); ! else appendPQExpBuffer(buf, " NOREPLICATION"); if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", --- 807,822 ---- else appendPQExpBuffer(buf, " NOLOGIN"); ! if (strcmp(PQgetvalue(res, i, i_rolreplication), "0") == 0) appendPQExpBuffer(buf, " NOREPLICATION"); + else{ + if (strcmp(PQgetvalue(res, i, i_rolreplication), "1") == 0) + appendPQExpBuffer(buf, " REPLICATION"); + else if (strcmp(PQgetvalue(res, i, i_rolreplication), "2") == 0) + appendPQExpBuffer(buf, " MASTER REPLICATION"); + else + appendPQExpBuffer(buf, " CASCADE REPLICATION"); + } if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", *** a/src/bin/scripts/common.h --- b/src/bin/scripts/common.h *************** *** 20,25 **** enum trivalue --- 20,34 ---- TRI_YES }; + enum repvalue + { + REP_DEFAULT, + REP_NOREPLICATION, + REP_REPLICATION, + REP_MASTER, + REP_CASCADE + }; + typedef void (*help_handler) (const char *progname); extern const char *get_user_name(const char *progname); *** a/src/bin/scripts/createuser.c --- b/src/bin/scripts/createuser.c *************** *** 39,45 **** main(int argc, char *argv[]) {"no-login", no_argument, NULL, 'L'}, {"replication", no_argument, NULL, 1}, {"no-replication", no_argument, NULL, 2}, ! {"interactive", no_argument, NULL, 3}, /* adduser is obsolete, undocumented spelling of superuser */ {"adduser", no_argument, NULL, 'a'}, {"no-adduser", no_argument, NULL, 'A'}, --- 39,47 ---- {"no-login", no_argument, NULL, 'L'}, {"replication", no_argument, NULL, 1}, {"no-replication", no_argument, NULL, 2}, ! {"master-replication", no_argument, NULL, 3}, ! {"cascade-replication", no_argument, NULL, 4}, ! {"interactive", no_argument, NULL, 5}, /* adduser is obsolete, undocumented spelling of superuser */ {"adduser", no_argument, NULL, 'a'}, {"no-adduser", no_argument, NULL, 'A'}, *************** *** 70,76 **** main(int argc, char *argv[]) createrole = TRI_DEFAULT, inherit = TRI_DEFAULT, login = TRI_DEFAULT, ! replication = TRI_DEFAULT, encrypted = TRI_DEFAULT; PQExpBufferData sql; --- 72,78 ---- createrole = TRI_DEFAULT, inherit = TRI_DEFAULT, login = TRI_DEFAULT, ! replication = REP_DEFAULT, encrypted = TRI_DEFAULT; PQExpBufferData sql; *************** *** 151,162 **** main(int argc, char *argv[]) encrypted = TRI_NO; break; case 1: ! replication = TRI_YES; break; case 2: ! replication = TRI_NO; break; case 3: interactive = true; break; default: --- 153,170 ---- encrypted = TRI_NO; break; case 1: ! replication = REP_REPLICATION; break; case 2: ! replication = REP_NOREPLICATION; break; case 3: + replication = REP_MASTER; + break; + case 4: + replication = REP_CASCADE; + break; + case 5: interactive = true; break; default: *************** *** 296,305 **** main(int argc, char *argv[]) appendPQExpBuffer(&sql, " LOGIN"); if (login == TRI_NO) appendPQExpBuffer(&sql, " NOLOGIN"); ! if (replication == TRI_YES) appendPQExpBuffer(&sql, " REPLICATION"); ! if (replication == TRI_NO) appendPQExpBuffer(&sql, " NOREPLICATION"); if (conn_limit != NULL) appendPQExpBuffer(&sql, " CONNECTION LIMIT %s", conn_limit); appendPQExpBuffer(&sql, ";\n"); --- 304,317 ---- appendPQExpBuffer(&sql, " LOGIN"); if (login == TRI_NO) appendPQExpBuffer(&sql, " NOLOGIN"); ! if (replication == REP_REPLICATION) appendPQExpBuffer(&sql, " REPLICATION"); ! if (replication == REP_NOREPLICATION) appendPQExpBuffer(&sql, " NOREPLICATION"); + if (replication == REP_MASTER) + appendPQExpBuffer(&sql, " MASTER REPLICATION"); + if (replication == REP_CASCADE) + appendPQExpBuffer(&sql, " CASCADE REPLICATION"); if (conn_limit != NULL) appendPQExpBuffer(&sql, " CONNECTION LIMIT %s", conn_limit); appendPQExpBuffer(&sql, ";\n"); *************** *** 348,355 **** help(const char *progname) printf(_(" -V, --version output version information, then exit\n")); printf(_(" --interactive prompt for missing role name and attributes rather\n" " than using defaults\n")); - printf(_(" --replication role can initiate replication\n")); printf(_(" --no-replication role cannot initiate replication\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); --- 360,369 ---- printf(_(" -V, --version output version information, then exit\n")); printf(_(" --interactive prompt for missing role name and attributes rather\n" " than using defaults\n")); printf(_(" --no-replication role cannot initiate replication\n")); + printf(_(" --replication role can initiate replication from master and cascade\n")); + printf(_(" --master-replication role can initiate replication from only master\n")); + printf(_(" --cascade-replication role can initiate replication from only cascade\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); *** a/src/include/catalog/pg_authid.h --- b/src/include/catalog/pg_authid.h *************** *** 51,57 **** CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC bool rolcreatedb; /* allowed to create databases? */ bool rolcatupdate; /* allowed to alter catalogs manually? */ bool rolcanlogin; /* allowed to log in as session user? */ ! bool rolreplication; /* role used for streaming replication */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */ /* remaining fields may be null; use heap_getattr to read them! */ --- 51,59 ---- bool rolcreatedb; /* allowed to create databases? */ bool rolcatupdate; /* allowed to alter catalogs manually? */ bool rolcanlogin; /* allowed to log in as session user? */ ! int32 rolreplication; /* role used for streaming replication */ ! /* 0:noreplication, 1:replication(master & cascade) */ ! /* 2:replication(master), 3:replication(cascade) */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */ /* remaining fields may be null; use heap_getattr to read them! */ *************** *** 93,99 **** typedef FormData_pg_authid *Form_pg_authid; * user choices. * ---------------- */ ! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_ )); #define BOOTSTRAP_SUPERUSERID 10 --- 95,101 ---- * user choices. * ---------------- */ ! DATA(insert OID = 10 ( "POSTGRES" t t t t t t 1 -1 _null_ _null_ )); #define BOOTSTRAP_SUPERUSERID 10 *** a/src/include/miscadmin.h --- b/src/include/miscadmin.h *************** *** 439,445 **** extern void ValidatePgVersion(const char *path); extern void process_shared_preload_libraries(void); extern void process_local_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); ! extern bool is_authenticated_user_replication_role(void); /* in access/transam/xlog.c */ extern bool BackupInProgress(void); --- 439,445 ---- extern void process_shared_preload_libraries(void); extern void process_local_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); ! extern int is_authenticated_user_replication_role(void); /* in access/transam/xlog.c */ extern bool BackupInProgress(void); *** a/src/include/parser/kwlist.h --- b/src/include/parser/kwlist.h *************** *** 231,236 **** PG_KEYWORD("localtimestamp", LOCALTIMESTAMP, RESERVED_KEYWORD) --- 231,237 ---- PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD) PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD) PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD) + PG_KEYWORD("master", MASTER, UNRESERVED_KEYWORD) PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD) PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD) PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD) *************** *** 308,313 **** PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD) --- 309,315 ---- PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD) PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD) PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD) + PG_KEYWORD("replication", REPLICATION, UNRESERVED_KEYWORD) PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD) PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD) PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD)