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]