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

Commitf938c2b

Browse files
committed
Revise syntax-error reporting behavior to give pleasant results for
errors in internally-generated queries, such as those submitted byplpgsql functions. Per recent discussions with Fabien Coelho.
1 parentbee3b2a commitf938c2b

File tree

25 files changed

+672
-125
lines changed

25 files changed

+672
-125
lines changed

‎doc/src/sgml/libpq.sgml

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.147 2004/03/11 02:39:10momjian Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.148 2004/03/21 22:29:10tgl Exp $
33
-->
44

55
<chapter id="libpq">
@@ -1390,13 +1390,37 @@ bytes.
13901390
</listitem>
13911391
</varlistentry>
13921392

1393+
<varlistentry>
1394+
<term><symbol>PG_DIAG_INTERNAL_POSITION</></term>
1395+
<listitem>
1396+
<para>
1397+
This is defined the same as the <symbol>PG_DIAG_STATEMENT_POSITION</>
1398+
field, but it is used when the cursor position refers to an internally
1399+
generated command rather than the one submitted by the client.
1400+
The <symbol>PG_DIAG_INTERNAL_QUERY</> field will always appear when this field
1401+
appears.
1402+
</para>
1403+
</listitem>
1404+
</varlistentry>
1405+
1406+
<varlistentry>
1407+
<term><symbol>PG_DIAG_INTERNAL_QUERY</></term>
1408+
<listitem>
1409+
<para>
1410+
The text of a failed internally-generated command.
1411+
This could be, for example, a SQL query issued by a PL/pgSQL function.
1412+
</para>
1413+
</listitem>
1414+
</varlistentry>
1415+
13931416
<varlistentry>
13941417
<term><symbol>PG_DIAG_CONTEXT</></term>
13951418
<listitem>
13961419
<para>
1397-
An indication of the context in which the error occurred. Presently
1398-
this includes a call stack traceback of active PL functions. The
1399-
trace is one entry per line, most recent first.
1420+
An indication of the context in which the error occurred.
1421+
Presently this includes a call stack traceback of active
1422+
procedural language functions and internally-generated queries.
1423+
The trace is one entry per line, most recent first.
14001424
</para>
14011425
</listitem>
14021426
</varlistentry>

‎doc/src/sgml/protocol.sgml

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.50 2004/03/09 16:57:46 neilc Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.51 2004/03/21 22:29:10 tgl Exp $ -->
22

33
<chapter id="protocol">
44
<title>Frontend/Backend Protocol</title>
@@ -3902,16 +3902,42 @@ message.
39023902
</ListItem>
39033903
</VarListEntry>
39043904

3905+
<VarListEntry>
3906+
<Term>
3907+
<literal>p</>
3908+
</Term>
3909+
<ListItem>
3910+
<Para>
3911+
Internal position: this is defined the same as the <literal>P</>
3912+
field, but it is used when the cursor position refers to an internally
3913+
generated command rather than the one submitted by the client.
3914+
The <literal>q</> field will always appear when this field appears.
3915+
</Para>
3916+
</ListItem>
3917+
</VarListEntry>
3918+
3919+
<VarListEntry>
3920+
<Term>
3921+
<literal>q</>
3922+
</Term>
3923+
<ListItem>
3924+
<Para>
3925+
Internal query: the text of a failed internally-generated command.
3926+
This could be, for example, a SQL query issued by a PL/pgSQL function.
3927+
</Para>
3928+
</ListItem>
3929+
</VarListEntry>
3930+
39053931
<VarListEntry>
39063932
<Term>
39073933
<literal>W</>
39083934
</Term>
39093935
<ListItem>
39103936
<Para>
39113937
Where: an indication of the context in which the error occurred.
3912-
Presently this includes a call stack traceback of active
3913-
procedural language functions. The trace is one entry per line,
3914-
most recent first.
3938+
Presently this includes a call stack traceback of active
3939+
procedural language functions and internally-generated queries.
3940+
The trace is one entry per line,most recent first.
39153941
</Para>
39163942
</ListItem>
39173943
</VarListEntry>

‎src/backend/catalog/pg_proc.c

Lines changed: 200 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.112 2004/03/14 01:58:41 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.113 2004/03/21 22:29:10 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -23,9 +23,11 @@
2323
#include"executor/executor.h"
2424
#include"fmgr.h"
2525
#include"miscadmin.h"
26+
#include"mb/pg_wchar.h"
2627
#include"parser/parse_coerce.h"
2728
#include"parser/parse_expr.h"
2829
#include"parser/parse_type.h"
30+
#include"tcop/pquery.h"
2931
#include"tcop/tcopprot.h"
3032
#include"utils/acl.h"
3133
#include"utils/builtins.h"
@@ -45,6 +47,10 @@ Datumfmgr_sql_validator(PG_FUNCTION_ARGS);
4547
staticDatumcreate_parameternames_array(intparameterCount,
4648
constchar*parameterNames[]);
4749
staticvoidsql_function_parse_error_callback(void*arg);
50+
staticintmatch_prosrc_to_query(constchar*prosrc,constchar*queryText,
51+
intcursorpos);
52+
staticboolmatch_prosrc_to_literal(constchar*prosrc,constchar*literal,
53+
intcursorpos,int*newcursorpos);
4854

4955

5056
/* ----------------------------------------------------------------
@@ -763,12 +769,10 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
763769
prosrc=DatumGetCString(DirectFunctionCall1(textout,tmp));
764770

765771
/*
766-
* Setup error traceback support for ereport(). This is mostly
767-
* so we can add context info that shows that a syntax-error
768-
* location is inside the function body, not out in CREATE FUNCTION.
772+
* Setup error traceback support for ereport().
769773
*/
770774
sqlerrcontext.callback=sql_function_parse_error_callback;
771-
sqlerrcontext.arg=proc;
775+
sqlerrcontext.arg=tuple;
772776
sqlerrcontext.previous=error_context_stack;
773777
error_context_stack=&sqlerrcontext;
774778

@@ -800,22 +804,203 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
800804
}
801805

802806
/*
803-
*error context callbackto let us supply a context marker
807+
*Error context callbackfor handling errors in SQL function definitions
804808
*/
805809
staticvoid
806810
sql_function_parse_error_callback(void*arg)
807811
{
808-
Form_pg_procproc= (Form_pg_proc)arg;
812+
HeapTupletuple= (HeapTuple)arg;
813+
Form_pg_procproc= (Form_pg_proc)GETSTRUCT(tuple);
814+
boolisnull;
815+
Datumtmp;
816+
char*prosrc;
817+
818+
/* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
819+
tmp=SysCacheGetAttr(PROCOID,tuple,Anum_pg_proc_prosrc,&isnull);
820+
if (isnull)
821+
elog(ERROR,"null prosrc");
822+
prosrc=DatumGetCString(DirectFunctionCall1(textout,tmp));
823+
824+
if (!function_parse_error_transpose(prosrc))
825+
{
826+
/* If it's not a syntax error, push info onto context stack */
827+
errcontext("SQL function \"%s\"",NameStr(proc->proname));
828+
}
829+
830+
pfree(prosrc);
831+
}
832+
833+
/*
834+
* Adjust a syntax error occurring inside the function body of a CREATE
835+
* FUNCTION command. This can be used by any function validator, not only
836+
* for SQL-language functions. It is assumed that the syntax error position
837+
* is initially relative to the function body string (as passed in). If
838+
* possible, we adjust the position to reference the original CREATE command;
839+
* if we can't manage that, we set up an "internal query" syntax error instead.
840+
*
841+
* Returns true if a syntax error was processed, false if not.
842+
*/
843+
bool
844+
function_parse_error_transpose(constchar*prosrc)
845+
{
846+
intorigerrposition;
847+
intnewerrposition;
848+
constchar*queryText;
849+
850+
/*
851+
* Nothing to do unless we are dealing with a syntax error that has
852+
* a cursor position.
853+
*
854+
* Some PLs may prefer to report the error position as an internal
855+
* error to begin with, so check that too.
856+
*/
857+
origerrposition=geterrposition();
858+
if (origerrposition <=0)
859+
{
860+
origerrposition=getinternalerrposition();
861+
if (origerrposition <=0)
862+
return false;
863+
}
864+
865+
/* We can get the original query text from the active portal (hack...) */
866+
Assert(ActivePortal&&ActivePortal->portalActive);
867+
queryText=ActivePortal->sourceText;
868+
869+
/* Try to locate the prosrc in the original text */
870+
newerrposition=match_prosrc_to_query(prosrc,queryText,origerrposition);
871+
872+
if (newerrposition>0)
873+
{
874+
/* Successful, so fix error position to reference original query */
875+
errposition(newerrposition);
876+
/* Get rid of any report of the error as an "internal query" */
877+
internalerrposition(0);
878+
internalerrquery(NULL);
879+
}
880+
else
881+
{
882+
/*
883+
* If unsuccessful, convert the position to an internal position
884+
* marker and give the function text as the internal query.
885+
*/
886+
errposition(0);
887+
internalerrposition(origerrposition);
888+
internalerrquery(prosrc);
889+
}
890+
891+
return true;
892+
}
809893

