Skip to content

Commit 35dee48

Browse files
Amy Taiamytai
authored andcommitted
Remove version-checking from client-side
Because the server now sends a boolean indicating whether the local version is up-to-date, we simply validate the server's response and remove any version-checking code from the client-side. The client now only checks for well-formed versions before printing, if the local version is not up-to-date.
1 parent 3686ff5 commit 35dee48

File tree

11 files changed

+171
-349
lines changed

11 files changed

+171
-349
lines changed

sql/updates/1.0.0--1.0.1-dev.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP FUNCTION IF EXISTS _timescaledb_internal.get_version();

sql/version.sql

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,5 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.get_os_info()
1010
RETURNS TABLE(sysname TEXT, version TEXT, release TEXT)
1111
AS '@MODULE_PATHNAME@', 'ts_get_os_info' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
1212

13-
CREATE OR REPLACE FUNCTION _timescaledb_internal.get_version()
14-
RETURNS TABLE(major INTEGER, minor INTEGER, patch INTEGER, modtag TEXT)
15-
AS '@MODULE_PATHNAME@', 'ts_version_get_info' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
16-
1713
CREATE OR REPLACE FUNCTION get_telemetry_report() RETURNS TEXT
1814
AS '@MODULE_PATHNAME@', 'ts_get_telemetry_report' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

src/telemetry/telemetry.c

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "net/http.h"
2525

2626
#define TS_VERSION_JSON_FIELD "current_timescaledb_version"
27+
#define TS_IS_UPTODATE_JSON_FIELD "is_up_to_date"
2728

2829
/* HTTP request details */
2930
#define TIMESCALE_TYPE "application/json"
@@ -49,9 +50,30 @@
4950

5051
static const char *related_extensions[] = {PG_PROMETHEUS, POSTGIS};
5152

53+
static bool
54+
char_in_valid_version_digits(const char c)
55+
{
56+
switch (c)
57+
{
58+
case '.':
59+
case '-':
60+
return true;
61+
default:
62+
return false;
63+
}
64+
}
65+
66+
/*
67+
* Makes sure the server version string is less than MAX_VERSION_STR_LEN
68+
* chars, and all digits are "valid". Valid chars are either
69+
* alphanumeric or in the array valid_version_digits above.
70+
*
71+
* Returns false if either of these conditions are false.
72+
*/
5273
bool
53-
telemetry_parse_version(const char *json, VersionInfo *installed_version, VersionResult *result)
74+
validate_server_version(const char *json, VersionResult *result)
5475
{
76+
int i;
5577
Datum version = DirectFunctionCall2(json_object_field_text,
5678
CStringGetTextDatum(json),
5779
PointerGetDatum(cstring_to_text(TS_VERSION_JSON_FIELD)));
@@ -60,26 +82,26 @@ telemetry_parse_version(const char *json, VersionInfo *installed_version, Versio
6082

6183
result->versionstr = text_to_cstring(DatumGetTextPP(version));
6284

63-
result->is_up_to_date = false;
64-
6585
if (result->versionstr == NULL)
6686
{
6787
result->errhint = "no version string in response";
6888
return false;
6989
}
7090

71-
/*
72-
* Now parse the version string. We expect format to be
73-
* XX.XX.XX-<prerelease_tag>, and if not, we error out
74-
*/
75-
if (!version_parse(result->versionstr, &result->vinfo))
91+
if (strlen(result->versionstr) > MAX_VERSION_STR_LEN)
7692
{
77-
result->errhint = psprintf("parsing failed for version string \"%s\"", result->versionstr);
93+
result->errhint = "version string is too long";
7894
return false;
7995
}
8096

81-
if (version_cmp(installed_version, &result->vinfo) >= 0)
82-
result->is_up_to_date = true;
97+
for (i = 0; i < strlen(result->versionstr); i++)
98+
{
99+
if (!isalpha(result->versionstr[i]) && !isdigit(result->versionstr[i]) && !char_in_valid_version_digits(result->versionstr[i]))
100+
{
101+
result->errhint = "version string has invalid characters";
102+
return false;
103+
}
104+
}
83105

84106
return true;
85107
}
@@ -89,29 +111,31 @@ telemetry_parse_version(const char *json, VersionInfo *installed_version, Versio
89111
* called "current_timescaledb_version". Check this against the local
90112
* version, and notify the user if it is behind.
91113
*/
92-
static bool
114+
static void
93115
process_response(const char *json)
94116
{
95-
VersionInfo installed_version;
96117
VersionResult result;
118+
bool is_uptodate = DatumGetBool(DirectFunctionCall2(texteq,
119+
DirectFunctionCall2(json_object_field_text,
120+
CStringGetTextDatum(json),
121+
PointerGetDatum(cstring_to_text(TS_IS_UPTODATE_JSON_FIELD))),
122+
PointerGetDatum(cstring_to_text("true"))));
97123

98-
version_get_info(&installed_version);
99-
100-
if (!telemetry_parse_version(json, &installed_version, &result))
101-
{
102-
elog(WARNING, "could not get TimescaleDB version from server response: %s", result.errhint);
103-
return false;
104-
}
105-
106-
if (result.is_up_to_date)
124+
if (is_uptodate)
107125
elog(NOTICE, "the \"%s\" extension is up-to-date", EXTENSION_NAME);
108126
else
127+
{
128+
if (!validate_server_version(json, &result))
129+
{
130+
elog(WARNING, "server did not return a valid TimescaleDB version: %s", result.errhint);
131+
return;
132+
}
133+
109134
ereport(LOG,
110135
(errmsg("the \"%s\" extension is not up-to-date", EXTENSION_NAME),
111136
errhint("The most up-to-date version is %s, the installed version is %s",
112137
result.versionstr, TIMESCALEDB_VERSION_MOD)));
113-
114-
return true;
138+
}
115139
}
116140

117141
static char *

src/telemetry/telemetry.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@
2121
#define TELEMETRY_HOST "telemetry.timescale.com"
2222
#define TELEMETRY_PATH "/v1/metrics"
2323

24+
#define MAX_VERSION_STR_LEN 128
25+
2426
typedef struct VersionResult
2527
{
26-
VersionInfo vinfo;
2728
const char *versionstr;
28-
bool is_up_to_date;
2929
const char *errhint;
3030
} VersionResult;
3131

3232
HttpRequest *build_version_request(const char *host, const char *path);
3333
Connection *telemetry_connect(const char *host, const char *service);
34-
bool telemetry_parse_version(const char *json, VersionInfo *vinfo, VersionResult *result);
34+
bool validate_server_version(const char *json, VersionResult *result);
3535

3636
/*
3737
* This function is intended as the main function for a BGW.

src/version.c

Lines changed: 0 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -20,140 +20,6 @@
2020
#define STR_EXPAND(x) #x
2121
#define STR(x) STR_EXPAND(x)
2222

23-
void
24-
version_get_info(VersionInfo *vinfo)
25-
{
26-
memset(vinfo, 0, sizeof(VersionInfo));
27-
vinfo->version[0] = strtol(TIMESCALEDB_MAJOR_VERSION, NULL, 10);
28-
vinfo->version[1] = strtol(TIMESCALEDB_MINOR_VERSION, NULL, 10);
29-
vinfo->version[2] = strtol(TIMESCALEDB_PATCH_VERSION, NULL, 10);
30-
31-
if (strlen(TIMESCALEDB_MOD_VERSION) > 0)
32-
{
33-
StrNCpy(vinfo->version_mod, TIMESCALEDB_MOD_VERSION, sizeof(vinfo->version_mod));
34-
vinfo->has_version_mod = true;
35-
}
36-
}
37-
38-
/*
39-
* Compare two versions.
40-
*
41-
* It returns an integer less than, equal to, or greater than zero if version
42-
* v1 is found, respectively, to be less than, to match, or be greater than
43-
* version v2.
44-
*/
45-
int
46-
version_cmp(VersionInfo *v1, VersionInfo *v2)
47-
{
48-
int i;
49-
50-
for (i = 0; i < 3; i++)
51-
{
52-
if (v1->version[i] > v2->version[i])
53-
return 1;
54-
55-
if (v1->version[i] < v2->version[i])
56-
return -1;
57-
}
58-
59-
/*
60-
* Note that the version mod signifies a pre-release version, so having a
61-
* version mod is "less" than not having one with otherwise identical
62-
* versions
63-
*/
64-
if (v1->has_version_mod && !v2->has_version_mod)
65-
return -1;
66-
67-
if (!v1->has_version_mod && v2->has_version_mod)
68-
return 1;
69-
70-
/* Compare the version mod lexicographically */
71-
if (v1->has_version_mod && v2->has_version_mod)
72-
return strncmp(v1->version_mod, v2->version_mod, sizeof(v1->version_mod));
73-
74-
return 0;
75-
}
76-
77-
bool
78-
version_parse(const char *version, VersionInfo *result)
79-
{
80-
size_t version_len = strlen(version);
81-
int fields_parsed = 0;
82-
int i = 0;
83-
int parse_length[4] = {0};
84-
85-
memset(result, 0, sizeof(VersionInfo));
86-
87-
/*
88-
* a version is a string of the form
89-
*
90-
* <number>[.<number>[.<number>[-<string>]]]
91-
*
92-
* this corresponds to the format-string
93-
*
94-
* "%lu.%lu.%lu-%<VERSION_INFO_LEN>s"
95-
*
96-
* after parsing each field in the version-string, we output the number of
97-
* bytes currently parsed using %n, a version-string is valid if all of
98-
* the bytes of the string were parsable using the above grammar.
99-
*/
100-
fields_parsed = sscanf(version, "%lu%n.%lu%n.%lu%n-%" STR(VERSION_INFO_LEN) "s%n",
101-
&result->version[0], &parse_length[0],
102-
&result->version[1], &parse_length[1],
103-
&result->version[2], &parse_length[2],
104-
result->version_mod, &parse_length[3]);
105-
106-
/*
107-
* sscanf is allowed to return EOF if no parses succeed, so make sure
108-
* fields_parsed is between 1 and 4;
109-
*/
110-
if (fields_parsed <= 0 || fields_parsed > 4)
111-
return false;
112-
113-
result->has_version_mod = fields_parsed > 3;
114-
115-
result->version_mod[VERSION_INFO_LEN - 1] = '\0';
116-
117-
for (i = 0; i < VERSION_INFO_LEN; i++)
118-
if (!isprint(result->version_mod[i]))
119-
result->version_mod[i] = '\0';
120-
121-
return ((size_t) parse_length[fields_parsed - 1]) == version_len;
122-
}
123-
124-
TS_FUNCTION_INFO_V1(ts_version_get_info);
125-
126-
Datum
127-
ts_version_get_info(PG_FUNCTION_ARGS)
128-
{
129-
VersionInfo info;
130-
TupleDesc tupdesc;
131-
Datum values[4];
132-
bool nulls[4] = {false};
133-
HeapTuple tuple;
134-
135-
version_get_info(&info);
136-
137-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
138-
ereport(ERROR,
139-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
140-
errmsg("function returning record called in context "
141-
"that cannot accept type record")));
142-
143-
values[0] = Int32GetDatum((int32) info.version[0]);
144-
values[1] = Int32GetDatum((int32) info.version[1]);
145-
values[2] = Int32GetDatum((int32) info.version[2]);
146-
147-
if (info.has_version_mod)
148-
values[3] = CStringGetTextDatum(info.version_mod);
149-
else
150-
nulls[3] = true;
151-
152-
tuple = heap_form_tuple(tupdesc, values, nulls);
153-
154-
return HeapTupleGetDatum(tuple);
155-
}
156-
15723
const char *git_commit = STR(EXT_GIT_COMMIT);
15824

15925
TS_FUNCTION_INFO_V1(ts_get_git_commit);

src/version.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@
1010
#include <postgres.h>
1111

1212
#define VERSION_INFO_LEN 128
13-
#define VERSION_PARTS 3
14-
15-
typedef struct VersionInfo
16-
{
17-
long version[VERSION_PARTS];
18-
char version_mod[VERSION_INFO_LEN];
19-
bool has_version_mod;
20-
} VersionInfo;
2113

2214
typedef struct VersionOSInfo
2315
{
@@ -26,10 +18,6 @@ typedef struct VersionOSInfo
2618
char release[VERSION_INFO_LEN];
2719
} VersionOSInfo;
2820

29-
extern void version_get_info(VersionInfo *vinfo);
30-
extern int version_cmp(VersionInfo *v1, VersionInfo *v2);
31-
extern bool version_parse(const char *version, VersionInfo *result);
32-
3321
extern bool version_get_os_info(VersionOSInfo *info);
3422

3523
#endif /* TIMESCALEDB_VERSION_H */

0 commit comments

Comments
 (0)