summaryrefslogtreecommitdiff
path: root/src/fe_utils/version.c
blob: 94f09233c01010a6b5633f74206c4db75d011572 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*-------------------------------------------------------------------------
 *
 * version.c
 *		Routine to retrieve information of PG_VERSION
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/fe_utils/version.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include <sys/stat.h>

#include "common/logging.h"
#include "fe_utils/version.h"

/*
 * Assumed maximum size of PG_VERSION.  This should be more than enough for
 * any version numbers that need to be handled.
 */
#define PG_VERSION_MAX_SIZE	64

/*
 * get_pg_version
 *
 * Retrieve the major version number of the given data folder, from
 * PG_VERSION.  The result returned is a version number, that can be used
 * for comparisons based on PG_VERSION_NUM.  For example, if PG_VERSION
 * contains "18\n", this function returns 180000.
 *
 * This supports both the pre-v10 and the post-v10 version numbering.
 *
 * Optionally, "version_str" can be specified to store the contents
 * retrieved from PG_VERSION.  It is allocated by this routine; the
 * caller is responsible for pg_free()-ing it.
 */
uint32
get_pg_version(const char *datadir, char **version_str)
{
	FILE	   *version_fd;
	char		ver_filename[MAXPGPATH];
	char		buf[PG_VERSION_MAX_SIZE];
	int			v1 = 0,
				v2 = 0;
	struct stat st;

	snprintf(ver_filename, sizeof(ver_filename), "%s/PG_VERSION",
			 datadir);

	if ((version_fd = fopen(ver_filename, "r")) == NULL)
		pg_fatal("could not open version file \"%s\": %m", ver_filename);

	if (fstat(fileno(version_fd), &st) != 0)
		pg_fatal("could not stat file \"%s\": %m", ver_filename);
	if (st.st_size > PG_VERSION_MAX_SIZE)
		pg_fatal("file \"%s\" is too large", ver_filename);

	if (fscanf(version_fd, "%63s", buf) == 0 ||
		sscanf(buf, "%d.%d", &v1, &v2) < 1)
		pg_fatal("could not parse version file \"%s\"", ver_filename);

	fclose(version_fd);

	if (version_str)
	{
		*version_str = pg_malloc(PG_VERSION_MAX_SIZE);
		memcpy(*version_str, buf, st.st_size);
	}

	if (v1 < 10)
	{
		/* pre-v10 style, e.g. 9.6.1 */
		return v1 * 10000 + v2 * 100;
	}
	else
	{
		/* post-v10 style, e.g. 10.1 */
		return v1 * 10000;
	}
}