public inbox for [email protected]  
help / color / mirror / Atom feed
Re: TAP test command_fails versus command_fails_like
6+ messages / 5 participants
[nested] [flat]

* Re: TAP test command_fails versus command_fails_like
@ 2025-02-12 13:58  Dagfinn Ilmari Mannsåker <[email protected]>
  0 siblings, 2 replies; 6+ messages in thread

From: Dagfinn Ilmari Mannsåker @ 2025-02-12 13:58 UTC (permalink / raw)
  To: Ashutosh Bapat <[email protected]>; +Cc: Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

Ashutosh Bapat <[email protected]> writes:

> On Wed, Feb 12, 2025 at 10:36 AM Peter Smith <[email protected]> wrote:
>>
>> Hi hackers,
>>
>> Recently, while writing some new TAP tests my colleague inadvertently
>> called the command_fails() subroutine instead of command_fails_like()
>> subroutine. Their parameters are almost the same but
>> command_fails_like() also takes a pattern for checking in the logs.
>> Notice if too many parameters are passed to command_fails they get
>> silently ignored.
>>
>> Because of the mistake, the tests were all bogus because they were no
>> longer testing error messages as was the intention. OTOH, because
>> command failure was expected those tests still would record a "pass"
>> despite the wrong subroutine being used, so there is no evidence that
>> anything is wrong.
>>
>> I wondered if the command_fails() subroutine could have done more to
>> protect users from accidentally shooting themselves. My attached patch
>> does this by ensuring that no "extra" (unexpected) parameters are
>> being passed to command_fails(). It seems more foolproof.
>>
>
> We will need to fix many perl functions this way but I think
> command_fails and command_fails_like or any other pair of similarly
> named functions needs better protection. Looking at
> https://stackoverflow.com/questions/19234209/perl-subroutine-arguments,
> I feel a construct like below is more readable and crisp.
>
> die "Too many arguments for subroutine" unless @_ <= 1;

I would write that as `if @_ > 1`, but otherwise I agree.  This is a
programmer error, not a test failure, so it should use die() (or even
croak(), to report the error from the caller's perspective), not is().

> Another question is whether command_fails and command_fails_like is
> the only pair or there are more which need stricter checks?

If we do this, we should do it across the board for
PostgreSQL::Test::Utils and ::Cluster at least.  Once we bump the
minimum perl version to 5.20 or beyond we should switch to using
function signatures (https://perldoc.perl.org/perlsub#Signatures), which
gives us this checking for free.

> This won't eliminate cases where command_like is used instead of
> command_like_safe, and vice-versa, where logic is slightly different.
> So the question is how far do we go detecting such misuses?

command_like and command_like_safe take exactly the same parameters, the
only difference is how they capture stdout/stderr, so there's no way to
tell which one the caller meant.  What we can detect however is if
command_ok is called when someone meant command_like.

- imari






^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: TAP test command_fails versus command_fails_like
@ 2025-02-12 15:40  Andrew Dunstan <[email protected]>
  parent: Dagfinn Ilmari Mannsåker <[email protected]>
  1 sibling, 1 reply; 6+ messages in thread

From: Andrew Dunstan @ 2025-02-12 15:40 UTC (permalink / raw)
  To: Dagfinn Ilmari Mannsåker <[email protected]>; Ashutosh Bapat <[email protected]>; +Cc: Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>


On 2025-02-12 We 8:58 AM, Dagfinn Ilmari Mannsåker wrote:
>
>> Another question is whether command_fails and command_fails_like is
>> the only pair or there are more which need stricter checks?
> If we do this, we should do it across the board for
> PostgreSQL::Test::Utils and ::Cluster at least.  Once we bump the
> minimum perl version to 5.20 or beyond we should switch to using
> function signatures (https://perldoc.perl.org/perlsub#Signatures), which
> gives us this checking for free.
>

Is there any reason we can't move to 5.20? Are there any buildfarm 
animals using such an old version? 5.20 is now almost 10 years old.


cheers


andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com







^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: TAP test command_fails versus command_fails_like
@ 2025-02-12 16:58  Dagfinn Ilmari Mannsåker <[email protected]>
  parent: Andrew Dunstan <[email protected]>
  0 siblings, 1 reply; 6+ messages in thread

From: Dagfinn Ilmari Mannsåker @ 2025-02-12 16:58 UTC (permalink / raw)
  To: Andrew Dunstan <[email protected]>; +Cc: Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

Andrew Dunstan <[email protected]> writes:

> On 2025-02-12 We 8:58 AM, Dagfinn Ilmari Mannsåker wrote:
>>
>>> Another question is whether command_fails and command_fails_like is
>>> the only pair or there are more which need stricter checks?
>> If we do this, we should do it across the board for
>> PostgreSQL::Test::Utils and ::Cluster at least.  Once we bump the
>> minimum perl version to 5.20 or beyond we should switch to using
>> function signatures (https://perldoc.perl.org/perlsub#Signatures), which
>> gives us this checking for free.
>
> Is there any reason we can't move to 5.20? Are there any buildfarm
> animals using such an old version? 5.20 is now almost 10 years old.

Red Hat Enterprise Linux 7 has Perl 5.16 and is on Extended Lifecycle
Support until 2028-06-30. I don't know how long other distros based on
that (e.g. CentOS, Scientific Linux) are supported, but I can see that
Amazon Linux 2 is almost out of support (2025-06-30).

> cheers
>
> andrew

- ilmari






^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: TAP test command_fails versus command_fails_like
@ 2025-02-12 17:18  Tom Lane <[email protected]>
  parent: Dagfinn Ilmari Mannsåker <[email protected]>
  0 siblings, 0 replies; 6+ messages in thread

From: Tom Lane @ 2025-02-12 17:18 UTC (permalink / raw)
  To: Dagfinn Ilmari Mannsåker <[email protected]>; +Cc: Andrew Dunstan <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

=?utf-8?Q?Dagfinn_Ilmari_Manns=C3=A5ker?= <[email protected]> writes:
> Andrew Dunstan <[email protected]> writes:
>> Is there any reason we can't move to 5.20? Are there any buildfarm
>> animals using such an old version? 5.20 is now almost 10 years old.

> Red Hat Enterprise Linux 7 has Perl 5.16 and is on Extended Lifecycle
> Support until 2028-06-30. I don't know how long other distros based on
> that (e.g. CentOS, Scientific Linux) are supported, but I can see that
> Amazon Linux 2 is almost out of support (2025-06-30).

The oldest perl versions I see in the buildfarm are

 longfin       | 2025-02-12 14:42:03 | configure: using perl 5.14.0
 lapwing       | 2025-02-10 16:24:03 | configure: using perl 5.14.2
 arowana       | 2025-02-12 04:08:19 | configure: using perl 5.16.3
 boa           | 2025-02-12 16:28:51 | configure: using perl 5.16.3
 buri          | 2025-02-11 21:11:11 | configure: using perl 5.16.3
 dhole         | 2025-02-12 06:08:04 | configure: using perl 5.16.3
 rhinoceros    | 2025-02-12 14:52:10 | configure: using perl 5.16.3
 siskin        | 2025-02-11 21:39:44 | configure: using perl 5.16.3
 snakefly      | 2025-02-10 01:00:04 | configure: using perl 5.16.3
 shelduck      | 2025-02-12 12:13:18 | configure: using perl 5.18.2
 topminnow     | 2025-01-27 08:22:18 | configure: using perl 5.20.2
 batfish       | 2025-02-12 15:38:00 | configure: using perl 5.22.1
 cuon          | 2025-02-12 05:08:03 | configure: using perl 5.22.1
 ayu           | 2025-02-12 13:15:26 | configure: using perl 5.24.1
 chimaera      | 2025-02-12 11:14:35 | configure: using perl 5.24.1
 urocryon      | 2025-02-12 02:07:16 | configure: using perl 5.24.1

(This scan did not account for meson-using animals, which I'm assuming
are generally newer.)

longfin is my own animal and is intentionally set up with the oldest
supported Perl version; I'd have no problem moving it to another
version if we decide to move that goalpost.  lapwing I believe we've
already nagged the owner of to update (its flex is museum-grade too).
But it looks like moving past 5.16 would be more problematic.

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* Re: TAP test command_fails versus command_fails_like
@ 2025-02-22 01:27  Peter Smith <[email protected]>
  parent: Dagfinn Ilmari Mannsåker <[email protected]>
  1 sibling, 0 replies; 6+ messages in thread

From: Peter Smith @ 2025-02-22 01:27 UTC (permalink / raw)
  To: Dagfinn Ilmari Mannsåker <[email protected]>; +Cc: Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Thu, Feb 13, 2025 at 12:58 AM Dagfinn Ilmari Mannsåker
<[email protected]> wrote:
>
> Ashutosh Bapat <[email protected]> writes:
>
> > On Wed, Feb 12, 2025 at 10:36 AM Peter Smith <[email protected]> wrote:
> >>
> >> Hi hackers,
> >>
> >> Recently, while writing some new TAP tests my colleague inadvertently
> >> called the command_fails() subroutine instead of command_fails_like()
> >> subroutine. Their parameters are almost the same but
> >> command_fails_like() also takes a pattern for checking in the logs.
> >> Notice if too many parameters are passed to command_fails they get
> >> silently ignored.
> >>
> >> Because of the mistake, the tests were all bogus because they were no
> >> longer testing error messages as was the intention. OTOH, because
> >> command failure was expected those tests still would record a "pass"
> >> despite the wrong subroutine being used, so there is no evidence that
> >> anything is wrong.
> >>
> >> I wondered if the command_fails() subroutine could have done more to
> >> protect users from accidentally shooting themselves. My attached patch
> >> does this by ensuring that no "extra" (unexpected) parameters are
> >> being passed to command_fails(). It seems more foolproof.
> >>
> >
> > We will need to fix many perl functions this way but I think
> > command_fails and command_fails_like or any other pair of similarly
> > named functions needs better protection. Looking at
> > https://stackoverflow.com/questions/19234209/perl-subroutine-arguments,
> > I feel a construct like below is more readable and crisp.
> >
> > die "Too many arguments for subroutine" unless @_ <= 1;
>
> I would write that as `if @_ > 1`, but otherwise I agree.  This is a
> programmer error, not a test failure, so it should use die() (or even
> croak(), to report the error from the caller's perspective), not is().
>
> > Another question is whether command_fails and command_fails_like is
> > the only pair or there are more which need stricter checks?
>
> If we do this, we should do it across the board for
> PostgreSQL::Test::Utils and ::Cluster at least.

Here is a v2 patch covering many more subroutines, using the syntax
that was suggested above.

make check-world passes.

======
Kind Regards,
Peter Smith.
Fujitsu Australia


Attachments:

  [application/octet-stream] v2-0001-Help-prevent-users-from-calling-the-wrong-functio.patch (21.1K, ../../CAHut+Puho_Xa4cKxf+8_4dASpGvbXJT2D4Dwk-JD=NRnP273NQ@mail.gmail.com/2-v2-0001-Help-prevent-users-from-calling-the-wrong-functio.patch)
  download | inline diff:
From 93554e07865945b2f8fdefdae0edfcb229f5e0f5 Mon Sep 17 00:00:00 2001
From: Peter Smith <[email protected]>
Date: Sat, 22 Feb 2025 12:14:11 +1100
Subject: [PATCH v2] Help prevent users from calling the wrong function

---
 src/test/perl/PostgreSQL/Test/Cluster.pm | 59 ++++++++++++++++++++++++++++++++
 src/test/perl/PostgreSQL/Test/Utils.pm   | 23 +++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index f521ad0..cd9de4b 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -201,6 +201,7 @@ Use $node->connstr() if you want a connection string.
 sub port
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_port};
 }
 
@@ -217,6 +218,7 @@ Use $node->connstr() if you want a connection string.
 sub host
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_host};
 }
 
@@ -232,6 +234,7 @@ backups, etc.
 sub basedir
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_basedir};
 }
 
@@ -246,6 +249,7 @@ The name assigned to the node at creation time.
 sub name
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_name};
 }
 
@@ -260,6 +264,7 @@ Path to the PostgreSQL log file for this instance.
 sub logfile
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_logfile};
 }
 
@@ -275,6 +280,7 @@ this node. Suitable for passing to psql, DBD::Pg, etc.
 sub connstr
 {
 	my ($self, $dbname) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $pgport = $self->port;
 	my $pghost = $self->host;
 	if (!defined($dbname))
@@ -302,6 +308,7 @@ used by low-level protocol and connection limit tests.
 sub raw_connect
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $pgport = $self->port;
 	my $pghost = $self->host;
 
@@ -348,6 +355,7 @@ architecture".
 sub raw_connect_works
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 
 	# If we're using Unix domain sockets, we need a working
 	# IO::Socket::UNIX implementation.
@@ -377,6 +385,7 @@ Does the data dir allow group access?
 sub group_access
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 
 	my $dir_stat = stat($self->data_dir);
 
@@ -398,6 +407,7 @@ always here.
 sub data_dir
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $res = $self->basedir;
 	return "$res/pgdata";
 }
@@ -413,6 +423,7 @@ If archiving is enabled, WAL files go here.
 sub archive_dir
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $basedir = $self->basedir;
 	return "$basedir/archives";
 }
@@ -428,6 +439,7 @@ The output path for backups taken with $node->backup()
 sub backup_dir
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $basedir = $self->basedir;
 	return "$basedir/backup";
 }
@@ -443,6 +455,7 @@ The configured install path (if any) for the node.
 sub install_path
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_install_path};
 }
 
@@ -457,6 +470,7 @@ The version number for the node, from PostgreSQL::Version.
 sub pg_version
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return $self->{_pg_version};
 }
 
@@ -477,6 +491,7 @@ If options are supplied, return the list of values.
 sub config_data
 {
 	my ($self, @options) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	local %ENV = $self->_get_env();
 
 	my ($stdout, $stderr);
@@ -516,6 +531,7 @@ about this node.
 sub info
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $_info = '';
 	open my $fh, '>', \$_info or die;
 	print $fh "Name: " . $self->name . "\n";
@@ -543,6 +559,7 @@ Print $node->info()
 sub dump_info
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	print $self->info;
 	return;
 }
@@ -553,6 +570,7 @@ sub dump_info
 sub set_replication_conf
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $pgdata = $self->data_dir;
 
 	$self->host eq $test_pghost
