mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 07:06:30 +00:00
the main difference is that this new function doesn't return a bool but a string representing the current major version of the postgresql server or an empty string if it's earlier than 9.0. Teach all commands that could connect to master and standby that they both should have the same major version.
227 lines
5.2 KiB
C
227 lines
5.2 KiB
C
/*
|
|
* dbutils.c
|
|
* Copyright (c) 2ndQuadrant, 2010
|
|
*
|
|
* Database connection/management functions
|
|
*
|
|
*/
|
|
|
|
#include "repmgr.h"
|
|
|
|
PGconn *
|
|
establishDBConnection(const char *conninfo, const bool exit_on_error)
|
|
{
|
|
PGconn *conn;
|
|
/* Make a connection to the database */
|
|
conn = PQconnectdb(conninfo);
|
|
/* Check to see that the backend connection was successfully made */
|
|
if ((PQstatus(conn) != CONNECTION_OK))
|
|
{
|
|
fprintf(stderr, "Connection to database failed: %s",
|
|
PQerrorMessage(conn));
|
|
if (exit_on_error)
|
|
{
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
is_standby(PGconn *conn)
|
|
{
|
|
PGresult *res;
|
|
bool result;
|
|
|
|
res = PQexec(conn, "SELECT pg_is_in_recovery()");
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "Can't query server mode: %s", PQerrorMessage(conn));
|
|
PQclear(res);
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
|
|
if (strcmp(PQgetvalue(res, 0, 0), "f") == 0)
|
|
result = false;
|
|
else
|
|
result = true;
|
|
|
|
PQclear(res);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* If postgreSQL version is 9 or superior returns the major version
|
|
* if 8 or inferior returns an empty string
|
|
*/
|
|
char *
|
|
pg_version(PGconn *conn)
|
|
{
|
|
PGresult *res;
|
|
char *major_version;
|
|
|
|
int major_version1;
|
|
char *major_version2;
|
|
|
|
res = PQexec(conn, "WITH pg_version(ver) AS (SELECT split_part(version(), ' ', 2)) "
|
|
"SELECT split_part(ver, '.', 1), split_part(ver, '.', 2) FROM pg_version");
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
|
|
PQclear(res);
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
major_version1 = atoi(PQgetvalue(res, 0, 0));
|
|
major_version2 = PQgetvalue(res, 0, 1);
|
|
PQclear(res);
|
|
|
|
major_version = malloc(10);
|
|
if (major_version1 >= 9)
|
|
{
|
|
/* form a major version string */
|
|
sprintf(major_version, "%d.%s", major_version1, major_version2);
|
|
}
|
|
else
|
|
strcpy(major_version, "");
|
|
|
|
return major_version;
|
|
}
|
|
|
|
|
|
bool
|
|
guc_setted(PGconn *conn, const char *parameter, const char *op, const char *value)
|
|
{
|
|
PGresult *res;
|
|
char sqlquery[8192];
|
|
|
|
sprintf(sqlquery, "SELECT true FROM pg_settings "
|
|
" WHERE name = '%s' AND setting %s '%s'",
|
|
parameter, op, value);
|
|
|
|
res = PQexec(conn, sqlquery);
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
|
|
PQclear(res);
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
if (PQntuples(res) == 0)
|
|
{
|
|
PQclear(res);
|
|
return false;
|
|
}
|
|
PQclear(res);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
const char *
|
|
get_cluster_size(PGconn *conn)
|
|
{
|
|
PGresult *res;
|
|
const char *size;
|
|
char sqlquery[8192];
|
|
|
|
sprintf(sqlquery, "SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
|
|
" FROM pg_database ");
|
|
|
|
res = PQexec(conn, sqlquery);
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
|
|
PQclear(res);
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
size = PQgetvalue(res, 0, 0);
|
|
PQclear(res);
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* get a connection to master by reading repl_nodes, creating a connection
|
|
* to each node (one at a time) and finding if it is a master or a standby
|
|
*/
|
|
PGconn *
|
|
getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id)
|
|
{
|
|
PGconn *master_conn = NULL;
|
|
PGresult *res1;
|
|
PGresult *res2;
|
|
char sqlquery[8192];
|
|
char master_conninfo[8192];
|
|
int i;
|
|
|
|
/* find all nodes belonging to this cluster */
|
|
sprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes "
|
|
" WHERE cluster = '%s' and id <> %d",
|
|
cluster, cluster, id);
|
|
|
|
res1 = PQexec(standby_conn, sqlquery);
|
|
if (PQresultStatus(res1) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "Can't get nodes info: %s\n", PQerrorMessage(standby_conn));
|
|
PQclear(res1);
|
|
PQfinish(standby_conn);
|
|
exit(1);
|
|
}
|
|
|
|
for (i = 0; i < PQntuples(res1); i++)
|
|
{
|
|
/* initialize with the values of the current node being processed */
|
|
*master_id = atoi(PQgetvalue(res1, i, 0));
|
|
strcpy(master_conninfo, PQgetvalue(res1, i, 2));
|
|
master_conn = establishDBConnection(master_conninfo, false);
|
|
|
|
/*
|
|
* I can't use the is_standby() function here because on error that
|
|
* function closes the connection i pass and exit, but i still need to close
|
|
* standby_conn
|
|
*/
|
|
res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()");
|
|
if (PQresultStatus(res2) != PGRES_TUPLES_OK)
|
|
{
|
|
fprintf(stderr, "Can't get recovery state from this node: %s\n", PQerrorMessage(master_conn));
|
|
PQclear(res2);
|
|
PQfinish(master_conn);
|
|
continue;
|
|
}
|
|
|
|
/* if false, this is the master */
|
|
if (strcmp(PQgetvalue(res2, 0, 0), "f") == 0)
|
|
{
|
|
PQclear(res2);
|
|
PQclear(res1);
|
|
return master_conn;
|
|
}
|
|
else
|
|
{
|
|
/* if it is a standby clear info */
|
|
PQclear(res2);
|
|
PQfinish(master_conn);
|
|
*master_id = -1;
|
|
}
|
|
}
|
|
|
|
/* If we finish this loop without finding a master then
|
|
* we doesn't have the info or the master has failed (or we
|
|
* reached max_connections or superuser_reserved_connections,
|
|
* anything else i'm missing?),
|
|
* Probably we will need to check the error to know if we need
|
|
* to start failover procedure or just fix some situation on the
|
|
* standby.
|
|
*/
|
|
PQclear(res1);
|
|
return NULL;
|
|
}
|
|
|