diff --git a/configure b/configure
index 49362c1f015..b9371321677 100755
--- a/configure
+++ b/configure
@@ -698,6 +698,7 @@ BISON
MKDIR_P
LN_S
TAR
+USE_MDBLOCALES
install_bin
INSTALL_DATA
INSTALL_SCRIPT
@@ -945,6 +946,7 @@ with_rt
with_libcurl
with_apr_config
with_gnu_ld
+with_mdblocales
with_ssl
with_openssl
enable_openssl_redirect
@@ -1693,6 +1695,7 @@ Optional Packages:
--without-libcurl do not use libcurl
--with-apr-config=PATH path to apr-1-config utility
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --without-mdblocales build without MDB locales
--with-ssl=LIB use LIB for SSL/TLS support (openssl)
--with-openssl obsolete spelling of --with-ssl=openssl
@@ -2909,7 +2912,6 @@ PG_PACKAGE_VERSION=14.4
-
ac_aux_dir=
for ac_dir in config "$srcdir"/config; do
if test -f "$ac_dir/install-sh"; then
@@ -12208,6 +12210,38 @@ case $INSTALL in
esac
+#
+# MDB locales
+#
+
+
+
+
+# Check whether --with-mdblocales was given.
+if test "${with_mdblocales+set}" = set; then :
+ withval=$with_mdblocales;
+ case $withval in
+ yes)
+
+$as_echo "#define USE_MDBLOCALES 1" >>confdefs.h
+
+ ;;
+ no)
+ :
+ ;;
+ *)
+ as_fn_error $? "no argument expected for --with-mdblocales option" "$LINENO" 5
+ ;;
+ esac
+
+else
+ with_mdblocales=no
+
+fi
+
+
+
+
if test -z "$TAR"; then
for ac_prog in tar
do
@@ -12844,6 +12878,56 @@ $as_echo "${python_libspec} ${python_additional_libs}" >&6; }
+fi
+
+if test "$with_mdblocales" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mdb_setlocale in -lmdblocales" >&5
+$as_echo_n "checking for mdb_setlocale in -lmdblocales... " >&6; }
+if ${ac_cv_lib_mdblocales_mdb_setlocale+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lmdblocales $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mdb_setlocale ();
+int
+main ()
+{
+return mdb_setlocale ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_mdblocales_mdb_setlocale=yes
+else
+ ac_cv_lib_mdblocales_mdb_setlocale=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mdblocales_mdb_setlocale" >&5
+$as_echo "$ac_cv_lib_mdblocales_mdb_setlocale" >&6; }
+if test "x$ac_cv_lib_mdblocales_mdb_setlocale" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBMDBLOCALES 1
+_ACEOF
+
+ LIBS="-lmdblocales $LIBS"
+
+else
+ as_fn_error $? "mdblocales library not found" "$LINENO" 5
+fi
+
fi
if test x"$cross_compiling" = x"yes" && test -z "$with_system_tzdata"; then
@@ -17065,6 +17149,17 @@ fi
done
+fi
+
+if test "$with_mdblocales" = yes; then
+ ac_fn_c_check_header_mongrel "$LINENO" "mdblocales.h" "ac_cv_header_mdblocales_h" "$ac_includes_default"
+if test "x$ac_cv_header_mdblocales_h" = xyes; then :
+
+else
+ as_fn_error $? "mdblocales header not found." "$LINENO" 5
+fi
+
+
fi
if test "$with_gssapi" = yes ; then
diff --git a/configure.ac b/configure.ac
index 8bfdcedf7f1..246edc4846e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1462,6 +1462,14 @@ case $INSTALL in
esac
AC_SUBST(install_bin)
+#
+# MDB locales
+#
+
+PGAC_ARG_BOOL(with, mdblocales, yes, [build without MDB locales],
+ [AC_DEFINE([USE_MDBLOCALES], 1, [Define to 1 to build with MDB locales. (--with-mdblocales)])])
+AC_SUBST(USE_MDBLOCALES)
+
PGAC_PATH_PROGS(TAR, tar)
AC_PROG_LN_S
AC_PROG_MKDIR_P
@@ -1620,6 +1628,11 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_mdblocales" = yes; then
+ AC_CHECK_LIB(mdblocales, mdb_setlocale, [],
+ [AC_MSG_ERROR([mdblocales library not found])])
+fi
+
if test "$enable_external_fts" = yes; then
AC_CHECK_LIB(jansson, jansson_version_str, [],
[AC_MSG_ERROR([jansson library not found or version is too old, version must >= 2.13])])
@@ -1999,6 +2012,10 @@ if test "$with_lz4" = yes; then
AC_CHECK_HEADERS(lz4.h, [], [AC_MSG_ERROR([lz4.h header file is required for LZ4])])
fi
+if test "$with_mdblocales" = yes; then
+ AC_CHECK_HEADER(mdblocales.h, [], [AC_MSG_ERROR([mdblocales header not found.])])
+fi
+
if test "$with_gssapi" = yes ; then
AC_CHECK_HEADERS(gssapi/gssapi.h, [],
[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
diff --git a/contrib/pax_storage/src/cpp/storage/oper/pax_oper.cc b/contrib/pax_storage/src/cpp/storage/oper/pax_oper.cc
index 44d4e49d7f8..d08c7a445b9 100644
--- a/contrib/pax_storage/src/cpp/storage/oper/pax_oper.cc
+++ b/contrib/pax_storage/src/cpp/storage/oper/pax_oper.cc
@@ -25,6 +25,7 @@
*-------------------------------------------------------------------------
*/
+#include "common/mdb_locale.h"
#include "storage/oper/pax_oper.h"
#include "comm/cbdb_wrappers.h"
@@ -588,9 +589,9 @@ static inline bool LocaleIsC(Oid collation) {
return (bool)result;
}
- localeptr = setlocale(LC_COLLATE, NULL);
+ localeptr = SETLOCALE(LC_COLLATE, NULL);
CBDB_CHECK(localeptr, cbdb::CException::ExType::kExTypeCError,
- fmt("Invalid locale, fail to `setlocale`, errno: %d", errno));
+ fmt("Invalid locale, fail to `SETLOCALE`, errno: %d", errno));
if (strcmp(localeptr, "C") == 0 || // cut line
strcmp(localeptr, "POSIX") == 0) {
diff --git a/contrib/pax_storage/src/test/regress/expected/create_function_3.out b/contrib/pax_storage/src/test/regress/expected/create_function_3.out
index 8380df1591f..7842a3c1c82 100644
--- a/contrib/pax_storage/src/test/regress/expected/create_function_3.out
+++ b/contrib/pax_storage/src/test/regress/expected/create_function_3.out
@@ -166,10 +166,10 @@ SET SESSION AUTHORIZATION regress_unpriv_user;
SET search_path TO temp_func_test, public;
ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF;
ALTER FUNCTION functest_E_2(int) LEAKPROOF;
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql'
LEAKPROOF AS 'SELECT $1 < 200'; -- fail
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
RESET SESSION AUTHORIZATION;
--
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
diff --git a/contrib/pax_storage/src/test/regress/expected/create_function_3_optimizer.out b/contrib/pax_storage/src/test/regress/expected/create_function_3_optimizer.out
index 3ae669d518a..3256709e1aa 100644
--- a/contrib/pax_storage/src/test/regress/expected/create_function_3_optimizer.out
+++ b/contrib/pax_storage/src/test/regress/expected/create_function_3_optimizer.out
@@ -166,10 +166,10 @@ SET SESSION AUTHORIZATION regress_unpriv_user;
SET search_path TO temp_func_test, public;
ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF;
ALTER FUNCTION functest_E_2(int) LEAKPROOF;
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql'
LEAKPROOF AS 'SELECT $1 < 200'; -- fail
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
RESET SESSION AUTHORIZATION;
--
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
diff --git a/gpcontrib/orafce/others.c b/gpcontrib/orafce/others.c
index 2fb612efe19..5bf8b650e4c 100644
--- a/gpcontrib/orafce/others.c
+++ b/gpcontrib/orafce/others.c
@@ -45,6 +45,7 @@
#include "utils/uuid.h"
#include "orafce.h"
#include "builtins.h"
+#include "common/mdb_locale.h"
/*
* Source code for nlssort is taken from postgresql-nls-string
@@ -322,7 +323,7 @@ _nls_run_strxfrm(text *string, text *locale)
*/
if (!lc_collate_cache)
{
- if ((lc_collate_cache = setlocale(LC_COLLATE, NULL)))
+ if ((lc_collate_cache = SETLOCALE(LC_COLLATE, NULL)))
/* Make a copy of the locale name string. */
#ifdef _MSC_VER
lc_collate_cache = _strdup(lc_collate_cache);
@@ -364,7 +365,7 @@ _nls_run_strxfrm(text *string, text *locale)
* If setlocale failed, we know the default stayed the same,
* co we can safely elog.
*/
- if (!setlocale(LC_COLLATE, locale_str))
+ if (!SETLOCALE(LC_COLLATE, locale_str))
elog(ERROR, "failed to set the requested LC_COLLATE value [%s]", locale_str);
changed_locale = true;
@@ -409,7 +410,7 @@ _nls_run_strxfrm(text *string, text *locale)
/*
* Set original locale
*/
- if (!setlocale(LC_COLLATE, lc_collate_cache))
+ if (!SETLOCALE(LC_COLLATE, lc_collate_cache))
elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache);
}
@@ -422,7 +423,7 @@ _nls_run_strxfrm(text *string, text *locale)
/*
* Set original locale
*/
- if (!setlocale(LC_COLLATE, lc_collate_cache))
+ if (!SETLOCALE(LC_COLLATE, lc_collate_cache))
elog(FATAL, "failed to set back the default LC_COLLATE value [%s]", lc_collate_cache);
pfree(locale_str);
}
diff --git a/gpcontrib/orafce/sqlparse.c b/gpcontrib/orafce/sqlparse.c
index bedd14efc0d..e8be54bf278 100644
--- a/gpcontrib/orafce/sqlparse.c
+++ b/gpcontrib/orafce/sqlparse.c
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.7.4. */
+/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison implementation for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+ Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
+ along with this program. If not, see . */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -46,10 +46,10 @@
USER NAME SPACE" below. */
/* Identify Bison output, and Bison version. */
-#define YYBISON 30704
+#define YYBISON 30802
/* Bison version string. */
-#define YYBISON_VERSION "3.7.4"
+#define YYBISON_VERSION "3.8.2"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -214,6 +214,18 @@ typedef int_least16_t yytype_int16;
typedef short yytype_int16;
#endif
+/* Work around bug in HP-UX 11.23, which defines these macros
+ incorrectly for preprocessor constants. This workaround can likely
+ be removed in 2023, as HPE has promised support for HP-UX 11.23
+ (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+ . */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
+#endif
+
#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
typedef __UINT_LEAST8_TYPE__ yytype_uint8;
#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
@@ -311,17 +323,23 @@ typedef int yy_state_fast_t;
/* Suppress unused-variable warnings by "using" E. */
#if ! defined lint || defined __GNUC__
-# define YYUSE(E) ((void) (E))
+# define YY_USE(E) ((void) (E))
#else
-# define YYUSE(E) /* empty */
+# define YY_USE(E) /* empty */
#endif
-#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
/* Suppress an incorrect diagnostic about yylval being uninitialized. */
-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
_Pragma ("GCC diagnostic push") \
_Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \
_Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# endif
# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
_Pragma ("GCC diagnostic pop")
#else
@@ -539,7 +557,7 @@ static const yytype_int8 yytranslate[] =
};
#if YYDEBUG
- /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_int8 yyrline[] =
{
0, 91, 91, 95, 96, 100, 101, 102, 103, 104,
@@ -571,16 +589,6 @@ yysymbol_name (yysymbol_kind_t yysymbol)
}
#endif
-#ifdef YYPRINT
-/* YYTOKNUM[NUM] -- (External) token number corresponding to the
- (internal) symbol number NUM (which must be that of a token). */
-static const yytype_int16 yytoknum[] =
-{
- 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
- 265, 266, 267
-};
-#endif
-
#define YYPACT_NINF (-4)
#define yypact_value_is_default(Yyn) \
@@ -591,38 +599,38 @@ static const yytype_int16 yytoknum[] =
#define yytable_value_is_error(Yyn) \
0
- /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
- STATE-NUM. */
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
static const yytype_int8 yypact[] =
{
-3, -4, -4, -4, -4, -4, -4, -4, -4, -4,
9, -3, -4, -4, -4
};
- /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
- Performed when YYTABLE does not specify something else to do. Zero
- means the default is an error. */
+/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
static const yytype_int8 yydefact[] =
{
0, 5, 6, 7, 8, 9, 10, 11, 12, 13,
0, 2, 3, 1, 4
};
- /* YYPGOTO[NTERM-NUM]. */
+/* YYPGOTO[NTERM-NUM]. */
static const yytype_int8 yypgoto[] =
{
-4, -4, -4, -1
};
- /* YYDEFGOTO[NTERM-NUM]. */
+/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int8 yydefgoto[] =
{
- -1, 10, 11, 12
+ 0, 10, 11, 12
};
- /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
- positive, shift that token. If negative, reduce the rule whose
- number is the opposite. If YYTABLE_NINF, syntax error. */
+/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
static const yytype_int8 yytable[] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 13,
@@ -635,22 +643,22 @@ static const yytype_int8 yycheck[] =
11
};
- /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
- symbol of state STATE-NUM. */
+/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+ state STATE-NUM. */
static const yytype_int8 yystos[] =
{
0, 3, 4, 5, 6, 7, 8, 9, 10, 11,
14, 15, 16, 0, 16
};
- /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */
static const yytype_int8 yyr1[] =
{
0, 13, 14, 15, 15, 16, 16, 16, 16, 16,
16, 16, 16, 16
};
- /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */
static const yytype_int8 yyr2[] =
{
0, 2, 1, 1, 2, 1, 1, 1, 1, 1,
@@ -666,6 +674,7 @@ enum { YYENOMEM = -2 };
#define YYACCEPT goto yyacceptlab
#define YYABORT goto yyabortlab
#define YYERROR goto yyerrorlab
+#define YYNOMEM goto yyexhaustedlab
#define YYRECOVERING() (!!yyerrstatus)
@@ -733,12 +742,19 @@ do { \
} while (0)
-/* YY_LOCATION_PRINT -- Print the location on the stream.
+/* YYLOCATION_PRINT -- Print the location on the stream.
This macro was not mandated originally: define only if we know
we won't break user code: when these are the locations we know. */
-# ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+# ifndef YYLOCATION_PRINT
+
+# if defined YY_LOCATION_PRINT
+
+ /* Temporary convenience wrapper in case some people defined the
+ undocumented and private YY_LOCATION_PRINT macros. */
+# define YYLOCATION_PRINT(File, Loc) YY_LOCATION_PRINT(File, *(Loc))
+
+# elif defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
/* Print *YYLOCP on YYO. Private, do not rely on its existence. */
@@ -766,15 +782,23 @@ yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp)
res += YYFPRINTF (yyo, "-%d", end_col);
}
return res;
- }
+}
-# define YY_LOCATION_PRINT(File, Loc) \
- yy_location_print_ (File, &(Loc))
+# define YYLOCATION_PRINT yy_location_print_
+
+ /* Temporary convenience wrapper in case some people defined the
+ undocumented and private YY_LOCATION_PRINT macros. */
+# define YY_LOCATION_PRINT(File, Loc) YYLOCATION_PRINT(File, &(Loc))
# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+
+# define YYLOCATION_PRINT(File, Loc) ((void) 0)
+ /* Temporary convenience wrapper in case some people defined the
+ undocumented and private YY_LOCATION_PRINT macros. */
+# define YY_LOCATION_PRINT YYLOCATION_PRINT
+
# endif
-# endif /* !defined YY_LOCATION_PRINT */
+# endif /* !defined YYLOCATION_PRINT */
# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \
@@ -798,17 +822,13 @@ yy_symbol_value_print (FILE *yyo,
yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, List **result)
{
FILE *yyoutput = yyo;
- YYUSE (yyoutput);
- YYUSE (yylocationp);
- YYUSE (result);
+ YY_USE (yyoutput);
+ YY_USE (yylocationp);
+ YY_USE (result);
if (!yyvaluep)
return;
-# ifdef YYPRINT
- if (yykind < YYNTOKENS)
- YYPRINT (yyo, yytoknum[yykind], *yyvaluep);
-# endif
YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
- YYUSE (yykind);
+ YY_USE (yykind);
YY_IGNORE_MAYBE_UNINITIALIZED_END
}
@@ -824,7 +844,7 @@ yy_symbol_print (FILE *yyo,
YYFPRINTF (yyo, "%s %s (",
yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
- YY_LOCATION_PRINT (yyo, *yylocationp);
+ YYLOCATION_PRINT (yyo, yylocationp);
YYFPRINTF (yyo, ": ");
yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, result);
YYFPRINTF (yyo, ")");
@@ -925,15 +945,15 @@ static void
yydestruct (const char *yymsg,
yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, List **result)
{
- YYUSE (yyvaluep);
- YYUSE (yylocationp);
- YYUSE (result);
+ YY_USE (yyvaluep);
+ YY_USE (yylocationp);
+ YY_USE (result);
if (!yymsg)
yymsg = "Deleting";
YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
- YYUSE (yykind);
+ YY_USE (yykind);
YY_IGNORE_MAYBE_UNINITIALIZED_END
}
@@ -1011,6 +1031,7 @@ yyparse (List **result)
YYDPRINTF ((stderr, "Starting parse\n"));
yychar = YYEMPTY; /* Cause a token to be read. */
+
yylsp[0] = yylloc;
goto yysetstate;
@@ -1037,7 +1058,7 @@ yyparse (List **result)
if (yyss + yystacksize - 1 <= yyssp)
#if !defined yyoverflow && !defined YYSTACK_RELOCATE
- goto yyexhaustedlab;
+ YYNOMEM;
#else
{
/* Get the current used size of the three stacks, in elements. */
@@ -1068,7 +1089,7 @@ yyparse (List **result)
# else /* defined YYSTACK_RELOCATE */
/* Extend the stack our own way. */
if (YYMAXDEPTH <= yystacksize)
- goto yyexhaustedlab;
+ YYNOMEM;
yystacksize *= 2;
if (YYMAXDEPTH < yystacksize)
yystacksize = YYMAXDEPTH;
@@ -1079,7 +1100,7 @@ yyparse (List **result)
YY_CAST (union yyalloc *,
YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
if (! yyptr)
- goto yyexhaustedlab;
+ YYNOMEM;
YYSTACK_RELOCATE (yyss_alloc, yyss);
YYSTACK_RELOCATE (yyvs_alloc, yyvs);
YYSTACK_RELOCATE (yyls_alloc, yyls);
@@ -1103,6 +1124,7 @@ yyparse (List **result)
}
#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
+
if (yystate == YYFINAL)
YYACCEPT;
@@ -1221,77 +1243,77 @@ yyparse (List **result)
case 2: /* root: elements */
#line 91 "sqlparse.y"
{ *((void**)result) = (yyvsp[0].list); }
-#line 1225 "sqlparse.c"
+#line 1247 "sqlparse.c"
break;
case 3: /* elements: anyelement */
#line 95 "sqlparse.y"
{ (yyval.list) = list_make1((yyvsp[0].node));}
-#line 1231 "sqlparse.c"
+#line 1253 "sqlparse.c"
break;
case 4: /* elements: elements anyelement */
#line 96 "sqlparse.y"
{ (yyval.list) = lappend((yyvsp[-1].list), (yyvsp[0].node));}
-#line 1237 "sqlparse.c"
+#line 1259 "sqlparse.c"
break;
case 5: /* anyelement: X_IDENT */
#line 100 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), IDENT); }
-#line 1243 "sqlparse.c"
+#line 1265 "sqlparse.c"
break;
case 6: /* anyelement: X_NCONST */
#line 101 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), NCONST); }
-#line 1249 "sqlparse.c"
+#line 1271 "sqlparse.c"
break;
case 7: /* anyelement: X_SCONST */
#line 102 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), SCONST); }
-#line 1255 "sqlparse.c"
+#line 1277 "sqlparse.c"
break;
case 8: /* anyelement: X_OP */
#line 103 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OP); }
-#line 1261 "sqlparse.c"
+#line 1283 "sqlparse.c"
break;
case 9: /* anyelement: X_PARAM */
#line 104 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), PARAM); }
-#line 1267 "sqlparse.c"
+#line 1289 "sqlparse.c"
break;
case 10: /* anyelement: X_COMMENT */
#line 105 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), COMMENT); }
-#line 1273 "sqlparse.c"
+#line 1295 "sqlparse.c"
break;
case 11: /* anyelement: X_WHITESPACE */
#line 106 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), WHITESPACE); }
-#line 1279 "sqlparse.c"
+#line 1301 "sqlparse.c"
break;
case 12: /* anyelement: X_KEYWORD */
#line 107 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), KEYWORD); }
-#line 1285 "sqlparse.c"
+#line 1307 "sqlparse.c"
break;
case 13: /* anyelement: X_OTHERS */
#line 108 "sqlparse.y"
{ (yyval.node) = (orafce_lexnode*) CREATE_NODE((yyvsp[0].val), OTHERS); }
-#line 1291 "sqlparse.c"
+#line 1313 "sqlparse.c"
break;
-#line 1295 "sqlparse.c"
+#line 1317 "sqlparse.c"
default: break;
}
@@ -1375,6 +1397,7 @@ yyparse (List **result)
label yyerrorlab therefore never appears in user code. */
if (0)
YYERROR;
+ ++yynerrs;
/* Do not reclaim the symbols of the rule whose action triggered
this YYERROR. */
@@ -1438,7 +1461,7 @@ yyparse (List **result)
`-------------------------------------*/
yyacceptlab:
yyresult = 0;
- goto yyreturn;
+ goto yyreturnlab;
/*-----------------------------------.
@@ -1446,24 +1469,22 @@ yyparse (List **result)
`-----------------------------------*/
yyabortlab:
yyresult = 1;
- goto yyreturn;
+ goto yyreturnlab;
-#if !defined yyoverflow
-/*-------------------------------------------------.
-| yyexhaustedlab -- memory exhaustion comes here. |
-`-------------------------------------------------*/
+/*-----------------------------------------------------------.
+| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. |
+`-----------------------------------------------------------*/
yyexhaustedlab:
yyerror (result, YY_("memory exhausted"));
yyresult = 2;
- goto yyreturn;
-#endif
+ goto yyreturnlab;
-/*-------------------------------------------------------.
-| yyreturn -- parsing is finished, clean up and return. |
-`-------------------------------------------------------*/
-yyreturn:
+/*----------------------------------------------------------.
+| yyreturnlab -- parsing is finished, clean up and return. |
+`----------------------------------------------------------*/
+yyreturnlab:
if (yychar != YYEMPTY)
{
/* Make sure we have latest lookahead translation. See comments at
diff --git a/gpcontrib/orafce/sqlparse.h b/gpcontrib/orafce/sqlparse.h
index ca1b993f3c9..588c8b2a376 100644
--- a/gpcontrib/orafce/sqlparse.h
+++ b/gpcontrib/orafce/sqlparse.h
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.7.4. */
+/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+ Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
+ along with this program. If not, see . */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -111,6 +111,8 @@ struct YYLTYPE
extern YYSTYPE orafce_sql_yylval;
extern YYLTYPE orafce_sql_yylloc;
+
int orafce_sql_yyparse (List **result);
+
#endif /* !YY_ORAFCE_SQL_YY_SQLPARSE_H_INCLUDED */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index f367b00a675..be09847022b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -2971,7 +2971,6 @@ LookupExplicitNamespace(const char *nspname, bool missing_ok)
{
Oid namespaceId;
AclResult aclresult;
-
/* check for pg_temp alias */
if (strcmp(nspname, "pg_temp") == 0)
{
@@ -2989,7 +2988,24 @@ LookupExplicitNamespace(const char *nspname, bool missing_ok)
if (missing_ok && !OidIsValid(namespaceId))
return InvalidOid;
- aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
+ HeapTuple tuple;
+ Oid ownerId;
+
+ tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(namespaceId));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema with OID %u does not exist", namespaceId)));
+
+ ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
+
+ ReleaseSysCache(tuple);
+
+ if (!mdb_admin_allow_bypass_owner_checks(GetUserId(), ownerId)) {
+ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
+ } else {
+ aclresult = ACLCHECK_OK;
+ }
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
nspname);
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index f5dfd6ff126..6f370a2c9aa 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -1085,7 +1085,8 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
if (!superuser())
{
/* must be owner */
- if (!has_privs_of_role(GetUserId(), old_ownerId))
+ if (!has_privs_of_role(GetUserId(), old_ownerId)
+ && !mdb_admin_allow_bypass_owner_checks(GetUserId(), old_ownerId))
{
char *objname;
char namebuf[NAMEDATALEN];
@@ -1105,14 +1106,13 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
objname);
}
- /* Must be able to become new owner */
- check_is_member_of_role(GetUserId(), new_ownerId);
+
+ check_mdb_admin_is_member_of_role(GetUserId(), new_ownerId);
/* New owner must have CREATE privilege on namespace */
if (OidIsValid(namespaceId))
{
AclResult aclresult;
-
aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index b99b2419fcc..1ab3b36dd59 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1525,9 +1525,13 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
* by security barrier views or row-level security policies.
*/
if (isLeakProof && !superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("only superuser can define a leakproof function")));
+ {
+ Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ if (!is_member_of_role(GetUserId(), role))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("only superuser or mdb_admin can define a leakproof function")));
+ }
if (transformDefElem)
{
@@ -1852,9 +1856,13 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
{
procForm->proleakproof = intVal(leakproof_item->arg);
if (procForm->proleakproof && !superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("only superuser can define a leakproof function")));
+ {
+ Oid role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ if (!is_member_of_role(GetUserId(), role))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("only superuser or mdb_admin can define a leakproof function")));
+ }
}
if (cost_item)
{
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 96757eaa814..03f96bb6499 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -598,12 +598,12 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
AclResult aclresult;
/* Otherwise, must be owner of the existing object */
- if (!pg_namespace_ownercheck(nspForm->oid, GetUserId()))
+ if (!mdb_admin_allow_bypass_owner_checks(GetUserId(), nspForm->nspowner)
+ && !pg_namespace_ownercheck(nspForm->oid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
NameStr(nspForm->nspname));
- /* Must be able to become new owner */
- check_is_member_of_role(GetUserId(), newOwnerId);
+ check_mdb_admin_is_member_of_role(GetUserId(), newOwnerId);
/*
* must have create-schema rights
@@ -614,8 +614,13 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
* schemas. Because superusers will always have this right, we need
* no special case for them.
*/
- aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
+ if (mdb_admin_allow_bypass_owner_checks(GetUserId(), nspForm->nspowner)) {
+ aclresult = ACLCHECK_OK;
+ } else {
+ aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
ACL_CREATE);
+ }
+
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_DATABASE,
get_database_name(MyDatabaseId));
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 42e00efe81d..07f00a212b0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15704,13 +15704,14 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
AclResult aclresult;
/* Otherwise, must be owner of the existing object */
- if (!pg_class_ownercheck(relationOid, GetUserId()))
+ if (!mdb_admin_allow_bypass_owner_checks(GetUserId(), tuple_class->relowner)
+ && !pg_class_ownercheck(relationOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
RelationGetRelationName(target_rel));
- /* Must be able to become new owner */
- check_is_member_of_role(GetUserId(), newOwnerId);
+ check_mdb_admin_is_member_of_role(GetUserId(), newOwnerId);
+
/* New owner must have CREATE privilege on namespace */
aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
ACL_CREATE);
@@ -20791,7 +20792,7 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
Form_pg_class classform;
AclResult aclresult;
char relkind;
-
+
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
return; /* concurrently dropped */
@@ -20799,7 +20800,8 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
relkind = classform->relkind;
/* Must own relation. */
- if (!pg_class_ownercheck(relid, GetUserId()))
+ if (!mdb_admin_allow_bypass_owner_checks(GetUserId(), classform->relowner)
+ && !pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
/* No system table modifications unless explicitly allowed. */
diff --git a/src/backend/gporca/libgpos/server/src/unittest/gpos/string/CWStringTest.cpp b/src/backend/gporca/libgpos/server/src/unittest/gpos/string/CWStringTest.cpp
index 60bccf59341..bb086954403 100644
--- a/src/backend/gporca/libgpos/server/src/unittest/gpos/string/CWStringTest.cpp
+++ b/src/backend/gporca/libgpos/server/src/unittest/gpos/string/CWStringTest.cpp
@@ -12,6 +12,7 @@
#include "unittest/gpos/string/CWStringTest.h"
#include
+#include "common/mdb_locale.h"
#include "gpos/base.h"
#include "gpos/error/CAutoTrace.h"
@@ -177,18 +178,18 @@ CWStringTest::EresUnittest_AppendFormatInvalidLocale()
CWStringDynamic *expected =
GPOS_NEW(mp) CWStringDynamic(mp, GPOS_WSZ_LIT("UNKNOWN"));
- CHAR *oldLocale = setlocale(LC_CTYPE, nullptr);
+ CHAR *oldLocale = SETLOCALE(LC_CTYPE, nullptr);
CWStringDynamic *pstr1 = GPOS_NEW(mp) CWStringDynamic(mp);
GPOS_RESULT eres = GPOS_OK;
- setlocale(LC_CTYPE, "C");
+ SETLOCALE(LC_CTYPE, "C");
pstr1->AppendFormat(GPOS_WSZ_LIT("%s"), (CHAR *) "ÃË", 123);
pstr1->Equals(expected);
// cleanup
- setlocale(LC_CTYPE, oldLocale);
+ SETLOCALE(LC_CTYPE, oldLocale);
GPOS_DELETE(pstr1);
GPOS_DELETE(expected);
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index 0d5ccaa201d..753b94752d3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -52,6 +52,7 @@ static int
pg_signal_backend(int pid, int sig, char *msg)
{
PGPROC *proc = BackendPidGetProc(pid);
+ LocalPgBackendStatus *local_beentry;
/*
* BackendPidGetProc returns NULL if the pid isn't valid; but by the time
@@ -72,9 +73,34 @@ pg_signal_backend(int pid, int sig, char *msg)
return SIGNAL_BACKEND_ERROR;
}
+ local_beentry = pgstat_fetch_stat_local_beentry_by_pid(pid);
+
/* Only allow superusers to signal superuser-owned backends. */
if (superuser_arg(proc->roleId) && !superuser())
- return SIGNAL_BACKEND_NOSUPERUSER;
+ {
+ Oid role;
+ char * appname;
+
+ if (local_beentry == NULL) {
+ return SIGNAL_BACKEND_NOSUPERUSER;
+ }
+
+ role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ appname = local_beentry->backendStatus.st_appname;
+
+ // only allow mdb_admin to kill su queries
+ if (!is_member_of_role(GetUserId(), role)) {
+ return SIGNAL_BACKEND_NOSUPERUSER;
+ }
+
+ if (local_beentry->backendStatus.st_backendType == B_AUTOVAC_WORKER) {
+ // ok
+ } else if (appname != NULL && strcmp(appname, "MDB") == 0) {
+ // ok
+ } else {
+ return SIGNAL_BACKEND_NOSUPERUSER;
+ }
+ }
/* Users can signal backends they have role membership in. */
if (!has_privs_of_role(GetUserId(), proc->roleId) &&
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 9a0918bceff..217483c1c61 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -1102,6 +1102,22 @@ pgstat_fetch_stat_local_beentry(int beid)
return &localBackendStatusTable[beid - 1];
}
+/* -- mdb admin patch -- */
+LocalPgBackendStatus *
+pgstat_fetch_stat_local_beentry_by_pid(int pid)
+{
+ pgstat_read_current_status();
+
+ for (int i = 1; i <= localNumBackends; ++i) {
+ if (localBackendStatusTable[i - 1].backendStatus.st_procpid == pid) {
+ return &localBackendStatusTable[i - 1];
+ }
+ }
+
+ return NULL;
+}
+
+/* -- mdb admin patch end -- */
/* ----------
* pgstat_fetch_stat_numbackends() -
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index bd5479c546b..58dd15a6f8b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -117,7 +117,8 @@ OBJS = \
windowfuncs.o \
xid.o \
xid8funcs.o \
- xml.o
+ xml.o \
+ mdb.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
jsonpath_scan.c: FLEX_NO_BACKUP=yes
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 714a536e93d..e3463f636ae 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -116,6 +116,7 @@ static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
+static bool has_privs_of_unwanted_system_role(Oid role);
/*
* getid
@@ -4991,9 +4992,65 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
* set; for such roles, membership implies the ability to do SET ROLE, but
* the privileges are not available until you've done so.
*/
+
+/*
+* This is basically original postgresql privs-check function
+*/
+
+// -- mdb_superuser patch
+
+bool
+has_privs_of_role_strict(Oid member, Oid role)
+{
+ /* Fast path for simple case */
+ if (member == role)
+ return true;
+
+ /* Superusers have every privilege, so are part of every role */
+ if (superuser_arg(member))
+ return true;
+
+ /*
+ * Find all the roles that member has the privileges of, including
+ * multi-level recursion, then see if target role is any one of them.
+ */
+ return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
+ InvalidOid, NULL),
+ role);
+}
+
+/*
+* Check that role is either one of "dangerous" system role
+* or has "strict" (not through mdb_admin or mdb_superuser)
+* privs of this role
+*/
+
+static bool
+has_privs_of_unwanted_system_role(Oid role) {
+ if (has_privs_of_role_strict(role, ROLE_PG_READ_SERVER_FILES)) {
+ return true;
+ }
+ if (has_privs_of_role_strict(role, ROLE_PG_WRITE_SERVER_FILES)) {
+ return true;
+ }
+ if (has_privs_of_role_strict(role, ROLE_PG_EXECUTE_SERVER_PROGRAM)) {
+ return true;
+ }
+ if (has_privs_of_role_strict(role, ROLE_PG_READ_ALL_DATA)) {
+ return true;
+ }
+ if (has_privs_of_role_strict(role, ROLE_PG_WRITE_ALL_DATA)) {
+ return true;
+ }
+
+ return false;
+}
+
bool
has_privs_of_role(Oid member, Oid role)
{
+ Oid mdb_superuser_roleoid;
+
/* Fast path for simple case */
if (member == role)
return true;
@@ -5002,6 +5059,23 @@ has_privs_of_role(Oid member, Oid role)
if (superuser_arg(member))
return true;
+ mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);
+
+ if (is_member_of_role(member, mdb_superuser_roleoid)) {
+ /* if target role is superuser, disallow */
+ if (!superuser_arg(role)) {
+ /* we want mdb_roles_admin to bypass
+ * has_priv_of_roles test
+ * if target role is neither superuser nor
+ * some dangerous system role
+ */
+ if (!has_privs_of_unwanted_system_role(role)) {
+ return true;
+ }
+ }
+ }
+
+
/*
* Find all the roles that member has the privileges of, including
* multi-level recursion, then see if target role is any one of them.
@@ -5011,6 +5085,49 @@ has_privs_of_role(Oid member, Oid role)
role);
}
+// -- mdb_superuser patch
+
+// -- non-upstream patch begin
+/*
+ * Is userId allowed to bypass ownership check
+ * and tranfer onwership to ownerId role?
+ */
+bool
+mdb_admin_allow_bypass_owner_checks(Oid userId, Oid ownerId)
+{
+ Oid mdb_admin_roleoid;
+ /*
+ * Never allow nobody to grant objects to
+ * superusers.
+ * This can result in various CVE.
+ * For paranoic reasons, check this even before
+ * membership of mdb_admin role.
+ */
+ if (superuser_arg(ownerId)) {
+ return false;
+ }
+
+ mdb_admin_roleoid = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ /* Is userId actually member of mdb admin? */
+ if (!is_member_of_role(userId, mdb_admin_roleoid)) {
+ /* if no, disallow. */
+ return false;
+ }
+
+ /*
+ * Now, we need to check if ownerId
+ * is some dangerous role to trasfer membership to.
+ *
+ * For now, we check that ownerId does not have
+ * priviledge to execute server program or/and
+ * read/write server files, or/and pg read/write all data
+ */
+
+ /* All checks passed, hope will not be hacked here (again) */
+ return !has_privs_of_unwanted_system_role(ownerId);
+}
+
+// -- non-upstream patch end
/*
* Is member a member of role (directly or indirectly)?
@@ -5051,6 +5168,53 @@ check_is_member_of_role(Oid member, Oid role)
GetUserNameFromId(role, false))));
}
+// -- mdb admin patch
+/*
+ * check_mdb_admin_is_member_of_role
+ * is_member_of_role with a standard permission-violation error if not in usual case
+ * Is case `member` in mdb_admin we check that role is neither of superuser, pg_read/write
+ * server files nor pg_execute_server_program or pg_read/write all data
+ */
+void
+check_mdb_admin_is_member_of_role(Oid member, Oid role)
+{
+ Oid mdb_admin_roleoid;
+ /* fast path - if we are superuser, its ok */
+ if (superuser_arg(member)) {
+ return;
+ }
+
+ mdb_admin_roleoid = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ /* Is userId actually member of mdb admin? */
+ if (is_member_of_role(member, mdb_admin_roleoid)) {
+
+ /* role is mdb admin */
+ if (superuser_arg(role)) {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("cannot transfer ownership to superuser \"%s\"",
+ GetUserNameFromId(role, false))));
+ }
+
+ if (has_privs_of_unwanted_system_role(role)) {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("forbidden to transfer ownership to this system role in Cloud")));
+ }
+ } else {
+ /* if no, check membership transfer in usual way. */
+
+ if (!is_member_of_role(member, role)) {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be member of role \"%s\"",
+ GetUserNameFromId(role, false))));
+ }
+ }
+}
+
+// -- mdb admin patch
+
/*
* Is member a member of role, not considering superuserness?
*
@@ -5175,6 +5339,7 @@ select_best_grantor(Oid roleId, AclMode privileges,
List *roles_list;
int nrights;
ListCell *l;
+ Oid mdb_superuser_roleoid;
/*
* The object owner is always treated as having all grant options, so if
@@ -5189,6 +5354,16 @@ select_best_grantor(Oid roleId, AclMode privileges,
return;
}
+ mdb_superuser_roleoid = get_role_oid("mdb_superuser", true /*if nodoby created mdb_superuser role in this database*/);
+
+ if (is_member_of_role(GetUserId(), mdb_superuser_roleoid)
+ && has_privs_of_role(GetUserId(), ownerId)) {
+ *grantorId = mdb_superuser_roleoid;
+ AclMode mdb_superuser_allowed_privs = needed_goptions;
+ *grantOptions = mdb_superuser_allowed_privs;
+ return;
+ }
+
/*
* Otherwise we have to do a careful search to see if roleId has the
* privileges of any suitable role. Note: we can hang onto the result of
@@ -5197,7 +5372,6 @@ select_best_grantor(Oid roleId, AclMode privileges,
*/
roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
InvalidOid, NULL);
-
/* initialize candidate result as default */
*grantorId = roleId;
*grantOptions = ACL_NO_RIGHTS;
diff --git a/src/backend/utils/adt/mdb.c b/src/backend/utils/adt/mdb.c
new file mode 100644
index 00000000000..e5c695de1b6
--- /dev/null
+++ b/src/backend/utils/adt/mdb.c
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * mdb.c
+ * mdb routines
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/mdb.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/fmgrprotos.h"
+
+/*
+ * mdb_admin_enabled
+ * Check that mdb locale patch is enabled
+ */
+Datum
+mdb_locale_enabled(PG_FUNCTION_ARGS)
+{
+ bool res;
+
+#if USE_MDBLOCALES
+ res = true;
+#else
+ res = false;
+#endif
+
+ PG_RETURN_BOOL(res);
+}
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 11392891538..a9acb875eee 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -66,6 +66,7 @@
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/syscache.h"
+#include "common/mdb_locale.h"
#ifdef USE_ICU
#include
@@ -147,7 +148,7 @@ pg_perm_setlocale(int category, const char *locale)
const char *envvar;
#ifndef WIN32
- result = setlocale(category, locale);
+ result = SETLOCALE(category, locale);
#else
/*
@@ -165,7 +166,7 @@ pg_perm_setlocale(int category, const char *locale)
}
else
#endif
- result = setlocale(category, locale);
+ result = SETLOCALE(category, locale);
#endif /* WIN32 */
if (result == NULL)
@@ -252,7 +253,7 @@ check_locale(int category, const char *locale, char **canonname)
if (canonname)
*canonname = NULL; /* in case of failure */
- save = setlocale(category, NULL);
+ save = SETLOCALE(category, NULL);
if (!save)
return false; /* won't happen, we hope */
@@ -260,14 +261,14 @@ check_locale(int category, const char *locale, char **canonname)
save = pstrdup(save);
/* set the locale with setlocale, to see if it accepts it. */
- res = setlocale(category, locale);
+ res = SETLOCALE(category, locale);
/* save canonical name if requested. */
if (res && canonname)
*canonname = pstrdup(res);
/* restore old value. */
- if (!setlocale(category, save))
+ if (!SETLOCALE(category, save))
elog(WARNING, "failed to restore old locale \"%s\"", save);
pfree(save);
@@ -501,12 +502,12 @@ PGLC_localeconv(void)
memset(&worklconv, 0, sizeof(worklconv));
/* Save prevailing values of monetary and numeric locales */
- save_lc_monetary = setlocale(LC_MONETARY, NULL);
+ save_lc_monetary = SETLOCALE(LC_MONETARY, NULL);
if (!save_lc_monetary)
elog(ERROR, "setlocale(NULL) failed");
save_lc_monetary = pstrdup(save_lc_monetary);
- save_lc_numeric = setlocale(LC_NUMERIC, NULL);
+ save_lc_numeric = SETLOCALE(LC_NUMERIC, NULL);
if (!save_lc_numeric)
elog(ERROR, "setlocale(NULL) failed");
save_lc_numeric = pstrdup(save_lc_numeric);
@@ -528,7 +529,7 @@ PGLC_localeconv(void)
*/
/* Save prevailing value of ctype locale */
- save_lc_ctype = setlocale(LC_CTYPE, NULL);
+ save_lc_ctype = SETLOCALE(LC_CTYPE, NULL);
if (!save_lc_ctype)
elog(ERROR, "setlocale(NULL) failed");
save_lc_ctype = pstrdup(save_lc_ctype);
@@ -536,11 +537,11 @@ PGLC_localeconv(void)
/* Here begins the critical section where we must not throw error */
/* use numeric to set the ctype */
- setlocale(LC_CTYPE, locale_numeric);
+ SETLOCALE(LC_CTYPE, locale_numeric);
#endif
/* Get formatting information for numeric */
- setlocale(LC_NUMERIC, locale_numeric);
+ SETLOCALE(LC_NUMERIC, locale_numeric);
extlconv = localeconv();
/* Must copy data now in case setlocale() overwrites it */
@@ -550,11 +551,11 @@ PGLC_localeconv(void)
#ifdef WIN32
/* use monetary to set the ctype */
- setlocale(LC_CTYPE, locale_monetary);
+ SETLOCALE(LC_CTYPE, locale_monetary);
#endif
/* Get formatting information for monetary */
- setlocale(LC_MONETARY, locale_monetary);
+ SETLOCALE(LC_MONETARY, locale_monetary);
extlconv = localeconv();
/* Must copy data now in case setlocale() overwrites it */
@@ -584,12 +585,12 @@ PGLC_localeconv(void)
* should fail.
*/
#ifdef WIN32
- if (!setlocale(LC_CTYPE, save_lc_ctype))
+ if (!SETLOCALE(LC_CTYPE, save_lc_ctype))
elog(FATAL, "failed to restore LC_CTYPE to \"%s\"", save_lc_ctype);
#endif
- if (!setlocale(LC_MONETARY, save_lc_monetary))
+ if (!SETLOCALE(LC_MONETARY, save_lc_monetary))
elog(FATAL, "failed to restore LC_MONETARY to \"%s\"", save_lc_monetary);
- if (!setlocale(LC_NUMERIC, save_lc_numeric))
+ if (!SETLOCALE(LC_NUMERIC, save_lc_numeric))
elog(FATAL, "failed to restore LC_NUMERIC to \"%s\"", save_lc_numeric);
/*
@@ -773,7 +774,7 @@ cache_locale_time(void)
*/
/* Save prevailing value of time locale */
- save_lc_time = setlocale(LC_TIME, NULL);
+ save_lc_time = SETLOCALE(LC_TIME, NULL);
if (!save_lc_time)
elog(ERROR, "setlocale(NULL) failed");
save_lc_time = pstrdup(save_lc_time);
@@ -788,16 +789,16 @@ cache_locale_time(void)
*/
/* Save prevailing value of ctype locale */
- save_lc_ctype = setlocale(LC_CTYPE, NULL);
+ save_lc_ctype = SETLOCALE(LC_CTYPE, NULL);
if (!save_lc_ctype)
elog(ERROR, "setlocale(NULL) failed");
save_lc_ctype = pstrdup(save_lc_ctype);
/* use lc_time to set the ctype */
- setlocale(LC_CTYPE, locale_time);
+ SETLOCALE(LC_CTYPE, locale_time);
#endif
- setlocale(LC_TIME, locale_time);
+ SETLOCALE(LC_TIME, locale_time);
/* We use times close to current time as data for strftime(). */
timenow = time(NULL);
@@ -846,10 +847,10 @@ cache_locale_time(void)
* failure to do so is fatal.
*/
#ifdef WIN32
- if (!setlocale(LC_CTYPE, save_lc_ctype))
+ if (!SETLOCALE(LC_CTYPE, save_lc_ctype))
elog(FATAL, "failed to restore LC_CTYPE to \"%s\"", save_lc_ctype);
#endif
- if (!setlocale(LC_TIME, save_lc_time))
+ if (!SETLOCALE(LC_TIME, save_lc_time))
elog(FATAL, "failed to restore LC_TIME to \"%s\"", save_lc_time);
/*
@@ -1225,7 +1226,7 @@ check_strxfrm_bug(void)
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg_internal("strxfrm(), in locale \"%s\", writes past the specified array length",
- setlocale(LC_COLLATE, NULL)),
+ SETLOCALE(LC_COLLATE, NULL)),
errhint("Apply system library package updates.")));
}
@@ -1339,7 +1340,7 @@ lc_collate_is_c(Oid collation)
if (result >= 0)
return (bool) result;
- localeptr = setlocale(LC_COLLATE, NULL);
+ localeptr = SETLOCALE(LC_COLLATE, NULL);
if (!localeptr)
elog(ERROR, "invalid LC_COLLATE setting");
@@ -1389,7 +1390,7 @@ lc_ctype_is_c(Oid collation)
if (result >= 0)
return (bool) result;
- localeptr = setlocale(LC_CTYPE, NULL);
+ localeptr = SETLOCALE(LC_CTYPE, NULL);
if (!localeptr)
elog(ERROR, "invalid LC_CTYPE setting");
@@ -1518,8 +1519,10 @@ pg_newlocale_from_collation(Oid collid)
/* Normal case where they're the same */
errno = 0;
#ifndef WIN32
- loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate,
+
+ loc = NEWLOCALE(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate,
NULL);
+
#else
loc = _create_locale(LC_ALL, collcollate);
#endif
@@ -1533,11 +1536,11 @@ pg_newlocale_from_collation(Oid collid)
locale_t loc1;
errno = 0;
- loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL);
+ loc1 = NEWLOCALE(LC_COLLATE_MASK, collcollate, NULL);
if (!loc1)
report_newlocale_failure(collcollate);
errno = 0;
- loc = newlocale(LC_CTYPE_MASK, collctype, loc1);
+ loc = NEWLOCALE(LC_CTYPE_MASK, collctype, loc1);
if (!loc)
report_newlocale_failure(collctype);
#else
@@ -1680,12 +1683,16 @@ get_collation_actual_version(char collprovider, const char *collcollate)
{
#if defined(__GLIBC__)
/* Use the glibc version because we don't have anything better. */
+#ifdef USE_MDBLOCALES
+ collversion = pstrdup(mdb_localesversion());
+#else
collversion = pstrdup(gnu_get_libc_version());
+#endif
#elif defined(LC_VERSION_MASK)
locale_t loc;
/* Look up FreeBSD collation version. */
- loc = newlocale(LC_COLLATE, collcollate, NULL);
+ loc = NEWLOCALE(LC_COLLATE, collcollate, NULL);
if (loc)
{
collversion =
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 29287088ecf..952d1474870 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -40,6 +40,7 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
+#include "common/mdb_locale.h"
/*
* We maintain a simple linked list caching the fmgr lookup info for the
@@ -1308,7 +1309,7 @@ pg_bind_textdomain_codeset(const char *domainname)
int new_msgenc;
#ifndef WIN32
- const char *ctype = setlocale(LC_CTYPE, NULL);
+ const char *ctype = SETLOCALE(LC_CTYPE, NULL);
if (pg_strcasecmp(ctype, "C") == 0 || pg_strcasecmp(ctype, "POSIX") == 0)
#endif
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fb09180ebe9..3b9d6da07fb 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4928,7 +4928,7 @@ static struct config_enum ConfigureNamesEnum[] =
{
{"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the session's behavior for triggers and rewrite rules."),
- NULL
+ NULL, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, 0, true,
},
&SessionReplicationRole,
SESSION_REPLICATION_ROLE_ORIGIN, session_replication_role_options,
@@ -7625,6 +7625,7 @@ set_config_option(const char *name, const char *value,
void *newextra = NULL;
bool prohibitValueChange = false;
bool makeDefault;
+ Oid role;
if (elevel == 0)
{
@@ -7782,10 +7783,13 @@ set_config_option(const char *name, const char *value,
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
{
- ereport(elevel,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to set parameter \"%s\"",
- name)));
+ role = get_role_oid("mdb_admin", true /*if nodoby created mdb_admin role in this database*/);
+ if (!(record->mdb_admin_allowed && is_member_of_role(GetUserId(), role))) {
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ }
return 0;
}
break;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 4ed9869a2c9..708cf77ffdf 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -75,6 +75,7 @@
#include "getopt_long.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "common/mdb_locale.h"
#include "catalog/catalog.h"
@@ -2274,12 +2275,13 @@ locale_date_order(const char *locale)
result = DATEORDER_MDY; /* default */
- save = setlocale(LC_TIME, NULL);
+ save = SETLOCALE(LC_TIME, NULL);
+
if (!save)
return result;
save = pg_strdup(save);
- setlocale(LC_TIME, locale);
+ SETLOCALE(LC_TIME, locale);
memset(&testtime, 0, sizeof(testtime));
testtime.tm_mday = 22;
@@ -2288,7 +2290,7 @@ locale_date_order(const char *locale)
res = my_strftime(buf, sizeof(buf), "%x", &testtime);
- setlocale(LC_TIME, save);
+ SETLOCALE(LC_TIME, save);
free(save);
if (res == 0)
@@ -2332,7 +2334,7 @@ check_locale_name(int category, const char *locale, char **canonname)
if (canonname)
*canonname = NULL; /* in case of failure */
- save = setlocale(category, NULL);
+ save = SETLOCALE(category, NULL);
if (!save)
{
pg_log_error("setlocale() failed");
@@ -2347,14 +2349,14 @@ check_locale_name(int category, const char *locale, char **canonname)
locale = "";
/* set the locale with setlocale, to see if it accepts it. */
- res = setlocale(category, locale);
+ res = SETLOCALE(category, locale);
/* save canonical name if requested. */
if (res && canonname)
*canonname = pg_strdup(res);
/* restore old value. */
- if (!setlocale(category, save))
+ if (!SETLOCALE(category, save))
{
pg_log_error("failed to restore old locale \"%s\"", save);
exit(1);
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index d0905f3d588..1859443ed87 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -16,6 +16,8 @@
#include "mb/pg_wchar.h"
#include "pg_upgrade.h"
#include "greenplum/pg_upgrade_greenplum.h"
+#include "common/mdb_locale.h"
+
static void check_new_cluster_is_empty(void);
static void check_databases_are_compatible(void);
@@ -1629,7 +1631,8 @@ get_canonical_locale_name(int category, const char *locale)
char *res;
/* get the current setting, so we can restore it. */
- save = setlocale(category, NULL);
+
+ save = SETLOCALE(category, NULL);
if (!save)
pg_fatal("failed to get the current locale\n");
@@ -1637,7 +1640,7 @@ get_canonical_locale_name(int category, const char *locale)
save = (char *) pg_strdup(save);
/* set the locale with setlocale, to see if it accepts it. */
- res = setlocale(category, locale);
+ res = SETLOCALE(category, locale);
if (!res)
pg_fatal("failed to get system locale name for \"%s\"\n", locale);
@@ -1645,7 +1648,7 @@ get_canonical_locale_name(int category, const char *locale)
res = pg_strdup(res);
/* restore old value. */
- if (!setlocale(category, save))
+ if (!SETLOCALE(category, save))
pg_fatal("failed to restore old locale \"%s\"\n", save);
pg_free(save);
diff --git a/src/common/exec.c b/src/common/exec.c
index 7dd2f8c4942..5159b616a39 100644
--- a/src/common/exec.c
+++ b/src/common/exec.c
@@ -24,6 +24,8 @@
#include
#include
#include
+#include "common/mdb_locale.h"
+
/* Inhibit mingw CRT's auto-globbing of command line arguments */
#if defined(WIN32) && !defined(_MSC_VER)
@@ -443,7 +445,7 @@ set_pglocale_pgservice(const char *argv0, const char *app)
/* don't set LC_ALL in the backend */
if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
{
- setlocale(LC_ALL, "");
+ SETLOCALE(LC_ALL, "");
/*
* One could make a case for reproducing here PostmasterMain()'s test
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index 4bbb035eaea..f053a30b009 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -30,8 +30,8 @@
#define MaxMultiXactOffset ((MultiXactOffset) 0xFFFFFFFF)
/* Number of SLRU buffers to use for multixact */
-#define NUM_MULTIXACTOFFSET_BUFFERS 8
-#define NUM_MULTIXACTMEMBER_BUFFERS 16
+#define NUM_MULTIXACTOFFSET_BUFFERS 32
+#define NUM_MULTIXACTMEMBER_BUFFERS 64
/*
* Possible multixact lock modes ("status"). The first four modes are for
diff --git a/src/include/access/subtrans.h b/src/include/access/subtrans.h
index 9a54dc0fb3b..73503a26dcc 100644
--- a/src/include/access/subtrans.h
+++ b/src/include/access/subtrans.h
@@ -12,7 +12,7 @@
#define SUBTRANS_H
/* Number of SLRU buffers to use for subtrans */
-#define NUM_SUBTRANS_BUFFERS 32
+#define NUM_SUBTRANS_BUFFERS 64
typedef struct SubTransData
{
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a47b1ef1615..1093fa948b8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11758,7 +11758,9 @@
#
# GPDB ADDITIONS START HERE
#
-
+{ oid => '16383', descr => 'contains',
+ proname => 'mdb_locale_enabled', prorettype => 'bool',
+ proargtypes => '', prosrc => 'mdb_locale_enabled' },
{ oid => '7178', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_preassigned_oids', provolatile => 'v',
proparallel => 'u', prorettype => 'void', proargtypes => '_oid',
diff --git a/src/include/common/mdb_locale.h b/src/include/common/mdb_locale.h
new file mode 100644
index 00000000000..91d8656c2c2
--- /dev/null
+++ b/src/include/common/mdb_locale.h
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * mdb_locale.h
+ * Generic headers for custom MDB-locales patch.
+ *
+ * IDENTIFICATION
+ * src/include/common/mdb_locale.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PG_MDB_LOCALE_H
+#define PG_MDB_LOCALE_H
+
+#ifdef USE_MDBLOCALES
+#include
+#define SETLOCALE(category, locale) mdb_setlocale(category, locale)
+#define NEWLOCALE(category, locale, base) mdb_newlocale(category, locale, base)
+#else
+#define SETLOCALE(category, locale) setlocale(category, locale)
+#define NEWLOCALE(category, locale, base) newlocale(category, locale, base)
+#endif
+
+#endif /* PG_MDB_LOCALE_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index aaa3ea32e8a..54de6844f58 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -392,6 +392,9 @@
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
+/* Define to 1 if you have the `mdblocales' library (-lmdblocales). */
+#undef HAVE_LIBMDBLOCALES
+
/* Define to 1 if you have the `numa' library (-lnuma). */
#undef HAVE_LIBNUMA
@@ -1041,6 +1044,9 @@
/* Define to 1 to build with LZ4 support. (--with-lz4) */
#undef USE_LZ4
+/* Define to 1 to build with MDB locales. (--with-mdblocales) */
+#undef USE_MDBLOCALES
+
/* Define to 1 to build with Mapreduce capabilities (--enable-mapreduce) */
#undef USE_MAPREDUCE
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 223175099bd..49068f04b2f 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -207,9 +207,17 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
extern int aclmembers(const Acl *acl, Oid **roleids);
extern bool has_privs_of_role(Oid member, Oid role);
+extern bool has_privs_of_role_strict(Oid member, Oid role);
extern bool is_member_of_role(Oid member, Oid role);
extern bool is_member_of_role_nosuper(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role);
+
+// -- non-upstream patch begin
+extern bool mdb_admin_allow_bypass_owner_checks(Oid userId, Oid ownerId);
+
+extern void check_mdb_admin_is_member_of_role(Oid member, Oid role);
+// -- non-upstream patch end
+
extern void check_is_member_of_role(Oid member, Oid role);
extern Oid get_role_oid(const char *rolename, bool missing_ok);
extern Oid get_role_oid_or_public(const char *rolename);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index 139b7355d13..139646d4a40 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -319,6 +319,9 @@ extern uint64 pgstat_get_my_query_id(void);
extern int pgstat_fetch_stat_numbackends(void);
extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid);
extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid);
+/* -- mdb admin patch -- */
+extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry_by_pid(int pid);
+/* -- mdb admin patch end -- */
extern char *pgstat_clip_activity(const char *raw_activity);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 17d2a166b09..08584e4db54 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -204,6 +204,8 @@ struct config_generic
char *sourcefile; /* file current setting is from (NULL if not
* set in config file) */
int sourceline; /* line in source file */
+
+ bool mdb_admin_allowed; /* is mdb admin allowed to change this, makes sence only for superuser/not superuser ctx */
};
/* bit values in status field */
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 056940cb252..f4d2da9173a 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -9,6 +9,7 @@
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "sqlca.h"
+#include "common/mdb_locale.h"
#ifdef HAVE_USELOCALE
locale_t ecpg_clocale = (locale_t) 0;
@@ -517,7 +518,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
#ifdef HAVE_USELOCALE
if (!ecpg_clocale)
{
- ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+ ecpg_clocale = NEWLOCALE(LC_NUMERIC_MASK, "C", (locale_t) 0);
if (!ecpg_clocale)
{
#ifdef ENABLE_THREAD_SAFETY
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index f1898dec6a6..2238febbbdd 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -15,6 +15,8 @@
#include "sql3types.h"
#include "sqlca.h"
#include "sqlda.h"
+#include "common/mdb_locale.h"
+
static void descriptor_free(struct descriptor *desc);
@@ -500,8 +502,8 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
#ifdef HAVE__CONFIGTHREADLOCALE
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
#endif
- stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
- setlocale(LC_NUMERIC, "C");
+ stmt.oldlocale = ecpg_strdup(SETLOCALE(LC_NUMERIC, NULL), lineno);
+ SETLOCALE(LC_NUMERIC, "C");
#endif
/* desperate try to guess something sensible */
@@ -514,7 +516,7 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
#else
if (stmt.oldlocale)
{
- setlocale(LC_NUMERIC, stmt.oldlocale);
+ SETLOCALE(LC_NUMERIC, stmt.oldlocale);
ecpg_free(stmt.oldlocale);
}
#ifdef HAVE__CONFIGTHREADLOCALE
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index e8e8fb2b2c3..eafdd8e421a 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -31,6 +31,7 @@
#include "sqlca.h"
#include "sqlda-compat.h"
#include "sqlda-native.h"
+#include "common/mdb_locale.h"
/*
* This function returns a newly malloced string that has ' and \
@@ -2002,13 +2003,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
#ifdef HAVE__CONFIGTHREADLOCALE
stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
#endif
- stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
+ stmt->oldlocale = ecpg_strdup(SETLOCALE(LC_NUMERIC, NULL), lineno);
if (stmt->oldlocale == NULL)
{
ecpg_do_epilogue(stmt);
return false;
}
- setlocale(LC_NUMERIC, "C");
+ SETLOCALE(LC_NUMERIC, "C");
#endif
/*
@@ -2222,7 +2223,7 @@ ecpg_do_epilogue(struct statement *stmt)
uselocale(stmt->oldlocale);
#else
if (stmt->oldlocale)
- setlocale(LC_NUMERIC, stmt->oldlocale);
+ SETLOCALE(LC_NUMERIC, stmt->oldlocale);
#ifdef HAVE__CONFIGTHREADLOCALE
/*
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 43682574b23..ed3df424ae4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -83,7 +83,7 @@ endif
# that are built correctly for use in a shlib.
SHLIB_LINK_INTERNAL = -lpgcommon_shlib -lpgport_shlib
ifneq ($(PORTNAME), win32)
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl -lm, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl -lm -lmdblocales, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
else
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl -lm $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
endif
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 48591e48429..3aff8e95450 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
+#include "common/mdb_locale.h"
/* define our text domain for translations */
#undef TEXTDOMAIN
@@ -743,15 +744,15 @@ plperl_init_interp(void)
*save_numeric,
*save_time;
- loc = setlocale(LC_COLLATE, NULL);
+ loc = SETLOCALE(LC_COLLATE, NULL);
save_collate = loc ? pstrdup(loc) : NULL;
- loc = setlocale(LC_CTYPE, NULL);
+ loc = SETLOCALE(LC_CTYPE, NULL);
save_ctype = loc ? pstrdup(loc) : NULL;
- loc = setlocale(LC_MONETARY, NULL);
+ loc = SETLOCALE(LC_MONETARY, NULL);
save_monetary = loc ? pstrdup(loc) : NULL;
- loc = setlocale(LC_NUMERIC, NULL);
+ loc = SETLOCALE(LC_NUMERIC, NULL);
save_numeric = loc ? pstrdup(loc) : NULL;
- loc = setlocale(LC_TIME, NULL);
+ loc = SETLOCALE(LC_TIME, NULL);
save_time = loc ? pstrdup(loc) : NULL;
#define PLPERL_RESTORE_LOCALE(name, saved) \
@@ -4167,7 +4168,7 @@ static char *
setlocale_perl(int category, char *locale)
{
dTHX;
- char *RETVAL = setlocale(category, locale);
+ char *RETVAL = SETLOCALE(category, locale);
if (RETVAL)
{
@@ -4182,7 +4183,7 @@ setlocale_perl(int category, char *locale)
#ifdef LC_ALL
if (category == LC_ALL)
- newctype = setlocale(LC_CTYPE, NULL);
+ newctype = SETLOCALE(LC_CTYPE, NULL);
else
#endif
newctype = RETVAL;
@@ -4200,7 +4201,7 @@ setlocale_perl(int category, char *locale)
#ifdef LC_ALL
if (category == LC_ALL)
- newcoll = setlocale(LC_COLLATE, NULL);
+ newcoll = SETLOCALE(LC_COLLATE, NULL);
else
#endif
newcoll = RETVAL;
@@ -4219,7 +4220,7 @@ setlocale_perl(int category, char *locale)
#ifdef LC_ALL
if (category == LC_ALL)
- newnum = setlocale(LC_NUMERIC, NULL);
+ newnum = SETLOCALE(LC_NUMERIC, NULL);
else
#endif
newnum = RETVAL;
diff --git a/src/port/chklocale.c b/src/port/chklocale.c
index 3d47d37eae4..2dae78e74e9 100644
--- a/src/port/chklocale.c
+++ b/src/port/chklocale.c
@@ -18,6 +18,8 @@
#else
#include "postgres_fe.h"
#endif
+#include "common/mdb_locale.h"
+
#ifdef HAVE_LANGINFO_H
#include
@@ -343,7 +345,7 @@ pg_get_encoding_from_locale(const char *ctype, bool write_message)
pg_strcasecmp(ctype, "POSIX") == 0)
return PG_SQL_ASCII;
- save = setlocale(LC_CTYPE, NULL);
+ save = SETLOCALE(LC_CTYPE, NULL);
if (!save)
return -1; /* setlocale() broken? */
/* must copy result, or it might change after setlocale */
@@ -351,7 +353,7 @@ pg_get_encoding_from_locale(const char *ctype, bool write_message)
if (!save)
return -1; /* out of memory; unlikely */
- name = setlocale(LC_CTYPE, ctype);
+ name = SETLOCALE(LC_CTYPE, ctype);
if (!name)
{
free(save);
@@ -366,13 +368,13 @@ pg_get_encoding_from_locale(const char *ctype, bool write_message)
sys = win32_langinfo(name);
#endif
- setlocale(LC_CTYPE, save);
+ SETLOCALE(LC_CTYPE, save);
free(save);
}
else
{
/* much easier... */
- ctype = setlocale(LC_CTYPE, NULL);
+ ctype = SETLOCALE(LC_CTYPE, NULL);
if (!ctype)
return -1; /* setlocale() broken? */
diff --git a/src/test/Makefile b/src/test/Makefile
index d84edb282df..150c4e97b73 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -18,6 +18,9 @@ SUBDIRS = perl regress isolation modules authentication recovery
SUBDIRS += fsync walrep heap_checksum isolation2 fdw singlenode_regress singlenode_isolation2
+# MDB addon
+SUBDIRS += mdb_admin
+
# Test suites that are not safe by default but can be run if selected
# by the user via the whitespace-separated list in variable
# PG_TEST_EXTRA:
diff --git a/src/test/locale/test-ctype.c b/src/test/locale/test-ctype.c
index a3f896c5ecb..10c2b49cb92 100644
--- a/src/test/locale/test-ctype.c
+++ b/src/test/locale/test-ctype.c
@@ -23,6 +23,8 @@ the author shall be liable for any damage, etc.
#include
#include
#include
+#include "common/mdb_locale.h"
+
char *flag(int b);
void describe_char(int c);
@@ -62,7 +64,7 @@ main()
short c;
char *cur_locale;
- cur_locale = setlocale(LC_ALL, "");
+ cur_locale = SETLOCALE(LC_ALL, "");
if (cur_locale)
fprintf(stderr, "Successfully set locale to \"%s\"\n", cur_locale);
else
diff --git a/src/test/mdb_admin/.gitignore b/src/test/mdb_admin/.gitignore
new file mode 100644
index 00000000000..871e943d50e
--- /dev/null
+++ b/src/test/mdb_admin/.gitignore
@@ -0,0 +1,2 @@
+# Generated by test suite
+/tmp_check/
diff --git a/src/test/mdb_admin/Makefile b/src/test/mdb_admin/Makefile
new file mode 100644
index 00000000000..e4e82367da9
--- /dev/null
+++ b/src/test/mdb_admin/Makefile
@@ -0,0 +1,23 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/mdb_admin
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/mdb_admin/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/mdb_admin
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+check:
+ $(prove_check)
+
+installcheck:
+ $(prove_installcheck)
+
+clean distclean maintainer-clean:
+ rm -rf tmp_check
diff --git a/src/test/mdb_admin/t/signals.pl b/src/test/mdb_admin/t/signals.pl
new file mode 100644
index 00000000000..a11db27a527
--- /dev/null
+++ b/src/test/mdb_admin/t/signals.pl
@@ -0,0 +1,74 @@
+
+# Copyright (c) 2024-2024, MDB, Mother Russia
+
+# Minimal test testing streaming replication
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Initialize primary node
+my $node_primary = PostgreSQL::Test::Cluster->new('primary');
+$node_primary->init();
+$node_primary->start;
+
+# Create some content on primary and check its presence in standby nodes
+$node_primary->safe_psql('postgres',
+ "
+ CREATE DATABASE regress;
+ CREATE ROLE mdb_admin;
+ CREATE ROLE mdb_reg_lh_1;
+ CREATE ROLE mdb_reg_lh_2;
+ GRANT pg_signal_backend TO mdb_admin;
+ GRANT pg_signal_backend TO mdb_reg_lh_1;
+ GRANT mdb_admin TO mdb_reg_lh_2;
+");
+
+# Create some content on primary and check its presence in standby nodes
+$node_primary->safe_psql('regress',
+ "
+ CREATE TABLE tab_int(i int);
+ INSERT INTO tab_int SELECT * FROm generate_series(1, 1000000);
+ ALTER SYSTEM SET autovacuum_vacuum_cost_limit TO 1;
+ ALTER SYSTEM SET autovacuum_vacuum_cost_delay TO 100;
+ ALTER SYSTEM SET autovacuum_naptime TO 1;
+");
+
+$node_primary->restart;
+
+sleep 1;
+
+my $res_pid = $node_primary->safe_psql('regress',
+ "
+ SELECT pid FROM pg_stat_activity WHERE backend_type = 'autovacuum worker' and datname = 'regress';;
+");
+
+
+print "pid is $res_pid\n";
+
+ok(1);
+
+
+my ($res_reg_lh_1, $stdout_reg_lh_1, $stderr_reg_lh_1) = $node_primary->psql('regress',
+ "
+ SET ROLE mdb_reg_lh_1;
+ SELECT pg_terminate_backend($res_pid);
+");
+
+# print ($res_reg_lh_1, $stdout_reg_lh_1, $stderr_reg_lh_1, "\n");
+
+ok($res_reg_lh_1 != 0, "should fail for non-mdb_admin");
+like($stderr_reg_lh_1, qr/ERROR: must be a superuser to terminate superuser process/, "matches");
+
+my ($res_reg_lh_2, $stdout_reg_lh_2, $stderr_reg_lh_2) = $node_primary->psql('regress',
+ "
+ SET ROLE mdb_reg_lh_2;
+ SELECT pg_terminate_backend($res_pid);
+");
+
+ok($res_reg_lh_2 == 0, "should success for mdb_admin");
+
+# print ($res_reg_lh_2, $stdout_reg_lh_2, $stderr_reg_lh_2, "\n");
+
+done_testing();
\ No newline at end of file
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 8380df1591f..7842a3c1c82 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -166,10 +166,10 @@ SET SESSION AUTHORIZATION regress_unpriv_user;
SET search_path TO temp_func_test, public;
ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF;
ALTER FUNCTION functest_E_2(int) LEAKPROOF;
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql'
LEAKPROOF AS 'SELECT $1 < 200'; -- fail
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
RESET SESSION AUTHORIZATION;
--
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
diff --git a/src/test/regress/expected/create_function_3_optimizer.out b/src/test/regress/expected/create_function_3_optimizer.out
index 3ae669d518a..3256709e1aa 100644
--- a/src/test/regress/expected/create_function_3_optimizer.out
+++ b/src/test/regress/expected/create_function_3_optimizer.out
@@ -166,10 +166,10 @@ SET SESSION AUTHORIZATION regress_unpriv_user;
SET search_path TO temp_func_test, public;
ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF;
ALTER FUNCTION functest_E_2(int) LEAKPROOF;
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql'
LEAKPROOF AS 'SELECT $1 < 200'; -- fail
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
RESET SESSION AUTHORIZATION;
--
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
diff --git a/src/test/regress/expected/mdb_admin.out b/src/test/regress/expected/mdb_admin.out
new file mode 100644
index 00000000000..e4dfc436802
--- /dev/null
+++ b/src/test/regress/expected/mdb_admin.out
@@ -0,0 +1,100 @@
+CREATE ROLE regress_mdb_admin_user1;
+CREATE ROLE regress_mdb_admin_user2;
+CREATE ROLE regress_mdb_admin_user3;
+CREATE ROLE regress_superuser WITH SUPERUSER;
+GRANT mdb_admin TO regress_mdb_admin_user1;
+GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user2;
+GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user3;
+-- mdb admin trasfers ownership to another role
+SET ROLE regress_mdb_admin_user2;
+CREATE FUNCTION regress_mdb_admin_add(integer, integer) RETURNS integer
+ AS 'SELECT $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+CREATE SCHEMA regress_mdb_admin_schema;
+GRANT CREATE ON SCHEMA regress_mdb_admin_schema TO regress_mdb_admin_user3;
+CREATE TABLE regress_mdb_admin_schema.regress_mdb_admin_table();
+CREATE TABLE regress_mdb_admin_table();
+CREATE VIEW regress_mdb_admin_view as SELECT 1;
+SET ROLE regress_mdb_admin_user1;
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_mdb_admin_user3;
+ALTER VIEW regress_mdb_admin_view OWNER TO regress_mdb_admin_user3;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
+ALTER TABLE regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_mdb_admin_user3;
+-- mdb admin fails to transfer ownership to superusers and particular system roles
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_superuser;
+ERROR: cannot transfer ownership to superuser "regress_superuser"
+ALTER VIEW regress_mdb_admin_view OWNER TO regress_superuser;
+ERROR: cannot transfer ownership to superuser "regress_superuser"
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_superuser;
+ERROR: cannot transfer ownership to superuser "regress_superuser"
+ALTER TABLE regress_mdb_admin_table OWNER TO regress_superuser;
+ERROR: cannot transfer ownership to superuser "regress_superuser"
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_superuser;
+ERROR: cannot transfer ownership to superuser "regress_superuser"
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_execute_server_program;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_execute_server_program;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_execute_server_program;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_execute_server_program;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_execute_server_program;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_server_files;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_all_data;
+ERROR: forbidden to transfer ownership to this system role in Cloud
+-- end tests
+RESET SESSION AUTHORIZATION;
+--
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_admin_user2;
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_admin_user3;
+DROP VIEW regress_mdb_admin_view;
+DROP FUNCTION regress_mdb_admin_add;
+DROP TABLE regress_mdb_admin_schema.regress_mdb_admin_table;
+DROP TABLE regress_mdb_admin_table;
+DROP SCHEMA regress_mdb_admin_schema;
+DROP ROLE regress_mdb_admin_user1;
+DROP ROLE regress_mdb_admin_user2;
+DROP ROLE regress_mdb_admin_user3;
+DROP ROLE regress_superuser;
diff --git a/src/test/regress/expected/mdb_superuser.out b/src/test/regress/expected/mdb_superuser.out
new file mode 100644
index 00000000000..21bafb1011b
--- /dev/null
+++ b/src/test/regress/expected/mdb_superuser.out
@@ -0,0 +1,115 @@
+CREATE ROLE regress_mdb_superuser_user1;
+CREATE ROLE regress_mdb_superuser_user2;
+CREATE ROLE regress_mdb_superuser_user3;
+GRANT mdb_admin TO mdb_superuser;
+CREATE ROLE regress_superuser WITH SUPERUSER;
+GRANT mdb_superuser TO regress_mdb_superuser_user1;
+GRANT CREATE ON DATABASE regression TO regress_mdb_superuser_user2;
+GRANT CREATE ON DATABASE regression TO regress_mdb_superuser_user3;
+SET ROLE regress_mdb_superuser_user2;
+CREATE FUNCTION regress_mdb_superuser_add(integer, integer) RETURNS integer
+ AS 'SELECT $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+CREATE SCHEMA regress_mdb_superuser_schema;
+CREATE TABLE regress_mdb_superuser_schema.regress_mdb_superuser_table();
+CREATE TABLE regress_mdb_superuser_table();
+CREATE VIEW regress_mdb_superuser_view as SELECT 1;
+SET ROLE regress_mdb_superuser_user3;
+INSERT INTO regress_mdb_superuser_table SELECT * FROM regress_mdb_superuser_table;
+ERROR: permission denied for table regress_mdb_superuser_table
+SET ROLE regress_mdb_superuser_user1;
+-- mdb_superuser can grant to other role
+GRANT USAGE, CREATE ON SCHEMA regress_mdb_superuser_schema TO regress_mdb_superuser_user3;
+GRANT ALL PRIVILEGES ON TABLE regress_mdb_superuser_table TO regress_mdb_superuser_user3;
+REVOKE ALL PRIVILEGES ON TABLE regress_mdb_superuser_table FROM regress_mdb_superuser_user3;
+GRANT INSERT, SELECT ON TABLE regress_mdb_superuser_table TO regress_mdb_superuser_user3;
+-- grant works
+SET ROLE regress_mdb_superuser_user3;
+INSERT INTO regress_mdb_superuser_table SELECT * FROM regress_mdb_superuser_table;
+SET ROLE mdb_superuser;
+-- mdb_superuser drop object of other role
+DROP TABLE regress_mdb_superuser_table;
+-- mdb admin fails to transfer ownership to superusers and system roles
+RESET SESSION AUTHORIZATION;
+CREATE TABLE regress_superuser_table();
+SET ROLE pg_read_server_files;
+CREATE TABLE regress_pgrsf_table();
+SET ROLE pg_write_server_files;
+CREATE TABLE regress_pgwsf_table();
+SET ROLE pg_execute_server_program;
+CREATE TABLE regress_pgxsp_table();
+SET ROLE pg_read_all_data;
+CREATE TABLE regress_pgrad_table();
+SET ROLE pg_write_all_data;
+CREATE TABLE regress_pgrwd_table();
+SET ROLE mdb_superuser;
+-- cannot read all data (fail)
+SELECT * FROM pg_authid;
+ERROR: permission denied for table pg_authid
+-- can not drop superuser objects, because does not has_privs_of pg_database_owner
+DROP TABLE regress_superuser_table;
+ERROR: must be owner of table regress_superuser_table
+DROP TABLE regress_pgrsf_table;
+ERROR: must be owner of table regress_pgrsf_table
+DROP TABLE regress_pgwsf_table;
+ERROR: must be owner of table regress_pgwsf_table
+DROP TABLE regress_pgxsp_table;
+ERROR: must be owner of table regress_pgxsp_table
+DROP TABLE regress_pgrad_table;
+ERROR: must be owner of table regress_pgrad_table
+DROP TABLE regress_pgrwd_table;
+ERROR: must be owner of table regress_pgrwd_table
+-- does allowed to creare database, role or extension
+-- or grant such priviledge
+CREATE DATABASE regress_db_fail;
+ERROR: permission denied to create database
+CREATE ROLE regress_role_fail;
+ERROR: permission denied to create role
+ALTER ROLE mdb_superuser WITH CREATEROLE;
+ERROR: permission denied
+ALTER ROLE mdb_superuser WITH CREATEDB;
+ERROR: permission denied
+ALTER ROLE regress_mdb_superuser_user2 WITH CREATEROLE;
+ERROR: permission denied
+ALTER ROLE regress_mdb_superuser_user2 WITH CREATEDB;
+ERROR: permission denied
+-- mdb_superuser more powerfull than pg_database_owner
+RESET SESSION AUTHORIZATION;
+CREATE DATABASE regress_check_owner OWNER regress_mdb_superuser_user2;
+\c regress_check_owner;
+SET ROLE regress_mdb_superuser_user2;
+CREATE SCHEMA regtest;
+CREATE TABLE regtest.regtest();
+-- this should fail
+SET ROLE regress_mdb_superuser_user3;
+GRANT ALL ON TABLE regtest.regtest TO regress_mdb_superuser_user3;
+ERROR: permission denied for schema regtest
+ALTER TABLE regtest.regtest OWNER TO regress_mdb_superuser_user3;
+ERROR: permission denied for schema regtest
+SET ROLE regress_mdb_superuser_user1;
+GRANT ALL ON TABLE regtest.regtest TO regress_mdb_superuser_user1;
+ALTER TABLE regtest.regtest OWNER TO regress_mdb_superuser_user1;
+\c regression
+DROP DATABASE regress_check_owner;
+-- end tests
+RESET SESSION AUTHORIZATION;
+--
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_superuser_user2;
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_superuser_user3;
+DROP VIEW regress_mdb_superuser_view;
+DROP FUNCTION regress_mdb_superuser_add;
+DROP TABLE regress_mdb_superuser_schema.regress_mdb_superuser_table;
+DROP TABLE regress_mdb_superuser_table;
+ERROR: table "regress_mdb_superuser_table" does not exist
+DROP SCHEMA regress_mdb_superuser_schema;
+DROP ROLE regress_mdb_superuser_user1;
+DROP ROLE regress_mdb_superuser_user2;
+DROP ROLE regress_mdb_superuser_user3;
+DROP TABLE regress_superuser_table;
+DROP TABLE regress_pgrsf_table;
+DROP TABLE regress_pgwsf_table;
+DROP TABLE regress_pgxsp_table;
+DROP TABLE regress_pgrad_table;
+DROP TABLE regress_pgrwd_table;
diff --git a/src/test/regress/expected/test_setup.out b/src/test/regress/expected/test_setup.out
new file mode 100644
index 00000000000..c1cb724ef37
--- /dev/null
+++ b/src/test/regress/expected/test_setup.out
@@ -0,0 +1,5 @@
+--
+-- TEST_SETUP --- prepare environment expected by regression test scripts
+--
+CREATE ROLE mdb_admin;
+CREATE ROLE mdb_superuser;
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source
index 331499a2aba..2abe2c82eb8 100644
--- a/src/test/regress/input/misc.source
+++ b/src/test/regress/input/misc.source
@@ -264,3 +264,8 @@ SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h;
--
-- rewrite rules
--
+
+
+--- mdb-related
+
+SELECT mdb_locale_enabled();
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source
index 18bcc227f0a..f2f7c0dee32 100644
--- a/src/test/regress/output/misc.source
+++ b/src/test/regress/output/misc.source
@@ -609,3 +609,10 @@ CONTEXT: SQL function "equipment" during startup
--
-- rewrite rules
--
+--- mdb-related
+SELECT mdb_locale_enabled();
+ mdb_locale_enabled
+--------------------
+ f
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e2df0208627..b2ed818f677 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -5,10 +5,18 @@
# this limits the number of connections needed to run the tests.
# ----------
+# mdb admin simple checks
+test: test_setup
+
# run tablespace by itself, and first, because it forces a checkpoint;
# we'd prefer not to have checkpoints later in the tests because that
# interferes with crash-recovery testing.
test: tablespace
+
+test: mdb_admin
+
+test: mdb_superuser
+
# ----------
# The first group of parallel tests
# ----------
diff --git a/src/test/regress/sql/mdb_admin.sql b/src/test/regress/sql/mdb_admin.sql
new file mode 100644
index 00000000000..b6b048e5692
--- /dev/null
+++ b/src/test/regress/sql/mdb_admin.sql
@@ -0,0 +1,87 @@
+CREATE ROLE regress_mdb_admin_user1;
+CREATE ROLE regress_mdb_admin_user2;
+CREATE ROLE regress_mdb_admin_user3;
+
+CREATE ROLE regress_superuser WITH SUPERUSER;
+
+GRANT mdb_admin TO regress_mdb_admin_user1;
+GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user2;
+GRANT CREATE ON DATABASE regression TO regress_mdb_admin_user3;
+
+-- mdb admin trasfers ownership to another role
+
+SET ROLE regress_mdb_admin_user2;
+CREATE FUNCTION regress_mdb_admin_add(integer, integer) RETURNS integer
+ AS 'SELECT $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+
+CREATE SCHEMA regress_mdb_admin_schema;
+GRANT CREATE ON SCHEMA regress_mdb_admin_schema TO regress_mdb_admin_user3;
+CREATE TABLE regress_mdb_admin_schema.regress_mdb_admin_table();
+CREATE TABLE regress_mdb_admin_table();
+CREATE VIEW regress_mdb_admin_view as SELECT 1;
+SET ROLE regress_mdb_admin_user1;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_mdb_admin_user3;
+ALTER VIEW regress_mdb_admin_view OWNER TO regress_mdb_admin_user3;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
+ALTER TABLE regress_mdb_admin_table OWNER TO regress_mdb_admin_user3;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_mdb_admin_user3;
+
+
+-- mdb admin fails to transfer ownership to superusers and particular system roles
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO regress_superuser;
+ALTER VIEW regress_mdb_admin_view OWNER TO regress_superuser;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO regress_superuser;
+ALTER TABLE regress_mdb_admin_table OWNER TO regress_superuser;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO regress_superuser;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_execute_server_program;
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_execute_server_program;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_execute_server_program;
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_execute_server_program;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_execute_server_program;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_server_files;
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_server_files;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_server_files;
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_server_files;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_server_files;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_server_files;
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_server_files;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_server_files;
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_server_files;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_server_files;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_write_all_data;
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_write_all_data;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_write_all_data;
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_write_all_data;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_write_all_data;
+
+ALTER FUNCTION regress_mdb_admin_add (integer, integer) OWNER TO pg_read_all_data;
+ALTER VIEW regress_mdb_admin_view OWNER TO pg_read_all_data;
+ALTER TABLE regress_mdb_admin_schema.regress_mdb_admin_table OWNER TO pg_read_all_data;
+ALTER TABLE regress_mdb_admin_table OWNER TO pg_read_all_data;
+ALTER SCHEMA regress_mdb_admin_schema OWNER TO pg_read_all_data;
+
+-- end tests
+
+RESET SESSION AUTHORIZATION;
+--
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_admin_user2;
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_admin_user3;
+
+DROP VIEW regress_mdb_admin_view;
+DROP FUNCTION regress_mdb_admin_add;
+DROP TABLE regress_mdb_admin_schema.regress_mdb_admin_table;
+DROP TABLE regress_mdb_admin_table;
+DROP SCHEMA regress_mdb_admin_schema;
+DROP ROLE regress_mdb_admin_user1;
+DROP ROLE regress_mdb_admin_user2;
+DROP ROLE regress_mdb_admin_user3;
+DROP ROLE regress_superuser;
diff --git a/src/test/regress/sql/mdb_superuser.sql b/src/test/regress/sql/mdb_superuser.sql
new file mode 100644
index 00000000000..f96338f3aec
--- /dev/null
+++ b/src/test/regress/sql/mdb_superuser.sql
@@ -0,0 +1,144 @@
+CREATE ROLE regress_mdb_superuser_user1;
+CREATE ROLE regress_mdb_superuser_user2;
+CREATE ROLE regress_mdb_superuser_user3;
+
+GRANT mdb_admin TO mdb_superuser;
+
+CREATE ROLE regress_superuser WITH SUPERUSER;
+
+GRANT mdb_superuser TO regress_mdb_superuser_user1;
+
+GRANT CREATE ON DATABASE regression TO regress_mdb_superuser_user2;
+GRANT CREATE ON DATABASE regression TO regress_mdb_superuser_user3;
+
+
+SET ROLE regress_mdb_superuser_user2;
+
+CREATE FUNCTION regress_mdb_superuser_add(integer, integer) RETURNS integer
+ AS 'SELECT $1 + $2;'
+ LANGUAGE SQL
+ IMMUTABLE
+ RETURNS NULL ON NULL INPUT;
+
+CREATE SCHEMA regress_mdb_superuser_schema;
+CREATE TABLE regress_mdb_superuser_schema.regress_mdb_superuser_table();
+CREATE TABLE regress_mdb_superuser_table();
+CREATE VIEW regress_mdb_superuser_view as SELECT 1;
+
+SET ROLE regress_mdb_superuser_user3;
+INSERT INTO regress_mdb_superuser_table SELECT * FROM regress_mdb_superuser_table;
+
+SET ROLE regress_mdb_superuser_user1;
+
+-- mdb_superuser can grant to other role
+GRANT USAGE, CREATE ON SCHEMA regress_mdb_superuser_schema TO regress_mdb_superuser_user3;
+GRANT ALL PRIVILEGES ON TABLE regress_mdb_superuser_table TO regress_mdb_superuser_user3;
+REVOKE ALL PRIVILEGES ON TABLE regress_mdb_superuser_table FROM regress_mdb_superuser_user3;
+
+GRANT INSERT, SELECT ON TABLE regress_mdb_superuser_table TO regress_mdb_superuser_user3;
+
+-- grant works
+SET ROLE regress_mdb_superuser_user3;
+INSERT INTO regress_mdb_superuser_table SELECT * FROM regress_mdb_superuser_table;
+
+SET ROLE mdb_superuser;
+
+-- mdb_superuser drop object of other role
+DROP TABLE regress_mdb_superuser_table;
+-- mdb admin fails to transfer ownership to superusers and system roles
+
+RESET SESSION AUTHORIZATION;
+
+CREATE TABLE regress_superuser_table();
+
+SET ROLE pg_read_server_files;
+
+CREATE TABLE regress_pgrsf_table();
+
+SET ROLE pg_write_server_files;
+
+CREATE TABLE regress_pgwsf_table();
+
+SET ROLE pg_execute_server_program;
+
+CREATE TABLE regress_pgxsp_table();
+
+SET ROLE pg_read_all_data;
+
+CREATE TABLE regress_pgrad_table();
+
+SET ROLE pg_write_all_data;
+
+CREATE TABLE regress_pgrwd_table();
+
+SET ROLE mdb_superuser;
+
+-- cannot read all data (fail)
+SELECT * FROM pg_authid;
+
+-- can not drop superuser objects, because does not has_privs_of pg_database_owner
+DROP TABLE regress_superuser_table;
+DROP TABLE regress_pgrsf_table;
+DROP TABLE regress_pgwsf_table;
+DROP TABLE regress_pgxsp_table;
+DROP TABLE regress_pgrad_table;
+DROP TABLE regress_pgrwd_table;
+
+
+-- does allowed to creare database, role or extension
+-- or grant such priviledge
+
+CREATE DATABASE regress_db_fail;
+CREATE ROLE regress_role_fail;
+
+ALTER ROLE mdb_superuser WITH CREATEROLE;
+ALTER ROLE mdb_superuser WITH CREATEDB;
+
+ALTER ROLE regress_mdb_superuser_user2 WITH CREATEROLE;
+ALTER ROLE regress_mdb_superuser_user2 WITH CREATEDB;
+
+-- mdb_superuser more powerfull than pg_database_owner
+
+RESET SESSION AUTHORIZATION;
+CREATE DATABASE regress_check_owner OWNER regress_mdb_superuser_user2;
+
+\c regress_check_owner;
+
+SET ROLE regress_mdb_superuser_user2;
+CREATE SCHEMA regtest;
+CREATE TABLE regtest.regtest();
+
+-- this should fail
+
+SET ROLE regress_mdb_superuser_user3;
+GRANT ALL ON TABLE regtest.regtest TO regress_mdb_superuser_user3;
+ALTER TABLE regtest.regtest OWNER TO regress_mdb_superuser_user3;
+
+SET ROLE regress_mdb_superuser_user1;
+GRANT ALL ON TABLE regtest.regtest TO regress_mdb_superuser_user1;
+ALTER TABLE regtest.regtest OWNER TO regress_mdb_superuser_user1;
+
+\c regression
+DROP DATABASE regress_check_owner;
+
+-- end tests
+
+RESET SESSION AUTHORIZATION;
+--
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_superuser_user2;
+REVOKE CREATE ON DATABASE regression FROM regress_mdb_superuser_user3;
+
+DROP VIEW regress_mdb_superuser_view;
+DROP FUNCTION regress_mdb_superuser_add;
+DROP TABLE regress_mdb_superuser_schema.regress_mdb_superuser_table;
+DROP TABLE regress_mdb_superuser_table;
+DROP SCHEMA regress_mdb_superuser_schema;
+DROP ROLE regress_mdb_superuser_user1;
+DROP ROLE regress_mdb_superuser_user2;
+DROP ROLE regress_mdb_superuser_user3;
+DROP TABLE regress_superuser_table;
+DROP TABLE regress_pgrsf_table;
+DROP TABLE regress_pgwsf_table;
+DROP TABLE regress_pgxsp_table;
+DROP TABLE regress_pgrad_table;
+DROP TABLE regress_pgrwd_table;
diff --git a/src/test/regress/sql/test_setup.sql b/src/test/regress/sql/test_setup.sql
new file mode 100644
index 00000000000..7ec5ccc7471
--- /dev/null
+++ b/src/test/regress/sql/test_setup.sql
@@ -0,0 +1,6 @@
+--
+-- TEST_SETUP --- prepare environment expected by regression test scripts
+--
+
+CREATE ROLE mdb_admin;
+CREATE ROLE mdb_superuser;
diff --git a/src/test/singlenode_regress/expected/create_function_3.out b/src/test/singlenode_regress/expected/create_function_3.out
index 3a4fd451471..6423fdb7965 100644
--- a/src/test/singlenode_regress/expected/create_function_3.out
+++ b/src/test/singlenode_regress/expected/create_function_3.out
@@ -166,10 +166,10 @@ SET SESSION AUTHORIZATION regress_unpriv_user;
SET search_path TO temp_func_test, public;
ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF;
ALTER FUNCTION functest_E_2(int) LEAKPROOF;
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql'
LEAKPROOF AS 'SELECT $1 < 200'; -- fail
-ERROR: only superuser can define a leakproof function
+ERROR: only superuser or mdb_admin can define a leakproof function
RESET SESSION AUTHORIZATION;
--
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT