
Distinguish current status from future plans
PostgreSQL's official announcement identifies the first version 19 release candidate as released on 15 October 2026. It plans another candidate for 12 November and general availability for 19 November. As reviewed on 27 October, version 19 must therefore not be described here as already stable. Plans can change; revisit the official announcement before a later decision rather than converting a scheduled milestone into a claim that a release has happened.
A candidate can be useful for isolated compatibility testing and feedback, but a larger version number does not automatically qualify a production service. Determine which server the application actually reaches and which extensions it uses. The local client utility's version is insufficient because it may connect to a different server release. The supplied inventory query reads information from the connected server and produces a structured object for a separate review step.
Inventory the server and reject silent promotion
The gate compares textual and numeric version evidence and rejects candidate, beta or development strings for production use. It does not automatically approve a hypothetical stable version 19 string either, because that would require a refreshed policy and source review. This small check does not assess extension compatibility, query plans or data migration. Its narrow purpose is to prevent a prerelease or an unreviewed version from silently becoming an approved deployment baseline.
Save the four files and run python -m unittest test_release_gate.py. Run python release_gate.py demo_inventory.json to inspect the supplied candidate fixture. A prerelease assessment and failing deployment exit status are the expected result, not a broken demonstration. An authorized operator with a real target connection can collect data using psql -At -f collect_version.sql > inventory.json. The offline example does not automatically connect to a server or claim to run PostgreSQL 19.
The inventory query is read-only, but its output can reveal installed internal extensions, so retain it in a private location. Passing Python tests against a fixture is not evidence that a cluster upgrade succeeded. A real major-version migration needs backup, restore, compatibility and downtime planning. Adapt the official upgrade guidance to the actual package distribution and operational environment instead of assuming that a generic version-checking script supplies a complete migration procedure.
Complete executable files
The four files are complete. The article provides prerequisites, command order and verification; code is neither shortened nor hidden.
1. Complete collect_version.sql
Save this entire file beside the others using the exact filename.
SELECT json_build_object(
'serverVersion', current_setting('server_version'),
'versionNum', current_setting('server_version_num')::integer,
'extensions', (SELECT coalesce(json_agg(extname ORDER BY extname), '[]'::json) FROM pg_extension)
);The execution section explains this file’s command, expected outcome and limits.
2. Complete demo_inventory.json
Save this entire file beside the others using the exact filename.
{"serverVersion": "19rc1", "versionNum": 190000, "extensions": ["plpgsql"]}The execution section explains this file’s command, expected outcome and limits.
3. Complete release_gate.py
Save this entire file beside the others using the exact filename.
from datetime import date
import json
from pathlib import Path
import re
import sys
REVIEWED = date(2026, 10, 27)
REVIEW_BY = date(2026, 11, 10)
def assess(inventory, today):
if not REVIEWED <= today <= REVIEW_BY:
return 'review-official-release-status'
version = inventory.get('serverVersion', '')
number = inventory.get('versionNum')
if re.search(r'beta|rc|devel', version, re.I):
return 'prerelease-staging-only'
match = re.fullmatch(r'(\d+)\.\d+', version)
if not match or type(number) is not int or int(match[1]) != number // 10000:
return 'review-inconsistent-version-data'
if 14 <= number // 10000 <= 18:
return 'existing-stable-branch-check-patches-and-extensions'
return 'unreviewed-release-check-official-status'
if __name__ == '__main__':
path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).with_name('demo_inventory.json')
result = assess(json.loads(path.read_text(encoding='utf-8')), date.today())
print(json.dumps({'assessment': result}))
raise SystemExit(0 if result.startswith('existing-stable') else 1)The execution section explains this file’s command, expected outcome and limits.
4. Complete test_release_gate.py
Save this entire file beside the others using the exact filename.
from datetime import date
import unittest
from release_gate import assess
class ReleaseTests(unittest.TestCase):
def test_candidate_and_future_stable_are_not_promoted(self):
today = date(2026, 10, 27)
self.assertEqual(assess({'serverVersion': '19rc1', 'versionNum': 190000}, today), 'prerelease-staging-only')
self.assertIn('unreviewed-release', assess({'serverVersion': '19.0', 'versionNum': 190000}, today))
self.assertIn('existing-stable', assess({'serverVersion': '18.6', 'versionNum': 180006}, today))
def test_policy_and_mismatched_inventory(self):
self.assertIn('official-release', assess({}, date(2026, 11, 11)))
self.assertIn('inconsistent', assess({'serverVersion': '18.6', 'versionNum': 190000}, date(2026, 10, 27)))
if __name__ == '__main__': unittest.main()The execution section explains this file’s command, expected outcome and limits.
Run a read-only check before upgrade experiments
Prepare a representative workload, non-sensitive test data and baseline measurements for a real evaluation. Compare results, response times and transaction behavior between environments and define rollback criteria. Read the eventual final release notes again on the decision date, because this article records a dated review. The practical goal is an auditable rehearsal before a stable release, rather than encouraging a release candidate to run against an organization's primary data.
Implementation checklist
- Do not label a candidate as stable.
- Read version evidence from the connected server.
- Expect the candidate fixture to stop deployment.
- Review final notes and recovery requirements again.
References
Editorial guidance: Liyan Knowledge.




