Commit 76d4abf2 authored by Tom Lane's avatar Tom Lane

Improve the recently-added support for properly pluralized error messages

by extending the ereport() API to cater for pluralization directly.  This
is better than the original method of calling ngettext outside the elog.c
code because (1) it avoids double translation, which wastes cycles and in
the worst case could give a wrong result; and (2) it avoids having to use
a different coding method in PL code than in the core backend.  The
client-side uses of ngettext are not touched since neither of these concerns
is very pressing in the client environment.  Per my proposal of yesterday.
parent fd416db4
<!-- $PostgreSQL: pgsql/doc/src/sgml/nls.sgml,v 1.17 2009/01/09 10:54:07 petere Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/nls.sgml,v 1.18 2009/06/04 18:33:06 tgl Exp $ -->
<chapter id="nls"> <chapter id="nls">
<chapterinfo> <chapterinfo>
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
<filename>msgmerge</filename>, respectively, in a GNU-compatible <filename>msgmerge</filename>, respectively, in a GNU-compatible
implementation. Later, we will try to arrange it so that if you implementation. Later, we will try to arrange it so that if you
use a packaged source distribution, you won't need use a packaged source distribution, you won't need
<filename>xgettext</filename>. (From CVS, you will still need <filename>xgettext</filename>. (If working from CVS, you will still need
it.) <application>GNU Gettext 0.10.36</application> or later is currently recommended. it.) <application>GNU Gettext 0.10.36</application> or later is currently recommended.
</para> </para>
...@@ -224,7 +224,7 @@ gmake update-po ...@@ -224,7 +224,7 @@ gmake update-po
that gives room for other people to pick up your work. However, that gives room for other people to pick up your work. However,
you are encouraged to give priority to removing fuzzy entries you are encouraged to give priority to removing fuzzy entries
after doing a merge. Remember that fuzzy entries will not be after doing a merge. Remember that fuzzy entries will not be
installed; they only serve as reference what might be the right installed; they only serve as reference for what might be the right
translation. translation.
</para> </para>
...@@ -347,8 +347,8 @@ fprintf(stderr, "panic level %d\n", lvl); ...@@ -347,8 +347,8 @@ fprintf(stderr, "panic level %d\n", lvl);
<programlisting> <programlisting>
fprintf(stderr, gettext("panic level %d\n"), lvl); fprintf(stderr, gettext("panic level %d\n"), lvl);
</programlisting> </programlisting>
(<symbol>gettext</symbol> is defined as a no-op if no NLS is (<symbol>gettext</symbol> is defined as a no-op if NLS support is
configured.) not configured.)
</para> </para>
<para> <para>
...@@ -421,6 +421,9 @@ fprintf(stderr, gettext("panic level %d\n"), lvl); ...@@ -421,6 +421,9 @@ fprintf(stderr, gettext("panic level %d\n"), lvl);
them here. If the translatable string is not the first them here. If the translatable string is not the first
argument, the item needs to be of the form argument, the item needs to be of the form
<literal>func:2</literal> (for the second argument). <literal>func:2</literal> (for the second argument).
If you have a function that supports pluralized messages,
the item should look like <literal>func:1,2</literal>
(identifying the singular and plural message arguments).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -451,8 +454,8 @@ fprintf(stderr, gettext("panic level %d\n"), lvl); ...@@ -451,8 +454,8 @@ fprintf(stderr, gettext("panic level %d\n"), lvl);
printf("Files were %s.\n", flag ? "copied" : "removed"); printf("Files were %s.\n", flag ? "copied" : "removed");
</programlisting> </programlisting>
The word order within the sentence might be different in other The word order within the sentence might be different in other
languages. Also, even if you remember to call gettext() on each languages. Also, even if you remember to call <function>gettext()</> on
fragment, the fragments might not translate well separately. It's each fragment, the fragments might not translate well separately. It's
better to duplicate a little code so that each message to be better to duplicate a little code so that each message to be
translated is a coherent whole. Only numbers, file names, and translated is a coherent whole. Only numbers, file names, and
such-like run-time variables should be inserted at run time into such-like run-time variables should be inserted at run time into
...@@ -475,13 +478,44 @@ else ...@@ -475,13 +478,44 @@ else
printf("copied %d files", n): printf("copied %d files", n):
</programlisting> </programlisting>
then be disappointed. Some languages have more than two forms, then be disappointed. Some languages have more than two forms,
with some peculiar rules. We might have a solution for this in with some peculiar rules. It's often best to design the message
the future, but for now the matter is best avoided altogether. to avoid the issue altogether, for instance like this:
You could write:
<programlisting> <programlisting>
printf("number of copied files: %d", n); printf("number of copied files: %d", n);
</programlisting> </programlisting>
</para> </para>
<para>
If you really want to construct a properly pluralized message,
there is support for this, but it's a bit awkward. When generating
a primary or detail error message in <function>ereport()</>, you can
write something like this:
<programlisting>
errmsg_plural("copied %d file",
"copied %d files",
n,
n)
</programlisting>
The first argument is the format string appropriate for English
singular form, the second is the format string appropriate for
English plural form, and the third is the integer control value
that determines which plural form to use. Subsequent arguments
are formatted per the format string as usual. (Normally, the
pluralization control value will also be one of the values to be
formatted, so it has to be written twice.) In English it only
matters whether <replaceable>n</> is 1 or not 1, but in other
languages there can be many different plural forms. The translator
sees the two English forms as a group and has the opportunity to
supply multiple substitute strings, with the appropriate one being
selected based on the run-time value of <replaceable>n</>.
</para>
<para>
If you need to pluralize a message that isn't going directly to an
<function>errmsg</> or <function>errdetail</> report, you have to use
the underlying function <function>ngettext</>. See the gettext
documentation.
</para>
</listitem> </listitem>
<listitem> <listitem>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.33 2009/04/27 16:27:36 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.34 2009/06/04 18:33:06 tgl Exp $ -->
<chapter id="source"> <chapter id="source">
<title>PostgreSQL Coding Conventions</title> <title>PostgreSQL Coding Conventions</title>
...@@ -181,6 +181,19 @@ ereport(ERROR, ...@@ -181,6 +181,19 @@ ereport(ERROR,
not worth expending translation effort on. not worth expending translation effort on.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<function>errmsg_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)</function> is like <function>errmsg</>, but with
support for various plural forms of the message.
<replaceable>fmt_singular</> is the English singular format,
<replaceable>fmt_plural</> is the English plural format,
<replaceable>n</> is the integer value that determines which plural
form is needed, and the remaining arguments are formatted according
to the selected format string. For more information see
<xref linkend="nls-guidelines">.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
<function>errdetail(const char *msg, ...)</function> supplies an optional <function>errdetail(const char *msg, ...)</function> supplies an optional
...@@ -201,6 +214,14 @@ ereport(ERROR, ...@@ -201,6 +214,14 @@ ereport(ERROR,
sent to the client. sent to the client.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<function>errdetail_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)</function> is like <function>errdetail</>, but with
support for various plural forms of the message.
For more information see <xref linkend="nls-guidelines">.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
<function>errhint(const char *msg, ...)</function> supplies an optional <function>errhint(const char *msg, ...)</function> supplies an optional
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.87 2009/03/26 22:26:06 petere Exp $ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.88 2009/06/04 18:33:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -914,9 +914,9 @@ reportDependentObjects(const ObjectAddresses *targetObjects, ...@@ -914,9 +914,9 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
{ {
ereport(msglevel, ereport(msglevel,
/* translator: %d always has a value larger than 1 */ /* translator: %d always has a value larger than 1 */
(errmsg(ngettext("drop cascades to %d other object", (errmsg_plural("drop cascades to %d other object",
"drop cascades to %d other objects", "drop cascades to %d other objects",
numReportedClient + numNotReportedClient), numReportedClient + numNotReportedClient,
numReportedClient + numNotReportedClient), numReportedClient + numNotReportedClient),
errdetail("%s", clientdetail.data), errdetail("%s", clientdetail.data),
errdetail_log("%s", logdetail.data))); errdetail_log("%s", logdetail.data)));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.162 2009/03/26 22:26:06 petere Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.163 2009/06/04 18:33:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -112,9 +112,9 @@ ProcedureCreate(const char *procedureName, ...@@ -112,9 +112,9 @@ ProcedureCreate(const char *procedureName,
if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("functions cannot have more than %d argument", errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments", "functions cannot have more than %d arguments",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS))); FUNC_MAX_ARGS)));
/* note: the above is correct, we do NOT count output arguments */ /* note: the above is correct, we do NOT count output arguments */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.32 2009/03/26 22:26:06 petere Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.33 2009/06/04 18:33:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1049,7 +1049,10 @@ storeObjectDescription(StringInfo descs, objectType type, ...@@ -1049,7 +1049,10 @@ storeObjectDescription(StringInfo descs, objectType type,
case REMOTE_OBJECT: case REMOTE_OBJECT:
/* translator: %s will always be "database %s" */ /* translator: %s will always be "database %s" */
appendStringInfo(descs, ngettext("%d object in %s", "%d objects in %s", count), count, objdesc); appendStringInfo(descs, ngettext("%d object in %s",
"%d objects in %s",
count),
count, objdesc);
break; break;
default: default:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.246 2009/04/08 21:51:38 petere Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.247 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -616,10 +616,11 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, ...@@ -616,10 +616,11 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("table row type and query-specified row type do not match"), errmsg("table row type and query-specified row type do not match"),
errdetail(ngettext("Table row contains %d attribute, but query expects %d.", errdetail_plural("Table row contains %d attribute, but query expects %d.",
"Table row contains %d attributes, but query expects %d.", "Table row contains %d attributes, but query expects %d.",
slot_tupdesc->natts), slot_tupdesc->natts,
slot_tupdesc->natts, var_tupdesc->natts))); slot_tupdesc->natts,
var_tupdesc->natts)));
else if (var_tupdesc->natts < slot_tupdesc->natts) else if (var_tupdesc->natts < slot_tupdesc->natts)
needslow = true; needslow = true;
...@@ -1043,9 +1044,9 @@ init_fcache(Oid foid, FuncExprState *fcache, ...@@ -1043,9 +1044,9 @@ init_fcache(Oid foid, FuncExprState *fcache,
if (list_length(fcache->args) > FUNC_MAX_ARGS) if (list_length(fcache->args) > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("cannot pass more than %d argument to a function", errmsg_plural("cannot pass more than %d argument to a function",
"cannot pass more than %d arguments to a function", "cannot pass more than %d arguments to a function",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS))); FUNC_MAX_ARGS)));
/* Set up the primary fmgr lookup information */ /* Set up the primary fmgr lookup information */
...@@ -1314,9 +1315,9 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc) ...@@ -1314,9 +1315,9 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function return row and query-specified return row do not match"), errmsg("function return row and query-specified return row do not match"),
errdetail(ngettext("Returned row contains %d attribute, but query expects %d.", errdetail_plural("Returned row contains %d attribute, but query expects %d.",
"Returned row contains %d attributes, but query expects %d.", "Returned row contains %d attributes, but query expects %d.",
src_tupdesc->natts), src_tupdesc->natts,
src_tupdesc->natts, dst_tupdesc->natts))); src_tupdesc->natts, dst_tupdesc->natts)));
for (i = 0; i < dst_tupdesc->natts; i++) for (i = 0; i < dst_tupdesc->natts; i++)
......
# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.25 2009/05/14 21:41:50 alvherre Exp $ # $PostgreSQL: pgsql/src/backend/nls.mk,v 1.26 2009/06/04 18:33:07 tgl Exp $
CATALOG_NAME := postgres CATALOG_NAME := postgres
AVAIL_LANGUAGES := af cs de es fr hr hu it ja ko nb nl pt_BR ro ru sk sl sv tr zh_CN zh_TW pl AVAIL_LANGUAGES := af cs de es fr hr hu it ja ko nb nl pt_BR ro ru sk sl sv tr zh_CN zh_TW pl
GETTEXT_FILES := + gettext-files GETTEXT_FILES := + gettext-files
GETTEXT_TRIGGERS:= _ errmsg errdetail errdetail_log errhint errcontext write_stderr yyerror GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext write_stderr yyerror
gettext-files: distprep gettext-files: distprep
find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@ find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.214 2009/05/12 00:56:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.215 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -85,9 +85,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -85,9 +85,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
if (list_length(fargs) > FUNC_MAX_ARGS) if (list_length(fargs) > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("cannot pass more than %d argument to a function", errmsg_plural("cannot pass more than %d argument to a function",
"cannot pass more than %d arguments to a function", "cannot pass more than %d arguments to a function",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS), FUNC_MAX_ARGS),
parser_errposition(pstate, location))); parser_errposition(pstate, location)));
...@@ -257,9 +257,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -257,9 +257,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
if (nargsplusdefs >= FUNC_MAX_ARGS) if (nargsplusdefs >= FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("cannot pass more than %d argument to a function", errmsg_plural("cannot pass more than %d argument to a function",
"cannot pass more than %d arguments to a function", "cannot pass more than %d arguments to a function",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS), FUNC_MAX_ARGS),
parser_errposition(pstate, location))); parser_errposition(pstate, location)));
...@@ -538,9 +538,9 @@ func_select_candidate(int nargs, ...@@ -538,9 +538,9 @@ func_select_candidate(int nargs,
if (nargs > FUNC_MAX_ARGS) if (nargs > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("cannot pass more than %d argument to a function", errmsg_plural("cannot pass more than %d argument to a function",
"cannot pass more than %d arguments to a function", "cannot pass more than %d arguments to a function",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS))); FUNC_MAX_ARGS)));
/* /*
...@@ -1332,9 +1332,9 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) ...@@ -1332,9 +1332,9 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
if (argcount > FUNC_MAX_ARGS) if (argcount > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("functions cannot have more than %d argument", errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments", "functions cannot have more than %d arguments",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS))); FUNC_MAX_ARGS)));
args_item = list_head(argtypes); args_item = list_head(argtypes);
...@@ -1372,9 +1372,9 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ...@@ -1372,9 +1372,9 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
if (argcount > FUNC_MAX_ARGS) if (argcount > FUNC_MAX_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg(ngettext("functions cannot have more than %d argument", errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments", "functions cannot have more than %d arguments",
FUNC_MAX_ARGS), FUNC_MAX_ARGS,
FUNC_MAX_ARGS))); FUNC_MAX_ARGS)));
i = 0; i = 0;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.58 2009/05/15 15:56:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.59 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -459,9 +459,9 @@ BackgroundWriterMain(void) ...@@ -459,9 +459,9 @@ BackgroundWriterMain(void)
(flags & CHECKPOINT_CAUSE_XLOG) && (flags & CHECKPOINT_CAUSE_XLOG) &&
elapsed_secs < CheckPointWarning) elapsed_secs < CheckPointWarning)
ereport(LOG, ereport(LOG,
(errmsg(ngettext("checkpoints are occurring too frequently (%d second apart)", (errmsg_plural("checkpoints are occurring too frequently (%d second apart)",
"checkpoints are occurring too frequently (%d seconds apart)", "checkpoints are occurring too frequently (%d seconds apart)",
elapsed_secs), elapsed_secs,
elapsed_secs), elapsed_secs),
errhint("Consider increasing the configuration parameter \"checkpoint_segments\"."))); errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.213 2009/03/02 21:18:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.214 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -681,6 +681,47 @@ errcode_for_socket_access(void) ...@@ -681,6 +681,47 @@ errcode_for_socket_access(void)
pfree(buf.data); \ pfree(buf.data); \
} }
/*
* Same as above, except for pluralized error messages. The calling routine
* must be declared like "const char *fmt_singular, const char *fmt_plural,
* unsigned long n, ...". Translation is assumed always wanted.
*/
#define EVALUATE_MESSAGE_PLURAL(targetfield, appendval) \
{ \
const char *fmt; \
char *fmtbuf; \
StringInfoData buf; \
/* Internationalize the error format string */ \
if (!in_error_recursion_trouble()) \
fmt = dngettext(edata->domain, fmt_singular, fmt_plural, n); \
else \
fmt = (n == 1 ? fmt_singular : fmt_plural); \
/* Expand %m in format string */ \
fmtbuf = expand_fmt_string(fmt, edata); \
initStringInfo(&buf); \
if ((appendval) && edata->targetfield) \
appendStringInfo(&buf, "%s\n", edata->targetfield); \
/* Generate actual output --- have to use appendStringInfoVA */ \
for (;;) \
{ \
va_list args; \
bool success; \
va_start(args, n); \
success = appendStringInfoVA(&buf, fmtbuf, args); \
va_end(args); \
if (success) \
break; \
enlargeStringInfo(&buf, buf.maxlen); \
} \
/* Done with expanded fmt */ \
pfree(fmtbuf); \
/* Save the completed message into the stack item */ \
if (edata->targetfield) \
pfree(edata->targetfield); \
edata->targetfield = pstrdup(buf.data); \
pfree(buf.data); \
}
/* /*
* errmsg --- add a primary error message text to the current error * errmsg --- add a primary error message text to the current error
...@@ -738,6 +779,29 @@ errmsg_internal(const char *fmt,...) ...@@ -738,6 +779,29 @@ errmsg_internal(const char *fmt,...)
} }
/*
* errmsg_plural --- add a primary error message text to the current error,
* with support for pluralization of the message text
*/
int
errmsg_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(ErrorContext);
EVALUATE_MESSAGE_PLURAL(message, false);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0; /* return value does not matter */
}
/* /*
* errdetail --- add a detail error message text to the current error * errdetail --- add a detail error message text to the current error
*/ */
...@@ -780,6 +844,29 @@ errdetail_log(const char *fmt,...) ...@@ -780,6 +844,29 @@ errdetail_log(const char *fmt,...)
} }
/*
* errdetail_plural --- add a detail error message text to the current error,
* with support for pluralization of the message text
*/
int
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(ErrorContext);
EVALUATE_MESSAGE_PLURAL(detail, false);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0; /* return value does not matter */
}
/* /*
* errhint --- add a hint error message text to the current error * errhint --- add a hint error message text to the current error
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.99 2009/01/06 16:39:52 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.100 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -106,8 +106,8 @@ ...@@ -106,8 +106,8 @@
(errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain) ? \ (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain) ? \
(errfinish rest) : (void) 0) (errfinish rest) : (void) 0)
#define ereport(level, rest) \ #define ereport(elevel, rest) \
ereport_domain(level, TEXTDOMAIN, rest) ereport_domain(elevel, TEXTDOMAIN, rest)
#define TEXTDOMAIN NULL #define TEXTDOMAIN NULL
...@@ -132,6 +132,14 @@ errmsg_internal(const char *fmt,...) ...@@ -132,6 +132,14 @@ errmsg_internal(const char *fmt,...)
the supplied arguments. */ the supplied arguments. */
__attribute__((format(printf, 1, 2))); __attribute__((format(printf, 1, 2)));
extern int
errmsg_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(printf, 1, 4)))
__attribute__((format(printf, 2, 4)));
extern int extern int
errdetail(const char *fmt,...) errdetail(const char *fmt,...)
/* This extension allows gcc to check the format string for consistency with /* This extension allows gcc to check the format string for consistency with
...@@ -144,6 +152,14 @@ errdetail_log(const char *fmt,...) ...@@ -144,6 +152,14 @@ errdetail_log(const char *fmt,...)
the supplied arguments. */ the supplied arguments. */
__attribute__((format(printf, 1, 2))); __attribute__((format(printf, 1, 2)));
extern int
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(printf, 1, 4)))
__attribute__((format(printf, 2, 4)));
extern int extern int
errhint(const char *fmt,...) errhint(const char *fmt,...)
/* This extension allows gcc to check the format string for consistency with /* This extension allows gcc to check the format string for consistency with
......
# $PostgreSQL: pgsql/src/pl/plperl/nls.mk,v 1.5 2009/05/14 21:41:53 alvherre Exp $ # $PostgreSQL: pgsql/src/pl/plperl/nls.mk,v 1.6 2009/06/04 18:33:07 tgl Exp $
CATALOG_NAME := plperl CATALOG_NAME := plperl
AVAIL_LANGUAGES := de es fr pt_BR tr AVAIL_LANGUAGES := de es fr pt_BR tr
GETTEXT_FILES := plperl.c SPI.c GETTEXT_FILES := plperl.c SPI.c
GETTEXT_TRIGGERS:= errmsg errdetail errdetail_log errhint errcontext GETTEXT_TRIGGERS:= errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext
# $PostgreSQL: pgsql/src/pl/plpgsql/src/nls.mk,v 1.7 2009/05/14 21:41:53 alvherre Exp $ # $PostgreSQL: pgsql/src/pl/plpgsql/src/nls.mk,v 1.8 2009/06/04 18:33:07 tgl Exp $
CATALOG_NAME := plpgsql CATALOG_NAME := plpgsql
AVAIL_LANGUAGES := de es fr ja ro tr AVAIL_LANGUAGES := de es fr ja ro tr
GETTEXT_FILES := pl_comp.c pl_exec.c pl_gram.c pl_funcs.c pl_handler.c pl_scan.c GETTEXT_FILES := pl_comp.c pl_exec.c pl_gram.c pl_funcs.c pl_handler.c pl_scan.c
GETTEXT_TRIGGERS:= _ errmsg errdetail errdetail_log errhint errcontext validate_tupdesc_compat:3 yyerror plpgsql_yyerror GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext validate_tupdesc_compat:3 yyerror plpgsql_yyerror
.PHONY: gettext-files .PHONY: gettext-files
gettext-files: distprep gettext-files: distprep
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.241 2009/05/02 17:27:57 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.242 2009/06/04 18:33:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -4083,7 +4083,9 @@ exec_eval_expr(PLpgSQL_execstate *estate, ...@@ -4083,7 +4083,9 @@ exec_eval_expr(PLpgSQL_execstate *estate,
if (estate->eval_tuptable->tupdesc->natts != 1) if (estate->eval_tuptable->tupdesc->natts != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg(dngettext(TEXTDOMAIN, "query \"%s\" returned %d column", "query \"%s\" returned %d columns", estate->eval_tuptable->tupdesc->natts), errmsg_plural("query \"%s\" returned %d column",
"query \"%s\" returned %d columns",
estate->eval_tuptable->tupdesc->natts,
expr->query, expr->query,
estate->eval_tuptable->tupdesc->natts))); estate->eval_tuptable->tupdesc->natts)));
......
# $PostgreSQL: pgsql/src/pl/plpython/nls.mk,v 1.4 2009/05/14 21:41:53 alvherre Exp $ # $PostgreSQL: pgsql/src/pl/plpython/nls.mk,v 1.5 2009/06/04 18:33:08 tgl Exp $
CATALOG_NAME := plpython CATALOG_NAME := plpython
AVAIL_LANGUAGES := de es fr pt_BR tr AVAIL_LANGUAGES := de es fr pt_BR tr
GETTEXT_FILES := plpython.c GETTEXT_FILES := plpython.c
GETTEXT_TRIGGERS:= errmsg errdetail errdetail_log errhint errcontext PLy_elog:2 PLy_exception_set:2 GETTEXT_TRIGGERS:= errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext PLy_elog:2 PLy_exception_set:2 PLy_exception_set_plural:2,3
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.120 2009/04/03 16:59:42 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.121 2009/06/04 18:33:08 tgl Exp $
* *
********************************************************************* *********************************************************************
*/ */
...@@ -205,9 +205,13 @@ static void PLy_init_interp(void); ...@@ -205,9 +205,13 @@ static void PLy_init_interp(void);
static void PLy_init_plpy(void); static void PLy_init_plpy(void);
/* call PyErr_SetString with a vprint interface and translation support */ /* call PyErr_SetString with a vprint interface and translation support */
static void static void PLy_exception_set(PyObject *, const char *,...)
PLy_exception_set(PyObject *, const char *,...)
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));
/* same, with pluralized message */
static void PLy_exception_set_plural(PyObject *, const char *, const char *,
unsigned long n,...)
__attribute__((format(printf, 2, 5)))
__attribute__((format(printf, 3, 5)));
/* Get the innermost python procedure called from the backend */ /* Get the innermost python procedure called from the backend */
static char *PLy_procedure_name(PLyProcedure *); static char *PLy_procedure_name(PLyProcedure *);
...@@ -2525,8 +2529,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) ...@@ -2525,8 +2529,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan", PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
sv = PyString_AsString(so); sv = PyString_AsString(so);
PLy_exception_set(PLy_exc_spi_error, PLy_exception_set_plural(PLy_exc_spi_error,
dngettext(TEXTDOMAIN, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs), "Expected sequence of %d argument, got %d: %s",
"Expected sequence of %d arguments, got %d: %s",
plan->nargs,
plan->nargs, nargs, sv); plan->nargs, nargs, sv);
Py_DECREF(so); Py_DECREF(so);
...@@ -2941,8 +2947,8 @@ PLy_procedure_name(PLyProcedure * proc) ...@@ -2941,8 +2947,8 @@ PLy_procedure_name(PLyProcedure * proc)
return proc->proname; return proc->proname;
} }
/* output a python traceback/exception via the postgresql elog /*
* function. not pretty. * Call PyErr_SetString with a vprint interface and translation support
*/ */
static void static void
PLy_exception_set(PyObject * exc, const char *fmt,...) PLy_exception_set(PyObject * exc, const char *fmt,...)
...@@ -2957,6 +2963,26 @@ PLy_exception_set(PyObject * exc, const char *fmt,...) ...@@ -2957,6 +2963,26 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
PyErr_SetString(exc, buf); PyErr_SetString(exc, buf);
} }
/*
* The same, pluralized.
*/
static void
PLy_exception_set_plural(PyObject *exc,
const char *fmt_singular, const char *fmt_plural,
unsigned long n,...)
{
char buf[1024];
va_list ap;
va_start(ap, n);
vsnprintf(buf, sizeof(buf),
dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
ap);
va_end(ap);
PyErr_SetString(exc, buf);
}
/* Emit a PG error or notice, together with any available info about the /* Emit a PG error or notice, together with any available info about the
* current Python error. This should be used to propagate Python errors * current Python error. This should be used to propagate Python errors
* into PG. * into PG.
......
# $PostgreSQL: pgsql/src/pl/tcl/nls.mk,v 1.4 2009/05/14 21:41:53 alvherre Exp $ # $PostgreSQL: pgsql/src/pl/tcl/nls.mk,v 1.5 2009/06/04 18:33:08 tgl Exp $
CATALOG_NAME := pltcl CATALOG_NAME := pltcl
AVAIL_LANGUAGES := de es fr pt_BR tr AVAIL_LANGUAGES := de es fr pt_BR tr
GETTEXT_FILES := pltcl.c GETTEXT_FILES := pltcl.c
GETTEXT_TRIGGERS:= errmsg errdetail errdetail_log errhint errcontext GETTEXT_TRIGGERS:= errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment