diff --git a/Osstest/Executive.pm b/Osstest/Executive.pm
index d95d848d..ec592ed5 100644
--- a/Osstest/Executive.pm
+++ b/Osstest/Executive.pm
@@ -115,7 +115,7 @@ augmentconfigdefaults(
     QueuePlanUpdateInterval => 300, # seconds
     Repos => "$ENV{'HOME'}/repos",
     BisectionRevisonGraphSize => '600x300',
-    ExecutiveDbOwningRoleRegexp => 'osstest',
+    ExecutiveDbOwningRoleMap => '=',
     MaxUmask => '007',
 );
 
diff --git a/README b/README
index 20d9802a..aa611f23 100644
--- a/README
+++ b/README
@@ -770,10 +770,19 @@ ExecutiveDbname_<DB>
    PostgreSQL dbname string for the database <DB>.  Default is to use
    ExecutiveDbnamePat.
 
-ExecutiveDbOwningRoleRegexp
-   Regexp which is supposed to match the database user used for schema
-   changes - because, that role will end up owning the database objects.
-   Defaults to `osstest'.
+ExecutiveDbOwningRoleMap
+   Adjust roles used for schema changes.  osstest uses the following
+   nominal roles
+      osstest        Main db runner, owns most tables, etc.
+      webflights     More privileged, can see webflights tokens
+   The syntaxes for ExecutiveDbOwningRoleMap are
+      =              use the built-in nominal role, literally (default)
+      -              always use the default role for the user
+      <NEW-ROLE>     always use NEW-ROLE for everything
+   or a list like this
+      <NOM-ROLE>=<NEW-ROLE>,...
+   where <NEW-ROLE> can be `-' (default user role); unspecified
+   roles are not mapped.
 
 Flights for by-hand testing
 ===========================
diff --git a/mg-schema-test-database b/mg-schema-test-database
index b185e8b9..33b644f5 100755
--- a/mg-schema-test-database
+++ b/mg-schema-test-database
@@ -361,7 +361,7 @@ OwnerDaemonHost $ctrlhost
 QueueDaemonHost $ctrlhost
 OwnerDaemonPort ${ctrlports%,*}
 QueueDaemonPort ${ctrlports#*,}
-ExecutiveDbOwningRoleRegexp .*
+ExecutiveDbOwningRoleMap -
 QueueDaemonHoldoff 3
 QueueDaemonRetry 5
 Logs $PWD/logs
diff --git a/mg-schema-update b/mg-schema-update
index fb754bed..5ec7220e 100755
--- a/mg-schema-update
+++ b/mg-schema-update
@@ -22,7 +22,6 @@
 #  ./mg-schema-update [<options>] apply [<updatename>...]
 #  ./mg-schema-update [<options>] show
 #  ./mg-schema-update [<options>] apply-all
-#  ./mg-schema-update [<options>] check-user
 #
 # Usual rune for applying updates:
 #
@@ -53,6 +52,7 @@
 
 use strict qw(vars);
 use DBI;
+use Carp;
 BEGIN { unshift @INC, qw(.); }
 use Osstest;
 use Osstest::Executive;
@@ -121,13 +121,6 @@ sub getstate () {
     @state = sort { $a->{Sortkey} <=> $b->{Sortkey} } values %state;
 }
 
-sub check_user () {
-    my $user = $dbh_tests->{pg_user};
-    my $re = $c{ExecutiveDbOwningRoleRegexp};
-    return if $user =~ m/^$re$/o;
-    die "running as wrong user \`$user', expected to match \`$re'\n";
-}
-
 sub cmd_list_applied () {
     die if @ARGV;
     getstate();
@@ -174,6 +167,56 @@ sub want_apply ($) {
     $v->{Todo} >= 2-$force;
 }
 
+sub check_user () {
+    my $user = $dbh_tests->{pg_user};
+    my ($map, $default) = roles_map();
+    my $n = $map->{'osstest'} // $default;
+    my $wanted =
+      ($n eq '-' ? return :
+       $n eq '=' ? 'osstest' :
+                   $n);
+    return if $user eq $wanted;
+    die "running as wrong user \`$user', expected \`$wanted'\n";
+}
+
+sub cmd_check_user () {
+    die "too many arguments\n" if @ARGV>0;
+    check_user();
+}
+
+sub roles_map () {
+    my $default;
+    my %map;
+    if ($c{ExecutiveDbOwningRoleMap} !~ m/,|.=/) {
+	$default = $c{ExecutiveDbOwningRoleMap};
+    } else {
+	foreach my $kv (split m/,/, $c{ExecutiveDbOwningRoleMap}) {
+	    my @kv = split m/=/, $kv;  @kv == 2 or die;
+	    my ($k, $v) = @kv;
+	    grep { $_ eq $k } qw(osstest webflights)
+	        or confess "bad NOM-ROLE $k";
+	    $map{$k} = $v;
+	}
+	$default = '=';
+    }
+    return (\%map, $default);
+}
+
+sub map_roles ($) {
+    local ($_) = @_;
+    my ($map, $default) = roles_map();
+
+    s{^\@\s*SET ROLE\s+'?(\S+?)'?\s*\;\s*$}{
+        my $n = $map->{$1} // $default;
+	($n eq '-' ? "SET ROLE NONE;" :
+	 $n eq '=' ? "SET ROLE '$1';" :
+	             "SET ROLE '$n';")
+    }mge;
+
+    #print STDERR ">$_<\n";
+    return $_;
+}    
+
 sub applyone ($) {
     my ($v) = @_;
     die "Will not apply $v->{Name}.sql: $v->{Msg}\n"
@@ -195,7 +238,8 @@ sub applyone ($) {
             SET client_min_messages = warning;
 END
 
-	$dbh_tests->do($sql);
+	$dbh_tests->do(map_roles('@SET ROLE osstest;'));
+	$dbh_tests->do(map_roles($sql));
 
 	$dbh_tests->do(<<END, {}, $v->{Name}, time);
 	    INSERT INTO schema_updates
@@ -253,12 +297,6 @@ sub cmd_apply_all () {
     print "Appropriate updates applied.\n" unless $quiet;
 }
 
-sub cmd_check_user () {
-    die "too many arguments\n" if @ARGV>1;
-    $c{ExecutiveDbOwningRoleRegexp} = shift @ARGV if @ARGV;
-    check_user();
-}
-
 GetOptions('f|force+' => \$force,
 	   'q+' => \$quiet,
 	   'o|oldest=s' => \$there);
