diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/replication/basebackup.c | 51 | ||||
-rw-r--r-- | src/backend/replication/repl_gram.y | 97 | ||||
-rw-r--r-- | src/bin/pg_basebackup/pg_basebackup.c | 71 | ||||
-rw-r--r-- | src/bin/pg_basebackup/streamutil.c | 61 | ||||
-rw-r--r-- | src/bin/pg_basebackup/streamutil.h | 12 |
5 files changed, 235 insertions, 57 deletions
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index e09108d0ece..4c97ab7b5a7 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -19,6 +19,7 @@ #include "access/xlog_internal.h" /* for pg_start/stop_backup */ #include "catalog/pg_type.h" #include "common/file_perm.h" +#include "commands/defrem.h" #include "commands/progress.h" #include "lib/stringinfo.h" #include "libpq/libpq.h" @@ -764,7 +765,7 @@ parse_basebackup_options(List *options, basebackup_options *opt) ListCell *lopt; bool o_label = false; bool o_progress = false; - bool o_fast = false; + bool o_checkpoint = false; bool o_nowait = false; bool o_wal = false; bool o_maxrate = false; @@ -787,7 +788,7 @@ parse_basebackup_options(List *options, basebackup_options *opt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->label = strVal(defel->arg); + opt->label = defGetString(defel); o_label = true; } else if (strcmp(defel->defname, "progress") == 0) @@ -796,25 +797,35 @@ parse_basebackup_options(List *options, basebackup_options *opt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->progress = true; + opt->progress = defGetBoolean(defel); o_progress = true; } - else if (strcmp(defel->defname, "fast") == 0) + else if (strcmp(defel->defname, "checkpoint") == 0) { - if (o_fast) + char *optval = defGetString(defel); + + if (o_checkpoint) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->fastcheckpoint = true; - o_fast = true; + if (pg_strcasecmp(optval, "fast") == 0) + opt->fastcheckpoint = true; + else if (pg_strcasecmp(optval, "spread") == 0) + opt->fastcheckpoint = false; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized checkpoint type: \"%s\"", + optval))); + o_checkpoint = true; } - else if (strcmp(defel->defname, "nowait") == 0) + else if (strcmp(defel->defname, "wait") == 0) { if (o_nowait) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->nowait = true; + opt->nowait = !defGetBoolean(defel); o_nowait = true; } else if (strcmp(defel->defname, "wal") == 0) @@ -823,19 +834,19 @@ parse_basebackup_options(List *options, basebackup_options *opt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->includewal = true; + opt->includewal = defGetBoolean(defel); o_wal = true; } else if (strcmp(defel->defname, "max_rate") == 0) { - long maxrate; + int64 maxrate; if (o_maxrate) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - maxrate = intVal(defel->arg); + maxrate = defGetInt64(defel); if (maxrate < MAX_RATE_LOWER || maxrate > MAX_RATE_UPPER) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), @@ -851,21 +862,21 @@ parse_basebackup_options(List *options, basebackup_options *opt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - opt->sendtblspcmapfile = true; + opt->sendtblspcmapfile = defGetBoolean(defel); o_tablespace_map = true; } - else if (strcmp(defel->defname, "noverify_checksums") == 0) + else if (strcmp(defel->defname, "verify_checksums") == 0) { if (o_noverify_checksums) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); - noverify_checksums = true; + noverify_checksums = !defGetBoolean(defel); o_noverify_checksums = true; } else if (strcmp(defel->defname, "manifest") == 0) { - char *optval = strVal(defel->arg); + char *optval = defGetString(defel); bool manifest_bool; if (o_manifest) @@ -890,7 +901,7 @@ parse_basebackup_options(List *options, basebackup_options *opt) } else if (strcmp(defel->defname, "manifest_checksums") == 0) { - char *optval = strVal(defel->arg); + char *optval = defGetString(defel); if (o_manifest_checksums) ereport(ERROR, @@ -905,8 +916,10 @@ parse_basebackup_options(List *options, basebackup_options *opt) o_manifest_checksums = true; } else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("option \"%s\" not recognized", + defel->defname)); } if (opt->label == NULL) opt->label = "base backup"; diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y index e1e8ec29cc4..3b59d62ed86 100644 --- a/src/backend/replication/repl_gram.y +++ b/src/backend/replication/repl_gram.y @@ -95,13 +95,13 @@ static SQLCmd *make_sqlcmd(void); %type <node> base_backup start_replication start_logical_replication create_replication_slot drop_replication_slot identify_system timeline_history show sql_cmd -%type <list> base_backup_opt_list -%type <defelt> base_backup_opt +%type <list> base_backup_legacy_opt_list generic_option_list +%type <defelt> base_backup_legacy_opt generic_option %type <uintval> opt_timeline %type <list> plugin_options plugin_opt_list %type <defelt> plugin_opt_elem %type <node> plugin_opt_arg -%type <str> opt_slot var_name +%type <str> opt_slot var_name ident_or_keyword %type <boolval> opt_temporary %type <list> create_slot_opt_list %type <defelt> create_slot_opt @@ -157,12 +157,24 @@ var_name: IDENT { $$ = $1; } ; /* + * BASE_BACKUP ( option [ 'value' ] [, ...] ) + * + * We also still support the legacy syntax: + * * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT] * [MAX_RATE %d] [TABLESPACE_MAP] [NOVERIFY_CHECKSUMS] * [MANIFEST %s] [MANIFEST_CHECKSUMS %s] + * + * Future options should be supported only using the new syntax. */ base_backup: - K_BASE_BACKUP base_backup_opt_list + K_BASE_BACKUP '(' generic_option_list ')' + { + BaseBackupCmd *cmd = makeNode(BaseBackupCmd); + cmd->options = $3; + $$ = (Node *) cmd; + } + | K_BASE_BACKUP base_backup_legacy_opt_list { BaseBackupCmd *cmd = makeNode(BaseBackupCmd); cmd->options = $2; @@ -170,14 +182,14 @@ base_backup: } ; -base_backup_opt_list: - base_backup_opt_list base_backup_opt +base_backup_legacy_opt_list: + base_backup_legacy_opt_list base_backup_legacy_opt { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; -base_backup_opt: +base_backup_legacy_opt: K_LABEL SCONST { $$ = makeDefElem("label", @@ -190,8 +202,8 @@ base_backup_opt: } | K_FAST { - $$ = makeDefElem("fast", - (Node *)makeInteger(true), -1); + $$ = makeDefElem("checkpoint", + (Node *)makeString("fast"), -1); } | K_WAL { @@ -200,8 +212,8 @@ base_backup_opt: } | K_NOWAIT { - $$ = makeDefElem("nowait", - (Node *)makeInteger(true), -1); + $$ = makeDefElem("wait", + (Node *)makeInteger(false), -1); } | K_MAX_RATE UCONST { @@ -215,8 +227,8 @@ base_backup_opt: } | K_NOVERIFY_CHECKSUMS { - $$ = makeDefElem("noverify_checksums", - (Node *)makeInteger(true), -1); + $$ = makeDefElem("verify_checksums", + (Node *)makeInteger(false), -1); } | K_MANIFEST SCONST { @@ -422,6 +434,65 @@ plugin_opt_arg: sql_cmd: IDENT { $$ = (Node *) make_sqlcmd(); } ; + +generic_option_list: + generic_option_list ',' generic_option + { $$ = lappend($1, $3); } + | generic_option + { $$ = list_make1($1); } + ; + +generic_option: + ident_or_keyword + { + $$ = makeDefElem($1, NULL, -1); + } + | ident_or_keyword IDENT + { + $$ = makeDefElem($1, (Node *) makeString($2), -1); + } + | ident_or_keyword SCONST + { + $$ = makeDefElem($1, (Node *) makeString($2), -1); + } + | ident_or_keyword UCONST + { + $$ = makeDefElem($1, (Node *) makeInteger($2), -1); + } + ; + +ident_or_keyword: + IDENT { $$ = $1; } + | K_BASE_BACKUP { $$ = "base_backup"; } + | K_IDENTIFY_SYSTEM { $$ = "identify_system"; } + | K_SHOW { $$ = "show"; } + | K_START_REPLICATION { $$ = "start_replication"; } + | K_CREATE_REPLICATION_SLOT { $$ = "create_replication_slot"; } + | K_DROP_REPLICATION_SLOT { $$ = "drop_replication_slot"; } + | K_TIMELINE_HISTORY { $$ = "timeline_history"; } + | K_LABEL { $$ = "label"; } + | K_PROGRESS { $$ = "progress"; } + | K_FAST { $$ = "fast"; } + | K_WAIT { $$ = "wait"; } + | K_NOWAIT { $$ = "nowait"; } + | K_MAX_RATE { $$ = "max_rate"; } + | K_WAL { $$ = "wal"; } + | K_TABLESPACE_MAP { $$ = "tablespace_map"; } + | K_NOVERIFY_CHECKSUMS { $$ = "noverify_checksums"; } + | K_TIMELINE { $$ = "timeline"; } + | K_PHYSICAL { $$ = "physical"; } + | K_LOGICAL { $$ = "logical"; } + | K_SLOT { $$ = "slot"; } + | K_RESERVE_WAL { $$ = "reserve_wal"; } + | K_TEMPORARY { $$ = "temporary"; } + | K_TWO_PHASE { $$ = "two_phase"; } + | K_EXPORT_SNAPSHOT { $$ = "export_snapshot"; } + | K_NOEXPORT_SNAPSHOT { $$ = "noexport_snapshot"; } + | K_USE_SNAPSHOT { $$ = "use_snapshot"; } + | K_MANIFEST { $$ = "manifest"; } + | K_MANIFEST_CHECKSUMS { $$ = "manifest_checksums"; } + ; + %% static SQLCmd * diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 669aa207a3c..27ee6394cfa 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -1804,10 +1804,6 @@ BaseBackup(void) TimeLineID latesttli; TimeLineID starttli; char *basebkp; - char escaped_label[MAXPGPATH]; - char *maxrate_clause = NULL; - char *manifest_clause = NULL; - char *manifest_checksums_clause = ""; int i; char xlogstart[64]; char xlogend[64]; @@ -1816,8 +1812,11 @@ BaseBackup(void) int serverVersion, serverMajor; int writing_to_stdout; + bool use_new_option_syntax = false; + PQExpBufferData buf; Assert(conn != NULL); + initPQExpBuffer(&buf); /* * Check server version. BASE_BACKUP command was introduced in 9.1, so we @@ -1835,6 +1834,8 @@ BaseBackup(void) serverver ? serverver : "'unknown'"); exit(1); } + if (serverMajor >= 1500) + use_new_option_syntax = true; /* * If WAL streaming was requested, also check that the server is new @@ -1865,20 +1866,48 @@ BaseBackup(void) /* * Start the actual backup */ - PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i); - + AppendStringCommandOption(&buf, use_new_option_syntax, "LABEL", label); + if (estimatesize) + AppendPlainCommandOption(&buf, use_new_option_syntax, "PROGRESS"); + if (includewal == FETCH_WAL) + AppendPlainCommandOption(&buf, use_new_option_syntax, "WAL"); + if (fastcheckpoint) + { + if (use_new_option_syntax) + AppendStringCommandOption(&buf, use_new_option_syntax, + "CHECKPOINT", "fast"); + else + AppendPlainCommandOption(&buf, use_new_option_syntax, "FAST"); + } + if (includewal != NO_WAL) + { + if (use_new_option_syntax) + AppendIntegerCommandOption(&buf, use_new_option_syntax, "WAIT", 0); + else + AppendPlainCommandOption(&buf, use_new_option_syntax, "NOWAIT"); + } if (maxrate > 0) - maxrate_clause = psprintf("MAX_RATE %u", maxrate); + AppendIntegerCommandOption(&buf, use_new_option_syntax, "MAX_RATE", + maxrate); + if (format == 't') + AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP"); + if (!verify_checksums) + { + if (use_new_option_syntax) + AppendIntegerCommandOption(&buf, use_new_option_syntax, + "VERIFY_CHECKSUMS", 0); + else + AppendPlainCommandOption(&buf, use_new_option_syntax, + "NOVERIFY_CHECKSUMS"); + } if (manifest) { - if (manifest_force_encode) - manifest_clause = "MANIFEST 'force-encode'"; - else - manifest_clause = "MANIFEST 'yes'"; + AppendStringCommandOption(&buf, use_new_option_syntax, "MANIFEST", + manifest_force_encode ? "force-encode" : "yes"); if (manifest_checksums != NULL) - manifest_checksums_clause = psprintf("MANIFEST_CHECKSUMS '%s'", - manifest_checksums); + AppendStringCommandOption(&buf, use_new_option_syntax, + "MANIFEST_CHECKSUMS", manifest_checksums); } if (verbose) @@ -1893,18 +1922,10 @@ BaseBackup(void) fprintf(stderr, "\n"); } - basebkp = - psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s %s %s", - escaped_label, - estimatesize ? "PROGRESS" : "", - includewal == FETCH_WAL ? "WAL" : "", - fastcheckpoint ? "FAST" : "", - includewal == NO_WAL ? "" : "NOWAIT", - maxrate_clause ? maxrate_clause : "", - format == 't' ? "TABLESPACE_MAP" : "", - verify_checksums ? "" : "NOVERIFY_CHECKSUMS", - manifest_clause ? manifest_clause : "", - manifest_checksums_clause); + if (use_new_option_syntax && buf.len > 0) + basebkp = psprintf("BASE_BACKUP (%s)", buf.data); + else + basebkp = psprintf("BASE_BACKUP %s", buf.data); if (PQsendQuery(conn, basebkp) == 0) { diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c index f5b3b476e52..d782b81adc6 100644 --- a/src/bin/pg_basebackup/streamutil.c +++ b/src/bin/pg_basebackup/streamutil.c @@ -603,6 +603,67 @@ DropReplicationSlot(PGconn *conn, const char *slot_name) return true; } +/* + * Append a "plain" option - one with no value - to a server command that + * is being constructed. + * + * In the old syntax, all options were parser keywords, so you could just + * write things like SOME_COMMAND OPTION1 OPTION2 'opt2value' OPTION3 42. The + * new syntax uses a comma-separated list surrounded by parentheses, so the + * equivalent is SOME_COMMAND (OPTION1, OPTION2 'optvalue', OPTION3 42). + */ +void +AppendPlainCommandOption(PQExpBuffer buf, bool use_new_option_syntax, + char *option_name) +{ + if (buf->len > 0 && buf->data[buf->len - 1] != '(') + { + if (use_new_option_syntax) + appendPQExpBufferStr(buf, ", "); + else + appendPQExpBufferChar(buf, ' '); + } + + appendPQExpBuffer(buf, " %s", option_name); +} + +/* + * Append an option with an associated string value to a server command that + * is being constructed. + * + * See comments for AppendPlainCommandOption, above. + */ +void +AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax, + char *option_name, char *option_value) +{ + AppendPlainCommandOption(buf, use_new_option_syntax, option_name); + + if (option_value != NULL) + { + size_t length = strlen(option_value); + char *escaped_value = palloc(1 + 2 * length); + + PQescapeStringConn(conn, escaped_value, option_value, length, NULL); + appendPQExpBuffer(buf, " '%s'", escaped_value); + pfree(escaped_value); + } +} + +/* + * Append an option with an associated integer value to a server command + * is being constructed. + * + * See comments for AppendPlainCommandOption, above. + */ +void +AppendIntegerCommandOption(PQExpBuffer buf, bool use_new_option_syntax, + char *option_name, int32 option_value) +{ + AppendPlainCommandOption(buf, use_new_option_syntax, option_name); + + appendPQExpBuffer(buf, " %d", option_value); +} /* * Frontend version of GetCurrentTimestamp(), since we are not linked with diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h index 504803b9763..65135c79e08 100644 --- a/src/bin/pg_basebackup/streamutil.h +++ b/src/bin/pg_basebackup/streamutil.h @@ -15,6 +15,7 @@ #include "access/xlogdefs.h" #include "datatype/timestamp.h" #include "libpq-fe.h" +#include "pqexpbuffer.h" extern const char *progname; extern char *connection_string; @@ -40,6 +41,17 @@ extern bool RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli, XLogRecPtr *startpos, char **db_name); + +extern void AppendPlainCommandOption(PQExpBuffer buf, + bool use_new_option_syntax, + char *option_value); +extern void AppendStringCommandOption(PQExpBuffer buf, + bool use_new_option_syntax, + char *option_name, char *option_value); +extern void AppendIntegerCommandOption(PQExpBuffer buf, + bool use_new_option_syntax, + char *option_name, int32 option_value); + extern bool RetrieveWalSegSize(PGconn *conn); extern TimestampTz feGetCurrentTimestamp(void); extern void feTimestampDifference(TimestampTz start_time, TimestampTz stop_time, |