894+
/*
895+
* Try to locate the string literal containing the function body in the
896+
* given text of the CREATE FUNCTION command. If successful, return the
897+
* character (not byte) index within the command corresponding to the
898+
* given character index within the literal. If not successful, return 0.
899+
*/
900+
staticint
901+
match_prosrc_to_query(constchar*prosrc,constchar*queryText,
902+
intcursorpos)
903+
{
810904
/*
811-
* XXX it'd be really nice to adjust the syntax error position to
812-
* account for the offset from the start of the statement to the
813-
* function body string, not to mention any quoting characters in
814-
* the string, but I can't see any decent way to do that...
905+
* Rather than fully parsing the CREATE FUNCTION command, we just scan
906+
* the command looking for $prosrc$ or 'prosrc'. This could be fooled
907+
* (though not in any very probable scenarios), so fail if we find
908+
* more than one match.
909+
*/
910+
intprosrclen=strlen(prosrc);
911+
intquerylen=strlen(queryText);
912+
intmatchpos=0;
913+
intcurpos;
914+
intnewcursorpos;
915+
916+
for (curpos=0;curpos<querylen-prosrclen;curpos++)
917+
{
918+
if (queryText[curpos]=='$'&&
919+
strncmp(prosrc,&queryText[curpos+1],prosrclen)==0&&
920+
queryText[curpos+1+prosrclen]=='$')
921+
{
922+
/*
923+
* Found a $foo$ match. Since there are no embedded quoting
924+
* characters in a dollar-quoted literal, we don't have to do
925+
* any fancy arithmetic; just offset by the starting position.
926+
*/
927+
if (matchpos)
928+
return0;/* multiple matches, fail */
929+
matchpos=pg_mbstrlen_with_len(queryText,curpos+1)
930+
+cursorpos;
931+
}
932+
elseif (queryText[curpos]=='\''&&
933+
match_prosrc_to_literal(prosrc,&queryText[curpos+1],
934+
cursorpos,&newcursorpos))
935+
{
936+
/*
937+
* Found a 'foo' match. match_prosrc_to_literal() has adjusted
938+
* for any quotes or backslashes embedded in the literal.
939+
*/
940+
if (matchpos)
941+
return0;/* multiple matches, fail */
942+
matchpos=pg_mbstrlen_with_len(queryText,curpos+1)
943+
+newcursorpos;
944+
}
945+
}
946+
947+
returnmatchpos;
948+
}
949+
950+
/*
951+
* Try to match the given source text to a single-quoted literal.
952+
* If successful, adjust newcursorpos to correspond to the character
953+
* (not byte) index corresponding to cursorpos in the source text.
954+
*
955+
* At entry, literal points just past a ' character. We must check for the
956+
* trailing quote.
957+
*/
958+
staticbool
959+
match_prosrc_to_literal(constchar*prosrc,constchar*literal,
960+
intcursorpos,int*newcursorpos)
961+
{
962+
intnewcp=cursorpos;
963+
intchlen;
964+
965+
/*
966+
* This implementation handles backslashes and doubled quotes in the
967+
* string literal. It does not handle the SQL syntax for literals
968+
* continued across line boundaries.
815969
*
816-
*Inthemeantime, put in a CONTEXT entry that can cue clients
817-
*not to trust the syntax error position completely.
970+
*We dothecomparison a character at a time, not a byte at a time,
971+
*so that we can do the correct cursorpos math.
818972
*/
819-
errcontext("SQL function \"%s\"",
820-
NameStr(proc->proname));
973+
while (*prosrc)
974+
{
975+
cursorpos--;/* characters left before cursor */
976+
/*
977+
* Check for backslashes and doubled quotes in the literal; adjust
978+
* newcp when one is found before the cursor.
979+
*/
980+
if (*literal=='\\')
981+
{
982+
literal++;
983+
if (cursorpos>0)
984+
newcp++;
985+
}
986+
elseif (*literal=='\'')
987+
{
988+
if (literal[1]!='\'')
989+
return false;
990+
literal++;
991+
if (cursorpos>0)
992+
newcp++;
993+
}
994+
chlen=pg_mblen(prosrc);
995+
if (strncmp(prosrc,literal,chlen)!=0)
996+
return false;
997+
prosrc+=chlen;
998+
literal+=chlen;
999+
}
1000+
1001+
*newcursorpos=newcp;
1002+
1003+
if (*literal=='\''&&literal[1]!='\'')
1004+
return true;
1005+
return false;
8211006
}

‎src/backend/commands/portalcmds.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.25 2003/11/29 19:51:47 pgsql Exp $
17+
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.26 2004/03/21 22:29:10 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -270,6 +270,7 @@ void
270270
PersistHoldablePortal(Portalportal)
271271
{
272272
QueryDesc*queryDesc=PortalGetQueryDesc(portal);
273+
PortalsaveActivePortal;
273274
MemoryContextsavePortalContext;
274275
MemoryContextsaveQueryContext;
275276
MemoryContextoldcxt;
@@ -311,6 +312,8 @@ PersistHoldablePortal(Portal portal)
311312
/*
312313
* Set global portal context pointers.
313314
*/
315+
saveActivePortal=ActivePortal;
316+
ActivePortal=portal;
314317
savePortalContext=PortalContext;
315318
PortalContext=PortalGetHeapMemory(portal);
316319
saveQueryContext=QueryContext;
@@ -342,6 +345,7 @@ PersistHoldablePortal(Portal portal)
342345
/* Mark portal not active */
343346
portal->portalActive= false;
344347

348+
ActivePortal=saveActivePortal;
345349
PortalContext=savePortalContext;
346350
QueryContext=saveQueryContext;
347351

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp