Compare commits

..

No commits in common. "9886744a361b1b1d376fcad661eb2857829f3fde" and "30b4955a4668887044568743debef804b14418ca" have entirely different histories.

18 changed files with 76 additions and 366 deletions

View File

@ -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>integer</type>)
<term><varname>wal_summary_keep_time</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>wal_summary_keep_time</varname> configuration parameter</primary>
</indexterm>
@ -4198,7 +4198,6 @@ 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.

View File

@ -26480,86 +26480,6 @@ 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">

View File

@ -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>LATIN2</literal> is an
encoding form applicable to the <literal>LATIN2</literal>
separate names for them (e.g., <literal>LATIN1</literal> is an
encoding form applicable to the <literal>LATIN1</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

View File

@ -7116,45 +7116,6 @@ 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>

View File

@ -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 utils/activity/wait_event_names.txt
storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lwlocknames.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

View File

@ -1900,10 +1900,8 @@ 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);
/* See remove_self_joins_one_group() */
Assert(root->parse->resultRelation != toRemove->relid);
Assert(root->parse->resultRelation != toKeep->relid);
if (root->parse->resultRelation == toRemove->relid)
root->parse->resultRelation = toKeep->relid;
/* Replace links in the planner info */
remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
@ -2088,14 +2086,6 @@ 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)
@ -2111,9 +2101,6 @@ 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);

View File

@ -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 $(top_srcdir)/src/backend/utils/activity/wait_event_names.txt generate-lwlocknames.pl
$(PERL) $(srcdir)/generate-lwlocknames.pl $^
lwlocknames.h: $(top_srcdir)/src/backend/storage/lmgr/lwlocknames.txt generate-lwlocknames.pl
$(PERL) $(srcdir)/generate-lwlocknames.pl $<
check: s_lock_test
./s_lock_test

View File

@ -15,7 +15,6 @@ 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$$";
@ -31,40 +30,6 @@ 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;
@ -85,15 +50,6 @@ 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;
@ -107,11 +63,6 @@ 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;

View File

@ -276,10 +276,6 @@ 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
@ -330,14 +326,6 @@ 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."

View File

@ -552,11 +552,11 @@ start_postmaster(void)
else
close(fd);
cmd = psprintf("\"%s\" /D /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
}
else
cmd = psprintf("\"%s\" /D /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts, DEVNULL);
if (!CreateRestrictedProcess(cmd, &pi, false))

View File

@ -2158,15 +2158,29 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
}
else
{
PGresult *res = PQchangePassword(pset.db, user, pw1);
char *encrypted_password;
if (PQresultStatus(res) != PGRES_COMMAND_OK)
encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
if (!encrypted_password)
{
pg_log_info("%s", PQerrorMessage(pset.db));
success = false;
}
else
{
PGresult *res;
PQclear(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);
}
}
free(user);

View File

@ -1,9 +1,7 @@
# Copyright (c) 2022-2024, PostgreSQL Global Development Group
lwlocknames = custom_target('lwlocknames',
input: files(
'../../backend/storage/lmgr/lwlocknames.txt',
'../../backend/utils/activity/wait_event_names.txt'),
input: files('../../backend/storage/lmgr/lwlocknames.txt'),
output: ['lwlocknames.h', 'lwlocknames.c'],
command: [
perl, files('../../backend/storage/lmgr/generate-lwlocknames.pl'),

View File

@ -191,4 +191,3 @@ PQclosePrepared 188
PQclosePortal 189
PQsendClosePrepared 190
PQsendClosePortal 191
PQchangePassword 192

View File

@ -1372,84 +1372,3 @@ 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;
}
}
}
}

View File

@ -659,7 +659,6 @@ 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 === */

View File

@ -6868,58 +6868,18 @@ select * from emp1 t1
-> Seq Scan on emp1 t3
(6 rows)
-- Check that SJE doesn't replace the target relation
-- Check that SJE replaces target relation correctly
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, t1.code;
QUERY PLAN
-------------------------------------------------------
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code;
QUERY PLAN
----------------------------------
Update on emp1
-> Nested Loop
-> Seq Scan on emp1
-> Index Scan using emp1_pkey on emp1 emp1_1
Index Cond: (id = emp1.id)
(5 rows)
-> Seq Scan on emp1
Filter: (id IS NOT NULL)
(3 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)
@ -7116,6 +7076,7 @@ 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
@ -7125,6 +7086,27 @@ 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;
--

View File

@ -2499,16 +2499,13 @@ 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 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)
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)
DELETE FROM rw_view1 WHERE id = 1 AND snoop(data);
NOTICE: snooped value: Row 1

View File

@ -2616,25 +2616,11 @@ 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 doesn't replace the target relation
-- Check that SJE replaces target relation correctly
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, 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;
WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code;
-- Check that SJE does not mistakenly omit qual clauses (bug #18187)
insert into emp1 values (1, 1);
@ -2743,9 +2729,19 @@ 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;