diff options
27 files changed, 337 insertions, 188 deletions
diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out index c6f4b16c556..6558f2c5a4f 100644 --- a/contrib/amcheck/expected/check_btree.out +++ b/contrib/amcheck/expected/check_btree.out @@ -60,6 +60,14 @@ SELECT bt_index_parent_check('bttest_a_brin_idx'); ERROR: expected "btree" index as targets for verification DETAIL: Relation "bttest_a_brin_idx" is a brin index. ROLLBACK; +-- verify partitioned indexes are rejected (error) +BEGIN; +CREATE TABLE bttest_partitioned (a int, b int) PARTITION BY list (a); +CREATE INDEX bttest_btree_partitioned_idx ON bttest_partitioned USING btree (b); +SELECT bt_index_parent_check('bttest_btree_partitioned_idx'); +ERROR: expected index as targets for verification +DETAIL: This operation is not supported for partitioned indexes. +ROLLBACK; -- normal check outside of xact SELECT bt_index_check('bttest_a_idx'); bt_index_check diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql index 0793dbfeebd..171f7f691ec 100644 --- a/contrib/amcheck/sql/check_btree.sql +++ b/contrib/amcheck/sql/check_btree.sql @@ -52,6 +52,13 @@ CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id); SELECT bt_index_parent_check('bttest_a_brin_idx'); ROLLBACK; +-- verify partitioned indexes are rejected (error) +BEGIN; +CREATE TABLE bttest_partitioned (a int, b int) PARTITION BY list (a); +CREATE INDEX bttest_btree_partitioned_idx ON bttest_partitioned USING btree (b); +SELECT bt_index_parent_check('bttest_btree_partitioned_idx'); +ROLLBACK; + -- normal check outside of xact SELECT bt_index_check('bttest_a_idx'); -- more expansive tests diff --git a/contrib/amcheck/verify_common.c b/contrib/amcheck/verify_common.c index d095e62ce55..a31ce06ed99 100644 --- a/contrib/amcheck/verify_common.c +++ b/contrib/amcheck/verify_common.c @@ -18,11 +18,13 @@ #include "verify_common.h" #include "catalog/index.h" #include "catalog/pg_am.h" +#include "commands/defrem.h" #include "commands/tablecmds.h" #include "utils/guc.h" #include "utils/syscache.h" static bool amcheck_index_mainfork_expected(Relation rel); +static bool index_checkable(Relation rel, Oid am_id); /* @@ -155,23 +157,21 @@ amcheck_lock_relation_and_check(Oid indrelid, * callable by non-superusers. If granted, it's useful to be able to check a * whole cluster. */ -bool +static bool index_checkable(Relation rel, Oid am_id) { - if (rel->rd_rel->relkind != RELKIND_INDEX || - rel->rd_rel->relam != am_id) - { - HeapTuple amtup; - HeapTuple amtuprel; + if (rel->rd_rel->relkind != RELKIND_INDEX) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("expected index as targets for verification"), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); - amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(am_id)); - amtuprel = SearchSysCache1(AMOID, ObjectIdGetDatum(rel->rd_rel->relam)); + if (rel->rd_rel->relam != am_id) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("expected \"%s\" index as targets for verification", NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname)), + errmsg("expected \"%s\" index as targets for verification", get_am_name(am_id)), errdetail("Relation \"%s\" is a %s index.", - RelationGetRelationName(rel), NameStr(((Form_pg_am) GETSTRUCT(amtuprel))->amname)))); - } + RelationGetRelationName(rel), get_am_name(rel->rd_rel->relam)))); if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, @@ -182,7 +182,7 @@ index_checkable(Relation rel, Oid am_id) if (!rel->rd_index->indisvalid) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot check index \"%s\"", RelationGetRelationName(rel)), errdetail("Index is not valid."))); diff --git a/contrib/amcheck/verify_common.h b/contrib/amcheck/verify_common.h index 42ef9c20fe2..3f4c57f963d 100644 --- a/contrib/amcheck/verify_common.h +++ b/contrib/amcheck/verify_common.h @@ -26,5 +26,3 @@ extern void amcheck_lock_relation_and_check(Oid indrelid, Oid am_id, IndexDoCheckCallback check, LOCKMODE lockmode, void *state); - -extern bool index_checkable(Relation rel, Oid am_id); diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 832b616a7bb..51b95ed04f3 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1003,8 +1003,9 @@ local db1,db2,@demodbs all md5 the remainder of the field is treated as a regular expression. (See <xref linkend="posix-syntax-details"/> for details of <productname>PostgreSQL</productname>'s regular expression syntax.) The regular - expression can include a single capture, or parenthesized subexpression, - which can then be referenced in the <replaceable>database-username</replaceable> + expression can include a single capture, or parenthesized subexpression. + The portion of the system user name that matched the capture can then + be referenced in the <replaceable>database-username</replaceable> field as <literal>\1</literal> (backslash-one). This allows the mapping of multiple user names in a single line, which is particularly useful for simple syntax substitutions. For example, these entries @@ -1022,12 +1023,11 @@ mymap /^(.*)@otherdomain\.com$ guest <para> If the <replaceable>database-username</replaceable> field starts with a slash (<literal>/</literal>), the remainder of the field is treated - as a regular expression (see <xref linkend="posix-syntax-details"/> - for details of <productname>PostgreSQL</productname>'s regular - expression syntax). It is not possible to use <literal>\1</literal> - to use a capture from regular expression on - <replaceable>system-username</replaceable> for a regular expression - on <replaceable>database-username</replaceable>. + as a regular expression. + When the <replaceable>database-username</replaceable> field is a regular + expression, it is not possible to use <literal>\1</literal> within it to + refer to a capture from the <replaceable>system-username</replaceable> + field. </para> <tip> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 6b327d4fd81..f5a0e0954a1 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -11247,10 +11247,10 @@ now() statement (more specifically, the time of receipt of the latest command message from the client). <function>statement_timestamp()</function> and <function>transaction_timestamp()</function> - return the same value during the first command of a transaction, but might - differ during subsequent commands. + return the same value during the first statement of a transaction, but might + differ during subsequent statements. <function>clock_timestamp()</function> returns the actual current time, and - therefore its value changes even within a single SQL command. + therefore its value changes even within a single SQL statement. <function>timeofday()</function> is a historical <productname>PostgreSQL</productname> function. Like <function>clock_timestamp()</function>, it returns the actual current time, diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index b2c2cf9eac8..5bf59a19855 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2320,6 +2320,19 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname </listitem> </varlistentry> + <varlistentry id="libpq-connect-servicefile" xreflabel="servicefile"> + <term><literal>servicefile</literal></term> + <listitem> + <para> + This option specifies the name of the per-user connection service file + (see <xref linkend="libpq-pgservice"/>). + Defaults to <filename>~/.pg_service.conf</filename>, or + <filename>%APPDATA%\postgresql\.pg_service.conf</filename> on + Microsoft Windows. + </para> + </listitem> + </varlistentry> + <varlistentry id="libpq-connect-target-session-attrs" xreflabel="target_session_attrs"> <term><literal>target_session_attrs</literal></term> <listitem> @@ -9140,12 +9153,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) <indexterm> <primary><envar>PGSERVICEFILE</envar></primary> </indexterm> - <envar>PGSERVICEFILE</envar> specifies the name of the per-user - connection service file - (see <xref linkend="libpq-pgservice"/>). - Defaults to <filename>~/.pg_service.conf</filename>, or - <filename>%APPDATA%\postgresql\.pg_service.conf</filename> on - Microsoft Windows. + <envar>PGSERVICEFILE</envar> behaves the same as the + <xref linkend="libpq-connect-servicefile"/> connection parameter. </para> </listitem> @@ -9576,7 +9585,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) On Microsoft Windows, it is named <filename>%APPDATA%\postgresql\.pg_service.conf</filename> (where <filename>%APPDATA%</filename> refers to the Application Data subdirectory - in the user's profile). A different file name can be specified by + in the user's profile). A different file name can be specified using the + <literal>servicefile</literal> key word in a libpq connection string or by setting the environment variable <envar>PGSERVICEFILE</envar>. The system-wide file is named <filename>pg_service.conf</filename>. By default it is sought in the <filename>etc</filename> directory diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 4265a22d4de..823afe1b30b 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -3980,6 +3980,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage </para> <para> Estimated number of rows inserted since this table was last vacuumed + (not counting <command>VACUUM FULL</command>) </para></entry> </row> @@ -4066,7 +4067,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage <structfield>total_vacuum_time</structfield> <type>double precision</type> </para> <para> - Total time this table has been manually vacuumed, in milliseconds. + Total time this table has been manually vacuumed, in milliseconds + (not counting <command>VACUUM FULL</command>). (This includes the time spent sleeping due to cost-based delays.) </para></entry> </row> diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 4cfd9767f7c..e74b5be1eff 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -886,6 +886,16 @@ SELCT 1/0;<!-- this typo is intentional --> Errors detected at semantic analysis or later, such as a misspelled table or column name, do not have this effect. </para> + + <para> + Lastly, note that all the statements within the Query message will + observe the same value of <function>statement_timestamp()</function>, + since that timestamp is updated only upon receipt of the Query + message. This will result in them all observing the same + value of <function>transaction_timestamp()</function> as well, + except in cases where the query string ends a previously-started + transaction and begins a new one. + </para> </sect3> </sect2> @@ -3482,6 +3492,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" <para> Boolean option to use binary transfer mode. Binary mode is faster than the text mode but slightly less robust. + The default is <literal>off</literal>. </para> </listitem> </varlistentry> @@ -3494,6 +3505,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" <para> Boolean option to enable sending the messages that are written by <function>pg_logical_emit_message</function>. + The default is <literal>off</literal>. </para> </listitem> </varlistentry> @@ -3504,11 +3516,13 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" </term> <listitem> <para> - Boolean option to enable streaming of in-progress transactions. - It accepts an additional value "parallel" to enable sending extra - information with some messages to be used for parallelisation. - Minimum protocol version 2 is required to turn it on. Minimum protocol - version 4 is required for the "parallel" option. + Option to enable streaming of in-progress transactions. Valid values are + <literal>off</literal> (the default), <literal>on</literal> and + <literal>parallel</literal>. The setting <literal>parallel</literal> + enables sending extra information with some messages to be used for + parallelization. Minimum protocol version 2 is required to turn it + <literal>on</literal>. Minimum protocol version 4 is required for the + <literal>parallel</literal> value. </para> </listitem> </varlistentry> @@ -3521,6 +3535,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" <para> Boolean option to enable two-phase transactions. Minimum protocol version 3 is required to turn it on. + The default is <literal>off</literal>. </para> </listitem> </varlistentry> @@ -3537,6 +3552,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;" to send the changes regardless of their origin. This can be used to avoid loops (infinite replication of the same data) among replication nodes. + The default is <literal>any</literal>. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 95f4cac2467..4f7b11175c6 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -4623,6 +4623,15 @@ bar </listitem> </varlistentry> + <varlistentry id="app-psql-variables-servicefile"> + <term><varname>SERVICEFILE</varname></term> + <listitem> + <para> + The service file name, if applicable. + </para> + </listitem> + </varlistentry> + <varlistentry id="app-psql-variables-shell-error"> <term><varname>SHELL_ERROR</varname></term> <listitem> diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c index 21f2f4af97e..926f1e4008a 100644 --- a/src/backend/access/common/toast_compression.c +++ b/src/backend/access/common/toast_compression.c @@ -25,11 +25,11 @@ /* GUC */ int default_toast_compression = TOAST_PGLZ_COMPRESSION; -#define NO_LZ4_SUPPORT() \ +#define NO_COMPRESSION_SUPPORT(method) \ ereport(ERROR, \ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ - errmsg("compression method lz4 not supported"), \ - errdetail("This functionality requires the server to be built with lz4 support."))) + errmsg("compression method %s not supported", method), \ + errdetail("This functionality requires the server to be built with %s support.", method))) /* * Compress a varlena using PGLZ. @@ -139,7 +139,7 @@ struct varlena * lz4_compress_datum(const struct varlena *value) { #ifndef USE_LZ4 - NO_LZ4_SUPPORT(); + NO_COMPRESSION_SUPPORT("lz4"); return NULL; /* keep compiler quiet */ #else int32 valsize; @@ -182,7 +182,7 @@ struct varlena * lz4_decompress_datum(const struct varlena *value) { #ifndef USE_LZ4 - NO_LZ4_SUPPORT(); + NO_COMPRESSION_SUPPORT("lz4"); return NULL; /* keep compiler quiet */ #else int32 rawsize; @@ -215,7 +215,7 @@ struct varlena * lz4_decompress_datum_slice(const struct varlena *value, int32 slicelength) { #ifndef USE_LZ4 - NO_LZ4_SUPPORT(); + NO_COMPRESSION_SUPPORT("lz4"); return NULL; /* keep compiler quiet */ #else int32 rawsize; @@ -289,7 +289,7 @@ CompressionNameToMethod(const char *compression) else if (strcmp(compression, "lz4") == 0) { #ifndef USE_LZ4 - NO_LZ4_SUPPORT(); + NO_COMPRESSION_SUPPORT("lz4"); #endif return TOAST_LZ4_COMPRESSION; } diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 88fb9b45b2a..8e7827c6ed9 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -449,7 +449,6 @@ typedef struct XLogCtlData /* Protected by info_lck: */ XLogwrtRqst LogwrtRqst; XLogRecPtr RedoRecPtr; /* a recent copy of Insert->RedoRecPtr */ - FullTransactionId ckptFullXid; /* nextXid of latest checkpoint */ XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */ XLogRecPtr replicationSlotMinLSN; /* oldest LSN needed by any slot */ @@ -5744,7 +5743,6 @@ StartupXLOG(void) SetMultiXactIdLimit(checkPoint.oldestMulti, checkPoint.oldestMultiDB, true); SetCommitTsLimit(checkPoint.oldestCommitTsXid, checkPoint.newestCommitTsXid); - XLogCtl->ckptFullXid = checkPoint.nextXid; /* * Clear out any old relcache cache files. This is *necessary* if we do @@ -7437,11 +7435,6 @@ CreateCheckPoint(int flags) UpdateControlFile(); LWLockRelease(ControlFileLock); - /* Update shared-memory copy of checkpoint XID/epoch */ - SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptFullXid = checkPoint.nextXid; - SpinLockRelease(&XLogCtl->info_lck); - /* * We are now done with critical updates; no need for system panic if we * have trouble while fooling with old log segments. @@ -8516,11 +8509,6 @@ xlog_redo(XLogReaderState *record) ControlFile->checkPointCopy.nextXid = checkPoint.nextXid; LWLockRelease(ControlFileLock); - /* Update shared-memory copy of checkpoint XID/epoch */ - SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptFullXid = checkPoint.nextXid; - SpinLockRelease(&XLogCtl->info_lck); - /* * We should've already switched to the new TLI before replaying this * record. @@ -8577,11 +8565,6 @@ xlog_redo(XLogReaderState *record) ControlFile->checkPointCopy.nextXid = checkPoint.nextXid; LWLockRelease(ControlFileLock); - /* Update shared-memory copy of checkpoint XID/epoch */ - SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptFullXid = checkPoint.nextXid; - SpinLockRelease(&XLogCtl->info_lck); - /* TLI should not change in an on-line checkpoint */ (void) GetCurrentReplayRecPtr(&replayTLI); if (checkPoint.ThisTimeLineID != replayTLI) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index aa216683b74..c4029a4f3d3 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -800,11 +800,11 @@ index_create(Relation heapRelation, errmsg("user-defined indexes on system catalog tables are not supported"))); /* - * Btree text_pattern_ops uses text_eq as the equality operator, which is - * fine as long as the collation is deterministic; text_eq then reduces to + * Btree text_pattern_ops uses texteq as the equality operator, which is + * fine as long as the collation is deterministic; texteq then reduces to * bitwise equality and so it is semantically compatible with the other * operators and functions in that opclass. But with a nondeterministic - * collation, text_eq could yield results that are incompatible with the + * collation, texteq could yield results that are incompatible with the * actual behavior of the index (which is determined by the opclass's * comparison function). We prevent such problems by refusing creation of * an index with that opclass and a nondeterministic collation. @@ -814,7 +814,7 @@ index_create(Relation heapRelation, * opclasses as incompatible with nondeterminism; but for now, this small * hack suffices. * - * Another solution is to use a special operator, not text_eq, as the + * Another solution is to use a special operator, not texteq, as the * equality opclass member; but that is undesirable because it would * prevent index usage in many queries that work fine today. */ diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 332fad27835..fecee8224d0 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -2873,8 +2873,11 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, !token_has_regexp(identLine->pg_user) && (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL) { + const char *repl_str; + size_t repl_len; + char *old_pg_user; char *expanded_pg_user; - int offset; + size_t offset; /* substitution of the first argument requested */ if (matches[1].rm_so < 0) @@ -2886,18 +2889,33 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, *error_p = true; return; } + repl_str = system_user + matches[1].rm_so; + repl_len = matches[1].rm_eo - matches[1].rm_so; /* - * length: original length minus length of \1 plus length of match - * plus null terminator + * It's allowed to have more than one \1 in the string, and we'll + * replace them all. But that's pretty unusual so we optimize on + * the assumption of only one occurrence, which motivates doing + * repeated replacements instead of making two passes over the + * string to determine the final length right away. */ - expanded_pg_user = palloc0(strlen(identLine->pg_user->string) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1); - offset = ofs - identLine->pg_user->string; - memcpy(expanded_pg_user, identLine->pg_user->string, offset); - memcpy(expanded_pg_user + offset, - system_user + matches[1].rm_so, - matches[1].rm_eo - matches[1].rm_so); - strcat(expanded_pg_user, ofs + 2); + old_pg_user = identLine->pg_user->string; + do + { + /* + * length: current length minus length of \1 plus length of + * replacement plus null terminator + */ + expanded_pg_user = palloc(strlen(old_pg_user) - 2 + repl_len + 1); + /* ofs points into the old_pg_user string at this point */ + offset = ofs - old_pg_user; + memcpy(expanded_pg_user, old_pg_user, offset); + memcpy(expanded_pg_user + offset, repl_str, repl_len); + strcpy(expanded_pg_user + offset + repl_len, ofs + 2); + if (old_pg_user != identLine->pg_user->string) + pfree(old_pg_user); + old_pg_user = expanded_pg_user; + } while ((ofs = strstr(old_pg_user + offset + repl_len, "\\1")) != NULL); /* * Mark the token as quoted, so it will only be compared literally diff --git a/src/backend/libpq/pg_ident.conf.sample b/src/backend/libpq/pg_ident.conf.sample index f5225f26cdf..8ee6c0ba315 100644 --- a/src/backend/libpq/pg_ident.conf.sample +++ b/src/backend/libpq/pg_ident.conf.sample @@ -13,25 +13,25 @@ # user names to their corresponding PostgreSQL user names. Records # are of the form: # -# MAPNAME SYSTEM-USERNAME PG-USERNAME +# MAPNAME SYSTEM-USERNAME DATABASE-USERNAME # # (The uppercase quantities must be replaced by actual values.) # # MAPNAME is the (otherwise freely chosen) map name that was used in # pg_hba.conf. SYSTEM-USERNAME is the detected user name of the -# client. PG-USERNAME is the requested PostgreSQL user name. The -# existence of a record specifies that SYSTEM-USERNAME may connect as -# PG-USERNAME. +# client. DATABASE-USERNAME is the requested PostgreSQL user name. +# The existence of a record specifies that SYSTEM-USERNAME may connect +# as DATABASE-USERNAME. # -# If SYSTEM-USERNAME starts with a slash (/), it will be treated as a -# regular expression. Optionally this can contain a capture (a -# parenthesized subexpression). The substring matching the capture -# will be substituted for \1 (backslash-one) if present in -# PG-USERNAME. +# If SYSTEM-USERNAME starts with a slash (/), the rest of it will be +# treated as a regular expression. Optionally this can contain a capture +# (a parenthesized subexpression). The substring matching the capture +# will be substituted for \1 (backslash-one) if that appears in +# DATABASE-USERNAME. # -# PG-USERNAME can be "all", a user name, a group name prefixed with "+", or -# a regular expression (if it starts with a slash (/)). If it is a regular -# expression, the substring matching with \1 has no effect. +# DATABASE-USERNAME can be "all", a user name, a group name prefixed with "+", +# or a regular expression (if it starts with a slash (/)). If it is a regular +# expression, no substitution for \1 will occur. # # Multiple maps may be specified in this file and used by pg_hba.conf. # @@ -69,4 +69,4 @@ # Put your actual configuration here # ---------------------------------- -# MAPNAME SYSTEM-USERNAME PG-USERNAME +# MAPNAME SYSTEM-USERNAME DATABASE-USERNAME diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 082b4d9d327..f4c977262c5 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -297,10 +297,12 @@ parse_output_parameters(List *options, PGOutputData *data) bool two_phase_option_given = false; bool origin_option_given = false; + /* Initialize optional parameters to defaults */ data->binary = false; data->streaming = LOGICALREP_STREAM_OFF; data->messages = false; data->two_phase = false; + data->publish_no_origin = false; foreach(lc, options) { diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c index 1ce1bcd57a0..bf8f77e6ff6 100644 --- a/src/backend/storage/aio/method_worker.c +++ b/src/backend/storage/aio/method_worker.c @@ -575,6 +575,12 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len) } CHECK_FOR_INTERRUPTS(); + + if (ConfigReloadPending) + { + ConfigReloadPending = false; + ProcessConfigFile(PGC_SIGHUP); + } } error_context_stack = errcallback.previous; diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index e5b945a9ee3..2418967def6 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -1622,58 +1622,6 @@ TransactionIdIsInProgress(TransactionId xid) return false; } -/* - * TransactionIdIsActive -- is xid the top-level XID of an active backend? - * - * This differs from TransactionIdIsInProgress in that it ignores prepared - * transactions, as well as transactions running on the primary if we're in - * hot standby. Also, we ignore subtransactions since that's not needed - * for current uses. - */ -bool -TransactionIdIsActive(TransactionId xid) -{ - bool result = false; - ProcArrayStruct *arrayP = procArray; - TransactionId *other_xids = ProcGlobal->xids; - int i; - - /* - * Don't bother checking a transaction older than RecentXmin; it could not - * possibly still be running. - */ - if (TransactionIdPrecedes(xid, RecentXmin)) - return false; - - LWLockAcquire(ProcArrayLock, LW_SHARED); - - for (i = 0; i < arrayP->numProcs; i++) - { - int pgprocno = arrayP->pgprocnos[i]; - PGPROC *proc = &allProcs[pgprocno]; - TransactionId pxid; - - /* Fetch xid just once - see GetNewTransactionId */ - pxid = UINT32_ACCESS_ONCE(other_xids[i]); - - if (!TransactionIdIsValid(pxid)) - continue; - - if (proc->pid == 0) - continue; /* ignore prepared transactions */ - - if (TransactionIdEquals(pxid, xid)) - { - result = true; - break; - } - } - - LWLockRelease(ProcArrayLock); - - return result; -} - /* * Determine XID horizons. diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index c8b6c15e059..82b807d067a 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -277,22 +277,16 @@ compareJsonbContainers(JsonbContainer *a, JsonbContainer *b) else { /* - * It's safe to assume that the types differed, and that the va - * and vb values passed were set. - * - * If the two values were of the same container type, then there'd - * have been a chance to observe the variation in the number of - * elements/pairs (when processing WJB_BEGIN_OBJECT, say). They're - * either two heterogeneously-typed containers, or a container and - * some scalar type. - * - * We don't have to consider the WJB_END_ARRAY and WJB_END_OBJECT - * cases here, because we would have seen the corresponding - * WJB_BEGIN_ARRAY and WJB_BEGIN_OBJECT tokens first, and - * concluded that they don't match. + * It's not possible for one iterator to report end of array or + * object while the other one reports something else, because we + * would have detected a length mismatch when we processed the + * container-start tokens above. Likewise we can't see WJB_DONE + * from one but not the other. So we have two different-type + * containers, or a container and some scalar type, or two + * different scalar types. Sort on the basis of the type code. */ - Assert(ra != WJB_END_ARRAY && ra != WJB_END_OBJECT); - Assert(rb != WJB_END_ARRAY && rb != WJB_END_OBJECT); + Assert(ra != WJB_DONE && ra != WJB_END_ARRAY && ra != WJB_END_OBJECT); + Assert(rb != WJB_DONE && rb != WJB_END_ARRAY && rb != WJB_END_OBJECT); Assert(va.type != vb.type); Assert(va.type != jbvBinary); @@ -852,15 +846,20 @@ JsonbIteratorInit(JsonbContainer *container) * It is our job to expand the jbvBinary representation without bothering them * with it. However, clients should not take it upon themselves to touch array * or Object element/pair buffers, since their element/pair pointers are - * garbage. Also, *val will not be set when returning WJB_END_ARRAY or - * WJB_END_OBJECT, on the assumption that it's only useful to access values - * when recursing in. + * garbage. + * + * *val is not meaningful when the result is WJB_DONE, WJB_END_ARRAY or + * WJB_END_OBJECT. However, we set val->type = jbvNull in those cases, + * so that callers may assume that val->type is always well-defined. */ JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) { if (*it == NULL) + { + val->type = jbvNull; return WJB_DONE; + } /* * When stepping into a nested container, we jump back here to start @@ -898,6 +897,7 @@ recurse: * nesting). */ *it = freeAndGetParent(*it); + val->type = jbvNull; return WJB_END_ARRAY; } @@ -951,6 +951,7 @@ recurse: * of nesting). */ *it = freeAndGetParent(*it); + val->type = jbvNull; return WJB_END_OBJECT; } else @@ -995,8 +996,10 @@ recurse: return WJB_VALUE; } - elog(ERROR, "invalid iterator state"); - return -1; + elog(ERROR, "invalid jsonb iterator state"); + /* satisfy compilers that don't know that elog(ERROR) doesn't return */ + val->type = jbvNull; + return WJB_DONE; } /* diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 0a5848a4ab2..25cff56c3d0 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -5312,10 +5312,10 @@ isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday) int date2isoweek(int year, int mon, int mday) { - float8 result; int day0, day4, - dayn; + dayn, + week; /* current day */ dayn = date2j(year, mon, mday); @@ -5338,13 +5338,13 @@ date2isoweek(int year, int mon, int mday) day0 = j2day(day4 - 1); } - result = (dayn - (day4 - day0)) / 7 + 1; + week = (dayn - (day4 - day0)) / 7 + 1; /* * Sometimes the last few days in a year will fall into the first week of * the next year, so check for this. */ - if (result >= 52) + if (week >= 52) { day4 = date2j(year + 1, 1, 4); @@ -5352,10 +5352,10 @@ date2isoweek(int year, int mon, int mday) day0 = j2day(day4 - 1); if (dayn >= day4 - day0) - result = (dayn - (day4 - day0)) / 7 + 1; + week = (dayn - (day4 - day0)) / 7 + 1; } - return (int) result; + return week; } @@ -5367,10 +5367,10 @@ date2isoweek(int year, int mon, int mday) int date2isoyear(int year, int mon, int mday) { - float8 result; int day0, day4, - dayn; + dayn, + week; /* current day */ dayn = date2j(year, mon, mday); @@ -5395,13 +5395,13 @@ date2isoyear(int year, int mon, int mday) year--; } - result = (dayn - (day4 - day0)) / 7 + 1; + week = (dayn - (day4 - day0)) / 7 + 1; /* * Sometimes the last few days in a year will fall into the first week of * the next year, so check for this. */ - if (result >= 52) + if (week >= 52) { day4 = date2j(year + 1, 1, 4); diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 0a55901b14e..0e00d73487c 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -4481,6 +4481,7 @@ SyncVariables(void) char vbuf[32]; const char *server_version; char *service_name; + char *service_file; /* get stuff from connection */ pset.encoding = PQclientEncoding(pset.db); @@ -4500,6 +4501,11 @@ SyncVariables(void) if (service_name) pg_free(service_name); + service_file = get_conninfo_value("servicefile"); + SetVariable(pset.vars, "SERVICEFILE", service_file); + if (service_file) + pg_free(service_file); + /* this bit should match connection_warnings(): */ /* Try to get full text form of version, might include "devel" etc */ server_version = PQparameterStatus(pset.db, "server_version"); @@ -4529,6 +4535,7 @@ UnsyncVariables(void) { SetVariable(pset.vars, "DBNAME", NULL); SetVariable(pset.vars, "SERVICE", NULL); + SetVariable(pset.vars, "SERVICEFILE", NULL); SetVariable(pset.vars, "USER", NULL); SetVariable(pset.vars, "HOST", NULL); SetVariable(pset.vars, "PORT", NULL); diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 6872653c6c8..37524364290 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -4619,10 +4619,14 @@ match_previous_words(int pattern_id, else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny)) COMPLETE_WITH("WITH GRANT OPTION"); /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */ - else if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny)) - COMPLETE_WITH("TO"); - else if (Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny)) - COMPLETE_WITH("FROM"); + else if (Matches("GRANT|REVOKE", MatchAnyN, "ON", MatchAny, MatchAny) && + !TailMatches("FOREIGN", "SERVER") && !TailMatches("LARGE", "OBJECT")) + { + if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny)) + COMPLETE_WITH("TO"); + else + COMPLETE_WITH("FROM"); + } /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny) || diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index ef0b733ebe8..e4877d88e8f 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -53,7 +53,6 @@ extern bool ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc); extern RunningTransactions GetRunningTransactionData(void); extern bool TransactionIdIsInProgress(TransactionId xid); -extern bool TransactionIdIsActive(TransactionId xid); extern TransactionId GetOldestNonRemovableTransactionId(Relation rel); extern TransactionId GetOldestTransactionIdConsideredRunning(void); extern TransactionId GetOldestActiveTransactionId(void); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 09eb79812ac..2a2b10d5a29 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -201,6 +201,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Database-Service", "", 20, offsetof(struct pg_conn, pgservice)}, + {"servicefile", "PGSERVICEFILE", NULL, NULL, + "Database-Service-File", "", 64, + offsetof(struct pg_conn, pgservicefile)}, + {"user", "PGUSER", NULL, NULL, "Database-User", "", 20, offsetof(struct pg_conn, pguser)}, @@ -5062,6 +5066,7 @@ freePGconn(PGconn *conn) free(conn->dbName); free(conn->replication); free(conn->pgservice); + free(conn->pgservicefile); free(conn->pguser); if (conn->pgpass) { @@ -5914,6 +5919,7 @@ static int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) { const char *service = conninfo_getval(options, "service"); + const char *service_fname = conninfo_getval(options, "servicefile"); char serviceFile[MAXPGPATH]; char *env; bool group_found = false; @@ -5933,10 +5939,13 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) return 0; /* - * Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that - * exists). + * First, try the "servicefile" option in connection string. Then, try + * the PGSERVICEFILE environment variable. Finally, check + * ~/.pg_service.conf (if that exists). */ - if ((env = getenv("PGSERVICEFILE")) != NULL) + if (service_fname != NULL) + strlcpy(serviceFile, service_fname, sizeof(serviceFile)); + else if ((env = getenv("PGSERVICEFILE")) != NULL) strlcpy(serviceFile, env, sizeof(serviceFile)); else { @@ -6092,7 +6101,17 @@ parseServiceFile(const char *serviceFile, if (strcmp(key, "service") == 0) { libpq_append_error(errorMessage, - "nested service specifications not supported in service file \"%s\", line %d", + "nested \"service\" specifications not supported in service file \"%s\", line %d", + serviceFile, + linenr); + result = 3; + goto exit; + } + + if (strcmp(key, "servicefile") == 0) + { + libpq_append_error(errorMessage, + "nested \"servicefile\" specifications not supported in service file \"%s\", line %d", serviceFile, linenr); result = 3; @@ -6135,6 +6154,33 @@ parseServiceFile(const char *serviceFile, } exit: + + /* + * If a service has been successfully found, set the "servicefile" option + * if not already set. This matters when we use a default service file or + * PGSERVICEFILE, where we want to be able track the value. + */ + if (*group_found && result == 0) + { + for (i = 0; options[i].keyword; i++) + { + if (strcmp(options[i].keyword, "servicefile") != 0) + continue; + + /* If value is already set, nothing to do */ + if (options[i].val != NULL) + break; + + options[i].val = strdup(serviceFile); + if (options[i].val == NULL) + { + libpq_append_error(errorMessage, "out of memory"); + result = 3; + } + break; + } + } + fclose(f); return result; diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index a6cfd7f5c9d..70c28f2ffca 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -389,6 +389,8 @@ struct pg_conn char *dbName; /* database name */ char *replication; /* connect as the replication standby? */ char *pgservice; /* Postgres service, if any */ + char *pgservicefile; /* path to a service file containing + * service(s) */ char *pguser; /* Postgres username and password, if any */ char *pgpass; char *pgpassfile; /* path to a file containing password(s) */ diff --git a/src/interfaces/libpq/t/006_service.pl b/src/interfaces/libpq/t/006_service.pl index d896558a6cc..797e6232b8f 100644 --- a/src/interfaces/libpq/t/006_service.pl +++ b/src/interfaces/libpq/t/006_service.pl @@ -53,6 +53,13 @@ copy($srvfile_valid, $srvfile_nested) or die "Could not copy $srvfile_valid to $srvfile_nested: $!"; append_to_file($srvfile_nested, 'service=invalid_srv' . $newline); +# Service file with nested "servicefile" defined. +my $srvfile_nested_2 = "$td/pg_service_nested_2.conf"; +copy($srvfile_valid, $srvfile_nested_2) + or die "Could not copy $srvfile_valid to $srvfile_nested_2: $!"; +append_to_file($srvfile_nested_2, + 'servicefile=' . $srvfile_default . $newline); + # Set the fallback directory lookup of the service file to the temporary # directory of this test. PGSYSCONFDIR is used if the service file # defined in PGSERVICEFILE cannot be found, or when a service file is @@ -158,9 +165,77 @@ local $ENV{PGSERVICEFILE} = "$srvfile_empty"; $dummy_node->connect_fails( 'service=my_srv', - 'connection with nested service file', + 'connection with "service" in nested service file', + expected_stderr => + qr/nested "service" specifications not supported in service file/); + + local $ENV{PGSERVICEFILE} = $srvfile_nested_2; + + $dummy_node->connect_fails( + 'service=my_srv', + 'connection with "servicefile" in nested service file', expected_stderr => - qr/nested service specifications not supported in service file/); + qr/nested "servicefile" specifications not supported in service file/ + ); +} + +# Properly escape backslashes in the path, to ensure the generation of +# correct connection strings. +my $srvfile_win_cared = $srvfile_valid; +$srvfile_win_cared =~ s/\\/\\\\/g; + +# Checks that the "servicefile" option works as expected +{ + $dummy_node->connect_ok( + q{service=my_srv servicefile='} . $srvfile_win_cared . q{'}, + 'connection with valid servicefile in connection string', + sql => "SELECT 'connect3_1'", + expected_stdout => qr/connect3_1/); + + # Encode slashes and backslash + my $encoded_srvfile = $srvfile_valid =~ s{([\\/])}{ + $1 eq '/' ? '%2F' : '%5C' + }ger; + + # Additionally encode a colon in servicefile path of Windows + $encoded_srvfile =~ s/:/%3A/g; + + $dummy_node->connect_ok( + 'postgresql:///?service=my_srv&servicefile=' . $encoded_srvfile, + 'connection with valid servicefile in URI', + sql => "SELECT 'connect3_2'", + expected_stdout => qr/connect3_2/); + + local $ENV{PGSERVICE} = 'my_srv'; + $dummy_node->connect_ok( + q{servicefile='} . $srvfile_win_cared . q{'}, + 'connection with PGSERVICE and servicefile in connection string', + sql => "SELECT 'connect3_3'", + expected_stdout => qr/connect3_3/); + + $dummy_node->connect_ok( + 'postgresql://?servicefile=' . $encoded_srvfile, + 'connection with PGSERVICE and servicefile in URI', + sql => "SELECT 'connect3_4'", + expected_stdout => qr/connect3_4/); +} + +# Check that the "servicefile" option takes priority over the PGSERVICEFILE +# environment variable. +{ + local $ENV{PGSERVICEFILE} = 'non-existent-file.conf'; + + $dummy_node->connect_fails( + 'service=my_srv', + 'connection with invalid PGSERVICEFILE', + expected_stderr => + qr/service file "non-existent-file\.conf" not found/); + + $dummy_node->connect_ok( + q{service=my_srv servicefile='} . $srvfile_win_cared . q{'}, + 'connection with both servicefile and PGSERVICEFILE', + sql => "SELECT 'connect4_1'", + expected_stdout => qr/connect4_1/); } $node->teardown_node; diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl index f2320b62c87..c751fbdbaa5 100644 --- a/src/test/authentication/t/003_peer.pl +++ b/src/test/authentication/t/003_peer.pl @@ -171,7 +171,8 @@ test_role( # Test with regular expression in user name map. # Extract the last 3 characters from the system_user -# or the entire system_user (if its length is <= -3). +# or the entire system_user name (if its length is <= 3). +# We trust this will not include any regex metacharacters. my $regex_test_string = substr($system_user, -3); # Success as the system user regular expression matches. @@ -210,12 +211,17 @@ test_role( log_like => [qr/connection authenticated: identity="$system_user" method=peer/]); +# Create target role for \1 tests. +my $mapped_name = "test${regex_test_string}map${regex_test_string}user"; +$node->safe_psql('postgres', "CREATE ROLE $mapped_name LOGIN"); + # Success as the regular expression matches and \1 is replaced in the given # subexpression. -reset_pg_ident($node, 'mypeermap', qq{/^$system_user(.*)\$}, 'test\1mapuser'); +reset_pg_ident($node, 'mypeermap', qq{/^.*($regex_test_string)\$}, + 'test\1map\1user'); test_role( $node, - qq{testmapuser}, + $mapped_name, 'peer', 0, 'with regular expression in user name map with \1 replaced', @@ -224,11 +230,11 @@ test_role( # Success as the regular expression matches and \1 is replaced in the given # subexpression, even if quoted. -reset_pg_ident($node, 'mypeermap', qq{/^$system_user(.*)\$}, - '"test\1mapuser"'); +reset_pg_ident($node, 'mypeermap', qq{/^.*($regex_test_string)\$}, + '"test\1map\1user"'); test_role( $node, - qq{testmapuser}, + $mapped_name, 'peer', 0, 'with regular expression in user name map with quoted \1 replaced', |