This is an automated email from the ASF dual-hosted git repository.

leborchuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git


The following commit(s) were added to refs/heads/main by this push:
     new 4af1cf5a09b Movable DataBase Locales for Cloudberry (#1363)
4af1cf5a09b is described below

commit 4af1cf5a09bcf5f199e4135db14bb6123987c102
Author: Leonid <[email protected]>
AuthorDate: Wed Feb 4 16:54:52 2026 +0300

    Movable DataBase Locales for Cloudberry (#1363)
    
    * Movable DataBase Locales for Cloudberry
    
    We inherited this issue from PostgreSQL.
    
    PostgreSQL uses glibc to sort strings. In version glibc=2.28, collations
    broke down badly (in general, there are no guarantees when updating glibc).
    Changing collations breaks indexes. Similarly, a cluster with different
    collations also behaves unpredictably.
    
    What and when something has changed in glibc can be found
    on https://github.com/ardentperf/glibc-unicode-sorting
    Also there is special postgresql-wiki 
https://wiki.postgresql.org/wiki/Locale_data_changes
    And you tube video https://www.youtube.com/watch?v=0E6O-V8Jato
    
    In short, the issue can be seen through the use of bash:
    
    ( echo "1-1"; echo "11" ) | LC_COLLATE=en_US.UTF-8 sort
    
    gives the different results in ubunru 18.04 and 22.04.
    
    There is no way to solve the problem other than by not changing the symbol 
order.
    We freeze symbol order and use it instead of glibc.
    
    Here the solution https://github.com/postgredients/mdb-locales.
    
    In this PR I have added PostgreSQL patch that replaces all glibc
    locale-related calls with a calls to an external libary. It activates
    using new configure parameter --with-mdblocales, which is off by
    default.
    
    Using custom locales needs libmdblocales1 package and mdb-locales
    package with symbol table.
    
    Build needs libmdblocales-dev package with headers.
    
    Fixing the symbol order is necessary for OS upgrade. For example Ubuntu 
22.04 EOL is April 2027, Rocky 8 Active Support ended May 2024, and Security 
support ends in 2029.
    
    We use Movable DataBase Locales in Greenplum 6 and all our PostgreSQL 
installations (starting with PostgreSQL 12). This patch is adopted patch 
version from our internal PostgreSQL 14 fork.
    
    * mdb_admin role
    
    This patch introcudes new pseudo-pre-defined role "mdb_admin".
    
    Introduces 2 new function:
    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);
    
    To check mdb admin belongship and role-to-role ownership transfer
    correctness.
    
    Our mdb_admin ACL model is the following:
     * Any roles user or/and roles can be granted with mdb_admin
     * mdb_admin memeber can tranfser ownershup of relations,
       namespaces and functions to other roles, if target role in neither:
       superuser, pg_read_server_files, pg_write_server_files nor
       pg_execute_server_program.
    
    * mdb_superuser role
    
    This patch introcudes new pseudo-pre-defined role "mdb_superuser".
    
    Role is capable of:
    
    GRANT/REVOKE any set of priviledges to/from any object in database.
    Has power of pg_database_owner in any database, including:
    DROP any object in database (except system catalog and stuff)
    
    Role is NOT capable of:
    
    Create database, role, extension or alter other roles with such
    priviledges.
    
    Transfer ownership to /pass has_priv of roles:
    
    PG_READ_ALL_DATA
    PG_WRITE_ALL_DATA
    PG_EXECUTE_SERVER_PROGRAM
    PG_READ_SERVER_FILES
    PG_WRITE_SERVER_FILES
    PG_DATABASE_OWNER
    
    Allow mdb_superuser to alter objects and grant ACl to
    objects, owner by pg_database_owner. Also, when acl check,
    allow mdb_supersuer use pg_database_owner role power to pass check
    
    * Extend multixact SLRU
    
    The issue here is the same as for the PG, good detail description I found 
in Nikolay blog post 
https://v2.postgres.ai/blog/20210831-postgresql-subtransactions-considered-harmful
    See also the history of original PG patches in 
https://commitfest.postgresql.org/patch/2627/  We could get all those fixes 
after rebasing to PG18, but for now, we need to adjust SLRU structure sizes.
    
    ---------
    Co-authored-by: usernamedt <[email protected]>
    Co-authored-by: reshke <[email protected]>
---
 configure                                          |  97 +++++++++++-
 configure.ac                                       |  17 ++
 .../pax_storage/src/cpp/storage/oper/pax_oper.cc   |   5 +-
 .../test/regress/expected/create_function_3.out    |   4 +-
 .../expected/create_function_3_optimizer.out       |   4 +-
 .../cloudberry/scripts/configure-cloudberry.sh     |  12 ++
 gpcontrib/orafce/others.c                          |   9 +-
 src/backend/catalog/namespace.c                    |  20 ++-
 src/backend/commands/alter.c                       |   8 +-
 src/backend/commands/functioncmds.c                |  20 ++-
 src/backend/commands/schemacmds.c                  |  13 +-
 src/backend/commands/tablecmds.c                   |  12 +-
 .../src/unittest/gpos/string/CWStringTest.cpp      |   7 +-
 src/backend/storage/ipc/signalfuncs.c              |  28 +++-
 src/backend/utils/activity/backend_status.c        |  16 ++
 src/backend/utils/adt/Makefile                     |   3 +-
 src/backend/utils/adt/acl.c                        | 176 ++++++++++++++++++++-
 src/backend/utils/adt/mdb.c                        |  37 +++++
 src/backend/utils/adt/pg_locale.c                  |  63 ++++----
 src/backend/utils/mb/mbutils.c                     |   3 +-
 src/backend/utils/misc/guc.c                       |  14 +-
 src/bin/initdb/initdb.c                            |  14 +-
 src/bin/pg_upgrade/check.c                         |   9 +-
 src/common/exec.c                                  |   4 +-
 src/include/access/multixact.h                     |   4 +-
 src/include/access/subtrans.h                      |   2 +-
 src/include/catalog/pg_proc.dat                    |   4 +-
 src/include/common/mdb_locale.h                    |  41 +++++
 src/include/pg_config.h.in                         |   6 +
 src/include/utils/acl.h                            |   8 +
 src/include/utils/backend_status.h                 |   3 +
 src/include/utils/guc_tables.h                     |   2 +
 src/interfaces/ecpg/ecpglib/connect.c              |   3 +-
 src/interfaces/ecpg/ecpglib/descriptor.c           |   8 +-
 src/interfaces/ecpg/ecpglib/execute.c              |   7 +-
 src/interfaces/libpq/Makefile                      |   2 +-
 src/pl/plperl/plperl.c                             |  19 +--
 src/port/chklocale.c                               |  10 +-
 src/test/Makefile                                  |   3 +
 src/test/locale/test-ctype.c                       |   4 +-
 src/test/mdb_admin/.gitignore                      |   2 +
 src/test/mdb_admin/Makefile                        |  23 +++
 src/test/mdb_admin/t/signals.pl                    |  74 +++++++++
 src/test/regress/expected/create_function_3.out    |   4 +-
 .../expected/create_function_3_optimizer.out       |   4 +-
 src/test/regress/expected/mdb_admin.out            | 100 ++++++++++++
 src/test/regress/expected/mdb_superuser.out        | 115 ++++++++++++++
 src/test/regress/expected/test_setup.out           |   5 +
 src/test/regress/input/misc.source                 |   5 +
 src/test/regress/output/misc.source                |   7 +
 src/test/regress/parallel_schedule                 |   8 +
 src/test/regress/sql/mdb_admin.sql                 |  87 ++++++++++
 src/test/regress/sql/mdb_superuser.sql             | 144 +++++++++++++++++
 .../regress/{input/misc.source => sql/misc.sql}    |   5 +
 src/test/regress/sql/test_setup.sql                |   6 +
 .../expected/create_function_3.out                 |   4 +-
 56 files changed, 1200 insertions(+), 114 deletions(-)

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/devops/build/automation/cloudberry/scripts/configure-cloudberry.sh 
b/devops/build/automation/cloudberry/scripts/configure-cloudberry.sh
index 54086736a5f..32a9f3d8657 100755
--- a/devops/build/automation/cloudberry/scripts/configure-cloudberry.sh
+++ b/devops/build/automation/cloudberry/scripts/configure-cloudberry.sh
@@ -62,6 +62,12 @@
 #                   --enable-cassert
 #                   --enable-debug-extensions
 #
+#   ENABLE_MDBLOCALES - Enable custom locales (true/false, defaults to
+#                       false)
+#
+#                 When true, add option:
+#                   --with-mdblocales
+#
 # Prerequisites:
 #   - System dependencies must be installed:
 #     * xerces-c development files
@@ -138,6 +144,11 @@ if [ "${ENABLE_DEBUG:-false}" = "true" ]; then
                           --enable-debug-extensions"
 fi
 
+CONFIGURE_MDBLOCALES_OPTS="--without-mdblocales"
+if [ "${ENABLE_MDBLOCALES:-false}" = "true" ]; then
+    CONFIGURE_MDBLOCALES_OPTS="--with-mdblocales"
+fi
+
 # Configure build
 log_section "Configure"
 execute_cmd ./configure --prefix=${BUILD_DESTINATION} \
@@ -164,6 +175,7 @@ execute_cmd ./configure --prefix=${BUILD_DESTINATION} \
             --with-ssl=openssl \
             --with-openssl \
             --with-uuid=e2fs \
+            ${CONFIGURE_MDBLOCALES_OPTS} \
             --with-includes=/usr/local/xerces-c/include \
             --with-libraries=${BUILD_DESTINATION}/lib || exit 4
 log_section_end "Configure"
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/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 <locale.h>
+#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 <unicode/ucnv.h>
@@ -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 154d6e39737..353c4988a0a 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 <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#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 <mdblocales.h>
+#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 <langinfo.h>
@@ -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 <stdio.h>
 #include <locale.h>
 #include <ctype.h>
+#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/input/misc.source b/src/test/regress/sql/misc.sql
similarity index 99%
copy from src/test/regress/input/misc.source
copy to src/test/regress/sql/misc.sql
index 331499a2aba..2abe2c82eb8 100644
--- a/src/test/regress/input/misc.source
+++ b/src/test/regress/sql/misc.sql
@@ -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/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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to