Skip to content

Commit cd37bb7

Browse files
committed
Improve PL/Tcl errorCode facility by providing decoded name for SQLSTATE.
We don't really want to encourage people to write numeric SQLSTATEs in programs; that's unreadable and error-prone. Copy plpgsql's infrastructure for converting between SQLSTATEs and exception names shown in Appendix A, and modify examples in tests and documentation to do it that way.
1 parent fb8d2a7 commit cd37bb7

File tree

9 files changed

+118
-13
lines changed

9 files changed

+118
-13
lines changed

doc/src/sgml/pltcl.sgml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -813,14 +813,16 @@ CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE PROCEDURE tclsnit
813813
word is <literal>POSTGRES</literal>, the second word is the Postgres
814814
version number, and additional words are field name/value pairs
815815
providing detailed information about the error.
816-
Fields <varname>message</> and <varname>SQLSTATE</> (the error code
817-
shown in <xref linkend="errcodes-appendix">) are always supplied.
816+
Fields <varname>SQLSTATE</>, <varname>condition</>,
817+
and <varname>message</> are always supplied
818+
(the first two represent the error code and condition name as shown
819+
in <xref linkend="errcodes-appendix">).
818820
Fields that may be present include
819821
<varname>detail</>, <varname>hint</>, <varname>context</>,
820822
<varname>schema</>, <varname>table</>, <varname>column</>,
821823
<varname>datatype</>, <varname>constraint</>,
822824
<varname>statement</>, <varname>cursor_position</>,
823-
<varname>filename</>, <varname>lineno</> and
825+
<varname>filename</>, <varname>lineno</>, and
824826
<varname>funcname</>.
825827
</para>
826828

@@ -832,7 +834,7 @@ CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE PROCEDURE tclsnit
832834
if {[catch { spi_exec $sql_command }]} {
833835
if {[lindex $::errorCode 0] == "POSTGRES"} {
834836
array set errorArray $::errorCode
835-
if {$errorArray(SQLSTATE) == "42P01"} { # UNDEFINED_TABLE
837+
if {$errorArray(condition) == "undefined_table"} {
836838
# deal with missing table
837839
} else {
838840
# deal with some other type of SQL error

src/backend/utils/errcodes.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
# src/pl/plpgsql/src/plerrcodes.h
1616
# a list of PL/pgSQL condition names and their SQLSTATE codes
1717
#
18+
# src/pl/tcl/pltclerrcodes.h
19+
# the same, for PL/Tcl
20+
#
1821
# doc/src/sgml/errcodes-list.sgml
1922
# a SGML table of error codes for inclusion in the documentation
2023
#

src/pl/tcl/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/pltclerrcodes.h
2+
13
# Generated subdirectories
24
/log/
35
/results/

src/pl/tcl/Makefile

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ include $(top_builddir)/src/Makefile.global
1313

1414
override CPPFLAGS := $(TCL_INCLUDE_SPEC) $(CPPFLAGS)
1515

16-
1716
# On Windows, we don't link directly with the Tcl library; see below
1817
ifneq ($(PORTNAME), win32)
1918
SHLIB_LINK = $(TCL_LIB_SPEC) $(TCL_LIBS) -lc
@@ -56,6 +55,14 @@ include $(top_srcdir)/src/Makefile.shlib
5655
all: all-lib
5756
$(MAKE) -C modules $@
5857

58+
# Force this dependency to be known even without dependency info built:
59+
pltcl.o: pltclerrcodes.h
60+
61+
# generate pltclerrcodes.h from src/backend/utils/errcodes.txt
62+
pltclerrcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-pltclerrcodes.pl
63+
$(PERL) $(srcdir)/generate-pltclerrcodes.pl $< > $@
64+
65+
distprep: pltclerrcodes.h
5966

6067
install: all install-lib install-data
6168
$(MAKE) -C modules $@
@@ -86,10 +93,14 @@ installcheck: submake
8693
submake:
8794
$(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X)
8895

89-
clean distclean maintainer-clean: clean-lib
96+
# pltclerrcodes.h is in the distribution tarball, so don't clean it here.
97+
clean distclean: clean-lib
9098
rm -f $(OBJS)
9199
rm -rf $(pg_regress_clean_files)
92100
ifeq ($(PORTNAME), win32)
93101
rm -f $(tclwithver).def
94102
endif
95103
$(MAKE) -C modules $@
104+
105+
maintainer-clean: distclean
106+
rm -f pltclerrcodes.h

src/pl/tcl/expected/pltcl_setup.out

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,10 @@ create function tcl_error_handling_test() returns text as $$
560560
global errorCode
561561
if {[catch { spi_exec "select no_such_column from foo;" }]} {
562562
array set errArray $errorCode
563-
if {$errArray(SQLSTATE) == "42P01"} {
563+
if {$errArray(condition) == "undefined_table"} {
564564
return "expected error: $errArray(message)"
565565
} else {
566-
return "unexpected error: $errArray(SQLSTATE) $errArray(message)"
566+
return "unexpected error: $errArray(condition) $errArray(message)"
567567
}
568568
} else {
569569
return "no error"
@@ -577,9 +577,9 @@ select tcl_error_handling_test();
577577

578578
create temp table foo(f1 int);
579579
select tcl_error_handling_test();
580-
tcl_error_handling_test
581-
----------------------------------------------------------------
582-
unexpected error: 42703 column "no_such_column" does not exist
580+
tcl_error_handling_test
581+
---------------------------------------------------------------------------
582+
unexpected error: undefined_column column "no_such_column" does not exist
583583
(1 row)
584584

585585
drop table foo;

src/pl/tcl/generate-pltclerrcodes.pl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/perl
2+
#
3+
# Generate the pltclerrcodes.h header from errcodes.txt
4+
# Copyright (c) 2000-2016, PostgreSQL Global Development Group
5+
6+
use warnings;
7+
use strict;
8+
9+
print
10+
"/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
11+
print "/* there is deliberately not an #ifndef PLTCLERRCODES_H here */\n";
12+
13+
open my $errcodes, $ARGV[0] or die;
14+
15+
while (<$errcodes>)
16+
{
17+
chomp;
18+
19+
# Skip comments
20+
next if /^#/;
21+
next if /^\s*$/;
22+
23+
# Skip section headers
24+
next if /^Section:/;
25+
26+
die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
27+
28+
(my $sqlstate, my $type, my $errcode_macro, my $condition_name) =
29+
($1, $2, $3, $4);
30+
31+
# Skip non-errors
32+
next unless $type eq 'E';
33+
34+
# Skip lines without PL/pgSQL condition names
35+
next unless defined($condition_name);
36+
37+
print "{\n\t\"$condition_name\", $errcode_macro\n},\n\n";
38+
}
39+
40+
close $errcodes;

src/pl/tcl/pltcl.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,20 @@ static HTAB *pltcl_proc_htab = NULL;
188188
static FunctionCallInfo pltcl_current_fcinfo = NULL;
189189
static pltcl_proc_desc *pltcl_current_prodesc = NULL;
190190

191+
/**********************************************************************
192+
* Lookup table for SQLSTATE condition names
193+
**********************************************************************/
194+
typedef struct
195+
{
196+
const char *label;
197+
int sqlerrstate;
198+
} TclExceptionNameMap;
199+
200+
static const TclExceptionNameMap exception_name_map[] = {
201+
#include "pltclerrcodes.h" /* pgrminclude ignore */
202+
{NULL, 0}
203+
};
204+
191205
/**********************************************************************
192206
* Forward declarations
193207
**********************************************************************/
@@ -213,6 +227,7 @@ static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
213227
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
214228
int objc, Tcl_Obj *const objv[]);
215229
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata);
230+
static const char *pltcl_get_condition_name(int sqlstate);
216231
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
217232
int objc, Tcl_Obj *const objv[]);
218233
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
@@ -1681,6 +1696,10 @@ pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
16811696
Tcl_NewStringObj("SQLSTATE", -1));
16821697
Tcl_ListObjAppendElement(interp, obj,
16831698
Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode), -1));
1699+
Tcl_ListObjAppendElement(interp, obj,
1700+
Tcl_NewStringObj("condition", -1));
1701+
Tcl_ListObjAppendElement(interp, obj,
1702+
Tcl_NewStringObj(pltcl_get_condition_name(edata->sqlerrcode), -1));
16841703
Tcl_ListObjAppendElement(interp, obj,
16851704
Tcl_NewStringObj("message", -1));
16861705
UTF_BEGIN;
@@ -1806,6 +1825,23 @@ pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
18061825
}
18071826

18081827

1828+
/**********************************************************************
1829+
* pltcl_get_condition_name() - find name for SQLSTATE
1830+
**********************************************************************/
1831+
static const char *
1832+
pltcl_get_condition_name(int sqlstate)
1833+
{
1834+
int i;
1835+
1836+
for (i = 0; exception_name_map[i].label != NULL; i++)
1837+
{
1838+
if (exception_name_map[i].sqlerrstate == sqlstate)
1839+
return exception_name_map[i].label;
1840+
}
1841+
return "unrecognized_sqlstate";
1842+
}
1843+
1844+
18091845
/**********************************************************************
18101846
* pltcl_quote() - quote literal strings that are to
18111847
* be used in SPI_execute query strings

src/pl/tcl/sql/pltcl_setup.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,10 @@ create function tcl_error_handling_test() returns text as $$
602602
global errorCode
603603
if {[catch { spi_exec "select no_such_column from foo;" }]} {
604604
array set errArray $errorCode
605-
if {$errArray(SQLSTATE) == "42P01"} {
605+
if {$errArray(condition) == "undefined_table"} {
606606
return "expected error: $errArray(message)"
607607
} else {
608-
return "unexpected error: $errArray(SQLSTATE) $errArray(message)"
608+
return "unexpected error: $errArray(condition) $errArray(message)"
609609
}
610610
} else {
611611
return "no error"

src/tools/msvc/Solution.pm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,17 @@ s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY
350350
);
351351
}
352352

353+
if ($self->{options}->{tcl}
354+
&& IsNewer(
355+
'src/pl/tcl/pltclerrcodes.h',
356+
'src/backend/utils/errcodes.txt'))
357+
{
358+
print "Generating pltclerrcodes.h...\n";
359+
system(
360+
'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h'
361+
);
362+
}
363+
353364
if (IsNewer(
354365
'src/backend/utils/sort/qsort_tuple.c',
355366
'src/backend/utils/sort/gen_qsort_tuple.pl'))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy