|
5 | 5 | # the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
|
6 | 6 |
|
7 | 7 |
|
8 |
| -from . import types |
| 8 | +import re |
| 9 | + |
| 10 | +from .types import ServerVersion |
| 11 | + |
| 12 | +version_regex = re.compile( |
| 13 | + r"(Postgre[^\s]*)?\s*" |
| 14 | + r"(?P<major>[0-9]+)\.?" |
| 15 | + r"((?P<minor>[0-9]+)\.?)?" |
| 16 | + r"(?P<micro>[0-9]+)?" |
| 17 | + r"(?P<releaselevel>[a-z]+)?" |
| 18 | + r"(?P<serial>[0-9]+)?" |
| 19 | +) |
9 | 20 |
|
10 | 21 |
|
11 | 22 | def split_server_version_string(version_string):
|
12 |
| - version_string = version_string.strip() |
13 |
| - if version_string.startswith('PostgreSQL '): |
14 |
| - version_string = version_string[len('PostgreSQL '):] |
15 |
| - if version_string.startswith('Postgres-XL'): |
16 |
| - version_string = version_string[len('Postgres-XL '):] |
17 |
| - |
18 |
| - # Some distros (e.g Debian) like may inject their branding |
19 |
| - # into the numeric version string, so make sure to only look |
20 |
| - # at stuff before the first space. |
21 |
| - version_string = version_string.split(' ')[0] |
22 |
| - parts = version_string.strip().split('.') |
23 |
| - if not parts[-1].isdigit(): |
24 |
| - # release level specified |
25 |
| - lastitem = parts[-1] |
26 |
| - levelpart = lastitem.rstrip('0123456789').lower() |
27 |
| - if levelpart != lastitem: |
28 |
| - serial = int(lastitem[len(levelpart):]) |
29 |
| - else: |
30 |
| - serial = 0 |
31 |
| - |
32 |
| - level = levelpart.lstrip('0123456789') |
33 |
| - if level != levelpart: |
34 |
| - parts[-1] = levelpart[:-len(level)] |
35 |
| - else: |
36 |
| - parts[-1] = 0 |
37 |
| - else: |
38 |
| - level = 'final' |
39 |
| - serial = 0 |
40 |
| - |
41 |
| - if int(parts[0]) >= 10: |
42 |
| - # Since PostgreSQL 10 the versioning scheme has changed. |
43 |
| - # 10.x really means 10.0.x. While parsing 10.1 |
44 |
| - # as (10, 1) may seem less confusing, in practice most |
45 |
| - # version checks are written as version[:2], and we |
46 |
| - # want to keep that behaviour consistent, i.e not fail |
47 |
| - # a major version check due to a bugfix release. |
48 |
| - parts.insert(1, 0) |
49 |
| - |
50 |
| - versions = [int(p) for p in parts][:3] |
51 |
| - if len(versions) < 3: |
52 |
| - versions += [0] * (3 - len(versions)) |
53 |
| - |
54 |
| - versions.append(level) |
55 |
| - versions.append(serial) |
56 |
| - |
57 |
| - return types.ServerVersion(*versions) |
| 23 | + version_match = version_regex.search(version_string) |
| 24 | + |
| 25 | + if version_match is None: |
| 26 | + raise ValueError( |
| 27 | + "Unable to parse Postgres " |
| 28 | + f'version from "{version_string}"' |
| 29 | + ) |
| 30 | + |
| 31 | + version = version_match.groupdict() |
| 32 | + for ver_key, ver_value in version.items(): |
| 33 | + # Cast all possible versions parts to int |
| 34 | + try: |
| 35 | + version[ver_key] = int(ver_value) |
| 36 | + except (TypeError, ValueError): |
| 37 | + pass |
| 38 | + |
| 39 | + if version.get("major") < 10: |
| 40 | + return ServerVersion( |
| 41 | + version.get("major"), |
| 42 | + version.get("minor") or 0, |
| 43 | + version.get("micro") or 0, |
| 44 | + version.get("releaselevel") or "final", |
| 45 | + version.get("serial") or 0, |
| 46 | + ) |
| 47 | + |
| 48 | + # Since PostgreSQL 10 the versioning scheme has changed. |
| 49 | + # 10.x really means 10.0.x. While parsing 10.1 |
| 50 | + # as (10, 1) may seem less confusing, in practice most |
| 51 | + # version checks are written as version[:2], and we |
| 52 | + # want to keep that behaviour consistent, i.e not fail |
| 53 | + # a major version check due to a bugfix release. |
| 54 | + return ServerVersion( |
| 55 | + version.get("major"), |
| 56 | + 0, |
| 57 | + version.get("minor") or 0, |
| 58 | + version.get("releaselevel") or "final", |
| 59 | + version.get("serial") or 0, |
| 60 | + ) |
0 commit comments