@@ -759,6 +777,7 @@ A newline is automatically appended to the string.
 sub append_conf
 {
 	my ($self, $filename, $str) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 
 	my $conffile = $self->data_dir . '/' . $filename;
 
@@ -787,6 +806,7 @@ responsibility to do that.
 sub adjust_conf
 {
 	my ($self, $filename, $setting, $value, $skip_equals) = @_;
+    die "Too many arguments for subroutine" if @_ > 5;
 
 	my $conffile = $self->data_dir . '/' . $filename;
 
@@ -863,6 +883,7 @@ Use B<backup> if you want to back up a running server.
 sub backup_fs_cold
 {
 	my ($self, $backup_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	PostgreSQL::Test::RecursiveCopy::copypath(
 		$self->data_dir,
@@ -1088,6 +1109,7 @@ will use the new name.  Return the new name.
 sub rotate_logfile
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	$self->{_logfile} = sprintf('%s_%d.log',
 		$self->{_logfile_base},
 		++$self->{_logfile_generation});
@@ -1171,6 +1193,7 @@ this to fail.  Otherwise, tests might fail to detect server crashes.
 sub kill9
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $name = $self->name;
 	return unless defined $self->{_pid};
 
@@ -1246,6 +1269,7 @@ Reload configuration parameters on the node.
 sub reload
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $port = $self->port;
 	my $pgdata = $self->data_dir;
 	my $name = $self->name;
@@ -1312,6 +1336,7 @@ Wrapper for pg_ctl promote
 sub promote
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $port = $self->port;
 	my $pgdata = $self->data_dir;
 	my $logfile = $self->logfile;
@@ -1336,6 +1361,7 @@ Wrapper for pg_ctl logrotate
 sub logrotate
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $port = $self->port;
 	my $pgdata = $self->data_dir;
 	my $logfile = $self->logfile;
@@ -1353,6 +1379,7 @@ sub logrotate
 sub enable_streaming
 {
 	my ($self, $root_node) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $root_connstr = $root_node->connstr;
 	my $name = $self->name;
 
@@ -1369,6 +1396,7 @@ primary_conninfo='$root_connstr'
 sub enable_restoring
 {
 	my ($self, $root_node, $standby) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my $path = $root_node->archive_dir;
 	my $name = $self->name;
 
@@ -1414,6 +1442,7 @@ Place recovery.signal file.
 sub set_recovery_mode
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 
 	$self->append_conf('recovery.signal', '');
 	return;
@@ -1430,6 +1459,7 @@ Place standby.signal file.
 sub set_standby_mode
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 
 	$self->append_conf('standby.signal', '');
 	return;
@@ -1439,6 +1469,7 @@ sub set_standby_mode
 sub enable_archiving
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $path = $self->archive_dir;
 	my $name = $self->name;
 
@@ -1472,6 +1503,7 @@ archive_command = '$copy_command'
 sub _update_pid
 {
 	my ($self, $is_running) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $name = $self->name;
 
 	# If we can open the PID file, read its first line and that's the PID we
@@ -1639,6 +1671,7 @@ sub new
 sub _set_pg_version
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my $inst = $self->{_install_path};
 	my $pg_config = "pg_config";
 
@@ -1759,6 +1792,7 @@ sub _get_env
 sub installed_command
 {
 	my ($self, $cmd) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	# Nodes using alternate installation locations use their installation's
 	# bin/ directory explicitly
@@ -1851,6 +1885,7 @@ sub get_free_port
 sub can_bind
 {
 	my ($host, $port) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $iaddr = inet_aton($host);
 	my $paddr = sockaddr_in($port, $iaddr);
 
@@ -2417,6 +2452,7 @@ sub interactive_psql
 sub _pgbench_make_files
 {
 	my ($self, $files) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my @file_opts;
 
 	if (defined $files)
@@ -2626,6 +2662,7 @@ Returns 1 if successful, 0 if timed out.
 sub poll_query_until
 {
 	my ($self, $dbname, $query, $expected) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 
 	local %ENV = $self->_get_env();
 
@@ -2787,6 +2824,7 @@ sub issues_sql_like
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 
 	my ($self, $cmd, $expected_sql, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 
 	local %ENV = $self->_get_env();
 
@@ -2811,6 +2849,7 @@ Returns the contents of log of the node
 sub log_content
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return PostgreSQL::Test::Utils::slurp_file($self->logfile);
 }
 
@@ -2887,6 +2926,7 @@ Find pattern in logfile of node after offset byte.
 sub log_contains
 {
 	my ($self, $pattern, $offset) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 
 	return PostgreSQL::Test::Utils::slurp_file($self->logfile, $offset) =~
 	  m/$pattern/;
@@ -2929,6 +2969,7 @@ mode must be specified.
 sub lsn
 {
 	my ($self, $mode) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my %modes = (
 		'insert' => 'pg_current_wal_insert_lsn()',
 		'flush' => 'pg_current_wal_flush_lsn()',
@@ -2967,6 +3008,7 @@ Returns the path of the WAL segment written to.
 sub write_wal
 {
 	my ($self, $tli, $lsn, $segment_size, $data) = @_;
+    die "Too many arguments for subroutine" if @_ > 5;
 
 	# Calculate segment number and offset position in segment based on the
 	# input LSN.
@@ -2996,6 +3038,7 @@ Returns the end LSN of the record inserted, in bytes.
 sub emit_wal
 {
 	my ($self, $size) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	return int(
 		$self->safe_psql(
@@ -3011,6 +3054,7 @@ sub emit_wal
 sub _get_insert_lsn
 {
 	my ($self) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	return int(
 		$self->safe_psql(
 			'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'"));
@@ -3033,6 +3077,7 @@ Returns the end LSN up to which WAL has advanced, in bytes.
 sub advance_wal_out_of_record_splitting_zone
 {
 	my ($self, $wal_block_size) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	my $page_threshold = $wal_block_size / 4;
 	my $end_lsn = $self->_get_insert_lsn();
@@ -3060,6 +3105,7 @@ Returns the end LSN up to which WAL has advanced, in bytes.
 sub advance_wal_to_record_splitting_zone
 {
 	my ($self, $wal_block_size) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	# Size of record header.
 	my $RECORD_HEADER_SIZE = 24;
@@ -3115,6 +3161,7 @@ Returns 1 if the extension is available, 0 otherwise.
 sub check_extension
 {
 	my ($self, $extension_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	my $result = $self->safe_psql('postgres',
 		"SELECT count(*) > 0 FROM pg_available_extensions WHERE name = '$extension_name';"
@@ -3134,6 +3181,7 @@ Poll pg_stat_activity until backend_type reaches wait_event_name.
 sub wait_for_event
 {
 	my ($self, $backend_type, $wait_event_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 
 	$self->poll_query_until(
 		'postgres', qq[
@@ -3184,6 +3232,7 @@ This is not a test. It die()s on failure.
 sub wait_for_catchup
 {
 	my ($self, $standby_name, $mode, $target_lsn) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 	$mode = defined($mode) ? $mode : 'replay';
 	my %valid_modes =
 	  ('sent' => 1, 'write' => 1, 'flush' => 1, 'replay' => 1);
@@ -3268,6 +3317,7 @@ This is not a test. It die()s on failure.
 sub wait_for_replay_catchup
 {
 	my ($self, $standby_name, $node) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	$node = defined($node) ? $node : $self;
 
 	$self->wait_for_catchup($standby_name, 'replay', $node->lsn('flush'));
@@ -3294,6 +3344,7 @@ Note that for logical slots, restart_lsn is held down by the oldest in-progress
 sub wait_for_slot_catchup
 {
 	my ($self, $slot_name, $mode, $target_lsn) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 	$mode = defined($mode) ? $mode : 'restart';
 	if (!($mode eq 'restart' || $mode eq 'confirmed_flush'))
 	{
@@ -3343,6 +3394,7 @@ This is not a test. It die()s on failure.
 sub wait_for_subscription_sync
 {
 	my ($self, $publisher, $subname, $dbname) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 	my $name = $self->name;
 
 	$dbname = defined($dbname) ? $dbname : 'postgres';
@@ -3387,6 +3439,7 @@ If successful, returns the length of the entire log file, in bytes.
 sub wait_for_log
 {
 	my ($self, $regexp, $offset) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	$offset = 0 unless defined $offset;
 
 	my $max_attempts = 10 * $PostgreSQL::Test::Utils::timeout_default;
@@ -3471,6 +3524,7 @@ either.
 sub slot
 {
 	my ($self, $slot_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my @columns = (
 		'plugin', 'slot_type', 'datoid', 'database',
 		'active', 'active_pid', 'xmin', 'catalog_xmin',
@@ -3588,6 +3642,7 @@ page_offset had better be a multiple of the cluster's block size.
 sub corrupt_page_checksum
 {
 	my ($self, $file, $page_offset) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my $pgdata = $self->data_dir;
 	my $pageheader;
 
@@ -3616,6 +3671,7 @@ the standby.
 sub log_standby_snapshot
 {
 	my ($self, $standby, $slot_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 
 	# Once the slot's restart_lsn is determined, the standby looks for
 	# xl_running_xacts WAL record from the restart_lsn onwards. First wait
@@ -3645,6 +3701,7 @@ Create logical replication slot on given standby
 sub create_logical_slot_on_standby
 {
 	my ($self, $primary, $slot_name, $dbname) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 	my ($stdout, $stderr);
 
 	my $handle;
@@ -3684,6 +3741,7 @@ time and return it.
 sub validate_slot_inactive_since
 {
 	my ($self, $slot_name, $reference_time) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my $name = $self->name;
 
 	my $inactive_since = $self->safe_psql(
@@ -3716,6 +3774,7 @@ Advance WAL of node by given number of segments.
 sub advance_wal
 {
 	my ($self, $num) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	# Advance by $n segments (= (wal_segment_size * $num) bytes).
 	# pg_switch_wal() forces a WAL flush, making pg_logical_emit_message()
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321..cbb0a21 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -290,6 +290,7 @@ Otherwise the template is C<tmp_test_XXXX>.
 sub tempdir
 {
 	my ($prefix) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	$prefix = "tmp_test" unless defined $prefix;
 	return File::Temp::tempdir(
 		$prefix . '_XXXX',
@@ -418,6 +419,7 @@ The return value is C<($stdout, $stderr)>.
 sub run_command
 {
 	my ($cmd) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my ($stdout, $stderr);
 	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
 	chomp($stdout);
@@ -436,6 +438,7 @@ Pump until string is matched on the specified stream, or timeout occurs.
 sub pump_until
 {
 	my ($proc, $timeout, $stream, $until) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 	$proc->pump_nb();
 	while (1)
 	{
@@ -470,6 +473,7 @@ Generate a string made of the given range of ASCII characters.
 sub generate_ascii_string
 {
 	my ($from_char, $to_char) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $res;
 
 	for my $i ($from_char .. $to_char)
@@ -490,6 +494,7 @@ Return the complete list of entries in the specified directory.
 sub slurp_dir
 {
 	my ($dir) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	opendir(my $dh, $dir)
 	  or croak "could not opendir \"$dir\": $!";
 	my @direntries = readdir $dh;
@@ -509,6 +514,7 @@ offset position if specified.
 sub slurp_file
 {
 	my ($filename, $offset) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	local $/;
 	my $contents;
 	my $fh;
@@ -553,6 +559,7 @@ end of file.)
 sub append_to_file
 {
 	my ($filename, $str) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	open my $fh, ">>", $filename
 	  or croak "could not write \"$filename\": $!";
 	print $fh $str;
@@ -571,6 +578,7 @@ Find and replace string of a given file.
 sub string_replace_file
 {
 	my ($filename, $find, $replace) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	open(my $in, '<', $filename) or croak $!;
 	my $content = '';
 	while (<$in>)
@@ -598,6 +606,7 @@ ignoring files in C<ignore_list> (basename only).
 sub check_mode_recursive
 {
 	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 
 	# Result defaults to true
 	my $result = 1;
@@ -688,6 +697,7 @@ C<chmod> recursively each file and directory within the given directory.
 sub chmod_recursive
 {
 	my ($dir, $dir_mode, $file_mode) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 
 	find(
 		{
@@ -721,6 +731,7 @@ retrieve specific value patterns from the installation's header files.
 sub scan_server_header
 {
 	my ($header_path, $regexp) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 
 	my ($stdout, $stderr);
 	my $result = IPC::Run::run [ 'pg_config', '--includedir-server' ], '>',
@@ -760,6 +771,7 @@ within the installation's C<pg_config.h>.
 sub check_pg_config
 {
 	my ($regexp) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my ($stdout, $stderr);
 	my $result = IPC::Run::run [ 'pg_config', '--includedir' ], '>',
 	  \$stdout, '2>', \$stderr
@@ -787,6 +799,7 @@ function, passed down as-is to File::Compare::compare_text.
 sub compare_files
 {
 	my ($file1, $file2, $testname, $line_comp_function) = @_;
+    die "Too many arguments for subroutine" if @_ > 4;
 
 	# If nothing is given, all lines should be equal.
 	$line_comp_function = sub { $_[0] ne $_[1] }
@@ -862,6 +875,7 @@ sub command_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $result = run_log($cmd);
 	ok($result, $test_name);
 	return;
@@ -879,6 +893,7 @@ sub command_fails
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 2;
 	my $result = run_log($cmd);
 	ok(!$result, $test_name);
 	return;
@@ -896,6 +911,7 @@ sub command_exit_is
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $expected, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
 	my $h = IPC::Run::start $cmd;
 	$h->finish();
@@ -923,6 +939,7 @@ sub program_help_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --help\n");
 	my $result = IPC::Run::run [ $cmd, '--help' ], '>', \$stdout, '2>',
@@ -954,6 +971,7 @@ sub program_version_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --version\n");
 	my $result = IPC::Run::run [ $cmd, '--version' ], '>', \$stdout, '2>',
@@ -977,6 +995,7 @@ sub program_options_handling_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd) = @_;
+    die "Too many arguments for subroutine" if @_ > 1;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --not-a-valid-option\n");
 	my $result = IPC::Run::run [ $cmd, '--not-a-valid-option' ], '>',
@@ -1000,6 +1019,7 @@ sub command_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $expected_stdout, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
 	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
@@ -1027,6 +1047,7 @@ sub command_like_safe
 	# which can fail, causing the process to hang, notably on Msys
 	# when used with 'pg_ctl start'
 	my ($cmd, $expected_stdout, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my ($stdout, $stderr);
 	my $stdoutfile = File::Temp->new();
 	my $stderrfile = File::Temp->new();
@@ -1053,6 +1074,7 @@ sub command_fails_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $expected_stderr, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 3;
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
 	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
@@ -1089,6 +1111,7 @@ sub command_checks_all
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 
 	my ($cmd, $expected_ret, $out, $err, $test_name) = @_;
+    die "Too many arguments for subroutine" if @_ > 5;
 
 	# run command
 	my ($stdout, $stderr);
-- 
1.8.3.1



^ permalink  raw  reply  [nested|flat] 6+ messages in thread

* [PATCH v39 7/8] Add regression tests for Incremental View Maintenance
@ 2026-05-29 09:31  Yugo Nagata <[email protected]>
  0 siblings, 0 replies; 6+ messages in thread

From: Yugo Nagata @ 2026-05-29 09:31 UTC (permalink / raw)

---
 .../isolation/expected/ivm-create-insert.out  | 285 ++++++++
 .../isolation/expected/ivm-create-insert2.out | 305 ++++++++
 .../isolation/expected/ivm-create-insert3.out | 305 ++++++++
 .../isolation/expected/ivm-insert-insert.out  | 497 +++++++++++++
 .../isolation/expected/ivm-insert-insert2.out | 373 ++++++++++
 .../isolation/expected/ivm-insert-insert3.out | 373 ++++++++++
 .../isolation/expected/ivm-refresh-insert.out | 213 ++++++
 .../expected/ivm-refresh-insert2.out          | 191 +++++
 .../expected/ivm-refresh-insert3.out          | 191 +++++
 src/test/isolation/isolation_schedule         |   9 +
 .../isolation/specs/ivm-create-insert.spec    |  47 ++
 .../isolation/specs/ivm-create-insert2.spec   |  54 ++
 .../isolation/specs/ivm-create-insert3.spec   |  54 ++
 .../isolation/specs/ivm-insert-insert.spec    |  62 ++
 .../isolation/specs/ivm-insert-insert2.spec   |  62 ++
 .../isolation/specs/ivm-insert-insert3.spec   |  62 ++
 .../isolation/specs/ivm-refresh-insert.spec   |  46 ++
 .../isolation/specs/ivm-refresh-insert2.spec  |  46 ++
 .../isolation/specs/ivm-refresh-insert3.spec  |  46 ++
 .../regress/expected/incremental_matview.out  | 667 ++++++++++++++++++
 src/test/regress/parallel_schedule            |   1 +
 src/test/regress/sql/incremental_matview.sql  | 407 +++++++++++
 22 files changed, 4296 insertions(+)
 create mode 100644 src/test/isolation/expected/ivm-create-insert.out
 create mode 100644 src/test/isolation/expected/ivm-create-insert2.out
 create mode 100644 src/test/isolation/expected/ivm-create-insert3.out
 create mode 100644 src/test/isolation/expected/ivm-insert-insert.out
 create mode 100644 src/test/isolation/expected/ivm-insert-insert2.out
 create mode 100644 src/test/isolation/expected/ivm-insert-insert3.out
 create mode 100644 src/test/isolation/expected/ivm-refresh-insert.out
 create mode 100644 src/test/isolation/expected/ivm-refresh-insert2.out
 create mode 100644 src/test/isolation/expected/ivm-refresh-insert3.out
 create mode 100644 src/test/isolation/specs/ivm-create-insert.spec
 create mode 100644 src/test/isolation/specs/ivm-create-insert2.spec
 create mode 100644 src/test/isolation/specs/ivm-create-insert3.spec
 create mode 100644 src/test/isolation/specs/ivm-insert-insert.spec
 create mode 100644 src/test/isolation/specs/ivm-insert-insert2.spec
 create mode 100644 src/test/isolation/specs/ivm-insert-insert3.spec
 create mode 100644 src/test/isolation/specs/ivm-refresh-insert.spec
 create mode 100644 src/test/isolation/specs/ivm-refresh-insert2.spec
 create mode 100644 src/test/isolation/specs/ivm-refresh-insert3.spec
 create mode 100644 src/test/regress/expected/incremental_matview.out
 create mode 100644 src/test/regress/sql/incremental_matview.sql

diff --git a/src/test/isolation/expected/ivm-create-insert.out b/src/test/isolation/expected/ivm-create-insert.out
new file mode 100644
index 00000000000..5da00043aba
--- /dev/null
+++ b/src/test/isolation/expected/ivm-create-insert.out
@@ -0,0 +1,285 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 create s2 insert c1 check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 create s2 c1 insert check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 create insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert create c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 create c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 create c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 create check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 create check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-create-insert2.out b/src/test/isolation/expected/ivm-create-insert2.out
new file mode 100644
index 00000000000..907d892c6d4
--- /dev/null
+++ b/src/test/isolation/expected/ivm-create-insert2.out
@@ -0,0 +1,305 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 create s2 insert c1 check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 create s2 c1 insert check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 create insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert create c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s1 s2 create c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 create c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 create check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 create check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-create-insert3.out b/src/test/isolation/expected/ivm-create-insert3.out
new file mode 100644
index 00000000000..907d892c6d4
--- /dev/null
+++ b/src/test/isolation/expected/ivm-create-insert3.out
@@ -0,0 +1,305 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 create s2 insert c1 check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 create s2 c1 insert check2 c2 mv
+step s1: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 create insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert create c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s1 s2 create c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 create c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+ <waiting ...>
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 create check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 create check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+tx1: NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+tx1: WARNING:  inconsistent view can be created in isolation level SERIALIZABLE or REPEATABLE READ
+DETAIL:  The view may not include effects of a concurrent transaction.
+HINT:  The view with incremental maintenance should be created in isolation level READ COMMITTED, or refreshed manually to make sure the view is consistent.
+step create: 
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ng      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-insert-insert.out b/src/test/isolation/expected/ivm-insert-insert.out
new file mode 100644
index 00000000000..5ca4cfea677
--- /dev/null
+++ b/src/test/isolation/expected/ivm-insert-insert.out
@@ -0,0 +1,497 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 update1 s2 update2 c1 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1; <waiting ...>
+step c1: COMMIT;
+step update2: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 update1 s2 c1 update2 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 update2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step update2: UPDATE b SET j = 111 WHERE i = 1; <waiting ...>
+step c1: COMMIT;
+step update2: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update2 update1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step update1: UPDATE a SET j = 11 WHERE i = 1; <waiting ...>
+step c2: COMMIT;
+step update1: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 c1 update2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 update1 c2 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1; <waiting ...>
+step c2: COMMIT;
+step update1: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 c2 update1 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 update2 c2 update1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201); <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201); <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert2 insert1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200); <waiting ...>
+step c2: COMMIT;
+step insert1: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 insert1 c2 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200); <waiting ...>
+step c2: COMMIT;
+step insert1: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert2 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+20|20|200
+20|20|201
+20|21|200
+20|21|201
+21|20|200
+21|20|201
+21|21|200
+21|21|201
+(12 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-insert-insert2.out b/src/test/isolation/expected/ivm-insert-insert2.out
new file mode 100644
index 00000000000..e1c66d4de5e
--- /dev/null
+++ b/src/test/isolation/expected/ivm-insert-insert2.out
@@ -0,0 +1,373 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 update1 s2 update2 c1 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 update1 s2 c1 update2 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 update2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update2 update1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 c1 update2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 update1 c2 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 c2 update1 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 update2 c2 update1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert2 insert1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 insert1 c2 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert2 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-insert-insert3.out b/src/test/isolation/expected/ivm-insert-insert3.out
new file mode 100644
index 00000000000..e1c66d4de5e
--- /dev/null
+++ b/src/test/isolation/expected/ivm-insert-insert3.out
@@ -0,0 +1,373 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 update1 s2 update2 c1 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 update1 s2 c1 update2 check2 c2 mv
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step s2: SELECT;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 update2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update2 update1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 update1 c1 update2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+step c1: COMMIT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+11|11|100
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 update1 c2 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 update2 s1 c2 update1 check1 c1 mv
+step s2: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step s1: SELECT;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 update2 c2 update1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step update2: UPDATE b SET j = 111 WHERE i = 1;
+step c2: COMMIT;
+step update1: UPDATE a SET j = 11 WHERE i = 1;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|111
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 insert1 s2 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step s2: SELECT;
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 insert2 c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert2 insert1 c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert1 c1 insert2 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+step c1: COMMIT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+20|20|200
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 insert1 c2 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c2: COMMIT;
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert2 s1 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step s1: SELECT;
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert2 c2 insert1 check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert2: INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201);
+step c2: COMMIT;
+step insert1: INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200);
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv();
+ x| y|  z
+--+--+---
+10|10|100
+10|11|100
+11|10|100
+11|11|100
+21|21|201
+(5 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-refresh-insert.out b/src/test/isolation/expected/ivm-refresh-insert.out
new file mode 100644
index 00000000000..a925938bf1c
--- /dev/null
+++ b/src/test/isolation/expected/ivm-refresh-insert.out
@@ -0,0 +1,213 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 refresh s2 insert c1 check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 refresh s2 c1 insert check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step insert: INSERT INTO a VALUES (2); <waiting ...>
+step c1: COMMIT;
+step insert: <... completed>
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert refresh c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 refresh c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 refresh check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 refresh check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step check1: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-refresh-insert2.out b/src/test/isolation/expected/ivm-refresh-insert2.out
new file mode 100644
index 00000000000..dc1b16ae34e
--- /dev/null
+++ b/src/test/isolation/expected/ivm-refresh-insert2.out
@@ -0,0 +1,191 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 refresh s2 insert c1 check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 refresh s2 c1 insert check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step insert: INSERT INTO a VALUES (2);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert refresh c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 refresh c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 refresh check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 refresh check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/expected/ivm-refresh-insert3.out b/src/test/isolation/expected/ivm-refresh-insert3.out
new file mode 100644
index 00000000000..dc1b16ae34e
--- /dev/null
+++ b/src/test/isolation/expected/ivm-refresh-insert3.out
@@ -0,0 +1,191 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 refresh s2 insert c1 check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 refresh s2 c1 insert check2 c2 mv
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step s2: SELECT;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh insert c1 check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step insert: INSERT INTO a VALUES (2);
+ERROR:  could not obtain lock on materialized view "mv" during incremental maintenance
+step c1: COMMIT;
+step check2: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+(1 row)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 insert refresh c2 check1 c1 mv
+step s1: SELECT;
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s1 s2 refresh c1 insert check2 c2 mv
+step s1: SELECT;
+step s2: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+step c1: COMMIT;
+step insert: INSERT INTO a VALUES (2);
+step check2: SELECT check_mv();
+check_mv
+--------
+ok      
+(1 row)
+
+step c2: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 refresh c2 check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step refresh: REFRESH MATERIALIZED VIEW mv; <waiting ...>
+step c2: COMMIT;
+step refresh: <... completed>
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 insert s1 c2 refresh check1 c1 mv
+step s2: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step s1: SELECT;
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
+
+starting permutation: s2 s1 insert c2 refresh check1 c1 mv
+step s2: SELECT;
+step s1: SELECT;
+step insert: INSERT INTO a VALUES (2);
+step c2: COMMIT;
+step refresh: REFRESH MATERIALIZED VIEW mv;
+ERROR:  the materialized view is incrementally updated in concurrent transaction
+step check1: SELECT check_mv();
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step c1: COMMIT;
+step mv: SELECT * FROM mv ORDER BY 1,2; SELECT check_mv();
+x|y
+-+-
+1|1
+2|2
+(2 rows)
+
+check_mv
+--------
+ok      
+(1 row)
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..6b7e873b370 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,12 @@ test: matview-write-skew
 test: lock-nowait
 test: for-portion-of
 test: ddl-dependency-locking
+test: ivm-create-insert
+test: ivm-create-insert2
+test: ivm-create-insert3
+test: ivm-insert-insert
+test: ivm-insert-insert2
+test: ivm-insert-insert3
+test: ivm-refresh-insert
+test: ivm-refresh-insert2
+test: ivm-refresh-insert3
diff --git a/src/test/isolation/specs/ivm-create-insert.spec b/src/test/isolation/specs/ivm-create-insert.spec
new file mode 100644
index 00000000000..ccd6fd080a9
--- /dev/null
+++ b/src/test/isolation/specs/ivm-create-insert.spec
@@ -0,0 +1,47 @@
+# Test interaction between concurrent transactions performing
+# create and insert in READ COMMITTED isolation level
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s1	{ SELECT; }
+step create {
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 { SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 create s2 insert c1 check2 c2 mv
+permutation s1 create s2 c1 insert check2 c2 mv
+permutation s1 s2 create insert c1 check2 c2 mv
+permutation s1 s2 insert create c2 check1 c1 mv
+permutation s1 s2 create c1 insert check2 c2 mv
+permutation s2 insert s1 create c2 check1 c1 mv
+permutation s2 insert s1 c2 create check1 c1 mv
+permutation s2 s1 insert c2 create check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-create-insert2.spec b/src/test/isolation/specs/ivm-create-insert2.spec
new file mode 100644
index 00000000000..02cec85ae07
--- /dev/null
+++ b/src/test/isolation/specs/ivm-create-insert2.spec
@@ -0,0 +1,54 @@
+# Test interaction between concurrent transactions performing
+# create and insert in REPEATABLE READ isolation level
+#
+# Note:
+# In this isolation level, it is possible that create_immv could
+# create an inconsistent view not including effects of a concurrent
+# transaction. So, an warning message is raised to suggest using it
+# in READ COMMITTED or executing refresh_immv to make sure to
+# make the view contents consistent.
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s1	{ SELECT; }
+step create {
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 {SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 create s2 insert c1 check2 c2 mv
+permutation s1 create s2 c1 insert check2 c2 mv
+permutation s1 s2 create insert c1 check2 c2 mv
+permutation s1 s2 insert create c2 check1 c1 mv
+permutation s1 s2 create c1 insert check2 c2 mv
+permutation s2 insert s1 create c2 check1 c1 mv
+permutation s2 insert s1 c2 create check1 c1 mv
+permutation s2 s1 insert c2 create check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-create-insert3.spec b/src/test/isolation/specs/ivm-create-insert3.spec
new file mode 100644
index 00000000000..19ce03185a4
--- /dev/null
+++ b/src/test/isolation/specs/ivm-create-insert3.spec
@@ -0,0 +1,54 @@
+# Test interaction between concurrent transactions performing
+# create and insert in SERIALIZABLE isolation level
+#
+# Note:
+# In this isolation level, it is possible that create_immv could
+# create an inconsistent view not including effects of a concurrent
+# transaction. So, an warning message is raised to suggest using it
+# in READ COMMITTED or executing refresh_immv to make sure to
+# make the view contents consistent.
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s1	{ SELECT; }
+step create {
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 {SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 create s2 insert c1 check2 c2 mv
+permutation s1 create s2 c1 insert check2 c2 mv
+permutation s1 s2 create insert c1 check2 c2 mv
+permutation s1 s2 insert create c2 check1 c1 mv
+permutation s1 s2 create c1 insert check2 c2 mv
+permutation s2 insert s1 create c2 check1 c1 mv
+permutation s2 insert s1 c2 create check1 c1 mv
+permutation s2 s1 insert c2 create check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-insert-insert.spec b/src/test/isolation/specs/ivm-insert-insert.spec
new file mode 100644
index 00000000000..b8f794ca191
--- /dev/null
+++ b/src/test/isolation/specs/ivm-insert-insert.spec
@@ -0,0 +1,62 @@
+# Test interaction between concurrent transactions performing
+# table modifications in READ COMMITTED isolation level
+
+setup
+{
+	CREATE TABLE a (i int, j int);
+	CREATE TABLE b (i int, j int);
+	INSERT INTO a VALUES (1,10);
+	INSERT INTO b VALUES (1,100);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE VIEW v(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+	DROP TABLE b;
+}
+
+session tx1
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s1	{ SELECT; }
+step insert1 { INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200); }
+step update1 { UPDATE a SET j = 11 WHERE i = 1; }
+step check1 { SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv(); }
+
+session tx2
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s2 { SELECT; }
+step insert2 { INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201); }
+step update2 { UPDATE b SET j = 111 WHERE i = 1; }
+step check2 { SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 update1 s2 update2 c1 check2 c2 mv
+permutation s1 update1 s2 c1 update2 check2 c2 mv
+permutation s1 s2 update1 update2 c1 check2 c2 mv
+permutation s1 s2 update2 update1 c2 check1 c1 mv
+permutation s1 s2 update1 c1 update2 check2 c2 mv
+permutation s2 update2 s1 update1 c2 check1 c1 mv
+permutation s2 update2 s1 c2 update1 check1 c1 mv
+permutation s2 s1 update2 c2 update1 check1 c1 mv
+
+permutation s1 insert1 s2 insert2 c1 check2 c2 mv
+permutation s1 insert1 s2 c1 insert2 check2 c2 mv
+permutation s1 s2 insert1 insert2 c1 check2 c2 mv
+permutation s1 s2 insert2 insert1 c2 check1 c1 mv
+permutation s1 s2 insert1 c1 insert2 check2 c2 mv
+permutation s2 insert2 s1 insert1 c2 check1 c1 mv
+permutation s2 insert2 s1 c2 insert1 check1 c1 mv
+permutation s2 s1 insert2 c2 insert1 check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-insert-insert2.spec b/src/test/isolation/specs/ivm-insert-insert2.spec
new file mode 100644
index 00000000000..ed086d1ba1e
--- /dev/null
+++ b/src/test/isolation/specs/ivm-insert-insert2.spec
@@ -0,0 +1,62 @@
+# Test interaction between concurrent transactions performing
+# table modifications in REPEATABLE READ isolation level
+
+setup
+{
+	CREATE TABLE a (i int, j int);
+	CREATE TABLE b (i int, j int);
+	INSERT INTO a VALUES (1,10);
+	INSERT INTO b VALUES (1,100);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE VIEW v(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+	DROP TABLE b;
+}
+
+session tx1
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s1	{ SELECT; }
+step insert1 { INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200); }
+step update1 { UPDATE a SET j = 11 WHERE i = 1; }
+step check1 { SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv(); }
+
+session tx2
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s2 { SELECT; }
+step insert2 { INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201); }
+step update2 { UPDATE b SET j = 111 WHERE i = 1; }
+step check2 { SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 update1 s2 update2 c1 check2 c2 mv
+permutation s1 update1 s2 c1 update2 check2 c2 mv
+permutation s1 s2 update1 update2 c1 check2 c2 mv
+permutation s1 s2 update2 update1 c2 check1 c1 mv
+permutation s1 s2 update1 c1 update2 check2 c2 mv
+permutation s2 update2 s1 update1 c2 check1 c1 mv
+permutation s2 update2 s1 c2 update1 check1 c1 mv
+permutation s2 s1 update2 c2 update1 check1 c1 mv
+
+permutation s1 insert1 s2 insert2 c1 check2 c2 mv
+permutation s1 insert1 s2 c1 insert2 check2 c2 mv
+permutation s1 s2 insert1 insert2 c1 check2 c2 mv
+permutation s1 s2 insert2 insert1 c2 check1 c1 mv
+permutation s1 s2 insert1 c1 insert2 check2 c2 mv
+permutation s2 insert2 s1 insert1 c2 check1 c1 mv
+permutation s2 insert2 s1 c2 insert1 check1 c1 mv
+permutation s2 s1 insert2 c2 insert1 check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-insert-insert3.spec b/src/test/isolation/specs/ivm-insert-insert3.spec
new file mode 100644
index 00000000000..b18bd121033
--- /dev/null
+++ b/src/test/isolation/specs/ivm-insert-insert3.spec
@@ -0,0 +1,62 @@
+# Test interaction between concurrent transactions performing
+# table modifications in SERIALIZABLE isolation level
+
+setup
+{
+	CREATE TABLE a (i int, j int);
+	CREATE TABLE b (i int, j int);
+	INSERT INTO a VALUES (1,10);
+	INSERT INTO b VALUES (1,100);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE VIEW v(x,y,z) AS
+		SELECT a1.j, a2.j,b.j FROM a AS a1, a AS a2,b WHERE a1.i = a2.i AND a1.i = b.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+	DROP TABLE b;
+}
+
+session tx1
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s1	{ SELECT; }
+step insert1 { INSERT INTO a VALUES (2,20); INSERT INTO b VALUES (2,200); }
+step update1 { UPDATE a SET j = 11 WHERE i = 1; }
+step check1 { SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2,3; SELECT check_mv(); }
+
+session tx2
+setup	{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s2 { SELECT; }
+step insert2 { INSERT INTO a VALUES (1,11), (2,21); INSERT INTO b VALUES (2,201); }
+step update2 { UPDATE b SET j = 111 WHERE i = 1; }
+step check2 { SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 update1 s2 update2 c1 check2 c2 mv
+permutation s1 update1 s2 c1 update2 check2 c2 mv
+permutation s1 s2 update1 update2 c1 check2 c2 mv
+permutation s1 s2 update2 update1 c2 check1 c1 mv
+permutation s1 s2 update1 c1 update2 check2 c2 mv
+permutation s2 update2 s1 update1 c2 check1 c1 mv
+permutation s2 update2 s1 c2 update1 check1 c1 mv
+permutation s2 s1 update2 c2 update1 check1 c1 mv
+
+permutation s1 insert1 s2 insert2 c1 check2 c2 mv
+permutation s1 insert1 s2 c1 insert2 check2 c2 mv
+permutation s1 s2 insert1 insert2 c1 check2 c2 mv
+permutation s1 s2 insert2 insert1 c2 check1 c1 mv
+permutation s1 s2 insert1 c1 insert2 check2 c2 mv
+permutation s2 insert2 s1 insert1 c2 check1 c1 mv
+permutation s2 insert2 s1 c2 insert1 check1 c1 mv
+permutation s2 s1 insert2 c2 insert1 check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-refresh-insert.spec b/src/test/isolation/specs/ivm-refresh-insert.spec
new file mode 100644
index 00000000000..d7181251962
--- /dev/null
+++ b/src/test/isolation/specs/ivm-refresh-insert.spec
@@ -0,0 +1,46 @@
+# Test interaction between concurrent transactions performing
+# refresh and insert in READ COMMITTED isolation level
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s1	{ SELECT; }
+step refresh { REFRESH MATERIALIZED VIEW mv; }
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 {SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 refresh s2 insert c1 check2 c2 mv
+permutation s1 refresh s2 c1 insert check2 c2 mv
+permutation s1 s2 refresh insert c1 check2 c2 mv
+permutation s1 s2 insert refresh c2 check1 c1 mv
+permutation s1 s2 refresh c1 insert check2 c2 mv
+permutation s2 insert s1 refresh c2 check1 c1 mv
+permutation s2 insert s1 c2 refresh check1 c1 mv
+permutation s2 s1 insert c2 refresh check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-refresh-insert2.spec b/src/test/isolation/specs/ivm-refresh-insert2.spec
new file mode 100644
index 00000000000..f2d1d5013f2
--- /dev/null
+++ b/src/test/isolation/specs/ivm-refresh-insert2.spec
@@ -0,0 +1,46 @@
+# Test interaction between concurrent transactions performing
+# refresh and insert in REPEATABLE READ isolation level
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s1	{ SELECT; }
+step refresh { REFRESH MATERIALIZED VIEW mv; }
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 {SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 refresh s2 insert c1 check2 c2 mv
+permutation s1 refresh s2 c1 insert check2 c2 mv
+permutation s1 s2 refresh insert c1 check2 c2 mv
+permutation s1 s2 insert refresh c2 check1 c1 mv
+permutation s1 s2 refresh c1 insert check2 c2 mv
+permutation s2 insert s1 refresh c2 check1 c1 mv
+permutation s2 insert s1 c2 refresh check1 c1 mv
+permutation s2 s1 insert c2 refresh check1 c1 mv
diff --git a/src/test/isolation/specs/ivm-refresh-insert3.spec b/src/test/isolation/specs/ivm-refresh-insert3.spec
new file mode 100644
index 00000000000..0ec2b1d1de0
--- /dev/null
+++ b/src/test/isolation/specs/ivm-refresh-insert3.spec
@@ -0,0 +1,46 @@
+# Test interaction between concurrent transactions performing
+# refresh and insert in SERIALIZABLE isolation level
+
+setup
+{
+	CREATE TABLE a (i int);
+	INSERT INTO a VALUES (1);
+	CREATE INCREMENTAL MATERIALIZED VIEW mv(x,y) AS SELECT * FROM a a1, a a2 WHERE a1.i = a2.i;
+	CREATE VIEW v(x,y) AS SELECT * FROM a AS a1, a AS a2 WHERE a1.i = a2.i;
+	CREATE FUNCTION check_mv() RETURNS text AS
+		$$ SELECT CASE WHEN count(*) = 0 THEN 'ok' ELSE 'ng' END
+			FROM ((SELECT * FROM mv EXCEPT ALL SELECT * FROM v) UNION ALL
+				  (SELECT * FROM v EXCEPT ALL SELECT * FROM mv)) v $$ LANGUAGE sql;
+}
+
+teardown
+{
+	DROP FUNCTION check_mv();
+	DROP MATERIALIZED VIEW mv;
+	DROP VIEW v;
+	DROP TABLE a;
+}
+
+session tx1
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s1	{ SELECT; }
+step refresh { REFRESH MATERIALIZED VIEW mv; }
+step check1 {SELECT check_mv();}
+step c1 { COMMIT; }
+step mv { SELECT * FROM mv ORDER BY 1,2; SELECT check_mv(); }
+
+session tx2
+setup		{ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; }
+step s2	{ SELECT; }
+step insert { INSERT INTO a VALUES (2); }
+step check2 {SELECT check_mv(); }
+step c2 { COMMIT; }
+
+permutation s1 refresh s2 insert c1 check2 c2 mv
+permutation s1 refresh s2 c1 insert check2 c2 mv
+permutation s1 s2 refresh insert c1 check2 c2 mv
+permutation s1 s2 insert refresh c2 check1 c1 mv
+permutation s1 s2 refresh c1 insert check2 c2 mv
+permutation s2 insert s1 refresh c2 check1 c1 mv
+permutation s2 insert s1 c2 refresh check1 c1 mv
+permutation s2 s1 insert c2 refresh check1 c1 mv
diff --git a/src/test/regress/expected/incremental_matview.out b/src/test/regress/expected/incremental_matview.out
new file mode 100644
index 00000000000..c7fd19fdca3
--- /dev/null
+++ b/src/test/regress/expected/incremental_matview.out
@@ -0,0 +1,667 @@
+-- create a table to use as a basis for views and materialized views in various combinations
+CREATE TABLE mv_base_a (x int, i int, y int, j int);
+CREATE TABLE mv_base_b (x int, i int, y int, k int);
+-- test for base tables with dropped columns
+ALTER TABLE mv_base_a DROP COLUMN x;
+ALTER TABLE mv_base_a DROP COLUMN y;
+ALTER TABLE mv_base_b DROP COLUMN x;
+ALTER TABLE mv_base_b DROP COLUMN y;
+INSERT INTO mv_base_a VALUES
+  (1,10),
+  (2,20),
+  (3,30),
+  (4,40),
+  (5,50);
+INSERT INTO mv_base_b VALUES
+  (1,101),
+  (2,102),
+  (3,103),
+  (4,104);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ERROR:  materialized view "mv_ivm_1" has not been populated
+HINT:  Use the REFRESH MATERIALIZED VIEW command.
+REFRESH MATERIALIZED VIEW mv_ivm_1;
+NOTICE:  could not create an index on materialized view "mv_ivm_1" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 | 10 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+(4 rows)
+
+-- REFRESH WITH NO DATA
+BEGIN;
+CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$
+  BEGIN
+    RETURN NULL;
+  END
+$$ language plpgsql;
+CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT
+ON mv_base_a FROM mv_ivm_1 FOR EACH ROW
+EXECUTE PROCEDURE dummy_ivm_trigger_func();
+SELECT COUNT(*)
+FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid
+WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass;
+ count 
+-------
+    17
+(1 row)
+
+REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA;
+SELECT COUNT(*)
+FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid
+WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass;
+ count 
+-------
+     1
+(1 row)
+
+ROLLBACK;
+-- immediate maintenance
+BEGIN;
+INSERT INTO mv_base_b VALUES(5,105);
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 | 10 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+ 5 | 50 | 105
+(5 rows)
+
+UPDATE mv_base_a SET j = 0 WHERE i = 1;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 |  0 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+ 5 | 50 | 105
+(5 rows)
+
+DELETE FROM mv_base_b WHERE (i,k) = (5,105);
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 |  0 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+(4 rows)
+
+ROLLBACK;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 | 10 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+(4 rows)
+
+-- DISTINCT not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT * FROM mv_base_a;
+ERROR:  DISTINCT is not supported on incrementally maintainable materialized view
+-- test for renaming column name to camel style
+BEGIN;
+ALTER TABLE mv_base_a RENAME i TO "I";
+ALTER TABLE mv_base_a RENAME j TO "J";
+UPDATE mv_base_a SET "J" = 0 WHERE "I" = 1;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ i | j  |  k  
+---+----+-----
+ 1 |  0 | 101
+ 2 | 20 | 102
+ 3 | 30 | 103
+ 4 | 40 | 104
+(4 rows)
+
+ROLLBACK;
+-- TRUNCATE a base table in join views
+BEGIN;
+TRUNCATE mv_base_a;
+SELECT * FROM mv_ivm_1;
+ i | j | k 
+---+---+---
+(0 rows)
+
+ROLLBACK;
+BEGIN;
+TRUNCATE mv_base_b;
+SELECT * FROM mv_ivm_1;
+ i | j | k 
+---+---+---
+(0 rows)
+
+ROLLBACK;
+-- some query syntax
+BEGIN;
+CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql'
+       AS 'SELECT 1' IMMUTABLE;
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func();
+NOTICE:  could not create an index on materialized view "mv_ivm_func" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1;
+NOTICE:  could not create an index on materialized view "mv_ivm_no_tbl" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+ROLLBACK;
+-- result of materialized view have duplicate result.
+BEGIN;
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a;
+NOTICE:  could not create an index on materialized view "mv_ivm_duplicate" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+INSERT INTO mv_base_a VALUES(6,20);
+SELECT * FROM mv_ivm_duplicate ORDER BY 1;
+ j  
+----
+ 10
+ 20
+ 20
+ 30
+ 40
+ 50
+(6 rows)
+
+DELETE FROM mv_base_a WHERE (i,j) = (2,20);
+SELECT * FROM mv_ivm_duplicate ORDER BY 1;
+ j  
+----
+ 10
+ 20
+ 30
+ 40
+ 50
+(5 rows)
+
+ROLLBACK;
+-- aggregate not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i;
+ERROR:  aggregate function is not supported on incrementally maintainable materialized view
+-- support self join view and multiple change on the same table
+BEGIN;
+CREATE TABLE base_t (i int, v int);
+INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS
+ SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i;
+NOTICE:  could not create an index on materialized view "mv_self" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT * FROM mv_self ORDER BY v1;
+ v1 | v2 
+----+----
+ 10 | 10
+ 20 | 20
+ 30 | 30
+(3 rows)
+
+INSERT INTO base_t VALUES (4,40);
+DELETE FROM base_t WHERE i = 1;
+UPDATE base_t SET v = v*10 WHERE i=2;
+SELECT * FROM mv_self ORDER BY v1;
+ v1  | v2  
+-----+-----
+  30 |  30
+  40 |  40
+ 200 | 200
+(3 rows)
+
+WITH
+ ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1),
+ ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1),
+ upd_t AS (UPDATE base_t SET v = v + 100  RETURNING 1),
+ dlt_t AS (DELETE FROM base_t WHERE i IN (4,5)  RETURNING 1)
+SELECT NULL;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT * FROM mv_self ORDER BY v1;
+ v1  | v2  
+-----+-----
+  50 |  50
+  60 |  60
+ 130 | 130
+ 300 | 300
+(4 rows)
+
+--- with sub-transactions
+SAVEPOINT p1;
+INSERT INTO base_t VALUES (7,70);
+RELEASE SAVEPOINT p1;
+INSERT INTO base_t VALUES (7,77);
+SELECT * FROM mv_self ORDER BY v1, v2;
+ v1  | v2  
+-----+-----
+  50 |  50
+  60 |  60
+  70 |  70
+  70 |  77
+  77 |  70
+  77 |  77
+ 130 | 130
+ 300 | 300
+(8 rows)
+
+ROLLBACK;
+-- support simultaneous table changes
+BEGIN;
+CREATE TABLE base_r (i int, v int);
+CREATE TABLE base_s (i int, v int);
+INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30);
+INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300);
+CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS
+ SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i);
+NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT * FROM mv ORDER BY v1;
+ v1 | v2  
+----+-----
+ 10 | 100
+ 20 | 200
+ 30 | 300
+(3 rows)
+
+WITH
+ ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1),
+ ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1),
+ ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1),
+ upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1),
+ dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1)
+SELECT NULL;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT * FROM mv ORDER BY v1;
+  v1  | v2  
+------+-----
+   10 | 100
+   11 | 100
+ 1020 | 200
+ 1020 | 222
+(4 rows)
+
+ROLLBACK;
+-- support foreign reference constraints
+BEGIN;
+CREATE TABLE ri1 (i int PRIMARY KEY);
+CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int);
+INSERT INTO ri1 VALUES (1),(2),(3);
+INSERT INTO ri2 VALUES (1),(2),(3);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS
+ SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i);
+NOTICE:  created index "mv_ri_index" on materialized view "mv_ri"
+SELECT * FROM mv_ri ORDER BY i1;
+ i1 | i2 
+----+----
+  1 |  1
+  2 |  2
+  3 |  3
+(3 rows)
+
+UPDATE ri1 SET i=10 where i=1;
+DELETE FROM ri1 WHERE i=2;
+SELECT * FROM mv_ri ORDER BY i2;
+ i1 | i2 
+----+----
+  3 |  3
+ 10 | 10
+(2 rows)
+
+ROLLBACK;
+-- views including NULL
+BEGIN;
+CREATE TABLE base_t (i int, v int);
+INSERT INTO base_t VALUES (1,10),(2, NULL);
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t;
+NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT * FROM mv ORDER BY i;
+ i | v  
+---+----
+ 1 | 10
+ 2 |   
+(2 rows)
+
+UPDATE base_t SET v = 20 WHERE i = 2;
+SELECT * FROM mv ORDER BY i;
+ i | v  
+---+----
+ 1 | 10
+ 2 | 20
+(2 rows)
+
+ROLLBACK;
+BEGIN;
+CREATE TABLE base_t (i int);
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t;
+NOTICE:  could not create an index on materialized view "mv" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT * FROM mv ORDER BY i;
+ i 
+---
+(0 rows)
+
+INSERT INTO base_t VALUES (1),(NULL);
+SELECT * FROM mv ORDER BY i;
+ i 
+---
+ 1
+  
+(2 rows)
+
+ROLLBACK;
+-- IMMV containing user defined type
+BEGIN;
+CREATE TYPE mytype;
+CREATE FUNCTION mytype_in(cstring)
+ RETURNS mytype AS 'int4in'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+NOTICE:  return type mytype is only a shell
+CREATE FUNCTION mytype_out(mytype)
+ RETURNS cstring AS 'int4out'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+NOTICE:  argument type mytype is only a shell
+LINE 1: CREATE FUNCTION mytype_out(mytype)
+                                   ^
+CREATE TYPE mytype (
+ LIKE = int4,
+ INPUT = mytype_in,
+ OUTPUT = mytype_out
+);
+CREATE FUNCTION mytype_eq(mytype, mytype)
+ RETURNS bool AS 'int4eq'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE FUNCTION mytype_lt(mytype, mytype)
+ RETURNS bool AS 'int4lt'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE FUNCTION mytype_cmp(mytype, mytype)
+ RETURNS integer AS 'btint4cmp'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE OPERATOR = (
+ leftarg = mytype, rightarg = mytype,
+ procedure = mytype_eq);
+CREATE OPERATOR < (
+ leftarg = mytype, rightarg = mytype,
+ procedure = mytype_lt);
+CREATE OPERATOR CLASS mytype_ops
+ DEFAULT FOR TYPE mytype USING btree AS
+ OPERATOR        1       <,
+ OPERATOR        3       = ,
+ FUNCTION		1		mytype_cmp(mytype,mytype);
+CREATE TABLE t_mytype (x mytype);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS
+ SELECT * FROM t_mytype;
+NOTICE:  could not create an index on materialized view "mv_mytype" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+INSERT INTO t_mytype VALUES ('1'::mytype);
+SELECT * FROM mv_mytype;
+ x 
+---
+ 1
+(1 row)
+
+ROLLBACK;
+-- outer join is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i;
+ERROR:  OUTER JOIN is not supported on incrementally maintainable materialized view
+-- CTE is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS
+    WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i;
+ERROR:  CTE is not supported on incrementally maintainable materialized view
+-- contain system column
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a;
+ERROR:  system column is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610';
+ERROR:  system column is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a;
+ERROR:  system column is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a;
+ERROR:  system column is not supported on incrementally maintainable materialized view
+-- contain subquery
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 );
+ERROR:  subquery is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i;
+ERROR:  subquery is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a;
+ERROR:  subquery is not supported on incrementally maintainable materialized view
+-- contain ORDER BY
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k;
+ERROR:  ORDER BY clause is not supported on incrementally maintainable materialized view
+-- contain HAVING
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5;
+ERROR:   HAVING clause is not supported on incrementally maintainable materialized view
+-- contain view or materialized view
+CREATE VIEW b_view AS SELECT i,k FROM mv_base_b;
+CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i;
+ERROR:  VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i;
+ERROR:  VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i;
+ERROR:  subquery is not supported on incrementally maintainable materialized view
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5;
+ERROR:  subquery is not supported on incrementally maintainable materialized view
+-- contain mutable functions
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int;
+ERROR:  mutable function is not supported on incrementally maintainable materialized view
+HINT:  functions must be marked IMMUTABLE
+-- LIMIT/OFFSET is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5;
+ERROR:  LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view
+-- DISTINCT ON is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a;
+ERROR:  DISTINCT is not supported on incrementally maintainable materialized view
+-- TABLESAMPLE clause is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50);
+ERROR:  TABLESAMPLE clause is not supported on incrementally maintainable materialized view
+-- window functions are not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a;
+ERROR:  window functions are not supported on incrementally maintainable materialized view
+-- inheritance parent is not supported
+BEGIN;
+CREATE TABLE parent (i int, v int);
+CREATE TABLE child_a(options text) INHERITS(parent);
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm21 AS SELECT * FROM parent;
+ERROR:  inheritance parent is not supported on incrementally maintainable materialized view
+ROLLBACK;
+-- UNION statement is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;;
+ERROR:  UNION/INTERSECT/EXCEPT statements are not supported on incrementally maintainable materialized view
+-- empty target list is not allowed with IVM
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm25 AS SELECT FROM mv_base_a;
+ERROR:  empty target list is not supported on incrementally maintainable materialized view
+-- FOR UPDATE/SHARE is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE;
+ERROR:  FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view
+-- tartget list cannot contain ivm column that start with '__ivm'
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a;
+ERROR:  column name __ivm_count__ is not supported on incrementally maintainable materialized view
+-- expressions specified in GROUP BY must appear in the target list.
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i;
+ERROR:  aggregate function is not supported on incrementally maintainable materialized view
+-- VALUES is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1);
+ERROR:  VALUES is not supported on incrementally maintainable materialized view
+-- views containing base tables with Row Level Security
+DROP USER IF EXISTS regress_ivm_admin;
+NOTICE:  role "regress_ivm_admin" does not exist, skipping
+DROP USER IF EXISTS regress_ivm_user;
+NOTICE:  role "regress_ivm_user" does not exist, skipping
+CREATE USER regress_ivm_admin;
+CREATE USER regress_ivm_user;
+--- create a table with RLS
+SET SESSION AUTHORIZATION regress_ivm_admin;
+CREATE TABLE rls_tbl(id int, data text, owner name);
+INSERT INTO rls_tbl VALUES
+  (1,'foo','regress_ivm_user'),
+  (2,'bar','postgres');
+CREATE TABLE num_tbl(id int, num text);
+INSERT INTO num_tbl VALUES
+  (1,'one'),
+  (2,'two'),
+  (3,'three'),
+  (4,'four'),
+  (5,'five'),
+  (6,'six');
+--- Users can access only their own rows
+CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user);
+ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
+GRANT ALL on rls_tbl TO PUBLIC;
+GRANT ALL on num_tbl TO PUBLIC;
+--- create a view owned by regress_ivm_user
+SET SESSION AUTHORIZATION regress_ivm_user;
+CREATE INCREMENTAL MATERIALIZED VIEW  ivm_rls AS SELECT * FROM rls_tbl;
+NOTICE:  could not create an index on materialized view "ivm_rls" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+ id | data |      owner       
+----+------+------------------
+  1 | foo  | regress_ivm_user
+(1 row)
+
+RESET SESSION AUTHORIZATION;
+--- inserts rows owned by different users
+INSERT INTO rls_tbl VALUES
+  (3,'baz','regress_ivm_user'),
+  (4,'qux','postgres');
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+ id | data |      owner       
+----+------+------------------
+  1 | foo  | regress_ivm_user
+  3 | baz  | regress_ivm_user
+(2 rows)
+
+--- combination of diffent kinds of commands
+WITH
+ i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
+ u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
+ u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2)
+SELECT;
+--
+(1 row)
+
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+ id | data  |      owner       
+----+-------+------------------
+  2 | bar   | regress_ivm_user
+  3 | baz   | regress_ivm_user
+  6 | corge | regress_ivm_user
+(3 rows)
+
+---
+SET SESSION AUTHORIZATION regress_ivm_user;
+CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id);
+NOTICE:  could not create an index on materialized view "ivm_rls2" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+RESET SESSION AUTHORIZATION;
+WITH
+ x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)),
+ y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4))
+SELECT;
+--
+(1 row)
+
+SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
+ id | data  |      owner       |   num   
+----+-------+------------------+---------
+  2 | bar   | regress_ivm_user | two
+  3 | baz_2 | regress_ivm_user | three_2
+  6 | corge | regress_ivm_user | six
+(3 rows)
+
+-- trigger updating the same table
+BEGIN;
+CREATE TABLE tbl_update_same (i int);
+CREATE FUNCTION func_update_same() RETURNS TRIGGER AS
+ $$ BEGIN UPDATE tbl_update_same SET i = i + 1; RETURN NEW; END; $$
+ LANGUAGE plpgsql;
+CREATE TRIGGER trig_update_same AFTER INSERT ON tbl_update_same FOR EACH ROW
+ EXECUTE FUNCTION func_update_same();
+CREATE INCREMENTAL MATERIALIZED VIEW mv_update_same AS SELECT * FROM tbl_update_same;
+NOTICE:  could not create an index on materialized view "mv_update_same" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+INSERT INTO tbl_update_same VALUES (1);
+SELECT * FROM mv_update_same;
+ i 
+---
+ 2
+(1 row)
+
+-- self-referential FKs
+CREATE TABLE tbl_self_ref (a int primary key,
+						   b int references tbl_self_ref(a) ON UPDATE CASCADE);
+INSERT INTO tbl_self_ref VALUES (1, null), (2, 1), (3, 2);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_self_ref AS SELECT * FROM tbl_self_ref;
+NOTICE:  created index "mv_self_ref_index" on materialized view "mv_self_ref"
+UPDATE tbl_self_ref set a = a + 10;
+SELECT * FROM mv_self_ref ORDER BY a;
+ a  | b  
+----+----
+ 11 |   
+ 12 | 11
+ 13 | 12
+(3 rows)
+
+ROLLBACK;
+-- automatic index creation
+BEGIN;
+CREATE TABLE base_a (i int primary key, j int);
+CREATE TABLE base_b (i int primary key, j int);
+--- with all pkey columns: create an index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b;
+NOTICE:  created index "mv_idx3_index" on materialized view "mv_idx3"
+--- missing some pkey columns: no index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a;
+NOTICE:  could not create an index on materialized view "mv_idx4" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b;
+NOTICE:  could not create an index on materialized view "mv_idx5" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+--- with set-returning function: no index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx6 AS SELECT i FROM base_a, generate_series(1,10);
+NOTICE:  could not create an index on materialized view "mv_idx6" automatically
+DETAIL:  This target list does not have all the primary key columns. 
+HINT:  Create an index on the materialized view for efficient incremental maintenance.
+ROLLBACK;
+-- type that doesn't have default operator class for access method btree
+BEGIN;
+CREATE TABLE table_json (j json);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_json AS SELECT * from table_json;
+ERROR:  data type json has no default operator class for access method "btree"
+ROLLBACK;
+-- cleanup
+DROP TABLE rls_tbl CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to materialized view ivm_rls
+drop cascades to materialized view ivm_rls2
+DROP TABLE num_tbl CASCADE;
+DROP USER regress_ivm_user;
+DROP USER regress_ivm_admin;
+DROP TABLE mv_base_b CASCADE;
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to materialized view mv_ivm_1
+drop cascades to view b_view
+drop cascades to materialized view b_mview
+DROP TABLE mv_base_a CASCADE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8fa0a6c47fb..7caca5f64fd 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -77,6 +77,7 @@ test: brin_bloom brin_multi
 # Another group of parallel tests
 # ----------
 test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions nls sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps generated_virtual
+test: incremental_matview
 
 # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
 # psql depends on create_am
diff --git a/src/test/regress/sql/incremental_matview.sql b/src/test/regress/sql/incremental_matview.sql
new file mode 100644
index 00000000000..7313ad82f3e
--- /dev/null
+++ b/src/test/regress/sql/incremental_matview.sql
@@ -0,0 +1,407 @@
+-- create a table to use as a basis for views and materialized views in various combinations
+CREATE TABLE mv_base_a (x int, i int, y int, j int);
+CREATE TABLE mv_base_b (x int, i int, y int, k int);
+-- test for base tables with dropped columns
+ALTER TABLE mv_base_a DROP COLUMN x;
+ALTER TABLE mv_base_a DROP COLUMN y;
+ALTER TABLE mv_base_b DROP COLUMN x;
+ALTER TABLE mv_base_b DROP COLUMN y;
+INSERT INTO mv_base_a VALUES
+  (1,10),
+  (2,20),
+  (3,30),
+  (4,40),
+  (5,50);
+INSERT INTO mv_base_b VALUES
+  (1,101),
+  (2,102),
+  (3,103),
+  (4,104);
+
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+REFRESH MATERIALIZED VIEW mv_ivm_1;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+
+-- REFRESH WITH NO DATA
+BEGIN;
+CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$
+  BEGIN
+    RETURN NULL;
+  END
+$$ language plpgsql;
+
+CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT
+ON mv_base_a FROM mv_ivm_1 FOR EACH ROW
+EXECUTE PROCEDURE dummy_ivm_trigger_func();
+
+SELECT COUNT(*)
+FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid
+WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass;
+
+REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA;
+
+SELECT COUNT(*)
+FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid
+WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass;
+ROLLBACK;
+
+-- immediate maintenance
+BEGIN;
+INSERT INTO mv_base_b VALUES(5,105);
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+UPDATE mv_base_a SET j = 0 WHERE i = 1;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+DELETE FROM mv_base_b WHERE (i,k) = (5,105);
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ROLLBACK;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+
+-- DISTINCT not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT * FROM mv_base_a;
+
+-- test for renaming column name to camel style
+BEGIN;
+ALTER TABLE mv_base_a RENAME i TO "I";
+ALTER TABLE mv_base_a RENAME j TO "J";
+UPDATE mv_base_a SET "J" = 0 WHERE "I" = 1;
+SELECT * FROM mv_ivm_1 ORDER BY 1,2,3;
+ROLLBACK;
+
+
+-- TRUNCATE a base table in join views
+BEGIN;
+TRUNCATE mv_base_a;
+SELECT * FROM mv_ivm_1;
+ROLLBACK;
+
+BEGIN;
+TRUNCATE mv_base_b;
+SELECT * FROM mv_ivm_1;
+ROLLBACK;
+
+-- some query syntax
+BEGIN;
+CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql'
+       AS 'SELECT 1' IMMUTABLE;
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func();
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1;
+ROLLBACK;
+
+-- result of materialized view have duplicate result.
+BEGIN;
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a;
+INSERT INTO mv_base_a VALUES(6,20);
+SELECT * FROM mv_ivm_duplicate ORDER BY 1;
+DELETE FROM mv_base_a WHERE (i,j) = (2,20);
+SELECT * FROM mv_ivm_duplicate ORDER BY 1;
+ROLLBACK;
+
+-- aggregate not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i;
+
+-- support self join view and multiple change on the same table
+BEGIN;
+CREATE TABLE base_t (i int, v int);
+INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS
+ SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i;
+SELECT * FROM mv_self ORDER BY v1;
+INSERT INTO base_t VALUES (4,40);
+DELETE FROM base_t WHERE i = 1;
+UPDATE base_t SET v = v*10 WHERE i=2;
+SELECT * FROM mv_self ORDER BY v1;
+WITH
+ ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1),
+ ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1),
+ upd_t AS (UPDATE base_t SET v = v + 100  RETURNING 1),
+ dlt_t AS (DELETE FROM base_t WHERE i IN (4,5)  RETURNING 1)
+SELECT NULL;
+SELECT * FROM mv_self ORDER BY v1;
+
+--- with sub-transactions
+SAVEPOINT p1;
+INSERT INTO base_t VALUES (7,70);
+RELEASE SAVEPOINT p1;
+INSERT INTO base_t VALUES (7,77);
+SELECT * FROM mv_self ORDER BY v1, v2;
+
+ROLLBACK;
+
+-- support simultaneous table changes
+BEGIN;
+CREATE TABLE base_r (i int, v int);
+CREATE TABLE base_s (i int, v int);
+INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30);
+INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300);
+CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS
+ SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i);
+SELECT * FROM mv ORDER BY v1;
+WITH
+ ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1),
+ ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1),
+ ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1),
+ upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1),
+ dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1)
+SELECT NULL;
+SELECT * FROM mv ORDER BY v1;
+ROLLBACK;
+
+-- support foreign reference constraints
+BEGIN;
+CREATE TABLE ri1 (i int PRIMARY KEY);
+CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int);
+INSERT INTO ri1 VALUES (1),(2),(3);
+INSERT INTO ri2 VALUES (1),(2),(3);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS
+ SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i);
+SELECT * FROM mv_ri ORDER BY i1;
+UPDATE ri1 SET i=10 where i=1;
+DELETE FROM ri1 WHERE i=2;
+SELECT * FROM mv_ri ORDER BY i2;
+ROLLBACK;
+
+-- views including NULL
+BEGIN;
+CREATE TABLE base_t (i int, v int);
+INSERT INTO base_t VALUES (1,10),(2, NULL);
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t;
+SELECT * FROM mv ORDER BY i;
+UPDATE base_t SET v = 20 WHERE i = 2;
+SELECT * FROM mv ORDER BY i;
+ROLLBACK;
+
+BEGIN;
+CREATE TABLE base_t (i int);
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t;
+SELECT * FROM mv ORDER BY i;
+INSERT INTO base_t VALUES (1),(NULL);
+SELECT * FROM mv ORDER BY i;
+ROLLBACK;
+
+-- IMMV containing user defined type
+BEGIN;
+
+CREATE TYPE mytype;
+CREATE FUNCTION mytype_in(cstring)
+ RETURNS mytype AS 'int4in'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE FUNCTION mytype_out(mytype)
+ RETURNS cstring AS 'int4out'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE TYPE mytype (
+ LIKE = int4,
+ INPUT = mytype_in,
+ OUTPUT = mytype_out
+);
+
+CREATE FUNCTION mytype_eq(mytype, mytype)
+ RETURNS bool AS 'int4eq'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE FUNCTION mytype_lt(mytype, mytype)
+ RETURNS bool AS 'int4lt'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+CREATE FUNCTION mytype_cmp(mytype, mytype)
+ RETURNS integer AS 'btint4cmp'
+ LANGUAGE INTERNAL STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+ leftarg = mytype, rightarg = mytype,
+ procedure = mytype_eq);
+CREATE OPERATOR < (
+ leftarg = mytype, rightarg = mytype,
+ procedure = mytype_lt);
+
+CREATE OPERATOR CLASS mytype_ops
+ DEFAULT FOR TYPE mytype USING btree AS
+ OPERATOR        1       <,
+ OPERATOR        3       = ,
+ FUNCTION		1		mytype_cmp(mytype,mytype);
+
+CREATE TABLE t_mytype (x mytype);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS
+ SELECT * FROM t_mytype;
+INSERT INTO t_mytype VALUES ('1'::mytype);
+SELECT * FROM mv_mytype;
+
+ROLLBACK;
+
+-- outer join is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i;
+-- CTE is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv AS
+    WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i;
+-- contain system column
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610';
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a;
+-- contain subquery
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 );
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a;
+-- contain ORDER BY
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k;
+-- contain HAVING
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5;
+
+-- contain view or materialized view
+CREATE VIEW b_view AS SELECT i,k FROM mv_base_b;
+CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i;
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5;
+
+-- contain mutable functions
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int;
+
+-- LIMIT/OFFSET is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5;
+
+-- DISTINCT ON is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a;
+
+-- TABLESAMPLE clause is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50);
+
+-- window functions are not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a;
+
+-- inheritance parent is not supported
+BEGIN;
+CREATE TABLE parent (i int, v int);
+CREATE TABLE child_a(options text) INHERITS(parent);
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm21 AS SELECT * FROM parent;
+ROLLBACK;
+
+-- UNION statement is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;;
+
+-- empty target list is not allowed with IVM
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm25 AS SELECT FROM mv_base_a;
+
+-- FOR UPDATE/SHARE is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE;
+
+-- tartget list cannot contain ivm column that start with '__ivm'
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a;
+
+-- expressions specified in GROUP BY must appear in the target list.
+CREATE INCREMENTAL MATERIALIZED VIEW  mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i;
+
+-- VALUES is not supported
+CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1);
+
+-- views containing base tables with Row Level Security
+DROP USER IF EXISTS regress_ivm_admin;
+DROP USER IF EXISTS regress_ivm_user;
+CREATE USER regress_ivm_admin;
+CREATE USER regress_ivm_user;
+
+--- create a table with RLS
+SET SESSION AUTHORIZATION regress_ivm_admin;
+CREATE TABLE rls_tbl(id int, data text, owner name);
+INSERT INTO rls_tbl VALUES
+  (1,'foo','regress_ivm_user'),
+  (2,'bar','postgres');
+CREATE TABLE num_tbl(id int, num text);
+INSERT INTO num_tbl VALUES
+  (1,'one'),
+  (2,'two'),
+  (3,'three'),
+  (4,'four'),
+  (5,'five'),
+  (6,'six');
+
+--- Users can access only their own rows
+CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user);
+ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
+GRANT ALL on rls_tbl TO PUBLIC;
+GRANT ALL on num_tbl TO PUBLIC;
+
+--- create a view owned by regress_ivm_user
+SET SESSION AUTHORIZATION regress_ivm_user;
+
+CREATE INCREMENTAL MATERIALIZED VIEW  ivm_rls AS SELECT * FROM rls_tbl;
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+RESET SESSION AUTHORIZATION;
+
+--- inserts rows owned by different users
+INSERT INTO rls_tbl VALUES
+  (3,'baz','regress_ivm_user'),
+  (4,'qux','postgres');
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+
+--- combination of diffent kinds of commands
+WITH
+ i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')),
+ u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1),
+ u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2)
+SELECT;
+SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3;
+
+---
+SET SESSION AUTHORIZATION regress_ivm_user;
+CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id);
+RESET SESSION AUTHORIZATION;
+
+WITH
+ x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)),
+ y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4))
+SELECT;
+SELECT * FROM ivm_rls2 ORDER BY 1,2,3;
+
+-- trigger updating the same table
+BEGIN;
+CREATE TABLE tbl_update_same (i int);
+CREATE FUNCTION func_update_same() RETURNS TRIGGER AS
+ $$ BEGIN UPDATE tbl_update_same SET i = i + 1; RETURN NEW; END; $$
+ LANGUAGE plpgsql;
+CREATE TRIGGER trig_update_same AFTER INSERT ON tbl_update_same FOR EACH ROW
+ EXECUTE FUNCTION func_update_same();
+CREATE INCREMENTAL MATERIALIZED VIEW mv_update_same AS SELECT * FROM tbl_update_same;
+INSERT INTO tbl_update_same VALUES (1);
+SELECT * FROM mv_update_same;
+
+-- self-referential FKs
+CREATE TABLE tbl_self_ref (a int primary key,
+						   b int references tbl_self_ref(a) ON UPDATE CASCADE);
+INSERT INTO tbl_self_ref VALUES (1, null), (2, 1), (3, 2);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_self_ref AS SELECT * FROM tbl_self_ref;
+UPDATE tbl_self_ref set a = a + 10;
+SELECT * FROM mv_self_ref ORDER BY a;
+ROLLBACK;
+
+-- automatic index creation
+BEGIN;
+CREATE TABLE base_a (i int primary key, j int);
+CREATE TABLE base_b (i int primary key, j int);
+
+--- with all pkey columns: create an index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b;
+
+--- missing some pkey columns: no index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a;
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b;
+
+--- with set-returning function: no index
+CREATE INCREMENTAL MATERIALIZED VIEW mv_idx6 AS SELECT i FROM base_a, generate_series(1,10);
+
+ROLLBACK;
+
+-- type that doesn't have default operator class for access method btree
+BEGIN;
+CREATE TABLE table_json (j json);
+CREATE INCREMENTAL MATERIALIZED VIEW mv_json AS SELECT * from table_json;
+ROLLBACK;
+
+-- cleanup
+
+DROP TABLE rls_tbl CASCADE;
+DROP TABLE num_tbl CASCADE;
+DROP USER regress_ivm_user;
+DROP USER regress_ivm_admin;
+
+DROP TABLE mv_base_b CASCADE;
+DROP TABLE mv_base_a CASCADE;
-- 
2.43.0


--Multipart=_Fri__3_Jul_2026_19_11_16_+0900_CskxSIu_iGcDMEyM
Content-Type: text/x-diff;
 name="v39-0006-Add-Incremental-View-Maintenance-support.patch"
Content-Disposition: attachment;
 filename="v39-0006-Add-Incremental-View-Maintenance-support.patch"
Content-Transfer-Encoding: 7bit



^ permalink  raw  reply  [nested|flat] 6+ messages in thread


end of thread, other threads:[~2026-05-29 09:31 UTC | newest]

Thread overview: 6+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-02-12 13:58 Re: TAP test command_fails versus command_fails_like Dagfinn Ilmari Mannsåker <[email protected]>
2025-02-12 15:40 ` Andrew Dunstan <[email protected]>
2025-02-12 16:58   ` Dagfinn Ilmari Mannsåker <[email protected]>
2025-02-12 17:18     ` Tom Lane <[email protected]>
2025-02-22 01:27 ` Peter Smith <[email protected]>
2026-05-29 09:31 [PATCH v39 7/8] Add regression tests for Incremental View Maintenance Yugo Nagata <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox