{"id":185,"date":"2026-10-09T14:25:35","date_gmt":"2026-10-09T14:25:35","guid":{"rendered":"https:\/\/devdojo.co.in\/?p=185"},"modified":"2026-10-09T14:25:35","modified_gmt":"2026-10-09T14:25:35","slug":"postgresql-19-new-features","status":"publish","type":"post","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/","title":{"rendered":"PostgreSQL 19 New Features: A Practical Guide for Developers"},"content":{"rendered":"\n<div class=\"dd-post\">\n<style>\n.single article:has(.dd-hero) .entry-media{display:none}\n.dd-post{font-size:17px;line-height:1.75;color:inherit}\n.dd-post h2{margin:2.2em 0 .6em;font-size:1.7em;line-height:1.3}\n.dd-post h3{margin:1.6em 0 .5em;font-size:1.3em}\n.dd-post p{margin:0 0 1.1em}\n.dd-post a{color:#3178c6}\n.dd-post pre{background:#0f1430;color:#e6e9f5;padding:18px 20px;border-radius:12px;overflow-x:auto;margin:1.2em 0 1.5em;border:1px solid #232a55;line-height:1.6;font-size:14.5px !important}\n.dd-post pre code{font-family:\"JetBrains Mono\",Consolas,Menlo,monospace;background:none;color:inherit;padding:0;font-size:1em !important;white-space:pre}\n.dd-post :not(pre)>code{background:rgba(124,92,255,.12);color:#5b3fd6;padding:2px 6px;border-radius:5px;font-size:.9em}\n.dd-post .dd-label{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;background:#3178c6;color:#fff;padding:2px 10px;border-radius:6px 6px 0 0;margin-bottom:-1.2em}\n.dd-post .dd-hero{margin:0 0 1.8em;border-radius:16px;overflow:hidden;box-shadow:0 10px 30px rgba(11,16,32,.35)}\n.dd-post .dd-hero svg{display:block;width:100%;height:auto}\n.dd-post .dd-hero .node{transition:transform .25s ease,filter .25s ease;transform-box:fill-box;transform-origin:center;cursor:default}\n.dd-post .dd-hero .node:hover{transform:scale(1.08);filter:drop-shadow(0 0 10px #7c5cff)}\n.dd-post .dd-summary{background:linear-gradient(135deg,rgba(49,120,198,.10),rgba(124,92,255,.10));border:1px solid rgba(124,92,255,.35);border-left:5px solid #7c5cff;border-radius:12px;padding:18px 22px;margin:0 0 1.6em}\n.dd-post .dd-summary h2{margin:0 0 .4em;font-size:1.2em}\n.dd-post .dd-summary ul{margin:0;padding-left:1.2em}\n.dd-post .dd-toc{background:rgba(127,127,127,.06);border:1px solid rgba(127,127,127,.25);border-radius:12px;padding:16px 22px;margin:0 0 2em}\n.dd-post .dd-toc strong{display:block;margin-bottom:.4em}\n.dd-post .dd-toc ol{margin:0;padding-left:1.3em}\n.dd-post .dd-toc a{text-decoration:none}\n.dd-post .dd-toc a:hover{text-decoration:underline}\n.dd-post .dd-tip,.dd-post .dd-note,.dd-post .dd-warn{border-radius:10px;padding:14px 18px;margin:1.2em 0 1.5em}\n.dd-post .dd-tip{background:rgba(34,197,94,.10);border-left:5px solid #22c55e}\n.dd-post .dd-note{background:rgba(49,120,198,.10);border-left:5px solid #3178c6}\n.dd-post .dd-warn{background:rgba(245,158,11,.12);border-left:5px solid #f59e0b}\n.dd-post .dd-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin:1.2em 0 1.8em}\n.dd-post .dd-card{border:1px solid rgba(127,127,127,.25);border-radius:12px;padding:14px 16px;background:rgba(127,127,127,.04)}\n.dd-post .dd-card b{display:block;color:#3178c6;margin-bottom:4px}\n.dd-post table{width:100%;border-collapse:collapse;margin:1.2em 0 1.8em;font-size:.95em}\n.dd-post th,.dd-post td{border:1px solid rgba(127,127,127,.3);padding:10px 12px;text-align:left;vertical-align:top}\n.dd-post th{background:#151b3b;color:#fff}\n.dd-post tr:nth-child(even) td{background:rgba(127,127,127,.05)}\n.dd-post .dd-table-wrap{overflow-x:auto}\n.dd-post figure{margin:1.6em 0 2em;text-align:center}\n.dd-post figure svg{width:100%;height:auto;max-width:900px;border-radius:12px}\n.dd-post figcaption{font-size:.9em;opacity:.8;margin-top:.5em}\n.dd-post details{border:1px solid rgba(127,127,127,.3);border-radius:10px;padding:12px 16px;margin:.7em 0;background:rgba(127,127,127,.03)}\n.dd-post details summary{cursor:pointer;font-weight:700}\n.dd-post details[open] summary{margin-bottom:.6em;color:#3178c6}\n.dd-post .dd-tabs{margin:1.4em 0 2em;border:1px solid rgba(127,127,127,.3);border-radius:12px;overflow:hidden}\n.dd-post .dd-tabs input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-tabs .dd-tab-labels{display:flex;flex-wrap:wrap;background:#151b3b}\n.dd-post .dd-tabs label{padding:12px 18px;color:#c9cff0;cursor:pointer;font-weight:600;font-size:.95em;border-bottom:3px solid transparent}\n.dd-post .dd-tabs label:hover{color:#fff}\n.dd-post .dd-tabs .dd-panel{display:none;padding:6px 18px 4px}\n.dd-post #dd-t1:checked~.dd-tab-labels label[for=dd-t1],.dd-post #dd-t2:checked~.dd-tab-labels label[for=dd-t2],.dd-post #dd-t3:checked~.dd-tab-labels label[for=dd-t3]{color:#fff;border-bottom-color:#7c5cff;background:rgba(124,92,255,.18)}\n.dd-post #dd-t1:checked~.dd-p1,.dd-post #dd-t2:checked~.dd-p2,.dd-post #dd-t3:checked~.dd-p3{display:block}\n.dd-post .dd-cta{text-align:center;background:linear-gradient(135deg,#0b1020,#151b3b);color:#fff;border-radius:16px;padding:28px 22px;margin:2em 0 1em}\n.dd-post .dd-cta h3{margin:0 0 .4em;color:#fff}\n.dd-post .dd-cta p{margin:0;color:#c9cff0}\n.dd-post .dd-cta a{color:#9fb8ff}\n<\/style>\n<div class=\"dd-hero\">\n<svg viewBox=\"0 0 1200 520\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-labelledby=\"ddHeroT ddHeroD\">\n<title id=\"ddHeroT\">PostgreSQL 19 New Features<\/title>\n<desc id=\"ddHeroD\">Animated banner showing a database at the centre connected to feature nodes: REPACK, Upsert Returns, Read Your Writes, Graph Queries and Partitions.<\/desc>\n<defs>\n<linearGradient id=\"ddBg\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#0b1020\"\/><stop offset=\"1\" stop-color=\"#151b3b\"\/><\/linearGradient>\n<linearGradient id=\"ddAcc\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient>\n<radialGradient id=\"ddGlow\" cx=\"0.5\" cy=\"0.5\" r=\"0.5\"><stop offset=\"0\" stop-color=\"#7c5cff\" stop-opacity=\".45\"\/><stop offset=\"1\" stop-color=\"#7c5cff\" stop-opacity=\"0\"\/><\/radialGradient>\n<\/defs>\n<rect x=\"0\" y=\"0\" width=\"1200\" height=\"520\" fill=\"url(#ddBg)\"\/>\n<g stroke=\"#ffffff\" stroke-opacity=\".05\">\n<path d=\"M0 104H1200M0 208H1200M0 312H1200M0 416H1200M150 0V520M300 0V520M450 0V520M600 0V520M750 0V520M900 0V520M1050 0V520\"\/>\n<\/g>\n<text x=\"60\" y=\"110\" fill=\"#9fb8ff\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"22\" font-weight=\"600\" letter-spacing=\"3\">DEVDOJO \u00b7 DATABASES<\/text>\n<text x=\"60\" y=\"180\" fill=\"#ffffff\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"58\" font-weight=\"800\">PostgreSQL 19<\/text>\n<text x=\"60\" y=\"246\" fill=\"url(#ddAcc)\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"58\" font-weight=\"800\">New Features<\/text>\n<text x=\"60\" y=\"300\" fill=\"#c9cff0\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"24\">REPACK, upsert returns, read-your-writes,<\/text>\n<text x=\"60\" y=\"334\" fill=\"#c9cff0\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"24\">graph queries &amp; safer defaults \u2014 with code<\/text>\n<rect x=\"60\" y=\"370\" width=\"250\" height=\"48\" rx=\"24\" fill=\"url(#ddAcc)\"\/>\n<text x=\"185\" y=\"401\" text-anchor=\"middle\" fill=\"#fff\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"20\" font-weight=\"700\">Practical guide \u2192<\/text>\n<g fill=\"none\" stroke=\"#3178c6\" stroke-opacity=\".55\" stroke-width=\"2\">\n<path id=\"ddP1\" d=\"M880 270 L730 130\"\/>\n<path id=\"ddP2\" d=\"M880 270 L1080 120\"\/>\n<path id=\"ddP3\" d=\"M880 270 L1110 300\"\/>\n<path id=\"ddP4\" d=\"M880 270 L1010 450\"\/>\n<path id=\"ddP5\" d=\"M880 270 L720 420\"\/>\n<\/g>\n<circle cx=\"880\" cy=\"270\" r=\"140\" fill=\"url(#ddGlow)\"\/>\n<g class=\"node\">\n<ellipse cx=\"880\" cy=\"235\" rx=\"62\" ry=\"18\" fill=\"#3178c6\"\/>\n<path d=\"M818 235v70c0 10 28 18 62 18s62-8 62-18v-70\" fill=\"#1e4f8f\"\/>\n<ellipse cx=\"880\" cy=\"235\" rx=\"62\" ry=\"18\" fill=\"#3d8be0\"\/>\n<path d=\"M818 260c0 10 28 18 62 18s62-8 62-18M818 284c0 10 28 18 62 18s62-8 62-18\" fill=\"none\" stroke=\"#9fc4f5\" stroke-width=\"2\"\/>\n<text x=\"880\" y=\"352\" text-anchor=\"middle\" fill=\"#fff\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\">PG 19<\/text>\n<\/g>\n<g font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" font-weight=\"700\" fill=\"#fff\" text-anchor=\"middle\">\n<g class=\"node\"><rect x=\"660\" y=\"106\" width=\"140\" height=\"44\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"730\" y=\"134\">REPACK<\/text><\/g>\n<g class=\"node\"><rect x=\"1000\" y=\"96\" width=\"160\" height=\"44\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"1080\" y=\"124\">DO SELECT<\/text><\/g>\n<g class=\"node\"><rect x=\"1030\" y=\"278\" width=\"150\" height=\"44\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"1105\" y=\"306\">WAIT FOR LSN<\/text><\/g>\n<g class=\"node\"><rect x=\"930\" y=\"428\" width=\"160\" height=\"44\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"1010\" y=\"456\">Graph Queries<\/text><\/g>\n<g class=\"node\"><rect x=\"640\" y=\"398\" width=\"160\" height=\"44\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"720\" y=\"426\">Partitions<\/text><\/g>\n<\/g>\n<g fill=\"#7c5cff\">\n<circle r=\"5\"><animateMotion dur=\"2.6s\" repeatCount=\"indefinite\"><mpath href=\"#ddP1\"\/><\/animateMotion><\/circle>\n<circle r=\"5\"><animateMotion dur=\"3.1s\" repeatCount=\"indefinite\"><mpath href=\"#ddP2\"\/><\/animateMotion><\/circle>\n<circle r=\"5\"><animateMotion dur=\"2.3s\" repeatCount=\"indefinite\"><mpath href=\"#ddP3\"\/><\/animateMotion><\/circle>\n<circle r=\"5\"><animateMotion dur=\"2.9s\" repeatCount=\"indefinite\"><mpath href=\"#ddP4\"\/><\/animateMotion><\/circle>\n<circle r=\"5\"><animateMotion dur=\"3.4s\" repeatCount=\"indefinite\"><mpath href=\"#ddP5\"\/><\/animateMotion><\/circle>\n<\/g>\n<\/svg>\n<\/div>\n\n<div class=\"dd-summary\">\n<h2>Quick Summary<\/h2>\n<ul>\n<li><strong>PostgreSQL 19<\/strong> is in its final beta stage (Beta 4 came out on 24 September), and the stable release is expected very soon.<\/li>\n<li><strong>REPACK<\/strong> replaces <code>VACUUM FULL<\/code> and <code>CLUSTER<\/code>, and its <code>CONCURRENTLY<\/code> option shrinks bloated tables while your app keeps reading and writing.<\/li>\n<li><strong>INSERT \u2026 ON CONFLICT DO SELECT<\/strong> finally gives you a clean &#8220;get or create&#8221; in one statement.<\/li>\n<li><strong>WAIT FOR LSN<\/strong> lets a read replica wait until it has caught up, so users always see their own changes.<\/li>\n<li>Also new: <strong>GROUP BY ALL<\/strong>, partition <strong>MERGE\/SPLIT<\/strong>, SQL-standard <strong>graph queries<\/strong>, <strong>COPY to JSON<\/strong> and online data checksums.<\/li>\n<li>Watch the changed defaults before upgrading: <strong>JIT is now off<\/strong>, TOAST uses <strong>lz4<\/strong>, and <strong>RADIUS login is removed<\/strong>.<\/li>\n<\/ul>\n<\/div>\n\n<nav class=\"dd-toc\" aria-label=\"Table of contents\">\n<strong>Table of Contents<\/strong>\n<ol>\n<li><a href=\"#what-is-new\">What is new in PostgreSQL 19?<\/a><\/li>\n<li><a href=\"#try-it\">Try PostgreSQL 19 locally with Docker<\/a><\/li>\n<li><a href=\"#repack\">REPACK: shrink tables without downtime<\/a><\/li>\n<li><a href=\"#do-select\">ON CONFLICT DO SELECT: get-or-create in one query<\/a><\/li>\n<li><a href=\"#wait-for-lsn\">WAIT FOR LSN: read your own writes on replicas<\/a><\/li>\n<li><a href=\"#sql-goodies\">Smaller SQL features you will use every day<\/a><\/li>\n<li><a href=\"#partitions-graphs\">Partition merge\/split and graph queries<\/a><\/li>\n<li><a href=\"#ops\">Operations: autovacuum, checksums and monitoring<\/a><\/li>\n<li><a href=\"#defaults\">Changed defaults: read this before upgrading<\/a><\/li>\n<li><a href=\"#errors\">Common errors and fixes<\/a><\/li>\n<li><a href=\"#best-practices\">Best practices for upgrading<\/a><\/li>\n<li><a href=\"#faq\">FAQ<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ol>\n<\/nav>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<p>Every autumn the PostgreSQL team ships a new major version, and this year&#8217;s release is a big one for everyday developers. In this guide we walk through the <strong>PostgreSQL 19 new features<\/strong> that actually change how you write SQL and run your database: a non-blocking way to fix table bloat, a one-line &#8220;get or create&#8221;, read-your-writes on replicas, cleaner grouping, JSON exports and more. Every feature comes with a small, runnable example, so you can try it yourself in a few minutes.<\/p>\n\n<p>This post is written for beginner and intermediate developers \u2014 whether you build APIs in Node.js or Python, or you just look after a Postgres database at work. You do not need to be a database administrator to follow along.<\/p>\n\n<h2 id=\"what-is-new\">What is new in PostgreSQL 19?<\/h2>\n\n<p>PostgreSQL 19 is still in the beta stage at the time of writing. The project released <strong>Beta 4 on 24 September<\/strong> and said the final release is expected around September\/October, so the stable version is very close. Features rarely change this late, but small details can, so always confirm with the <a href=\"https:\/\/www.postgresql.org\/docs\/19\/release-19.html\" target=\"_blank\" rel=\"noopener\">official PostgreSQL 19 release notes<\/a> before you upgrade production.<\/p>\n\n<p>Here are the headline changes, grouped by who benefits most:<\/p>\n\n<div class=\"dd-cards\">\n<div class=\"dd-card\"><b>For app developers<\/b><code>ON CONFLICT DO SELECT<\/code>, <code>GROUP BY ALL<\/code>, <code>WAIT FOR LSN<\/code>, <code>COPY \u2026 (FORMAT json)<\/code>, new jsonpath string methods, <code>random()<\/code> for dates.<\/div>\n<div class=\"dd-card\"><b>For data modelling<\/b>Merge and split partitions, SQL-standard property graph queries (SQL\/PGQ), <code>bytea<\/code> \u2194 <code>uuid<\/code> casts.<\/div>\n<div class=\"dd-card\"><b>For operations<\/b><code>REPACK (CONCURRENTLY)<\/code>, parallel autovacuum, online data checksums, new <code>pg_stat_lock<\/code> view, faster foreign-key inserts.<\/div>\n<div class=\"dd-card\"><b>For security<\/b>RADIUS removed, warnings for md5 passwords and soon-to-expire passwords, TLS certificates per hostname (SNI).<\/div>\n<\/div>\n\n<h2 id=\"try-it\">Try PostgreSQL 19 locally with Docker<\/h2>\n\n<p>The quickest way to play with a new version is Docker. The official image publishes beta tags; once the final version ships you can simply use <code>postgres:19<\/code>.<\/p>\n\n<pre><code># Start a throwaway PostgreSQL 19 container\ndocker run --name pg19 -e POSTGRES_PASSWORD=devdojo \\\n  -p 5433:5432 -d postgres:19beta4\n\n# Open psql inside it\ndocker exec -it pg19 psql -U postgres\n\n# Check the version\nSELECT version();<\/code><\/pre>\n\n<div class=\"dd-tip\"><strong>Tip:<\/strong> We map the container to port <code>5433<\/code> so it does not clash with a Postgres server you may already have on <code>5432<\/code>. When you are done, run <code>docker rm -f pg19<\/code>.<\/div>\n\n<p>Now create a tiny sample table that we will use in the next sections:<\/p>\n\n<pre><code>CREATE TABLE users (\n  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  email      text NOT NULL UNIQUE,\n  name       text,\n  country    text,\n  plan       text DEFAULT 'free',\n  created_at timestamptz DEFAULT now()\n);\n\nINSERT INTO users (email, name, country, plan) VALUES\n  ('asha@example.com',  'Asha',  'IN', 'pro'),\n  ('ravi@example.com',  'Ravi',  'IN', 'free'),\n  ('emma@example.com',  'Emma',  'UK', 'pro'),\n  ('lucas@example.com', 'Lucas', 'BR', 'free');<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"repack\">REPACK: shrink tables without downtime<\/h2>\n\n<p>When you update or delete lots of rows, PostgreSQL leaves &#8220;dead&#8221; space behind. Normal autovacuum marks that space as reusable, but it does not give it back to the operating system. Over time a busy table can become <em>bloated<\/em> \u2014 much larger on disk than the data it holds \u2014 which slows down scans and backups.<\/p>\n\n<p>Until now the fix was <code>VACUUM FULL<\/code> or <code>CLUSTER<\/code>. Both rewrite the whole table but hold an exclusive lock the entire time, so your app cannot even read the table. Many teams used the external <code>pg_repack<\/code> extension to avoid that. PostgreSQL 19 brings this idea into the core with a new command called <strong>REPACK<\/strong>.<\/p>\n\n<pre><code>-- Same behaviour as VACUUM FULL (takes an exclusive lock)\nREPACK users;\n\n-- Rewrite in index order, like CLUSTER used to do\nREPACK users USING INDEX users_pkey;\n\n-- The big one: rebuild while reads and writes continue\nREPACK (CONCURRENTLY) users;\n\n-- Rebuild, then refresh planner statistics, with progress output\nREPACK (CONCURRENTLY, ANALYZE, VERBOSE) users;<\/code><\/pre>\n\n<p>You can follow a running repack in the new <code>pg_stat_progress_repack<\/code> view. The old <code>VACUUM FULL<\/code> and <code>CLUSTER<\/code> commands still work for compatibility, so existing scripts will not break.<\/p>\n\n<figure>\n<svg viewBox=\"0 0 900 300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-labelledby=\"ddF1T\">\n<title id=\"ddF1T\">How REPACK CONCURRENTLY works in three steps<\/title>\n<rect width=\"900\" height=\"300\" rx=\"14\" fill=\"#0f1430\"\/>\n<g font-family=\"Segoe UI,Arial,sans-serif\" text-anchor=\"middle\">\n<rect x=\"30\" y=\"60\" width=\"240\" height=\"150\" rx=\"12\" fill=\"#151b3b\" stroke=\"#3178c6\" stroke-width=\"2\"\/>\n<text x=\"150\" y=\"95\" fill=\"#9fb8ff\" font-size=\"15\" font-weight=\"700\">STEP 1 \u00b7 COPY<\/text>\n<text x=\"150\" y=\"128\" fill=\"#fff\" font-size=\"15\">Live rows are copied<\/text>\n<text x=\"150\" y=\"150\" fill=\"#fff\" font-size=\"15\">into a new, compact table<\/text>\n<text x=\"150\" y=\"185\" fill=\"#22c55e\" font-size=\"14\">App: reads + writes OK<\/text>\n<rect x=\"330\" y=\"60\" width=\"240\" height=\"150\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/>\n<text x=\"450\" y=\"95\" fill=\"#c4b5fd\" font-size=\"15\" font-weight=\"700\">STEP 2 \u00b7 CATCH UP<\/text>\n<text x=\"450\" y=\"128\" fill=\"#fff\" font-size=\"15\">Changes made meanwhile<\/text>\n<text x=\"450\" y=\"150\" fill=\"#fff\" font-size=\"15\">are replayed via decoding<\/text>\n<text x=\"450\" y=\"185\" fill=\"#22c55e\" font-size=\"14\">App: reads + writes OK<\/text>\n<rect x=\"630\" y=\"60\" width=\"240\" height=\"150\" rx=\"12\" fill=\"#151b3b\" stroke=\"#f59e0b\" stroke-width=\"2\"\/>\n<text x=\"750\" y=\"95\" fill=\"#fcd34d\" font-size=\"15\" font-weight=\"700\">STEP 3 \u00b7 SWAP<\/text>\n<text x=\"750\" y=\"128\" fill=\"#fff\" font-size=\"15\">New file replaces the old<\/text>\n<text x=\"750\" y=\"150\" fill=\"#fff\" font-size=\"15\">one in a very short lock<\/text>\n<text x=\"750\" y=\"185\" fill=\"#fcd34d\" font-size=\"14\">App: brief pause only<\/text>\n<path d=\"M272 135h52M572 135h52\" stroke=\"#7c5cff\" stroke-width=\"3\" marker-end=\"url(#ddArr)\"\/>\n<text x=\"450\" y=\"255\" fill=\"#c9cff0\" font-size=\"15\">Result: the bloated table shrinks without a long outage<\/text>\n<\/g>\n<defs><marker id=\"ddArr\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#7c5cff\"\/><\/marker><\/defs>\n<\/svg>\n<figcaption>REPACK (CONCURRENTLY) copies the table, catches up with new changes, then swaps files with only a short lock.<\/figcaption>\n<\/figure>\n\n<div class=\"dd-warn\"><strong>Requirements for CONCURRENTLY:<\/strong> the table needs a primary key (or an index-based replica identity), it cannot be partitioned or <code>UNLOGGED<\/code>, the command cannot run inside a transaction block, and there must be a free slot under the new <code>max_repack_replication_slots<\/code> setting. Because it uses logical decoding behind the scenes, also make sure your WAL settings allow it.<\/div>\n\n<h2 id=\"do-select\">ON CONFLICT DO SELECT: get-or-create in one query<\/h2>\n\n<p>A very common task: &#8220;insert this tag (or user, or product) if it is new, otherwise give me the existing row&#8221;. Before PostgreSQL 19, <code>ON CONFLICT DO NOTHING<\/code> returned <em>no row<\/em> when the value already existed, so you had to run a second <code>SELECT<\/code> \u2014 or use a fake update like <code>DO UPDATE SET email = EXCLUDED.email<\/code>, which creates a dead row every time.<\/p>\n\n<p>PostgreSQL 19 adds <strong><code>ON CONFLICT \u2026 DO SELECT<\/code><\/strong>. On a conflict, it simply returns the existing row through <code>RETURNING<\/code>. Click the tabs below to compare the old and new approaches:<\/p>\n\n<div class=\"dd-tabs\">\n<input type=\"radio\" name=\"dd-tabs\" id=\"dd-t1\" checked>\n<input type=\"radio\" name=\"dd-tabs\" id=\"dd-t2\">\n<input type=\"radio\" name=\"dd-tabs\" id=\"dd-t3\">\n<div class=\"dd-tab-labels\">\n<label for=\"dd-t1\">Old way (2 queries)<\/label>\n<label for=\"dd-t2\">PostgreSQL 19 way<\/label>\n<label for=\"dd-t3\">Node.js example<\/label>\n<\/div>\n<div class=\"dd-panel dd-p1\">\n<pre><code>-- Step 1: try to insert, ignore duplicates\nINSERT INTO users (email, name)\nVALUES ('asha@example.com', 'Asha')\nON CONFLICT (email) DO NOTHING\nRETURNING id, email;   -- returns 0 rows if Asha exists!\n\n-- Step 2: so we need another round trip\nSELECT id, email FROM users WHERE email = 'asha@example.com';<\/code><\/pre>\n<\/div>\n<div class=\"dd-panel dd-p2\">\n<pre><code>-- One statement, always returns exactly one row\nINSERT INTO users (email, name)\nVALUES ('asha@example.com', 'Asha')\nON CONFLICT (email) DO SELECT\nRETURNING id, email, plan;\n\n-- Lock the existing row too, if you will update it next\nINSERT INTO users (email, name)\nVALUES ('asha@example.com', 'Asha')\nON CONFLICT (email) DO SELECT FOR UPDATE\nRETURNING id, plan;<\/code><\/pre>\n<\/div>\n<div class=\"dd-panel dd-p3\">\n<pre><code>\/\/ npm install pg\nimport pg from \"pg\";\n\nconst pool = new pg.Pool({\n  connectionString: \"postgres:\/\/postgres:devdojo@localhost:5433\/postgres\",\n});\n\nexport async function getOrCreateUser(email, name) {\n  const { rows } = await pool.query(\n    `INSERT INTO users (email, name)\n     VALUES ($1, $2)\n     ON CONFLICT (email) DO SELECT\n     RETURNING id, email, plan`,\n    [email, name]\n  );\n  return rows[0]; \/\/ always defined: new row or existing row\n}\n\nconsole.log(await getOrCreateUser(\"asha@example.com\", \"Asha\"));\nconsole.log(await getOrCreateUser(\"neha@example.com\", \"Neha\"));\nawait pool.end();<\/code><\/pre>\n<\/div>\n<\/div>\n\n<div class=\"dd-note\"><strong>Rules to remember:<\/strong> <code>DO SELECT<\/code> needs a conflict target (like <code>(email)<\/code>) and a <code>RETURNING<\/code> clause. You can add a <code>WHERE<\/code> condition to return the existing row only in some cases, and an optional <code>FOR UPDATE<\/code>\/<code>FOR SHARE<\/code> to lock it.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"wait-for-lsn\">WAIT FOR LSN: read your own writes on replicas<\/h2>\n\n<p>Many apps send writes to the primary server and reads to a read replica to spread the load. The catch is <em>replication lag<\/em>: a user saves their profile, the page reloads from the replica, and the old data shows up for a moment. It looks like a bug, and users lose trust.<\/p>\n\n<p>PostgreSQL 19 adds the <strong><code>WAIT FOR LSN<\/code><\/strong> command. An LSN (Log Sequence Number) is simply a position in the database&#8217;s change log. The idea is simple:<\/p>\n\n<ol>\n<li>After writing on the primary, ask for the current log position with <code>pg_current_wal_lsn()<\/code>.<\/li>\n<li>On the replica, run <code>WAIT FOR LSN '\u2026'<\/code> \u2014 it pauses until the replica has replayed up to that point.<\/li>\n<li>Now run your <code>SELECT<\/code>. The user is guaranteed to see their own change.<\/li>\n<\/ol>\n\n<figure>\n<svg viewBox=\"0 0 900 320\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-labelledby=\"ddF2T\">\n<title id=\"ddF2T\">Read-your-writes flow using WAIT FOR LSN between a primary and a replica<\/title>\n<rect width=\"900\" height=\"320\" rx=\"14\" fill=\"#0f1430\"\/>\n<defs><marker id=\"ddArr2\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#9fb8ff\"\/><\/marker><\/defs>\n<g font-family=\"Segoe UI,Arial,sans-serif\" text-anchor=\"middle\">\n<rect x=\"40\" y=\"120\" width=\"170\" height=\"80\" rx=\"12\" fill=\"#151b3b\" stroke=\"#9fb8ff\" stroke-width=\"2\"\/>\n<text x=\"125\" y=\"155\" fill=\"#fff\" font-size=\"17\" font-weight=\"700\">Your API<\/text>\n<text x=\"125\" y=\"180\" fill=\"#c9cff0\" font-size=\"13\">Node.js \/ FastAPI<\/text>\n<rect x=\"360\" y=\"30\" width=\"200\" height=\"90\" rx=\"12\" fill=\"#151b3b\" stroke=\"#3178c6\" stroke-width=\"2\"\/>\n<text x=\"460\" y=\"68\" fill=\"#fff\" font-size=\"17\" font-weight=\"700\">Primary<\/text>\n<text x=\"460\" y=\"94\" fill=\"#c9cff0\" font-size=\"13\">writes happen here<\/text>\n<rect x=\"360\" y=\"200\" width=\"200\" height=\"90\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/>\n<text x=\"460\" y=\"238\" fill=\"#fff\" font-size=\"17\" font-weight=\"700\">Read replica<\/text>\n<text x=\"460\" y=\"264\" fill=\"#c9cff0\" font-size=\"13\">may lag a little<\/text>\n<path d=\"M212 140 L355 80\" stroke=\"#9fb8ff\" stroke-width=\"2.5\" fill=\"none\" marker-end=\"url(#ddArr2)\"\/>\n<text x=\"250\" y=\"92\" fill=\"#9fb8ff\" font-size=\"13\">1. UPDATE + get LSN<\/text>\n<path d=\"M212 180 L355 240\" stroke=\"#9fb8ff\" stroke-width=\"2.5\" fill=\"none\" marker-end=\"url(#ddArr2)\"\/>\n<text x=\"248\" y=\"250\" fill=\"#9fb8ff\" font-size=\"13\">2. WAIT FOR LSN<\/text>\n<path d=\"M460 122 L460 196\" stroke=\"#7c5cff\" stroke-width=\"2.5\" stroke-dasharray=\"6 6\" fill=\"none\" marker-end=\"url(#ddArr2)\"\/>\n<text x=\"540\" y=\"164\" fill=\"#c4b5fd\" font-size=\"13\">replication<\/text>\n<rect x=\"640\" y=\"200\" width=\"220\" height=\"90\" rx=\"12\" fill=\"#14301f\" stroke=\"#22c55e\" stroke-width=\"2\"\/>\n<text x=\"750\" y=\"238\" fill=\"#fff\" font-size=\"16\" font-weight=\"700\">3. SELECT<\/text>\n<text x=\"750\" y=\"264\" fill=\"#bbf7d0\" font-size=\"13\">fresh data, every time<\/text>\n<path d=\"M562 245 L635 245\" stroke=\"#22c55e\" stroke-width=\"2.5\" fill=\"none\" marker-end=\"url(#ddArr2)\"\/>\n<\/g>\n<\/svg>\n<figcaption>Write on the primary, remember the LSN, make the replica wait for it, then read.<\/figcaption>\n<\/figure>\n\n<p>Here is a complete Python example using <code>psycopg<\/code> 3. We build the <code>WAIT FOR LSN<\/code> statement with <code>sql.Literal<\/code>, because utility commands like this one do not accept normal query parameters.<\/p>\n\n<pre><code># pip install \"psycopg[binary]\"\nimport psycopg\nfrom psycopg import sql\n\nPRIMARY = \"postgresql:\/\/postgres:devdojo@primary-host:5432\/app\"\nREPLICA = \"postgresql:\/\/postgres:devdojo@replica-host:5432\/app\"\n\ndef update_plan_and_read_back(email: str, plan: str):\n    # 1. Write on the primary and capture the log position\n    with psycopg.connect(PRIMARY, autocommit=True) as primary:\n        primary.execute(\n            \"UPDATE users SET plan = %s WHERE email = %s\", (plan, email)\n        )\n        lsn = primary.execute(\"SELECT pg_current_wal_lsn()\").fetchone()[0]\n\n    # 2. Make the replica wait (max 2 seconds), then 3. read\n    with psycopg.connect(REPLICA, autocommit=True) as replica:\n        wait = sql.SQL(\"WAIT FOR LSN {} WITH (TIMEOUT '2s', NO_THROW)\").format(\n            sql.Literal(str(lsn))\n        )\n        status = replica.execute(wait).fetchone()[0]\n        if status != \"success\":\n            # Replica is too far behind: fall back to the primary\n            raise RuntimeError(\"replica lagging, read from primary instead\")\n        return replica.execute(\n            \"SELECT email, plan FROM users WHERE email = %s\", (email,)\n        ).fetchone()\n\nprint(update_plan_and_read_back(\"ravi@example.com\", \"pro\"))<\/code><\/pre>\n\n<p>The command also supports modes: <code>standby_replay<\/code> (the default \u2014 data is visible to queries), <code>standby_write<\/code>, <code>standby_flush<\/code> and <code>primary_flush<\/code>. Without <code>NO_THROW<\/code>, a timeout raises an error instead of returning <code>timeout<\/code>.<\/p>\n\n<div class=\"dd-tip\"><strong>Tip:<\/strong> You only need this for &#8220;just wrote, now reading&#8221; requests. Normal browsing pages can keep reading from the replica without waiting.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"sql-goodies\">Smaller SQL features you will use every day<\/h2>\n\n<h3>GROUP BY ALL<\/h3>\n<p>Tired of copying every column from <code>SELECT<\/code> into <code>GROUP BY<\/code>? PostgreSQL 19 adds <code>GROUP BY ALL<\/code>, which groups by every column in the select list that is not an aggregate (or window function). Other databases like DuckDB and Snowflake already have this, and it is a real time-saver for reports.<\/p>\n\n<pre><code>-- Before\nSELECT country, plan, count(*) AS users\nFROM users\nGROUP BY country, plan;\n\n-- PostgreSQL 19\nSELECT country, plan, count(*) AS users\nFROM users\nGROUP BY ALL;<\/code><\/pre>\n\n<h3>COPY to JSON<\/h3>\n<p><code>COPY TO<\/code> can now write JSON directly \u2014 one JSON object per line, or a single JSON array with the <code>FORCE_ARRAY<\/code> option. Great for quick exports and data hand-offs.<\/p>\n\n<pre><code>-- One JSON object per row (newline-delimited JSON)\nCOPY (SELECT id, email, plan FROM users) TO STDOUT (FORMAT json);\n\n-- From psql, save a single JSON array to a file on your machine\n\\copy (SELECT id, email, plan FROM users) TO 'users.json' (FORMAT json, FORCE_ARRAY)<\/code><\/pre>\n\n<h3>New jsonpath string methods<\/h3>\n<p>If you store JSON in <code>jsonb<\/code> columns, you can now clean strings right inside a jsonpath expression with <code>lower()<\/code>, <code>upper()<\/code>, <code>initcap()<\/code>, <code>replace()<\/code>, <code>split_part()<\/code>, <code>ltrim()<\/code>, <code>rtrim()<\/code> and <code>btrim()<\/code>.<\/p>\n\n<pre><code>SELECT jsonb_path_query('{\"city\": \"  new delhi \"}', '$.city.btrim().initcap()');\n-- Expected result: \"New Delhi\"<\/code><\/pre>\n\n<h3>Handy little helpers<\/h3>\n<ul>\n<li><strong><code>random()<\/code> for dates and timestamps<\/strong> \u2014 perfect for seed data: <code>SELECT random('2026-01-01'::date, '2026-12-31'::date);<\/code><\/li>\n<li><strong>base64url and base32hex encodings<\/strong> \u2014 URL-safe tokens without manual replace calls: <code>SELECT encode(gen_random_bytes(16), 'base64url');<\/code> (<code>gen_random_bytes<\/code> comes from the <code>pgcrypto<\/code> extension).<\/li>\n<li><strong>Casts between <code>bytea<\/code> and <code>uuid<\/code><\/strong> \u2014 useful when another system hands you raw 16-byte IDs.<\/li>\n<li><strong>Faster foreign-key checks<\/strong> \u2014 inserts that check foreign keys can be up to twice as fast, with no code change on your side.<\/li>\n<\/ul>\n\n<div class=\"dd-note\">Some of these are brand-new and the documentation for them is still being polished during the beta. If an example behaves differently on your build, check the <code>\\h<\/code> help in <code>psql<\/code> and the release notes for the exact syntax.<\/div>\n\n<h2 id=\"partitions-graphs\">Partition merge\/split and graph queries<\/h2>\n\n<h3>MERGE and SPLIT partitions<\/h3>\n<p>Partitioned tables (for example, one partition per month of logs) are great, but reshaping them used to mean creating new tables and moving data by hand. PostgreSQL 19 adds two commands:<\/p>\n\n<pre><code>CREATE TABLE metrics (\n  recorded_at timestamptz NOT NULL,\n  value       numeric\n) PARTITION BY RANGE (recorded_at);\n\nCREATE TABLE metrics_jan PARTITION OF metrics\n  FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');\nCREATE TABLE metrics_feb PARTITION OF metrics\n  FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');\n\n-- Combine two small monthly partitions into one\nALTER TABLE metrics MERGE PARTITIONS (metrics_jan, metrics_feb)\n  INTO metrics_jan_feb;\n\n-- Split it back into months later\nALTER TABLE metrics SPLIT PARTITION metrics_jan_feb INTO\n  (PARTITION metrics_jan FOR VALUES FROM ('2026-01-01') TO ('2026-02-01'),\n   PARTITION metrics_feb FOR VALUES FROM ('2026-02-01') TO ('2026-03-01'));<\/code><\/pre>\n\n<div class=\"dd-warn\"><strong>Heads up:<\/strong> both commands take a strong lock on the parent table while they run. On a busy production table, schedule them in a quiet maintenance window.<\/div>\n\n<h3>Property graph queries (SQL\/PGQ)<\/h3>\n<p>PostgreSQL 19 supports the SQL-standard way to query relationships as a graph \u2014 think &#8220;who follows whom&#8221; or &#8220;which services call which&#8221;. You define a graph on top of normal tables, then use <code>GRAPH_TABLE<\/code> with a <code>MATCH<\/code> pattern:<\/p>\n\n<pre><code>CREATE TABLE people  (id int PRIMARY KEY, name text);\nCREATE TABLE follows (follower int REFERENCES people, followee int REFERENCES people,\n                      PRIMARY KEY (follower, followee));\n\nINSERT INTO people VALUES (1, 'Asha'), (2, 'Ravi'), (3, 'Emma');\nINSERT INTO follows VALUES (1, 2), (2, 3), (1, 3);\n\nCREATE PROPERTY GRAPH social\n  VERTEX TABLES (people KEY (id) LABEL person PROPERTIES (name))\n  EDGE TABLES (follows KEY (follower, followee)\n    SOURCE KEY (follower) REFERENCES people (id)\n    DESTINATION KEY (followee) REFERENCES people (id)\n    LABEL follows);\n\nSELECT * FROM GRAPH_TABLE (social\n  MATCH (a IS person)-[IS follows]-&gt;(b IS person)\n  COLUMNS (a.name AS follower, b.name AS following));<\/code><\/pre>\n\n<p>Your data still lives in ordinary tables, so you keep transactions, indexes and backups \u2014 you just get a much more readable way to express relationship queries.<\/p>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"ops\">Operations: autovacuum, checksums and monitoring<\/h2>\n\n<p>Even if you are mainly an app developer, these changes make your database healthier with less effort:<\/p>\n\n<ul>\n<li><strong>Parallel autovacuum:<\/strong> autovacuum can now use parallel workers for big tables. It is off by default \u2014 the server-wide limit <code>autovacuum_max_parallel_workers<\/code> starts at 0.<\/li>\n<li><strong>Smarter vacuum order:<\/strong> a new scoring system decides which tables need vacuuming most urgently.<\/li>\n<li><strong>Online data checksums:<\/strong> you can turn page checksums (which detect silent disk corruption) on or off <em>without<\/em> re-creating the cluster or restarting.<\/li>\n<li><strong>New monitoring views:<\/strong> <code>pg_stat_lock<\/code> shows statistics per lock type, and <code>pg_stat_recovery<\/code> shows replica recovery state.<\/li>\n<li><strong>Logical replication of sequences:<\/strong> sequence values are now replicated, which makes upgrades and failovers via logical replication much safer.<\/li>\n<li><strong>EXPLAIN (ANALYZE, IO):<\/strong> shows statistics for the asynchronous I\/O system introduced in PostgreSQL 18.<\/li>\n<\/ul>\n\n<pre><code>-- Allow up to 2 parallel autovacuum workers server-wide\nALTER SYSTEM SET autovacuum_max_parallel_workers = 2;\nSELECT pg_reload_conf();\n\n-- Turn on data checksums in the background (no restart needed)\nSELECT pg_enable_data_checksums();\nSHOW data_checksums;   -- shows the progress state, then \"on\"\n\n-- Which lock types cause the most waiting?\nSELECT * FROM pg_stat_lock;<\/code><\/pre>\n\n<h2 id=\"defaults\">Changed defaults: read this before upgrading<\/h2>\n\n<p>Some defaults changed in PostgreSQL 19. Most are improvements, but a couple can surprise you after an upgrade:<\/p>\n\n<div class=\"dd-table-wrap\">\n<table>\n<thead><tr><th>Setting<\/th><th>PostgreSQL 18<\/th><th>PostgreSQL 19<\/th><th>What it means for you<\/th><\/tr><\/thead>\n<tbody>\n<tr><td><code>jit<\/code><\/td><td>on<\/td><td>off<\/td><td>Most web queries get faster to start. Heavy analytics queries may be slower \u2014 set <code>jit = on<\/code> for those databases if needed.<\/td><\/tr>\n<tr><td><code>default_toast_compression<\/code><\/td><td>pglz<\/td><td>lz4<\/td><td>Large text\/JSON values compress and decompress faster.<\/td><\/tr>\n<tr><td><code>log_lock_waits<\/code><\/td><td>off<\/td><td>on<\/td><td>Long lock waits now show up in logs, which helps debugging.<\/td><\/tr>\n<tr><td><code>max_locks_per_transaction<\/code><\/td><td>64<\/td><td>128<\/td><td>Fewer &#8220;out of shared memory&#8221; errors with many partitions.<\/td><\/tr>\n<tr><td>RADIUS authentication<\/td><td>available<\/td><td>removed<\/td><td>A <code>radius<\/code> line in <code>pg_hba.conf<\/code> will stop the server from starting.<\/td><\/tr>\n<tr><td>md5 passwords<\/td><td>allowed<\/td><td>allowed, with a warning<\/td><td>Plan your move to <code>scram-sha-256<\/code>; md5 is on its way out.<\/td><\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n\n<h2 id=\"errors\">Common errors and fixes<\/h2>\n\n<div class=\"dd-table-wrap\">\n<table>\n<thead><tr><th>Problem<\/th><th>Why it happens<\/th><th>Fix<\/th><\/tr><\/thead>\n<tbody>\n<tr><td><code>REPACK (CONCURRENTLY)<\/code> refuses to run<\/td><td>The table has no primary key or replica identity index, is partitioned\/unlogged, or you are inside a transaction.<\/td><td>Add a primary key, run it outside <code>BEGIN \u2026 COMMIT<\/code>, or use plain <code>REPACK<\/code> in a maintenance window.<\/td><\/tr>\n<tr><td>REPACK fails because no replication slot is available<\/td><td><code>max_repack_replication_slots<\/code> is used up.<\/td><td>Wait for other repacks to finish or raise the setting.<\/td><\/tr>\n<tr><td>Syntax error on <code>ON CONFLICT DO SELECT<\/code><\/td><td>Missing conflict target or <code>RETURNING<\/code> clause.<\/td><td>Write <code>ON CONFLICT (email) DO SELECT RETURNING \u2026<\/code>.<\/td><\/tr>\n<tr><td><code>WAIT FOR LSN<\/code> times out with an error<\/td><td>The replica is behind by more than your timeout.<\/td><td>Use <code>WITH (TIMEOUT '2s', NO_THROW)<\/code> and fall back to reading from the primary.<\/td><\/tr>\n<tr><td>Server will not start after upgrade<\/td><td>A <code>radius<\/code> method is still in <code>pg_hba.conf<\/code>.<\/td><td>Switch those entries to <code>scram-sha-256<\/code>, LDAP or certificate login.<\/td><\/tr>\n<tr><td>Reports got slower after upgrade<\/td><td>JIT is now off by default.<\/td><td>Test with <code>SET jit = on;<\/code> and enable it per database if it helps.<\/td><\/tr>\n<tr><td><code>docker run<\/code> cannot find the image tag<\/td><td>Beta tags change name between releases.<\/td><td>Check the current tags on Docker Hub, or use <code>postgres:19<\/code> after the final release.<\/td><\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n\n<h2 id=\"best-practices\">Best practices for upgrading<\/h2>\n\n<ol>\n<li><strong>Test on a copy first.<\/strong> Restore last night&#8217;s backup into a PostgreSQL 19 instance and run your test suite against it. A CI pipeline makes this painless \u2014 see our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/06\/github-actions-ci-cd-nodejs\/\">GitHub Actions CI\/CD guide for Node.js<\/a> to run tests against a Postgres service container.<\/li>\n<li><strong>Check extensions.<\/strong> Make sure extensions like PostGIS, pgvector or TimescaleDB have released PostgreSQL 19 builds before you upgrade.<\/li>\n<li><strong>Review <code>pg_hba.conf<\/code>.<\/strong> Remove RADIUS lines and move md5 users to <code>scram-sha-256<\/code>.<\/li>\n<li><strong>Benchmark analytics queries.<\/strong> Compare timings with JIT on and off before deciding.<\/li>\n<li><strong>Adopt features gradually.<\/strong> Swap the riskiest workaround first \u2014 usually the double-query &#8220;get or create&#8221; or a nightly <code>VACUUM FULL<\/code> job.<\/li>\n<li><strong>Wait for the first minor release for critical systems.<\/strong> Many teams upgrade production at x.1 or x.2, after early bugs are fixed.<\/li>\n<\/ol>\n\n<p>Using Python? Pair this upgrade with the language improvements in our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/05\/python-3-15-new-features\/\">Python 3.15 new features<\/a> guide.<\/p>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"faq\">FAQ<\/h2>\n\n<details>\n<summary>Is PostgreSQL 19 released and ready for production?<\/summary>\n<p>At the time of writing PostgreSQL 19 is in its final beta (Beta 4, released 24 September), and the stable release is expected around September\/October. Use the beta for testing only. Once the final version is out, test your app on a copy and consider waiting for the first minor release on critical systems.<\/p>\n<\/details>\n\n<details>\n<summary>Do I still need the pg_repack extension?<\/summary>\n<p>For most cases, no. <code>REPACK (CONCURRENTLY)<\/code> covers the main job of pg_repack \u2014 shrinking a bloated table without a long lock \u2014 directly in core PostgreSQL. You may still need pg_repack while you are on older versions.<\/p>\n<\/details>\n\n<details>\n<summary>What is the difference between DO NOTHING and DO SELECT?<\/summary>\n<p><code>DO NOTHING<\/code> skips the conflicting row and returns nothing for it. <code>DO SELECT<\/code> also skips the insert, but returns the existing row through <code>RETURNING<\/code>, so you always get a result back.<\/p>\n<\/details>\n\n<details>\n<summary>Will my old VACUUM FULL and CLUSTER scripts break?<\/summary>\n<p>No. Both commands are kept for backward compatibility. REPACK is the new recommended command, and it adds the non-blocking <code>CONCURRENTLY<\/code> option.<\/p>\n<\/details>\n\n<details>\n<summary>Does WAIT FOR LSN slow down my app?<\/summary>\n<p>Only the requests that use it, and usually only by a few milliseconds, because replicas are normally very close behind. Always set a <code>TIMEOUT<\/code> so a lagging replica cannot hang your request.<\/p>\n<\/details>\n\n<details>\n<summary>How do I upgrade from PostgreSQL 17 or 18?<\/summary>\n<p>Use <code>pg_upgrade<\/code> for a fast in-place upgrade, <code>pg_dump<\/code>\/<code>pg_restore<\/code> for small databases, or logical replication for near-zero downtime. Managed services like RDS, Cloud SQL and Supabase usually offer a one-click upgrade a little after the final release.<\/p>\n<\/details>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n\n<p>The <strong>PostgreSQL 19 new features<\/strong> are very practical. <code>REPACK (CONCURRENTLY)<\/code> removes the scariest maintenance job, <code>ON CONFLICT DO SELECT<\/code> and <code>GROUP BY ALL<\/code> make everyday SQL shorter, and <code>WAIT FOR LSN<\/code> solves the classic &#8220;I saved it but it didn&#8217;t change&#8221; bug with read replicas. Spin up the Docker container, run the examples above, and make a short upgrade checklist for your team \u2014 especially around JIT, RADIUS and your extensions.<\/p>\n\n<p>Want to go deeper? Read the official <a href=\"https:\/\/www.postgresql.org\/docs\/19\/sql-repack.html\" target=\"_blank\" rel=\"noopener\">REPACK documentation<\/a> and the <a href=\"https:\/\/www.postgresql.org\/docs\/19\/sql-wait.html\" target=\"_blank\" rel=\"noopener\">WAIT FOR LSN reference<\/a>.<\/p>\n\n<div class=\"dd-cta\">\n<h3>New posts every day on DevDojo<\/h3>\n<p>Practical guides on AI, frontend, backend, DevOps and interview prep \u2014 written in simple English. Bookmark <a href=\"https:\/\/devdojo.co.in\/\">devdojo.co.in<\/a> and come back tomorrow for the next one!<\/p>\n<\/div>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.<\/p>\n","protected":false},"author":1,"featured_media":184,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,69],"tags":[56,73,70,71,72],"class_list":["post-185","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-backend","category-databases","tag-backend-development","tag-database","tag-postgresql","tag-postgresql-19","tag-sql"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Lokendra\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DevDojo - Empowering Developers\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"PostgreSQL 19 New Features: A Practical Guide for Developers\" \/>\n\t\t<meta property=\"og:description\" content=\"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-09T14:25:35+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-09T14:25:35+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"PostgreSQL 19 New Features: A Practical Guide for Developers\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#blogposting\",\"name\":\"PostgreSQL 19 New Features: A Practical Guide for Developers\",\"headline\":\"PostgreSQL 19 New Features: A Practical Guide for Developers\",\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/postgresql-19-new-features.jpg\",\"width\":1200,\"height\":630,\"caption\":\"PostgreSQL 19 new features banner showing REPACK, DO SELECT, WAIT FOR LSN, graph queries and partitions\"},\"datePublished\":\"2026-10-09T14:25:35+00:00\",\"dateModified\":\"2026-10-09T14:25:35+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#webpage\"},\"articleSection\":\"Backend, Databases, Backend Development, Database, PostgreSQL, PostgreSQL 19, SQL\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"name\":\"Backend\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"position\":2,\"name\":\"Backend\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#listItem\",\"name\":\"PostgreSQL 19 New Features: A Practical Guide for Developers\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#listItem\",\"position\":3,\"name\":\"PostgreSQL 19 New Features: A Practical Guide for Developers\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"name\":\"Backend\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/\",\"name\":\"Lokendra\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Lokendra\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#webpage\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/\",\"name\":\"PostgreSQL 19 New Features: A Practical Guide for Developers\",\"description\":\"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/postgresql-19-new-features.jpg\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"PostgreSQL 19 new features banner showing REPACK, DO SELECT, WAIT FOR LSN, graph queries and partitions\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/09\\\/postgresql-19-new-features\\\/#mainImage\"},\"datePublished\":\"2026-10-09T14:25:35+00:00\",\"dateModified\":\"2026-10-09T14:25:35+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"PostgreSQL 19 New Features: A Practical Guide for Developers","description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.","canonical_url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#blogposting","name":"PostgreSQL 19 New Features: A Practical Guide for Developers","headline":"PostgreSQL 19 New Features: A Practical Guide for Developers","author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/postgresql-19-new-features.jpg","width":1200,"height":630,"caption":"PostgreSQL 19 new features banner showing REPACK, DO SELECT, WAIT FOR LSN, graph queries and partitions"},"datePublished":"2026-10-09T14:25:35+00:00","dateModified":"2026-10-09T14:25:35+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#webpage"},"isPartOf":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#webpage"},"articleSection":"Backend, Databases, Backend Development, Database, PostgreSQL, PostgreSQL 19, SQL"},{"@type":"BreadcrumbList","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","position":1,"name":"Home","item":"https:\/\/devdojo.co.in\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","name":"Backend"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","position":2,"name":"Backend","item":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#listItem","name":"PostgreSQL 19 New Features: A Practical Guide for Developers"},"previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#listItem","position":3,"name":"PostgreSQL 19 New Features: A Practical Guide for Developers","previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","name":"Backend"}}]},{"@type":"Organization","@id":"https:\/\/devdojo.co.in\/#organization","name":"DevDojo","description":"Empowering Developers","url":"https:\/\/devdojo.co.in\/"},{"@type":"Person","@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author","url":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/","name":"Lokendra","image":{"@type":"ImageObject","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g","width":96,"height":96,"caption":"Lokendra"}},{"@type":"WebPage","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#webpage","url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/","name":"PostgreSQL 19 New Features: A Practical Guide for Developers","description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/devdojo.co.in\/#website"},"breadcrumb":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#breadcrumblist"},"author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"creator":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/postgresql-19-new-features.jpg","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#mainImage","width":1200,"height":630,"caption":"PostgreSQL 19 new features banner showing REPACK, DO SELECT, WAIT FOR LSN, graph queries and partitions"},"primaryImageOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/#mainImage"},"datePublished":"2026-10-09T14:25:35+00:00","dateModified":"2026-10-09T14:25:35+00:00"},{"@type":"WebSite","@id":"https:\/\/devdojo.co.in\/#website","url":"https:\/\/devdojo.co.in\/","name":"DevDojo","description":"Empowering Developers","inLanguage":"en-US","publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"}}]},"og:locale":"en_US","og:site_name":"DevDojo - Empowering Developers","og:type":"article","og:title":"PostgreSQL 19 New Features: A Practical Guide for Developers","og:description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.","og:url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/","article:published_time":"2026-10-09T14:25:35+00:00","article:modified_time":"2026-10-09T14:25:35+00:00","twitter:card":"summary_large_image","twitter:title":"PostgreSQL 19 New Features: A Practical Guide for Developers","twitter:description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips."},"aioseo_meta_data":{"post_id":"185","title":"PostgreSQL 19 New Features: A Practical Guide for Developers","description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.","keywords":null,"keyphrases":{"focus":{"keyphrase":"PostgreSQL 19 new features","score":69},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":"PostgreSQL 19 New Features: A Practical Guide for Developers","og_description":"Explore PostgreSQL 19 new features with runnable examples: REPACK CONCURRENTLY, ON CONFLICT DO SELECT, WAIT FOR LSN, GROUP BY ALL and key upgrade tips.","og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":0,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-10-09 14:20:47","updated":"2026-10-09 14:25:35","focus_keyword":"PostgreSQL 19 new features","additional_keywords":null,"truseo_locale":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/index.php\/category\/backend\/\" title=\"Backend\">Backend<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tPostgreSQL 19 New Features: A Practical Guide for Developers\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/devdojo.co.in\/"},{"label":"Backend","link":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/"},{"label":"PostgreSQL 19 New Features: A Practical Guide for Developers","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/"}],"_links":{"self":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/comments?post=185"}],"version-history":[{"count":1,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":186,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions\/186"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media\/184"}],"wp:attachment":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}