Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)