mirror of
https://github.com/postgres/postgres.git
synced 2025-07-29 00:03:13 -04:00
Compare commits
8 Commits
30b4955a46
...
9886744a36
Author | SHA1 | Date | |
---|---|---|---|
|
9886744a36 | ||
|
f896057e46 | ||
|
d97ef756af | ||
|
5b1b9bce84 | ||
|
a7be2a6c26 | ||
|
d596736a49 | ||
|
028b15405b | ||
|
8c441c0827 |
@ -4179,7 +4179,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="guc-wal-summary-keep-time" xreflabel="wal_summary_keep_time">
|
||||
<term><varname>wal_summary_keep_time</varname> (<type>boolean</type>)
|
||||
<term><varname>wal_summary_keep_time</varname> (<type>integer</type>)
|
||||
<indexterm>
|
||||
<primary><varname>wal_summary_keep_time</varname> configuration parameter</primary>
|
||||
</indexterm>
|
||||
@ -4198,6 +4198,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
|
||||
know will not be required for future incremental backups.
|
||||
This parameter can only be set in the
|
||||
<filename>postgresql.conf</filename> file or on the server command line.
|
||||
If this value is specified without units, it is taken as minutes.
|
||||
The default is 10 days. If <literal>summarize_wal = off</literal>,
|
||||
existing WAL summaries will not be removed regardless of the value of
|
||||
this parameter, because the WAL summarizer will not run.
|
||||
|
@ -26480,6 +26480,86 @@ SELECT collation for ('foo' COLLATE "de_DE");
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="functions-info-wal-summary">
|
||||
<title>WAL Summarization Information Functions</title>
|
||||
|
||||
<para>
|
||||
The functions shown in <xref linkend="functions-wal-summary"/>
|
||||
print information about the status of WAL summarization.
|
||||
See <xref linkend="guc-summarize-wal" />.
|
||||
</para>
|
||||
|
||||
<table id="functions-wal-summary">
|
||||
<title>WAL Summarization Information Functions</title>
|
||||
<tgroup cols="1">
|
||||
<thead>
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
Function
|
||||
</para>
|
||||
<para>
|
||||
Description
|
||||
</para></entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<indexterm>
|
||||
<primary>pg_available_wal_summaries</primary>
|
||||
</indexterm>
|
||||
<function>pg_available_wal_summaries</function> ()
|
||||
<returnvalue>setof record</returnvalue>
|
||||
( <parameter>tli</parameter> <type>bigint</type>,
|
||||
<parameter>start_lsn</parameter> <type>pg_lsn</type>,
|
||||
<parameter>end_lsn</parameter> <type>pg_lsn</type> )
|
||||
</para>
|
||||
<para>
|
||||
Returns information about the WAL summary files present in the
|
||||
data directory, under <literal>pg_wal/summaries</literal>.
|
||||
One row will be returned per WAL summary file. Each file summarizes
|
||||
WAL on the indicated TLI within the indicated LSN range. This function
|
||||
might be useful to determine whether enough WAL summaries are present
|
||||
on the server to take an incremental backup based on some prior
|
||||
backup whose start LSN is known.
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<indexterm>
|
||||
<primary>pg_wal_summary_contents</primary>
|
||||
</indexterm>
|
||||
<function>pg_wal_summary_contents</function> ( <parameter>tli</parameter> <type>bigint</type>, <parameter>start_lsn</parameter> <type>pg_lsn</type>, <parameter>end_lsn</parameter> <type>pg_lsn</type> )
|
||||
<returnvalue>setof record</returnvalue>
|
||||
( <parameter>relfilenode</parameter> <type>oid</type>,
|
||||
<parameter>reltablespace</parameter> <type>oid</type>,
|
||||
<parameter>reldatabase</parameter> <type>oid</type>,
|
||||
<parameter>relforknumber</parameter> <type>smallint</type>,
|
||||
<parameter>relblocknumber</parameter> <type>bigint</type>,
|
||||
<parameter>is_limit_block</parameter> <type>boolean</type> )
|
||||
</para>
|
||||
<para>
|
||||
Returns one information about the contents of a single WAL summary file
|
||||
identified by TLI and starting and ending LSNs. Each row with
|
||||
<literal>is_limit_block</literal> false indicates that the block
|
||||
identified by the remaining output columns was modified by at least
|
||||
one WAL record within the range of records summarized by this file.
|
||||
Each row with <literal>is_limit_block</literal> true indicates either
|
||||
that (a) the relation fork was truncated to the length given by
|
||||
<literal>relblocknumber</literal> within the relevant range of WAL
|
||||
records or (b) that the relation fork was created or dropped within
|
||||
the relevant range of WAL records; in such cases,
|
||||
<literal>relblocknumber</literal> will be zero.
|
||||
</para></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="functions-admin">
|
||||
|
@ -697,8 +697,8 @@
|
||||
<para>
|
||||
An encoding of some character repertoire. Most older character
|
||||
repertoires only use one encoding form, and so there are no
|
||||
separate names for them (e.g., <literal>LATIN1</literal> is an
|
||||
encoding form applicable to the <literal>LATIN1</literal>
|
||||
separate names for them (e.g., <literal>LATIN2</literal> is an
|
||||
encoding form applicable to the <literal>LATIN2</literal>
|
||||
repertoire). But for example Unicode has the encoding forms
|
||||
<literal>UTF8</literal>, <literal>UTF16</literal>, etc. (not
|
||||
all supported by PostgreSQL). Encoding forms are not exposed
|
||||
|
@ -7116,6 +7116,45 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="libpq-PQchangePassword">
|
||||
<term><function>PQchangePassword</function><indexterm><primary>PQchangePassword</primary></indexterm></term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Changes a <productname>PostgreSQL</productname> password.
|
||||
<synopsis>
|
||||
PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
|
||||
</synopsis>
|
||||
This function uses <function>PQencryptPasswordConn</function>
|
||||
to build and execute the command <literal>ALTER USER ... PASSWORD
|
||||
'...'</literal>, thereby changing the user's password. It exists for
|
||||
the same reason as <function>PQencryptPasswordConn</function>, but
|
||||
is more convenient as it both builds and runs the command for you.
|
||||
<xref linkend="libpq-PQencryptPasswordConn"/> is passed a
|
||||
<symbol>NULL</symbol> for the algorithm argument, hence encryption is
|
||||
done according to the server's <xref linkend="guc-password-encryption"/>
|
||||
setting.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <parameter>user</parameter> and <parameter>passwd</parameter> arguments
|
||||
are the SQL name of the target user, and the new cleartext password.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Returns a <structname>PGresult</structname> pointer representing
|
||||
the result of the <literal>ALTER USER</literal> command, or
|
||||
a null pointer if the routine failed before issuing any command.
|
||||
The <xref linkend="libpq-PQresultStatus"/> function should be called
|
||||
to check the return value for any errors (including the value of a null
|
||||
pointer, in which case it will return
|
||||
<symbol>PGRES_FATAL_ERROR</symbol>). Use
|
||||
<xref linkend="libpq-PQerrorMessage"/> to get more information about
|
||||
such errors.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="libpq-PQencryptPassword">
|
||||
<term><function>PQencryptPassword</function><indexterm><primary>PQencryptPassword</primary></indexterm></term>
|
||||
|
||||
|
@ -130,7 +130,7 @@ $(top_builddir)/src/port/libpgport_srv.a: | submake-libpgport
|
||||
parser/gram.h: parser/gram.y
|
||||
$(MAKE) -C parser gram.h
|
||||
|
||||
storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lwlocknames.txt
|
||||
storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lwlocknames.txt utils/activity/wait_event_names.txt
|
||||
$(MAKE) -C storage/lmgr lwlocknames.h lwlocknames.c
|
||||
|
||||
utils/activity/wait_event_types.h: utils/activity/generate-wait_event_types.pl utils/activity/wait_event_names.txt
|
||||
|
@ -1900,8 +1900,10 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
|
||||
/* Replace varno in all the query structures */
|
||||
query_tree_walker(root->parse, replace_varno_walker, &ctx,
|
||||
QTW_EXAMINE_SORTGROUP);
|
||||
if (root->parse->resultRelation == toRemove->relid)
|
||||
root->parse->resultRelation = toKeep->relid;
|
||||
|
||||
/* See remove_self_joins_one_group() */
|
||||
Assert(root->parse->resultRelation != toRemove->relid);
|
||||
Assert(root->parse->resultRelation != toKeep->relid);
|
||||
|
||||
/* Replace links in the planner info */
|
||||
remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
|
||||
@ -2086,6 +2088,14 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids)
|
||||
{
|
||||
RelOptInfo *inner = root->simple_rel_array[r];
|
||||
|
||||
/*
|
||||
* We don't accept result relation as either source or target relation
|
||||
* of SJE, because result relation has different behavior in
|
||||
* EvalPlanQual() and RETURNING clause.
|
||||
*/
|
||||
if (root->parse->resultRelation == r)
|
||||
continue;
|
||||
|
||||
k = r;
|
||||
|
||||
while ((k = bms_next_member(relids, k)) > 0)
|
||||
@ -2101,6 +2111,9 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids)
|
||||
PlanRowMark *imark = NULL;
|
||||
List *uclauses = NIL;
|
||||
|
||||
if (root->parse->resultRelation == k)
|
||||
continue;
|
||||
|
||||
/* A sanity check: the relations have the same Oid. */
|
||||
Assert(root->simple_rte_array[k]->relid ==
|
||||
root->simple_rte_array[r]->relid);
|
||||
|
@ -39,8 +39,8 @@ s_lock_test: s_lock.c $(top_builddir)/src/common/libpgcommon.a $(top_builddir)/s
|
||||
lwlocknames.c: lwlocknames.h
|
||||
touch $@
|
||||
|
||||
lwlocknames.h: $(top_srcdir)/src/backend/storage/lmgr/lwlocknames.txt generate-lwlocknames.pl
|
||||
$(PERL) $(srcdir)/generate-lwlocknames.pl $<
|
||||
lwlocknames.h: $(top_srcdir)/src/backend/storage/lmgr/lwlocknames.txt $(top_srcdir)/src/backend/utils/activity/wait_event_names.txt generate-lwlocknames.pl
|
||||
$(PERL) $(srcdir)/generate-lwlocknames.pl $^
|
||||
|
||||
check: s_lock_test
|
||||
./s_lock_test
|
||||
|
@ -15,6 +15,7 @@ my $continue = "\n";
|
||||
GetOptions('outdir:s' => \$output_path);
|
||||
|
||||
open my $lwlocknames, '<', $ARGV[0] or die;
|
||||
open my $wait_event_names, '<', $ARGV[1] or die;
|
||||
|
||||
# Include PID in suffix in case parallel make runs this multiple times.
|
||||
my $htmp = "$output_path/lwlocknames.h.tmp$$";
|
||||
@ -30,6 +31,40 @@ print $c $autogen, "\n";
|
||||
|
||||
print $c "const char *const IndividualLWLockNames[] = {";
|
||||
|
||||
#
|
||||
# First, record the predefined LWLocks listed in wait_event_names.txt. We'll
|
||||
# cross-check those with the ones in lwlocknames.txt.
|
||||
#
|
||||
my @wait_event_lwlocks;
|
||||
my $record_lwlocks = 0;
|
||||
|
||||
while (<$wait_event_names>)
|
||||
{
|
||||
chomp;
|
||||
|
||||
# Check for end marker.
|
||||
last if /^# END OF PREDEFINED LWLOCKS/;
|
||||
|
||||
# Skip comments and empty lines.
|
||||
next if /^#/;
|
||||
next if /^\s*$/;
|
||||
|
||||
# Start recording LWLocks when we find the WaitEventLWLock section.
|
||||
if (/^Section: ClassName - WaitEventLWLock$/)
|
||||
{
|
||||
$record_lwlocks = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
# Go to the next line if we are not yet recording LWLocks.
|
||||
next if not $record_lwlocks;
|
||||
|
||||
# Record the LWLock.
|
||||
(my $waiteventname, my $waitevendocsentence) = split(/\t/, $_);
|
||||
push(@wait_event_lwlocks, $waiteventname . "Lock");
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
while (<$lwlocknames>)
|
||||
{
|
||||
chomp;
|
||||
@ -50,6 +85,15 @@ while (<$lwlocknames>)
|
||||
die "lwlocknames.txt not in order" if $lockidx < $lastlockidx;
|
||||
die "lwlocknames.txt has duplicates" if $lockidx == $lastlockidx;
|
||||
|
||||
die "$lockname defined in lwlocknames.txt but missing from "
|
||||
. "wait_event_names.txt"
|
||||
if $i >= scalar @wait_event_lwlocks;
|
||||
die "lists of predefined LWLocks do not match (first mismatch at "
|
||||
. "$wait_event_lwlocks[$i] in wait_event_names.txt and $lockname in "
|
||||
. "lwlocknames.txt)"
|
||||
if $wait_event_lwlocks[$i] ne $lockname;
|
||||
$i++;
|
||||
|
||||
while ($lastlockidx < $lockidx - 1)
|
||||
{
|
||||
++$lastlockidx;
|
||||
@ -63,6 +107,11 @@ while (<$lwlocknames>)
|
||||
print $h "#define $lockname (&MainLWLockArray[$lockidx].lock)\n";
|
||||
}
|
||||
|
||||
die
|
||||
"$wait_event_lwlocks[$i] defined in wait_event_names.txt but missing from "
|
||||
. "lwlocknames.txt"
|
||||
if $i < scalar @wait_event_lwlocks;
|
||||
|
||||
printf $c "\n};\n";
|
||||
print $h "\n";
|
||||
printf $h "#define NUM_INDIVIDUAL_LWLOCKS %s\n", $lastlockidx + 1;
|
||||
|
@ -276,6 +276,10 @@ Extension "Waiting in an extension."
|
||||
# This class of wait events has its own set of C structure, so these are
|
||||
# only used for the documentation.
|
||||
#
|
||||
# NB: Predefined LWLocks (i.e., those declared in lwlocknames.txt) must be
|
||||
# listed in the top section of locks and must be listed in the same order as in
|
||||
# lwlocknames.txt.
|
||||
#
|
||||
|
||||
Section: ClassName - WaitEventLWLock
|
||||
|
||||
@ -326,6 +330,14 @@ NotifyQueueTail "Waiting to update limit on <command>NOTIFY</command> message st
|
||||
WaitEventExtension "Waiting to read or update custom wait events information for extensions."
|
||||
WALSummarizer "Waiting to read or update WAL summarization state."
|
||||
|
||||
#
|
||||
# END OF PREDEFINED LWLOCKS (DO NOT CHANGE THIS LINE)
|
||||
#
|
||||
# Predefined LWLocks (i.e., those declared in lwlocknames.txt) must be listed
|
||||
# in the section above and must be listed in the same order as in
|
||||
# lwlocknames.txt. Other LWLocks must be listed in the section below.
|
||||
#
|
||||
|
||||
XactBuffer "Waiting for I/O on a transaction status SLRU buffer."
|
||||
CommitTsBuffer "Waiting for I/O on a commit timestamp SLRU buffer."
|
||||
SubtransBuffer "Waiting for I/O on a sub-transaction SLRU buffer."
|
||||
|
@ -552,11 +552,11 @@ start_postmaster(void)
|
||||
else
|
||||
close(fd);
|
||||
|
||||
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
|
||||
cmd = psprintf("\"%s\" /D /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
|
||||
comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
|
||||
}
|
||||
else
|
||||
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
|
||||
cmd = psprintf("\"%s\" /D /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
|
||||
comspec, exec_path, pgdata_opt, post_opts, DEVNULL);
|
||||
|
||||
if (!CreateRestrictedProcess(cmd, &pi, false))
|
||||
|
@ -2158,29 +2158,15 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
|
||||
}
|
||||
else
|
||||
{
|
||||
char *encrypted_password;
|
||||
PGresult *res = PQchangePassword(pset.db, user, pw1);
|
||||
|
||||
encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
|
||||
|
||||
if (!encrypted_password)
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
pg_log_info("%s", PQerrorMessage(pset.db));
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ",
|
||||
fmtId(user));
|
||||
appendStringLiteralConn(&buf, encrypted_password, pset.db);
|
||||
res = PSQLexec(buf.data);
|
||||
if (!res)
|
||||
success = false;
|
||||
else
|
||||
PQclear(res);
|
||||
PQfreemem(encrypted_password);
|
||||
}
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
free(user);
|
||||
|
@ -1,7 +1,9 @@
|
||||
# Copyright (c) 2022-2024, PostgreSQL Global Development Group
|
||||
|
||||
lwlocknames = custom_target('lwlocknames',
|
||||
input: files('../../backend/storage/lmgr/lwlocknames.txt'),
|
||||
input: files(
|
||||
'../../backend/storage/lmgr/lwlocknames.txt',
|
||||
'../../backend/utils/activity/wait_event_names.txt'),
|
||||
output: ['lwlocknames.h', 'lwlocknames.c'],
|
||||
command: [
|
||||
perl, files('../../backend/storage/lmgr/generate-lwlocknames.pl'),
|
||||
|
@ -191,3 +191,4 @@ PQclosePrepared 188
|
||||
PQclosePortal 189
|
||||
PQsendClosePrepared 190
|
||||
PQsendClosePortal 191
|
||||
PQchangePassword 192
|
||||
|
@ -1372,3 +1372,84 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
|
||||
|
||||
return crypt_pwd;
|
||||
}
|
||||
|
||||
/*
|
||||
* PQchangePassword -- exported routine to change a password
|
||||
*
|
||||
* This is intended to be used by client applications that wish to
|
||||
* change the password for a user. The password is not sent in
|
||||
* cleartext because it is encrypted on the client side. This is
|
||||
* good because it ensures the cleartext password is never known by
|
||||
* the server, and therefore won't end up in logs, pg_stat displays,
|
||||
* etc. The password encryption is performed by PQencryptPasswordConn(),
|
||||
* which is passed a NULL for the algorithm argument. Hence encryption
|
||||
* is done according to the server's password_encryption
|
||||
* setting. We export the function so that clients won't be dependent
|
||||
* on the implementation specific details with respect to how the
|
||||
* server changes passwords.
|
||||
*
|
||||
* Arguments are a connection object, the SQL name of the target user,
|
||||
* and the cleartext password.
|
||||
*
|
||||
* Return value is the PGresult of the executed ALTER USER statement
|
||||
* or NULL if we never get there. The caller is responsible to PQclear()
|
||||
* the returned PGresult.
|
||||
*
|
||||
* PQresultStatus() should be called to check the return value for errors,
|
||||
* and PQerrorMessage() used to get more information about such errors.
|
||||
*/
|
||||
PGresult *
|
||||
PQchangePassword(PGconn *conn, const char *user, const char *passwd)
|
||||
{
|
||||
char *encrypted_password = PQencryptPasswordConn(conn, passwd,
|
||||
user, NULL);
|
||||
|
||||
if (!encrypted_password)
|
||||
{
|
||||
/* PQencryptPasswordConn() already registered the error */
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *fmtpw = PQescapeLiteral(conn, encrypted_password,
|
||||
strlen(encrypted_password));
|
||||
|
||||
/* no longer needed, so clean up now */
|
||||
PQfreemem(encrypted_password);
|
||||
|
||||
if (!fmtpw)
|
||||
{
|
||||
/* PQescapeLiteral() already registered the error */
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *fmtuser = PQescapeIdentifier(conn, user, strlen(user));
|
||||
|
||||
if (!fmtuser)
|
||||
{
|
||||
/* PQescapeIdentifier() already registered the error */
|
||||
PQfreemem(fmtpw);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PQExpBufferData buf;
|
||||
PGresult *res;
|
||||
|
||||
initPQExpBuffer(&buf);
|
||||
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %s",
|
||||
fmtuser, fmtpw);
|
||||
|
||||
res = PQexec(conn, buf.data);
|
||||
|
||||
/* clean up */
|
||||
termPQExpBuffer(&buf);
|
||||
PQfreemem(fmtuser);
|
||||
PQfreemem(fmtpw);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,6 +659,7 @@ extern int PQenv2encoding(void);
|
||||
|
||||
extern char *PQencryptPassword(const char *passwd, const char *user);
|
||||
extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
|
||||
extern PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
|
||||
|
||||
/* === in encnames.c === */
|
||||
|
||||
|
@ -6868,18 +6868,58 @@ select * from emp1 t1
|
||||
-> Seq Scan on emp1 t3
|
||||
(6 rows)
|
||||
|
||||
-- Check that SJE replaces target relation correctly
|
||||
-- Check that SJE doesn't replace the target relation
|
||||
explain (costs off)
|
||||
WITH t1 AS (SELECT * FROM emp1)
|
||||
UPDATE emp1 SET code = t1.code + 1 FROM t1
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code;
|
||||
QUERY PLAN
|
||||
----------------------------------
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Update on emp1
|
||||
-> Seq Scan on emp1
|
||||
Filter: (id IS NOT NULL)
|
||||
(3 rows)
|
||||
-> Nested Loop
|
||||
-> Seq Scan on emp1
|
||||
-> Index Scan using emp1_pkey on emp1 emp1_1
|
||||
Index Cond: (id = emp1.id)
|
||||
(5 rows)
|
||||
|
||||
INSERT INTO emp1 VALUES (1, 1), (2, 1);
|
||||
WITH t1 AS (SELECT * FROM emp1)
|
||||
UPDATE emp1 SET code = t1.code + 1 FROM t1
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code;
|
||||
id | code | code
|
||||
----+------+------
|
||||
1 | 2 | 1
|
||||
2 | 2 | 1
|
||||
(2 rows)
|
||||
|
||||
TRUNCATE emp1;
|
||||
EXPLAIN (COSTS OFF)
|
||||
UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq.a = sz.a;
|
||||
QUERY PLAN
|
||||
-------------------------------------
|
||||
Update on sj sq
|
||||
-> Nested Loop
|
||||
Join Filter: (sq.a = sz.a)
|
||||
-> Seq Scan on sj sq
|
||||
-> Materialize
|
||||
-> Seq Scan on sj sz
|
||||
(6 rows)
|
||||
|
||||
CREATE RULE sj_del_rule AS ON DELETE TO sj
|
||||
DO INSTEAD
|
||||
UPDATE sj SET a = 1 WHERE a = old.a;
|
||||
EXPLAIN (COSTS OFF) DELETE FROM sj;
|
||||
QUERY PLAN
|
||||
--------------------------------------
|
||||
Update on sj sj_1
|
||||
-> Nested Loop
|
||||
Join Filter: (sj.a = sj_1.a)
|
||||
-> Seq Scan on sj sj_1
|
||||
-> Materialize
|
||||
-> Seq Scan on sj
|
||||
(6 rows)
|
||||
|
||||
DROP RULE sj_del_rule ON sj CASCADE;
|
||||
-- Check that SJE does not mistakenly omit qual clauses (bug #18187)
|
||||
insert into emp1 values (1, 1);
|
||||
explain (costs off)
|
||||
@ -7076,7 +7116,6 @@ ON sj_t1.id = _t2t3t4.id;
|
||||
--
|
||||
-- Test RowMarks-related code
|
||||
--
|
||||
-- TODO: Why this select returns two copies of ctid field? Should we fix it?
|
||||
EXPLAIN (COSTS OFF) -- Both sides have explicit LockRows marks
|
||||
SELECT a1.a FROM sj a1,sj a2 WHERE (a1.a=a2.a) FOR UPDATE;
|
||||
QUERY PLAN
|
||||
@ -7086,27 +7125,6 @@ SELECT a1.a FROM sj a1,sj a2 WHERE (a1.a=a2.a) FOR UPDATE;
|
||||
Filter: (a IS NOT NULL)
|
||||
(3 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) -- A RowMark exists for the table being kept
|
||||
UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq.a = sz.a;
|
||||
QUERY PLAN
|
||||
---------------------------------
|
||||
Update on sj sz
|
||||
-> Seq Scan on sj sz
|
||||
Filter: (a IS NOT NULL)
|
||||
(3 rows)
|
||||
|
||||
CREATE RULE sj_del_rule AS ON DELETE TO sj
|
||||
DO INSTEAD
|
||||
UPDATE sj SET a = 1 WHERE a = old.a;
|
||||
EXPLAIN (COSTS OFF) DELETE FROM sj; -- A RowMark exists for the table being dropped
|
||||
QUERY PLAN
|
||||
---------------------------------
|
||||
Update on sj
|
||||
-> Seq Scan on sj
|
||||
Filter: (a IS NOT NULL)
|
||||
(3 rows)
|
||||
|
||||
DROP RULE sj_del_rule ON sj CASCADE;
|
||||
reset enable_hashjoin;
|
||||
reset enable_mergejoin;
|
||||
--
|
||||
|
@ -2499,13 +2499,16 @@ SELECT * FROM rw_view1;
|
||||
(1 row)
|
||||
|
||||
EXPLAIN (costs off) DELETE FROM rw_view1 WHERE id = 1 AND snoop(data);
|
||||
QUERY PLAN
|
||||
--------------------------------------------------
|
||||
Update on base_tbl
|
||||
-> Index Scan using base_tbl_pkey on base_tbl
|
||||
Index Cond: (id = 1)
|
||||
Filter: ((NOT deleted) AND snoop(data))
|
||||
(4 rows)
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------
|
||||
Update on base_tbl base_tbl_1
|
||||
-> Nested Loop
|
||||
-> Index Scan using base_tbl_pkey on base_tbl base_tbl_1
|
||||
Index Cond: (id = 1)
|
||||
-> Index Scan using base_tbl_pkey on base_tbl
|
||||
Index Cond: (id = 1)
|
||||
Filter: ((NOT deleted) AND snoop(data))
|
||||
(7 rows)
|
||||
|
||||
DELETE FROM rw_view1 WHERE id = 1 AND snoop(data);
|
||||
NOTICE: snooped value: Row 1
|
||||
|
@ -2616,11 +2616,25 @@ select * from emp1 t1
|
||||
inner join emp1 t2 on t1.id = t2.id
|
||||
left join emp1 t3 on t1.id > 1 and t1.id < 2;
|
||||
|
||||
-- Check that SJE replaces target relation correctly
|
||||
-- Check that SJE doesn't replace the target relation
|
||||
explain (costs off)
|
||||
WITH t1 AS (SELECT * FROM emp1)
|
||||
UPDATE emp1 SET code = t1.code + 1 FROM t1
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code;
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code;
|
||||
INSERT INTO emp1 VALUES (1, 1), (2, 1);
|
||||
WITH t1 AS (SELECT * FROM emp1)
|
||||
UPDATE emp1 SET code = t1.code + 1 FROM t1
|
||||
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code;
|
||||
TRUNCATE emp1;
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq.a = sz.a;
|
||||
|
||||
CREATE RULE sj_del_rule AS ON DELETE TO sj
|
||||
DO INSTEAD
|
||||
UPDATE sj SET a = 1 WHERE a = old.a;
|
||||
EXPLAIN (COSTS OFF) DELETE FROM sj;
|
||||
DROP RULE sj_del_rule ON sj CASCADE;
|
||||
|
||||
-- Check that SJE does not mistakenly omit qual clauses (bug #18187)
|
||||
insert into emp1 values (1, 1);
|
||||
@ -2729,19 +2743,9 @@ ON sj_t1.id = _t2t3t4.id;
|
||||
-- Test RowMarks-related code
|
||||
--
|
||||
|
||||
-- TODO: Why this select returns two copies of ctid field? Should we fix it?
|
||||
EXPLAIN (COSTS OFF) -- Both sides have explicit LockRows marks
|
||||
SELECT a1.a FROM sj a1,sj a2 WHERE (a1.a=a2.a) FOR UPDATE;
|
||||
|
||||
EXPLAIN (COSTS OFF) -- A RowMark exists for the table being kept
|
||||
UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq.a = sz.a;
|
||||
|
||||
CREATE RULE sj_del_rule AS ON DELETE TO sj
|
||||
DO INSTEAD
|
||||
UPDATE sj SET a = 1 WHERE a = old.a;
|
||||
EXPLAIN (COSTS OFF) DELETE FROM sj; -- A RowMark exists for the table being dropped
|
||||
DROP RULE sj_del_rule ON sj CASCADE;
|
||||
|
||||
reset enable_hashjoin;
|
||||
reset enable_mergejoin;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user