diff --git a/cmake/FindPsqlDevel.cmake b/cmake/FindPsqlDevel.cmake
index 6762aea..9ba18a5 100644
--- a/cmake/FindPsqlDevel.cmake
+++ b/cmake/FindPsqlDevel.cmake
@@ -2,19 +2,41 @@
# postgresql-server-devel Support
#
+find_program(PG_CONFIG pg_config)
+
+if (NOT PG_CONFIG)
+ message(FATAL_ERROR "pg_config executable not found. Ensure PostgreSQL is installed and pg_config is in your PATH.")
+endif()
+
+# Use pg_config to get the include and library directories
+execute_process(COMMAND ${PG_CONFIG} --includedir
+ OUTPUT_VARIABLE PSQLDEVEL_INCLUDE_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+execute_process(COMMAND ${PG_CONFIG} --libdir
+ OUTPUT_VARIABLE PSQLDEVEL_LIBRARY_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
find_path(PSQLDEVEL_INCLUDE_DIR
NAMES libpq-fe.h
- PATH_SUFFIXES postgresql
+ PATHS ${PSQLDEVEL_INCLUDE_DIR}
)
find_library(PSQLDEVEL_LIBRARY
NAMES pq
+ PATHS ${PSQLDEVEL_LIBRARY_DIR}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PsqlDevel DEFAULT_MSG
PSQLDEVEL_INCLUDE_DIR PSQLDEVEL_LIBRARY)
+if (PSQLDEVEL_INCLUDE_DIR AND PSQLDEVEL_LIBRARY)
+ set(PSQLDEVEL_FOUND TRUE)
+else()
+ set(PSQLDEVEL_FOUND FALSE)
+endif()
+
if (PSQLDEVEL_FOUND)
set(PSQLDEVEL_INCLUDE_DIRS ${PSQLDEVEL_INCLUDE_DIR})
set(PSQLDEVEL_LIBRARIES ${PSQLDEVEL_LIBRARY})
diff --git a/doc/manual/user-03-functions.md b/doc/manual/user-03-functions.md
index bbeba0f..566fb15 100644
--- a/doc/manual/user-03-functions.md
+++ b/doc/manual/user-03-functions.md
@@ -4,11 +4,12 @@
## Security
-After you create the extension `pgmoneta_ext` using the `postgres` role, you can test all functions as shown below. Some functions may require superuser privileges (For the column `Superuser`, the value is `true`), so if you log in with a role without superuser privileges, the function will return `false`. If you have superuser privileges, it will work as desired.
+After you create the extension `pgmoneta_ext` using the `postgres` role, you can test all functions as shown below. Some functions may require specific privileges (For the column `Privilege`), so if you log in with a role without the specific privileges, the function will return `false`. If you have the specific privileges, it will work as desired.
## Extension functions
-| Function | Superuser | Description |
+| Function | Privilege | Description |
|-----------------------------|-----------|--------------------------------------------------------|
-| `pgmoneta_ext_version()` | false | Return the version number of `pgmoneta_ext` as a Datum.|
-| `pgmoneta_ext_switch_wal()` | true | A function for switching to a new WAL file. |
+| `pgmoneta_ext_version()` | Default | Return the version number of `pgmoneta_ext` as a Datum.|
+| `pgmoneta_ext_switch_wal()` | SUPERUSER | A function for switching to a new WAL file. |
+| `pgmoneta_ext_checkpoint()` | SUPERUSER
pg_checkpoint | A function which forces a checkpoint.
This function can only be executed by a `SUPERUSER` in PostgreSQL 13/14, but can also be executed by `pg_checkpoint` in PostgreSQL 15+.
You can use the SQL command `GRANT pg_checkpoint TO repl;` to assign the role in PostgreSQL 15+.|
diff --git a/sql/pgmoneta_ext--0.1.0.sql b/sql/pgmoneta_ext--0.1.0.sql
index e2c7d73..fe2d79b 100644
--- a/sql/pgmoneta_ext--0.1.0.sql
+++ b/sql/pgmoneta_ext--0.1.0.sql
@@ -7,4 +7,11 @@ CREATE FUNCTION pgmoneta_ext_switch_wal(OUT success bool,
)
RETURNS record
AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pgmoneta_ext_checkpoint(OUT success bool,
+ OUT value text
+)
+RETURNS record
+AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
\ No newline at end of file
diff --git a/src/include/utils.h b/src/include/utils.h
index e9ad1ee..7dc7483 100644
--- a/src/include/utils.h
+++ b/src/include/utils.h
@@ -39,14 +39,24 @@ extern "C" {
#include "fmgr.h"
/* system */
+#include
/**
* Check if the role has superuser privileges.
* @param roleid The current role's ID
- * @return 0 upon success, otherwise 1
+ * @return The result
*/
-int
-pgmoneta_ext_check_privilege(Oid roleid);
+bool
+pgmoneta_ext_check_superuser(Oid roleid);
+
+/**
+ * Check if the role has specific role.
+ * @param roleid The current role's ID
+ * @param rolename Specific role name
+ * @return The result
+ */
+bool
+pgmoneta_ext_check_role(Oid roleid, const char* rolename);
#ifdef __cplusplus
}
diff --git a/src/pgmoneta_ext/lib.c b/src/pgmoneta_ext/lib.c
index 35f2351..2b683fb 100644
--- a/src/pgmoneta_ext/lib.c
+++ b/src/pgmoneta_ext/lib.c
@@ -27,7 +27,7 @@
*
*/
-/* pgmoneta */
+/* pgmoneta_ext */
#include
#include
@@ -52,6 +52,7 @@ PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(pgmoneta_ext_version);
PG_FUNCTION_INFO_V1(pgmoneta_ext_switch_wal);
+PG_FUNCTION_INFO_V1(pgmoneta_ext_checkpoint);
Datum
pgmoneta_ext_version(PG_FUNCTION_ARGS)
@@ -78,15 +79,15 @@ pgmoneta_ext_switch_wal(PG_FUNCTION_ARGS)
XLogRecPtr recptr;
bool nulls[2];
char str_res[1024];
- int is_superuser;
+ bool is_superuser;
memset(nulls, 0, sizeof(nulls));
roleid = GetUserId();
- is_superuser = pgmoneta_ext_check_privilege(roleid);
+ is_superuser = pgmoneta_ext_check_superuser(roleid);
- if (!is_superuser)
+ if (is_superuser)
{
// Request to switch WAL with mark_unimportant set to false.
recptr = RequestXLogSwitch(false);
@@ -114,5 +115,64 @@ pgmoneta_ext_switch_wal(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(tupdesc, values, nulls);
result = HeapTupleGetDatum(tuple);
+ PG_RETURN_DATUM(result);
+}
+
+#ifndef RequestCheckpoint
+extern void RequestCheckpoint(int flags);
+#endif
+
+Datum
+pgmoneta_ext_checkpoint(PG_FUNCTION_ARGS)
+{
+ Datum values[2];
+ Datum result;
+ HeapTuple tuple;
+ Oid roleid;
+ TupleDesc tupdesc;
+ bool nulls[2];
+ bool is_superuser;
+ bool is_pg_checkpoint;
+ char cp[1024];
+
+ memset(nulls, 0, sizeof(nulls));
+ memset(&cp, 0, sizeof(cp));
+ is_superuser = false;
+ is_pg_checkpoint = false;
+
+ roleid = GetUserId();
+ is_superuser = pgmoneta_ext_check_superuser(roleid);
+
+#if PG_VERSION_NUM >= 150000
+ is_pg_checkpoint = pgmoneta_ext_check_role(roleid, "pg_checkpoint");
+#endif
+
+ if (is_superuser || is_pg_checkpoint)
+ {
+ // Perform the checkpoint
+ RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT | CHECKPOINT_FORCE);
+
+ values[0] = BoolGetDatum(true);
+ snprintf(&cp[0], sizeof(cp), "%s", "CHECKPOINT");
+ values[1] = CStringGetTextDatum(cp);
+ }
+ else
+ {
+ ereport(LOG, errmsg_internal("pgmoneta_ext_checkpoint: Current role is not a superuser"));
+
+ values[0] = BoolGetDatum(false);
+ nulls[1] = true;
+ }
+
+ // Create a tuple descriptor for our result type
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ {
+ ereport(ERROR, errmsg_internal("pgmoneta_ext_checkpoint: Return type must be a row type"));
+ }
+
+ // Build the result tuple
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+
PG_RETURN_DATUM(result);
}
\ No newline at end of file
diff --git a/src/pgmoneta_ext/utils.c b/src/pgmoneta_ext/utils.c
index 7bff953..ea88b7b 100644
--- a/src/pgmoneta_ext/utils.c
+++ b/src/pgmoneta_ext/utils.c
@@ -27,33 +27,37 @@
*
*/
-/* pgmoneta */
+/* pgmoneta_ext */
#include
/* PostgreSQL */
#include "access/htup_details.h"
#include "catalog/pg_authid.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/errcodes.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
/* system */
-#include
-int
-pgmoneta_ext_check_privilege(Oid roleid)
+bool
+pgmoneta_ext_check_superuser(Oid roleid)
{
bool is_superuser;
HeapTuple roletuple;
Form_pg_authid roleform;
+ is_superuser = false;
+
// Fetch the role's tuple from pg_authid
roletuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (!HeapTupleIsValid(roletuple))
{
- ereport(ERROR, errmsg_internal("pgmoneta_ext_switch_wal: Role with OID %u does not exist", roleid));
- return 1;
+ ereport(ERROR, errmsg_internal("pgmoneta_ext_check_superuser: Role with OID %u does not exist", roleid));
+ return false;
}
// Get the role's superuser attribute
@@ -64,5 +68,43 @@ pgmoneta_ext_check_privilege(Oid roleid)
// Release the role tuple
ReleaseSysCache(roletuple);
- return !is_superuser;
+ return is_superuser;
}
+
+bool
+pgmoneta_ext_check_role(Oid roleid, const char* rolename)
+{
+ bool is_success;
+ HeapTuple roletuple;
+ List* role_oids;
+ ListCell* cell;
+
+ is_success = false;
+
+ roletuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
+ if (!HeapTupleIsValid(roletuple))
+ {
+ ereport(ERROR, errmsg_internal("pgmoneta_ext_check_role: Role with OID %u does not exist", roleid));
+ return false;
+ }
+
+ // Get role OIDs of a given role name
+ Oid role_oid = get_role_oid(rolename, false);
+ role_oids = list_make1_oid(role_oid);
+
+ // Check if the role has the specific privilege
+ foreach (cell, role_oids)
+ {
+ Oid checkpoint_role_oid = lfirst_oid(cell);
+ if (is_member_of_role(roleid, checkpoint_role_oid))
+ {
+ is_success = true;
+ break;
+ }
+ }
+
+ ReleaseSysCache(roletuple);
+ list_free(role_oids);
+
+ return is_success;
+}
\ No newline at end of file