From e617b9cf0adf52427dde5ab804d0a8e75d55e7e6 Mon Sep 17 00:00:00 2001
From: Ishjaap Singh <ishjaap.singh07@gmail.com>
Date: Sat, 24 Oct 2026 17:01:52 +0000
Subject: [PATCH v2] psql: Add test for unterminated prompt escapes

Exercise %:name: and %`command` prompt escapes, both terminated and
missing their closing delimiter.  Without the preceding fix, the
unterminated cases read past the end of the prompt string.  In a build
with AddressSanitizer the test then fails; in a normal build the
over-read usually goes unnoticed and the test passes either way.
---
 src/bin/psql/meson.build     |  1 +
 src/bin/psql/t/040_prompt.pl | 49 ++++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 src/bin/psql/t/040_prompt.pl

diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index 922b2845..1106698e 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -78,6 +78,7 @@ tests += {
       't/010_tab_completion.pl',
       't/020_cancel.pl',
       't/030_pager.pl',
+      't/040_prompt.pl',
     ],
   },
 }
diff --git a/src/bin/psql/t/040_prompt.pl b/src/bin/psql/t/040_prompt.pl
new file mode 100644
index 00000000..6367aeec
--- /dev/null
+++ b/src/bin/psql/t/040_prompt.pl
@@ -0,0 +1,49 @@
+
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test psql prompt escapes that are missing their closing delimiter.
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# If we don't have IO::Pty, forget it, because IPC::Run depends on that
+# to support pty connections
+eval { require IO::Pty; };
+if ($@)
+{
+	plan skip_all => 'IO::Pty is needed to run this test';
+}
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->start;
+
+my $h = $node->interactive_psql('postgres');
+
+# Check that the prompt is displayed as expected after setting PROMPT1.
+sub check_prompt
+{
+	my ($prompt, $pattern, $annotation) = @_;
+
+	my $out = $h->query_until($pattern, "\\set PROMPT1 '$prompt'\n");
+	like($out, $pattern, $annotation);
+}
+
+$h->query_until(qr/=# $/, "\\set myvar VAL\n");
+
+check_prompt('%:myvar:> ', qr/VAL> $/, 'terminated %:name: escape');
+check_prompt('%`echo cmd`> ', qr/cmd> $/, 'terminated %`command` escape');
+
+# An unterminated escape uses the rest of the prompt string as the
+# variable name or command.  This used to read past the end of the string.
+check_prompt('X%:myvar', qr/XVAL$/, 'unterminated %:name escape');
+check_prompt('Y%`echo cmd', qr/Ycmd$/, 'unterminated %`command escape');
+check_prompt('Z%:', qr/Z$/, 'unterminated empty %: escape');
+check_prompt('W%`', qr/W$/, 'unterminated empty %` escape');
+
+$h->quit or die "psql returned $?";
+
+done_testing();
-- 
2.43.0

