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

Commita4847fc

Browse files
committed
Add an ASSERT statement in plpgsql.
This is meant to make it easier to insert simple debugging cross-checksin plpgsql functions.Pavel Stehule, reviewed by Jim Nasby
1 parent83ff161 commita4847fc

File tree

10 files changed

+316
-16
lines changed

10 files changed

+316
-16
lines changed

‎doc/src/sgml/plpgsql.sgml

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2562,8 +2562,9 @@ END;
25622562
those shown in <xref linkend="errcodes-appendix">. A category
25632563
name matches any error within its category. The special
25642564
condition name <literal>OTHERS</> matches every error type except
2565-
<literal>QUERY_CANCELED</>. (It is possible, but often unwise,
2566-
to trap <literal>QUERY_CANCELED</> by name.) Condition names are
2565+
<literal>QUERY_CANCELED</> and <literal>ASSERT_FAILURE</>.
2566+
(It is possible, but often unwise, to trap those two error types
2567+
by name.) Condition names are
25672568
not case-sensitive. Also, an error condition can be specified
25682569
by <literal>SQLSTATE</> code; for example these are equivalent:
25692570
<programlisting>
@@ -3387,8 +3388,12 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
33873388
<sect1 id="plpgsql-errors-and-messages">
33883389
<title>Errors and Messages</title>
33893390

3391+
<sect2 id="plpgsql-statements-raise">
3392+
<title>Reporting Errors and Messages</title>
3393+
33903394
<indexterm>
33913395
<primary>RAISE</primary>
3396+
<secondary>in PL/pgSQL</secondary>
33923397
</indexterm>
33933398

33943399
<indexterm>
@@ -3580,6 +3585,67 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
35803585
</para>
35813586
</note>
35823587

3588+
</sect2>
3589+
3590+
<sect2 id="plpgsql-statements-assert">
3591+
<title>Checking Assertions</title>
3592+
3593+
<indexterm>
3594+
<primary>ASSERT</primary>
3595+
<secondary>in PL/pgSQL</secondary>
3596+
</indexterm>
3597+
3598+
<indexterm>
3599+
<primary>assertions</primary>
3600+
<secondary>in PL/pgSQL</secondary>
3601+
</indexterm>
3602+
3603+
<indexterm>
3604+
<primary><varname>plpgsql.check_asserts</> configuration parameter</primary>
3605+
</indexterm>
3606+
3607+
<para>
3608+
The <command>ASSERT</command> statement is a convenient shorthand for
3609+
inserting debugging checks into <application>PL/pgSQL</application>
3610+
functions.
3611+
3612+
<synopsis>
3613+
ASSERT <replaceable class="parameter">condition</replaceable> <optional> , <replaceable class="parameter">message</replaceable> </optional>;
3614+
</synopsis>
3615+
3616+
The <replaceable class="parameter">condition</replaceable> is a boolean
3617+
expression that is expected to always evaluate to TRUE; if it does,
3618+
the <command>ASSERT</command> statement does nothing further. If the
3619+
result is FALSE or NULL, then an <literal>ASSERT_FAILURE</> exception
3620+
is raised. (If an error occurs while evaluating
3621+
the <replaceable class="parameter">condition</replaceable>, it is
3622+
reported as a normal error.)
3623+
</para>
3624+
3625+
<para>
3626+
If the optional <replaceable class="parameter">message</replaceable> is
3627+
provided, it is an expression whose result (if not null) replaces the
3628+
default error message text <quote>assertion failed</>, should
3629+
the <replaceable class="parameter">condition</replaceable> fail.
3630+
The <replaceable class="parameter">message</replaceable> expression is
3631+
not evaluated in the normal case where the assertion succeeds.
3632+
</para>
3633+
3634+
<para>
3635+
Testing of assertions can be enabled or disabled via the configuration
3636+
parameter <literal>plpgsql.check_asserts</>, which takes a boolean
3637+
value; the default is <literal>on</>. If this parameter
3638+
is <literal>off</> then <command>ASSERT</> statements do nothing.
3639+
</para>
3640+
3641+
<para>
3642+
Note that <command>ASSERT</command> is meant for detecting program
3643+
bugs, not for reporting ordinary error conditions. Use
3644+
the <command>RAISE</> statement, described above, for that.
3645+
</para>
3646+
3647+
</sect2>
3648+
35833649
</sect1>
35843650

35853651
<sect1 id="plpgsql-trigger">
@@ -5075,8 +5141,7 @@ $func$ LANGUAGE plpgsql;
50755141
<productname>PostgreSQL</> does not have a built-in
50765142
<function>instr</function> function, but you can create one
50775143
using a combination of other
5078-
functions.<indexterm><primary>instr</></indexterm> In <xref
5079-
linkend="plpgsql-porting-appendix"> there is a
5144+
functions. In <xref linkend="plpgsql-porting-appendix"> there is a
50805145
<application>PL/pgSQL</application> implementation of
50815146
<function>instr</function> that you can use to make your porting
50825147
easier.
@@ -5409,6 +5474,10 @@ $$ LANGUAGE plpgsql STRICT IMMUTABLE;
54095474
your porting efforts.
54105475
</para>
54115476

5477+
<indexterm>
5478+
<primary><function>instr</> function</primary>
5479+
</indexterm>
5480+
54125481
<programlisting>
54135482
--
54145483
-- instr functions that mimic Oracle's counterpart

‎src/backend/utils/errcodes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ P0000 E ERRCODE_PLPGSQL_ERROR plp
454454
P0001 E ERRCODE_RAISE_EXCEPTION raise_exception
455455
P0002 E ERRCODE_NO_DATA_FOUND no_data_found
456456
P0003 E ERRCODE_TOO_MANY_ROWS too_many_rows
457+
P0004 E ERRCODE_ASSERT_FAILURE assert_failure
457458

458459
Section: Class XX - Internal Error
459460

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ static int exec_stmt_return_query(PLpgSQL_execstate *estate,
153153
PLpgSQL_stmt_return_query*stmt);
154154
staticintexec_stmt_raise(PLpgSQL_execstate*estate,
155155
PLpgSQL_stmt_raise*stmt);
156+
staticintexec_stmt_assert(PLpgSQL_execstate*estate,
157+
PLpgSQL_stmt_assert*stmt);
156158
staticintexec_stmt_execsql(PLpgSQL_execstate*estate,
157159
PLpgSQL_stmt_execsql*stmt);
158160
staticintexec_stmt_dynexecute(PLpgSQL_execstate*estate,
@@ -363,8 +365,8 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
363365
estate.err_text=NULL;
364366

365367
/*
366-
* Provide a more helpful message if a CONTINUEor RAISEhas been used
367-
*outsidethe context it can work in.
368+
* Provide a more helpful message if a CONTINUE has been used outside
369+
* the context it can work in.
368370
*/
369371
if (rc==PLPGSQL_RC_CONTINUE)
370372
ereport(ERROR,
@@ -730,8 +732,8 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
730732
estate.err_text=NULL;
731733

732734
/*
733-
* Provide a more helpful message if a CONTINUEor RAISEhas been used
734-
*outsidethe context it can work in.
735+
* Provide a more helpful message if a CONTINUE has been used outside
736+
* the context it can work in.
735737
*/
736738
if (rc==PLPGSQL_RC_CONTINUE)
737739
ereport(ERROR,
@@ -862,8 +864,8 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
862864
estate.err_text=NULL;
863865

864866
/*
865-
* Provide a more helpful message if a CONTINUEor RAISEhas been used
866-
*outsidethe context it can work in.
867+
* Provide a more helpful message if a CONTINUE has been used outside
868+
* the context it can work in.
867869
*/
868870
if (rc==PLPGSQL_RC_CONTINUE)
869871
ereport(ERROR,
@@ -1027,12 +1029,14 @@ exception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond)
10271029
intsqlerrstate=cond->sqlerrstate;
10281030

10291031
/*
1030-
* OTHERS matches everything *except* query-canceled; if you're
1031-
* foolish enough, you can match that explicitly.
1032+
* OTHERS matches everything *except* query-canceled and
1033+
* assert-failure. If you're foolish enough, you can match those
1034+
* explicitly.
10321035
*/
10331036
if (sqlerrstate==0)
10341037
{
1035-
if (edata->sqlerrcode!=ERRCODE_QUERY_CANCELED)
1038+
if (edata->sqlerrcode!=ERRCODE_QUERY_CANCELED&&
1039+
edata->sqlerrcode!=ERRCODE_ASSERT_FAILURE)
10361040
return true;
10371041
}
10381042
/* Exact match? */
@@ -1471,6 +1475,10 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
14711475
rc=exec_stmt_raise(estate, (PLpgSQL_stmt_raise*)stmt);
14721476
break;
14731477

1478+
casePLPGSQL_STMT_ASSERT:
1479+
rc=exec_stmt_assert(estate, (PLpgSQL_stmt_assert*)stmt);
1480+
break;
1481+
14741482
casePLPGSQL_STMT_EXECSQL:
14751483
rc=exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql*)stmt);
14761484
break;
@@ -3117,6 +3125,48 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
31173125
returnPLPGSQL_RC_OK;
31183126
}
31193127

3128+
/* ----------
3129+
* exec_stmt_assertAssert statement
3130+
* ----------
3131+
*/
3132+
staticint
3133+
exec_stmt_assert(PLpgSQL_execstate*estate,PLpgSQL_stmt_assert*stmt)
3134+
{
3135+
boolvalue;
3136+
boolisnull;
3137+
3138+
/* do nothing when asserts are not enabled */
3139+
if (!plpgsql_check_asserts)
3140+
returnPLPGSQL_RC_OK;
3141+
3142+
value=exec_eval_boolean(estate,stmt->cond,&isnull);
3143+
exec_eval_cleanup(estate);
3144+
3145+
if (isnull|| !value)
3146+
{
3147+
char*message=NULL;
3148+
3149+
if (stmt->message!=NULL)
3150+
{
3151+
Datumval;
3152+
Oidtypeid;
3153+
int32typmod;
3154+
3155+
val=exec_eval_expr(estate,stmt->message,
3156+
&isnull,&typeid,&typmod);
3157+
if (!isnull)
3158+
message=convert_value_to_string(estate,val,typeid);
3159+
/* we mustn't do exec_eval_cleanup here */
3160+
}
3161+
3162+
ereport(ERROR,
3163+
(errcode(ERRCODE_ASSERT_FAILURE),
3164+
message ?errmsg_internal("%s",message) :
3165+
errmsg("assertion failed")));
3166+
}
3167+
3168+
returnPLPGSQL_RC_OK;
3169+
}
31203170

31213171
/* ----------
31223172
* Initialize a mostly empty execution state

‎src/pl/plpgsql/src/pl_funcs.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
244244
return"RETURN QUERY";
245245
casePLPGSQL_STMT_RAISE:
246246
return"RAISE";
247+
casePLPGSQL_STMT_ASSERT:
248+
return"ASSERT";
247249
casePLPGSQL_STMT_EXECSQL:
248250
return_("SQL statement");
249251
casePLPGSQL_STMT_DYNEXECUTE:
@@ -330,6 +332,7 @@ static void free_return(PLpgSQL_stmt_return *stmt);
330332
staticvoidfree_return_next(PLpgSQL_stmt_return_next*stmt);
331333
staticvoidfree_return_query(PLpgSQL_stmt_return_query*stmt);
332334
staticvoidfree_raise(PLpgSQL_stmt_raise*stmt);
335+
staticvoidfree_assert(PLpgSQL_stmt_assert*stmt);
333336
staticvoidfree_execsql(PLpgSQL_stmt_execsql*stmt);
334337
staticvoidfree_dynexecute(PLpgSQL_stmt_dynexecute*stmt);
335338
staticvoidfree_dynfors(PLpgSQL_stmt_dynfors*stmt);
@@ -391,6 +394,9 @@ free_stmt(PLpgSQL_stmt *stmt)
391394
casePLPGSQL_STMT_RAISE:
392395
free_raise((PLpgSQL_stmt_raise*)stmt);
393396
break;
397+
casePLPGSQL_STMT_ASSERT:
398+
free_assert((PLpgSQL_stmt_assert*)stmt);
399+
break;
394400
casePLPGSQL_STMT_EXECSQL:
395401
free_execsql((PLpgSQL_stmt_execsql*)stmt);
396402
break;
@@ -610,6 +616,13 @@ free_raise(PLpgSQL_stmt_raise *stmt)
610616
}
611617
}
612618

619+
staticvoid
620+
free_assert(PLpgSQL_stmt_assert*stmt)
621+
{
622+
free_expr(stmt->cond);
623+
free_expr(stmt->message);
624+
}
625+
613626
staticvoid
614627
free_execsql(PLpgSQL_stmt_execsql*stmt)
615628
{
@@ -732,6 +745,7 @@ static void dump_return(PLpgSQL_stmt_return *stmt);
732745
staticvoiddump_return_next(PLpgSQL_stmt_return_next*stmt);
733746
staticvoiddump_return_query(PLpgSQL_stmt_return_query*stmt);
734747
staticvoiddump_raise(PLpgSQL_stmt_raise*stmt);
748+
staticvoiddump_assert(PLpgSQL_stmt_assert*stmt);
735749
staticvoiddump_execsql(PLpgSQL_stmt_execsql*stmt);
736750
staticvoiddump_dynexecute(PLpgSQL_stmt_dynexecute*stmt);
737751
staticvoiddump_dynfors(PLpgSQL_stmt_dynfors*stmt);
@@ -804,6 +818,9 @@ dump_stmt(PLpgSQL_stmt *stmt)
804818
casePLPGSQL_STMT_RAISE:
805819
dump_raise((PLpgSQL_stmt_raise*)stmt);
806820
break;
821+
casePLPGSQL_STMT_ASSERT:
822+
dump_assert((PLpgSQL_stmt_assert*)stmt);
823+
break;
807824
casePLPGSQL_STMT_EXECSQL:
808825
dump_execsql((PLpgSQL_stmt_execsql*)stmt);
809826
break;
@@ -1353,6 +1370,25 @@ dump_raise(PLpgSQL_stmt_raise *stmt)
13531370
dump_indent-=2;
13541371
}
13551372

1373+
staticvoid
1374+
dump_assert(PLpgSQL_stmt_assert*stmt)
1375+
{
1376+
dump_ind();
1377+
printf("ASSERT ");
1378+
dump_expr(stmt->cond);
1379+
printf("\n");
1380+
1381+
dump_indent+=2;
1382+
if (stmt->message!=NULL)
1383+
{
1384+
dump_ind();
1385+
printf(" MESSAGE = ");
1386+
dump_expr(stmt->message);
1387+
printf("\n");
1388+
}
1389+
dump_indent-=2;
1390+
}
1391+
13561392
staticvoid
13571393
dump_execsql(PLpgSQL_stmt_execsql*stmt)
13581394
{

‎src/pl/plpgsql/src/pl_gram.y

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ staticvoidcheck_raise_parameters(PLpgSQL_stmt_raise *stmt);
192192
%type<loop_body>loop_body
193193
%type<stmt>proc_stmtpl_block
194194
%type<stmt>stmt_assignstmt_ifstmt_loopstmt_whilestmt_exit
195-
%type<stmt>stmt_returnstmt_raisestmt_execsql
195+
%type<stmt>stmt_returnstmt_raisestmt_assertstmt_execsql
196196
%type<stmt>stmt_dynexecutestmt_forstmt_performstmt_getdiag
197197
%type<stmt>stmt_openstmt_fetchstmt_movestmt_closestmt_null
198198
%type<stmt>stmt_casestmt_foreach_a
@@ -247,6 +247,7 @@ staticvoidcheck_raise_parameters(PLpgSQL_stmt_raise *stmt);
247247
%token<keyword>K_ALIAS
248248
%token<keyword>K_ALL
249249
%token<keyword>K_ARRAY
250+
%token<keyword>K_ASSERT
250251
%token<keyword>K_BACKWARD
251252
%token<keyword>K_BEGIN
252253
%token<keyword>K_BY
@@ -871,6 +872,8 @@ proc_stmt: pl_block ';'
871872
{ $$ = $1; }
872873
| stmt_raise
873874
{ $$ = $1; }
875+
| stmt_assert
876+
{ $$ = $1; }
874877
| stmt_execsql
875878
{ $$ = $1; }
876879
| stmt_dynexecute
@@ -1847,6 +1850,29 @@ stmt_raise: K_RAISE
18471850
}
18481851
;
18491852

1853+
stmt_assert: K_ASSERT
1854+
{
1855+
PLpgSQL_stmt_assert*new;
1856+
inttok;
1857+
1858+
new =palloc(sizeof(PLpgSQL_stmt_assert));
1859+
1860+
new->cmd_type= PLPGSQL_STMT_ASSERT;
1861+
new->lineno=plpgsql_location_to_lineno(@1);
1862+
1863+
new->cond =read_sql_expression2(',',';',
1864+
", or ;",
1865+
&tok);
1866+
1867+
if (tok ==',')
1868+
new->message =read_sql_expression(';',";");
1869+
else
1870+
new->message =NULL;
1871+
1872+
$$ = (PLpgSQL_stmt *)new;
1873+
}
1874+
;
1875+
18501876
loop_body: proc_sect K_END K_LOOP opt_label';'
18511877
{
18521878
$$.stmts = $1;
@@ -2315,6 +2341,7 @@ unreserved_keyword:
23152341
K_ABSOLUTE
23162342
| K_ALIAS
23172343
| K_ARRAY
2344+
| K_ASSERT
23182345
| K_BACKWARD
23192346
| K_CLOSE
23202347
| K_COLLATE

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp