Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfb8d2a7

Browse files
committed
In PL/Tcl, make database errors return additional info in the errorCode.
Tcl has a convention for returning additional info about an error in aglobal variable named errorCode. Up to now PL/Tcl has ignored that,but this patch causes database errors caught by PL/Tcl to fill inerrorCode with useful information from the ErrorData struct.Jim Nasby, reviewed by Pavel Stehule and myself
1 parentc94959d commitfb8d2a7

File tree

4 files changed

+272
-5
lines changed

4 files changed

+272
-5
lines changed

‎doc/src/sgml/pltcl.sgml

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,9 @@ SELECT 'doesn''t' AS ret
507507
written to the server log, or both is controlled by the
508508
<xref linkend="guc-log-min-messages"> and
509509
<xref linkend="guc-client-min-messages"> configuration
510-
variables. See <xref linkend="runtime-config"> for more
511-
information.
510+
variables. See <xref linkend="runtime-config">
511+
and <xref linkend="pltcl-error-handling">
512+
for more information.
512513
</para>
513514
</listitem>
514515
</varlistentry>
@@ -775,6 +776,75 @@ CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE PROCEDURE tclsnit
775776
</para>
776777
</sect1>
777778

779+
<sect1 id="pltcl-error-handling">
780+
<title>Error Handling in PL/Tcl</title>
781+
782+
<indexterm>
783+
<primary>exceptions</primary>
784+
<secondary>in PL/Tcl</secondary>
785+
</indexterm>
786+
787+
<para>
788+
Tcl code within or called from a PL/Tcl function can raise an error,
789+
either by executing some invalid operation or by generating an error
790+
using the Tcl <function>error</function> command or
791+
PL/Tcl's <function>elog</function> command. Such errors can be caught
792+
within Tcl using the Tcl <function>catch</function> command. If they
793+
are not caught but are allowed to propagate out to the top level of
794+
execution of the PL/Tcl function, they turn into database errors.
795+
</para>
796+
797+
<para>
798+
Conversely, database errors that occur within PL/Tcl's
799+
<function>spi_exec</function>, <function>spi_prepare</function>,
800+
and <function>spi_execp</function> commands are reported as Tcl errors,
801+
so they are catchable by Tcl's <function>catch</function> command.
802+
Again, if they propagate out to the top level without being caught,
803+
they turn back into database errors.
804+
</para>
805+
806+
<para>
807+
Tcl provides an <varname>errorCode</varname> variable that can represent
808+
additional information about an error in a form that is easy for Tcl
809+
programs to interpret. The contents are in Tcl list format, and the
810+
first word identifies the subsystem or library reporting the error;
811+
beyond that the contents are left to the individual subsystem or
812+
library. For database errors reported by PL/Tcl commands, the first
813+
word is <literal>POSTGRES</literal>, the second word is the Postgres
814+
version number, and additional words are field name/value pairs
815+
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.
818+
Fields that may be present include
819+
<varname>detail</>, <varname>hint</>, <varname>context</>,
820+
<varname>schema</>, <varname>table</>, <varname>column</>,
821+
<varname>datatype</>, <varname>constraint</>,
822+
<varname>statement</>, <varname>cursor_position</>,
823+
<varname>filename</>, <varname>lineno</> and
824+
<varname>funcname</>.
825+
</para>
826+
827+
<para>
828+
A convenient way to work with PL/Tcl's <varname>errorCode</varname>
829+
information is to load it into an array, so that the field names become
830+
array subscripts. Code for doing that might look like
831+
<programlisting>
832+
if {[catch { spi_exec $sql_command }]} {
833+
if {[lindex $::errorCode 0] == "POSTGRES"} {
834+
array set errorArray $::errorCode
835+
if {$errorArray(SQLSTATE) == "42P01"} { # UNDEFINED_TABLE
836+
# deal with missing table
837+
} else {
838+
# deal with some other type of SQL error
839+
}
840+
}
841+
}
842+
</programlisting>
843+
(The double colons explicitly specify that <varname>errorCode</varname>
844+
is a global variable.)
845+
</para>
846+
</sect1>
847+
778848
<sect1 id="pltcl-unknown">
779849
<title>Modules and the <function>unknown</> Command</title>
780850
<para>

‎src/pl/tcl/expected/pltcl_setup.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,31 @@ NOTICE: tclsnitch: ddl_command_start DROP TABLE
555555
NOTICE: tclsnitch: ddl_command_end DROP TABLE
556556
drop event trigger tcl_a_snitch;
557557
drop event trigger tcl_b_snitch;
558+
-- test use of errorCode in error handling
559+
create function tcl_error_handling_test() returns text as $$
560+
global errorCode
561+
if {[catch { spi_exec "select no_such_column from foo;" }]} {
562+
array set errArray $errorCode
563+
if {$errArray(SQLSTATE) == "42P01"} {
564+
return "expected error: $errArray(message)"
565+
} else {
566+
return "unexpected error: $errArray(SQLSTATE) $errArray(message)"
567+
}
568+
} else {
569+
return "no error"
570+
}
571+
$$ language pltcl;
572+
select tcl_error_handling_test();
573+
tcl_error_handling_test
574+
-----------------------------------------------
575+
expected error: relation "foo" does not exist
576+
(1 row)
577+
578+
create temp table foo(f1 int);
579+
select tcl_error_handling_test();
580+
tcl_error_handling_test
581+
----------------------------------------------------------------
582+
unexpected error: 42703 column "no_such_column" does not exist
583+
(1 row)
584+
585+
drop table foo;

‎src/pl/tcl/pltcl.c

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
212212

213213
staticintpltcl_elog(ClientDatacdata,Tcl_Interp*interp,
214214
intobjc,Tcl_Obj*constobjv[]);
215+
staticvoidpltcl_construct_errorCode(Tcl_Interp*interp,ErrorData*edata);
215216
staticintpltcl_quote(ClientDatacdata,Tcl_Interp*interp,
216217
intobjc,Tcl_Obj*constobjv[]);
217218
staticintpltcl_argisnull(ClientDatacdata,Tcl_Interp*interp,
@@ -1648,7 +1649,8 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
16481649
edata=CopyErrorData();
16491650
FlushErrorState();
16501651

1651-
/* Pass the error message to Tcl */
1652+
/* Pass the error data to Tcl */
1653+
pltcl_construct_errorCode(interp,edata);
16521654
UTF_BEGIN;
16531655
Tcl_SetObjResult(interp,Tcl_NewStringObj(UTF_E2U(edata->message),-1));
16541656
UTF_END;
@@ -1662,6 +1664,148 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
16621664
}
16631665

16641666

1667+
/**********************************************************************
1668+
* pltcl_construct_errorCode()- construct a Tcl errorCode
1669+
*list with detailed information from the PostgreSQL server
1670+
**********************************************************************/
1671+
staticvoid
1672+
pltcl_construct_errorCode(Tcl_Interp*interp,ErrorData*edata)
1673+
{
1674+
Tcl_Obj*obj=Tcl_NewObj();
1675+
1676+
Tcl_ListObjAppendElement(interp,obj,
1677+
Tcl_NewStringObj("POSTGRES",-1));
1678+
Tcl_ListObjAppendElement(interp,obj,
1679+
Tcl_NewStringObj(PG_VERSION,-1));
1680+
Tcl_ListObjAppendElement(interp,obj,
1681+
Tcl_NewStringObj("SQLSTATE",-1));
1682+
Tcl_ListObjAppendElement(interp,obj,
1683+
Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode),-1));
1684+
Tcl_ListObjAppendElement(interp,obj,
1685+
Tcl_NewStringObj("message",-1));
1686+
UTF_BEGIN;
1687+
Tcl_ListObjAppendElement(interp,obj,
1688+
Tcl_NewStringObj(UTF_E2U(edata->message),-1));
1689+
UTF_END;
1690+
if (edata->detail)
1691+
{
1692+
Tcl_ListObjAppendElement(interp,obj,
1693+
Tcl_NewStringObj("detail",-1));
1694+
UTF_BEGIN;
1695+
Tcl_ListObjAppendElement(interp,obj,
1696+
Tcl_NewStringObj(UTF_E2U(edata->detail),-1));
1697+
UTF_END;
1698+
}
1699+
if (edata->hint)
1700+
{
1701+
Tcl_ListObjAppendElement(interp,obj,
1702+
Tcl_NewStringObj("hint",-1));
1703+
UTF_BEGIN;
1704+
Tcl_ListObjAppendElement(interp,obj,
1705+
Tcl_NewStringObj(UTF_E2U(edata->hint),-1));
1706+
UTF_END;
1707+
}
1708+
if (edata->context)
1709+
{
1710+
Tcl_ListObjAppendElement(interp,obj,
1711+
Tcl_NewStringObj("context",-1));
1712+
UTF_BEGIN;
1713+
Tcl_ListObjAppendElement(interp,obj,
1714+
Tcl_NewStringObj(UTF_E2U(edata->context),-1));
1715+
UTF_END;
1716+
}
1717+
if (edata->schema_name)
1718+
{
1719+
Tcl_ListObjAppendElement(interp,obj,
1720+
Tcl_NewStringObj("schema",-1));
1721+
UTF_BEGIN;
1722+
Tcl_ListObjAppendElement(interp,obj,
1723+
Tcl_NewStringObj(UTF_E2U(edata->schema_name),-1));
1724+
UTF_END;
1725+
}
1726+
if (edata->table_name)
1727+
{
1728+
Tcl_ListObjAppendElement(interp,obj,
1729+
Tcl_NewStringObj("table",-1));
1730+
UTF_BEGIN;
1731+
Tcl_ListObjAppendElement(interp,obj,
1732+
Tcl_NewStringObj(UTF_E2U(edata->table_name),-1));
1733+
UTF_END;
1734+
}
1735+
if (edata->column_name)
1736+
{
1737+
Tcl_ListObjAppendElement(interp,obj,
1738+
Tcl_NewStringObj("column",-1));
1739+
UTF_BEGIN;
1740+
Tcl_ListObjAppendElement(interp,obj,
1741+
Tcl_NewStringObj(UTF_E2U(edata->column_name),-1));
1742+
UTF_END;
1743+
}
1744+
if (edata->datatype_name)
1745+
{
1746+
Tcl_ListObjAppendElement(interp,obj,
1747+
Tcl_NewStringObj("datatype",-1));
1748+
UTF_BEGIN;
1749+
Tcl_ListObjAppendElement(interp,obj,
1750+
Tcl_NewStringObj(UTF_E2U(edata->datatype_name),-1));
1751+
UTF_END;
1752+
}
1753+
if (edata->constraint_name)
1754+
{
1755+
Tcl_ListObjAppendElement(interp,obj,
1756+
Tcl_NewStringObj("constraint",-1));
1757+
UTF_BEGIN;
1758+
Tcl_ListObjAppendElement(interp,obj,
1759+
Tcl_NewStringObj(UTF_E2U(edata->constraint_name),-1));
1760+
UTF_END;
1761+
}
1762+
/* cursorpos is never interesting here; report internal query/pos */
1763+
if (edata->internalquery)
1764+
{
1765+
Tcl_ListObjAppendElement(interp,obj,
1766+
Tcl_NewStringObj("statement",-1));
1767+
UTF_BEGIN;
1768+
Tcl_ListObjAppendElement(interp,obj,
1769+
Tcl_NewStringObj(UTF_E2U(edata->internalquery),-1));
1770+
UTF_END;
1771+
}
1772+
if (edata->internalpos>0)
1773+
{
1774+
Tcl_ListObjAppendElement(interp,obj,
1775+
Tcl_NewStringObj("cursor_position",-1));
1776+
Tcl_ListObjAppendElement(interp,obj,
1777+
Tcl_NewIntObj(edata->internalpos));
1778+
}
1779+
if (edata->filename)
1780+
{
1781+
Tcl_ListObjAppendElement(interp,obj,
1782+
Tcl_NewStringObj("filename",-1));
1783+
UTF_BEGIN;
1784+
Tcl_ListObjAppendElement(interp,obj,
1785+
Tcl_NewStringObj(UTF_E2U(edata->filename),-1));
1786+
UTF_END;
1787+
}
1788+
if (edata->lineno>0)
1789+
{
1790+
Tcl_ListObjAppendElement(interp,obj,
1791+
Tcl_NewStringObj("lineno",-1));
1792+
Tcl_ListObjAppendElement(interp,obj,
1793+
Tcl_NewIntObj(edata->lineno));
1794+
}
1795+
if (edata->funcname)
1796+
{
1797+
Tcl_ListObjAppendElement(interp,obj,
1798+
Tcl_NewStringObj("funcname",-1));
1799+
UTF_BEGIN;
1800+
Tcl_ListObjAppendElement(interp,obj,
1801+
Tcl_NewStringObj(UTF_E2U(edata->funcname),-1));
1802+
UTF_END;
1803+
}
1804+
1805+
Tcl_SetObjErrorCode(interp,obj);
1806+
}
1807+
1808+
16651809
/**********************************************************************
16661810
* pltcl_quote()- quote literal strings that are to
16671811
* be used in SPI_execute query strings
@@ -1880,9 +2024,10 @@ pltcl_subtrans_abort(Tcl_Interp *interp,
18802024
*/
18812025
SPI_restore_connection();
18822026

1883-
/* Pass the error message to Tcl */
2027+
/* Pass the error data to Tcl */
2028+
pltcl_construct_errorCode(interp,edata);
18842029
UTF_BEGIN;
1885-
Tcl_SetResult(interp,UTF_E2U(edata->message),TCL_VOLATILE);
2030+
Tcl_SetObjResult(interp,Tcl_NewStringObj(UTF_E2U(edata->message),-1));
18862031
UTF_END;
18872032
FreeErrorData(edata);
18882033
}

‎src/pl/tcl/sql/pltcl_setup.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,27 @@ drop table foo;
595595

596596
drop event trigger tcl_a_snitch;
597597
drop event trigger tcl_b_snitch;
598+
599+
-- test use of errorCode in error handling
600+
601+
createfunctiontcl_error_handling_test() returnstextas $$
602+
global errorCode
603+
if {[catch { spi_exec"select no_such_column from foo;" }]} {
604+
arrayset errArray $errorCode
605+
if {$errArray(SQLSTATE)=="42P01"} {
606+
return"expected error: $errArray(message)"
607+
} else {
608+
return"unexpected error: $errArray(SQLSTATE) $errArray(message)"
609+
}
610+
} else {
611+
return"no error"
612+
}
613+
$$ language pltcl;
614+
615+
select tcl_error_handling_test();
616+
617+
create temp table foo(f1int);
618+
619+
select tcl_error_handling_test();
620+
621+
droptable foo;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp