psql-sqlstate-macro-name.patch
application/octet-stream
Filename: psql-sqlstate-macro-name.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Subject: psql: Display SQLSTATE macro name in verbose error reports
| File | + | − |
|---|---|---|
| src/bin/psql/command.c | 9 | 0 |
| src/bin/psql/common.c | 28 | 0 |
| src/bin/psql/generate-errcode-names.pl | 71 | 0 |
| src/bin/psql/Makefile | 6 | 1 |
| src/bin/psql/meson.build | 11 | 0 |
From 4098c6d1a27f60027b9e4afa5d3ace98cd969712 Mon Sep 17 00:00:00 2001
From: Aaryan Parik <aaryanparik124@gmail.com>
Date: Sat, 23 May 2026 19:10:57 +0000
Subject: [PATCH] psql: Display SQLSTATE macro name in verbose error reports
---
src/bin/psql/Makefile | 7 ++-
src/bin/psql/command.c | 9 ++++
src/bin/psql/common.c | 28 ++++++++++
src/bin/psql/generate-errcode-names.pl | 71 ++++++++++++++++++++++++++
src/bin/psql/meson.build | 11 ++++
5 files changed, 125 insertions(+), 1 deletion(-)
create mode 100644 src/bin/psql/generate-errcode-names.pl
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index be00326..3b66ec1 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -64,6 +64,11 @@ psqlscanslash.c: FLEX_NO_BACKUP=yes
tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
$(PERL) $^ --outfile $@
+common.o command.o: sqlstate_names.h
+
+sqlstate_names.h: generate-errcode-names.pl $(top_srcdir)/src/backend/utils/errcodes.txt
+ $(PERL) $^ $@
+
install: all installdirs
$(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)'
$(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample'
@@ -77,7 +82,7 @@ uninstall:
clean distclean:
rm -f psql$(X) $(OBJS) lex.backup
rm -rf tmp_check
- rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c
+ rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c sqlstate_names.h
check:
$(prove_check)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 01b8f11..7d2731b 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -28,6 +28,7 @@
#include "command.h"
#include "common.h"
#include "common/logging.h"
+#include "sqlstate_names.h"
#include "common/string.h"
#include "copy.h"
#include "describe.h"
@@ -1656,6 +1657,14 @@ exec_command_errverbose(PsqlScanState scan_state, bool active_branch)
{
pg_log_error("%s", msg);
PQfreemem(msg);
+
+ const char *sqlstate = PQresultErrorField(pset.last_error_result, PG_DIAG_SQLSTATE);
+ if (sqlstate)
+ {
+ const char *sym = get_sqlstate_symbolic_name(sqlstate);
+ if (sym)
+ pg_log_error("SQLSTATE name: %s\n", sym);
+ }
}
else
puts(_("out of memory"));
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 1a4e2ea..4a54a08 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -22,6 +22,7 @@
#include "command.h"
#include "common.h"
#include "common/logging.h"
+#include "sqlstate_names.h"
#include "copy.h"
#include "crosstabview.h"
#include "fe_utils/cancel.h"
@@ -454,8 +455,21 @@ AcceptResult(const PGresult *result, bool show_error)
const char *error = PQerrorMessage(pset.db);
if (strlen(error))
+ {
pg_log_info("%s", error);
+ if (pset.verbosity == PQERRORS_VERBOSE)
+ {
+ const char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ if (sqlstate)
+ {
+ const char *sym = get_sqlstate_symbolic_name(sqlstate);
+ if (sym)
+ pg_log_info("SQLSTATE name: %s\n", sym);
+ }
+ }
+ }
+
CheckConnection();
}
@@ -1784,8 +1798,22 @@ ExecQueryAndProcessResults(const char *query,
const char *error = PQresultErrorMessage(result);
if (strlen(error))
+ {
pg_log_info("%s", error);
+ if (pset.verbosity == PQERRORS_VERBOSE)
+ {
+ const char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ if (sqlstate)
+ {
+ const char *sym = get_sqlstate_symbolic_name(sqlstate);
+ if (sym)
+ pg_log_info("SQLSTATE name: %s\n", sym);
+ }
+ }
+ }
+
+
CheckConnection();
if (!is_watch)
SetResultVariables(result, false);
diff --git a/src/bin/psql/generate-errcode-names.pl b/src/bin/psql/generate-errcode-names.pl
new file mode 100644
index 0000000..80d5087
--- /dev/null
+++ b/src/bin/psql/generate-errcode-names.pl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# Generate the sqlstate_names.h header from errcodes.txt
+# This maps the 5-character SQLSTATE code to its symbolic macro name for psql's verbose error reports.
+
+use strict;
+use warnings;
+
+my $infile = shift;
+my $outfile = shift;
+
+open my $in, '<', $infile or die "Could not open $infile: $!";
+open my $out, '>', $outfile or die "Could not open $outfile: $!";
+
+print $out "/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
+print $out "/* This file maps SQLSTATE codes to their symbolic names. */\n\n";
+
+print $out "static const char *\n";
+print $out "get_sqlstate_symbolic_name(const char *sqlstate)\n";
+print $out "{\n";
+print $out " if (sqlstate == NULL)\n";
+print $out " return NULL;\n\n";
+
+my %sqlstates;
+
+while (<$in>) {
+ chomp;
+ s/#.*//; # Remove comments
+ next if /^\s*$/; # Skip empty lines
+ next if /^Section:/; # Skip section headers
+
+ if (/^([^\s]{5})\s+[EWS]\s+([^\s]+)/) {
+ my $sqlstate = $1;
+ my $macro_name = $2;
+ $sqlstates{$sqlstate} = $macro_name;
+ }
+}
+
+# We use a simple if-else chain. Since there are around 200-300 codes, a switch statement
+# based on strings isn't natively supported in C, but a series of if-strcmp is fast enough
+# for error reporting, or we can use a small static array and binary search it.
+# A static array and binary search is cleaner and scales better.
+
+print $out " static const struct {\n";
+print $out " const char sqlstate[6];\n";
+print $out " const char *macro_name;\n";
+print $out " } errcodes[] = {\n";
+
+foreach my $sqlstate (sort keys %sqlstates) {
+ print $out " {\"$sqlstate\", \"$sqlstates{$sqlstate}\"},\n";
+}
+
+print $out " };\n\n";
+print $out " int low = 0;\n";
+print $out " int high = (sizeof(errcodes) / sizeof(errcodes[0])) - 1;\n\n";
+print $out " while (low <= high) {\n";
+print $out " int mid = low + (high - low) / 2;\n";
+print $out " int cmp = strcmp(errcodes[mid].sqlstate, sqlstate);\n";
+print $out " if (cmp == 0)\n";
+print $out " return errcodes[mid].macro_name;\n";
+print $out " else if (cmp < 0)\n";
+print $out " low = mid + 1;\n";
+print $out " else\n";
+print $out " high = mid - 1;\n";
+print $out " }\n\n";
+
+print $out " return NULL;\n";
+print $out "}\n";
+
+close $in;
+close $out;
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index 922b284..8a11de7 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -34,6 +34,17 @@ tabcomplete = custom_target('tabcomplete',
generated_sources += tabcomplete
psql_sources += tabcomplete
+sqlstate_names = custom_target('sqlstate_names',
+ input: '@SOURCE_ROOT@/src/backend/utils/errcodes.txt',
+ output: 'sqlstate_names.h',
+ command: [
+ perl, files('generate-errcode-names.pl'), '@INPUT@', '@OUTPUT@',
+ ],
+)
+generated_sources += sqlstate_names
+psql_sources += sqlstate_names
+
+
sql_help = custom_target('psql_help',
output: ['sql_help.c', 'sql_help.h'],
depfile: 'sql_help.dep',
--
2.53.0