]> granicus.if.org Git - postgresql/commitdiff
Improve the recently-added support for properly pluralized error messages
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Jun 2009 18:33:08 +0000 (18:33 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Jun 2009 18:33:08 +0000 (18:33 +0000)
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.

17 files changed:
doc/src/sgml/nls.sgml
doc/src/sgml/sources.sgml
src/backend/catalog/dependency.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_shdepend.c
src/backend/executor/execQual.c
src/backend/nls.mk
src/backend/parser/parse_func.c
src/backend/postmaster/bgwriter.c
src/backend/utils/error/elog.c
src/include/utils/elog.h
src/pl/plperl/nls.mk
src/pl/plpgsql/src/nls.mk
src/pl/plpgsql/src/pl_exec.c
src/pl/plpython/nls.mk
src/pl/plpython/plpython.c
src/pl/tcl/nls.mk

index 3a567df16c4266ad57078dc1e0efe7f69e5f49e2..063b575328b4fbc0f4d1c84a06d49b8a042bddae 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
  <chapterinfo>
@@ -46,7 +46,7 @@
     <filename>msgmerge</filename>, respectively, in a GNU-compatible
     implementation.  Later, we will try to arrange it so that if you
     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.
    </para>
 
@@ -152,7 +152,7 @@ msgstr "another translated"
     If there are already some <filename>.po</filename> files, then
     someone has already done some translation work.  The files are
     named <filename><replaceable>language</replaceable>.po</filename>,
-    where <replaceable>language</replaceable> is the 
+    where <replaceable>language</replaceable> is the
     <ulink url="http://lcweb.loc.gov/standards/iso639-2/englangn.html">
     ISO 639-1 two-letter language code (in lower case)</ulink>, e.g.,
     <filename>fr.po</filename> for French.  If there is really a need
@@ -224,7 +224,7 @@ gmake update-po
     that gives room for other people to pick up your work.  However,
     you are encouraged to give priority to removing fuzzy entries
     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.
    </para>
 
@@ -347,8 +347,8 @@ fprintf(stderr, "panic level %d\n", lvl);
 <programlisting>
 fprintf(stderr, gettext("panic level %d\n"), lvl);
 </programlisting>
-     (<symbol>gettext</symbol> is defined as a no-op if no NLS is
-     configured.)
+     (<symbol>gettext</symbol> is defined as a no-op if NLS support is
+     not configured.)
     </para>
 
     <para>
@@ -421,6 +421,9 @@ fprintf(stderr, gettext("panic level %d\n"), lvl);
          them here.  If the translatable string is not the first
          argument, the item needs to be of the form
          <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>
        </listitem>
       </varlistentry>
@@ -451,8 +454,8 @@ fprintf(stderr, gettext("panic level %d\n"), lvl);
 printf("Files were %s.\n", flag ? "copied" : "removed");
 </programlisting>
       The word order within the sentence might be different in other
-      languages.  Also, even if you remember to call gettext() on each
-      fragment, the fragments might not translate well separately.  It's
+      languages.  Also, even if you remember to call <function>gettext()</> on
+      each fragment, the fragments might not translate well separately.  It's
       better to duplicate a little code so that each message to be
       translated is a coherent whole.  Only numbers, file names, and
       such-like run-time variables should be inserted at run time into
@@ -475,13 +478,44 @@ else
     printf("copied %d files", n):
 </programlisting>
       then be disappointed.  Some languages have more than two forms,
-      with some peculiar rules.  We might have a solution for this in
-      the future, but for now the matter is best avoided altogether.
-      You could write:
+      with some peculiar rules.  It's often best to design the message
+      to avoid the issue altogether, for instance like this:
 <programlisting>
 printf("number of copied files: %d", n);
 </programlisting>
      </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>
index 426ab01fe1c3c565bcbe2b9635fcc0a73a1f03bc..0872eacee761537573bfa739ccc76d4553b49187 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
   <title>PostgreSQL Coding Conventions</title>
@@ -181,6 +181,19 @@ ereport(ERROR,
      not worth expending translation effort on.
     </para>
    </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>
     <para>
      <function>errdetail(const char *msg, ...)</function> supplies an optional
@@ -201,6 +214,14 @@ ereport(ERROR,
      sent to the client.
     </para>
    </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>
     <para>
      <function>errhint(const char *msg, ...)</function> supplies an optional
@@ -390,14 +411,14 @@ Hint:       the addendum
    <para>
     There are functions in the backend that will double-quote their own output
     at need (for example, <function>format_type_be</>()).  Do not put
-    additional quotes around the output of such functions. 
+    additional quotes around the output of such functions.
    </para>
 
    <para>
     Rationale: Objects can have names that create ambiguity when embedded in a
     message.  Be consistent about denoting where a plugged-in name starts and
     ends.  But don't clutter messages with unnecessary or duplicate quote
-    marks. 
+    marks.
    </para>
 
   </simplesect>
@@ -413,7 +434,7 @@ Hint:       the addendum
    <para>
     Primary error messages: Do not capitalize the first letter.  Do not end a
     message with a period.  Do not even think about ending a message with an
-    exclamation point. 
+    exclamation point.
    </para>
 
    <para>
@@ -430,7 +451,7 @@ Hint:       the addendum
     long enough to be more than one sentence, they should be split into
     primary and detail parts.)  However, detail and hint messages are longer
     and might need to include multiple sentences.  For consistency, they should
-    follow complete-sentence style even when there's only one sentence. 
+    follow complete-sentence style even when there's only one sentence.
    </para>
 
   </simplesect>
@@ -473,7 +494,7 @@ Hint:       the addendum
    <para>
     Use past tense if an attempt to do something failed, but could perhaps
     succeed next time (perhaps after fixing some problem).  Use present tense
-    if the failure is certainly permanent. 
+    if the failure is certainly permanent.
    </para>
 
    <para>
@@ -489,20 +510,20 @@ cannot open file "%s"
     message should give a reason, such as <quote>disk full</quote> or
     <quote>file doesn't exist</quote>.  The past tense is appropriate because
     next time the disk might not be full anymore or the file in question might
-    exist. 
+    exist.
    </para>
 
    <para>
     The second form indicates that the functionality of opening the named file
     does not exist at all in the program, or that it's conceptually
     impossible.  The present tense is appropriate because the condition will
-    persist indefinitely. 
+    persist indefinitely.
    </para>
 
    <para>
     Rationale: Granted, the average user will not be able to draw great
     conclusions merely from the tense of the message, but since the language
-    provides us with a grammar we should use it correctly. 
+    provides us with a grammar we should use it correctly.
    </para>
 
   </simplesect>
@@ -552,7 +573,7 @@ could not open file %s: %m
     to paste this into a single smooth sentence, so some sort of punctuation
     is needed.  Putting the embedded text in parentheses has also been
     suggested, but it's unnatural if the embedded text is likely to be the
-    most important part of the message, as is often the case. 
+    most important part of the message, as is often the case.
    </para>
 
   </simplesect>
@@ -579,7 +600,7 @@ BETTER: could not open file %s (I/O failure)
     Don't include the name of the reporting routine in the error text. We have
     other mechanisms for finding that out when needed, and for most users it's
     not helpful information.  If the error text doesn't make as much sense
-    without the function name, reword it. 
+    without the function name, reword it.
 <programlisting>
 BAD:    pg_atoi: error in "z": cannot parse "z"
 BETTER: invalid input syntax for integer: "z"
@@ -620,7 +641,7 @@ BETTER: could not open file %s: %m
    <para>
     Error messages like <quote>bad result</quote> are really hard to interpret
     intelligently.  It's better to write why the result is <quote>bad</quote>,
-    e.g., <quote>invalid format</quote>. 
+    e.g., <quote>invalid format</quote>.
    </para>
   </formalpara>
 
@@ -638,7 +659,7 @@ BETTER: could not open file %s: %m
     Try to avoid <quote>unknown</quote>.  Consider <quote>error: unknown
     response</quote>.  If you don't know what the response is, how do you know
     it's erroneous? <quote>Unrecognized</quote> is often a better choice.
-    Also, be sure to include the value being complained of. 
+    Also, be sure to include the value being complained of.
 <programlisting>
 BAD:    unknown node type
 BETTER: unrecognized node type: 42
@@ -654,7 +675,7 @@ BETTER: unrecognized node type: 42
     couldn't <quote>find</quote> the resource.  If, on the other hand, the
     expected location of the resource is known but the program cannot access
     it there then say that the resource doesn't <quote>exist</quote>.  Using
-    <quote>find</quote> in this case sounds weak and confuses the issue. 
+    <quote>find</quote> in this case sounds weak and confuses the issue.
    </para>
   </formalpara>
 
index 957cac49f3895d8ba26813155f5f9ecef493cfba..377ae8b712b451c507436e4884b854abc0fb8b10 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,10 +914,10 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
        {
                ereport(msglevel,
                                /* translator: %d always has a value larger than 1 */
-                               (errmsg(ngettext("drop cascades to %d other object",
-                                                                "drop cascades to %d other objects",
-                                                                numReportedClient + numNotReportedClient),
-                                               numReportedClient + numNotReportedClient),
+                               (errmsg_plural("drop cascades to %d other object",
+                                                          "drop cascades to %d other objects",
+                                                          numReportedClient + numNotReportedClient,
+                                                          numReportedClient + numNotReportedClient),
                                 errdetail("%s", clientdetail.data),
                                 errdetail_log("%s", logdetail.data)));
        }
index ad9539234f303b6360ab602457ea406eaf7f54db..ce42910e785ef8b1253f031592bc7393ce2c5977 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,10 +112,10 @@ ProcedureCreate(const char *procedureName,
        if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("functions cannot have more than %d argument",
-                                                                "functions cannot have more than %d arguments",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("functions cannot have more than %d argument",
+                                                          "functions cannot have more than %d arguments",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
        /* note: the above is correct, we do NOT count output arguments */
 
        if (allParameterTypes != PointerGetDatum(NULL))
index a4de276b8d608abf38eb05e6fd0d132735b10f14..19cfe08f4f818db4e8fb295964a51754afc5f6d6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
 
                case REMOTE_OBJECT:
                        /* 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;
 
                default:
index d9cbb1d763abb17e53a13f35fe13d4e208af0acf..c7bfe7c76ca6b926f2dcbe1eee05b3a0b8b6ecc0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                                 errmsg("table row type and query-specified row type do not match"),
-                                                errdetail(ngettext("Table row contains %d attribute, but query expects %d.",
-                                                                       "Table row contains %d attributes, but query expects %d.",
-                                                                       slot_tupdesc->natts),
-                                                                  slot_tupdesc->natts, var_tupdesc->natts)));
+                                                errdetail_plural("Table row contains %d attribute, but query expects %d.",
+                                                                                 "Table row contains %d attributes, but query expects %d.",
+                                                                                 slot_tupdesc->natts,
+                                                                                 slot_tupdesc->natts,
+                                                                                 var_tupdesc->natts)));
                        else if (var_tupdesc->natts < slot_tupdesc->natts)
                                needslow = true;
 
@@ -1043,10 +1044,10 @@ init_fcache(Oid foid, FuncExprState *fcache,
        if (list_length(fcache->args) > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("cannot pass more than %d argument to a function",
-                                                                "cannot pass more than %d arguments to a function",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("cannot pass more than %d argument to a function",
+                                                          "cannot pass more than %d arguments to a function",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
 
        /* Set up the primary fmgr lookup information */
        fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
@@ -1314,10 +1315,10 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                 errmsg("function return row and query-specified return row do not match"),
-                                errdetail(ngettext("Returned row contains %d attribute, but query expects %d.",
-                                                       "Returned row contains %d attributes, but query expects %d.",
-                                                       src_tupdesc->natts),
-                                                  src_tupdesc->natts, dst_tupdesc->natts)));
+                                errdetail_plural("Returned row contains %d attribute, but query expects %d.",
+                                                                 "Returned row contains %d attributes, but query expects %d.",
+                                                                 src_tupdesc->natts,
+                                                                 src_tupdesc->natts, dst_tupdesc->natts)));
 
        for (i = 0; i < dst_tupdesc->natts; i++)
        {
index 8e66d2d19991f1da60721d9c60b2e09043b1181c..3a79ca50488e05e9b083ade87dfd05302176598d 100644 (file)
@@ -1,8 +1,8 @@
-# $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
 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_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
        find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@
index 38008b9cea90e4086c1115ad58147e9535717d4e..260f74d5957802e59b156d87741b189b37ca1e67 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,10 +85,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        if (list_length(fargs) > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("cannot pass more than %d argument to a function",
-                                                                "cannot pass more than %d arguments to a function",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS),
+                                errmsg_plural("cannot pass more than %d argument to a function",
+                                                          "cannot pass more than %d arguments to a function",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS),
                                 parser_errposition(pstate, location)));
 
        /*
@@ -257,10 +257,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                if (nargsplusdefs >= FUNC_MAX_ARGS)
                        ereport(ERROR,
                                        (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                        errmsg(ngettext("cannot pass more than %d argument to a function",
-                                                                        "cannot pass more than %d arguments to a function",
-                                                                        FUNC_MAX_ARGS),
-                                                       FUNC_MAX_ARGS),
+                                        errmsg_plural("cannot pass more than %d argument to a function",
+                                                                  "cannot pass more than %d arguments to a function",
+                                                                  FUNC_MAX_ARGS,
+                                                                  FUNC_MAX_ARGS),
                                         parser_errposition(pstate, location)));
 
                actual_arg_types[nargsplusdefs++] = exprType(expr);
@@ -538,10 +538,10 @@ func_select_candidate(int nargs,
        if (nargs > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("cannot pass more than %d argument to a function",
-                                                                "cannot pass more than %d arguments to a function",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("cannot pass more than %d argument to a function",
+                                                          "cannot pass more than %d arguments to a function",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
 
        /*
         * If any input types are domains, reduce them to their base types. This
@@ -1332,10 +1332,10 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
        if (argcount > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("functions cannot have more than %d argument",
-                                                                "functions cannot have more than %d arguments",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("functions cannot have more than %d argument",
+                                                          "functions cannot have more than %d arguments",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
 
        args_item = list_head(argtypes);
        for (i = 0; i < argcount; i++)
@@ -1372,10 +1372,10 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
        if (argcount > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg(ngettext("functions cannot have more than %d argument",
-                                                                "functions cannot have more than %d arguments",
-                                                                FUNC_MAX_ARGS),
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("functions cannot have more than %d argument",
+                                                          "functions cannot have more than %d arguments",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
 
        i = 0;
        foreach(lc, argtypes)
index 7a23c0130da4c6baf4aaf1ef664ae4633dbfd6d8..b2a90528b65ddd9240bcf77477e7ef64b4aa22df 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * 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,10 +459,10 @@ BackgroundWriterMain(void)
                                (flags & CHECKPOINT_CAUSE_XLOG) &&
                                elapsed_secs < CheckPointWarning)
                                ereport(LOG,
-                                               (errmsg(ngettext("checkpoints are occurring too frequently (%d second apart)",
-                                                                                "checkpoints are occurring too frequently (%d seconds apart)",
-                                                                                elapsed_secs),
-                                                               elapsed_secs),
+                                               (errmsg_plural("checkpoints are occurring too frequently (%d second apart)",
+                                                                          "checkpoints are occurring too frequently (%d seconds apart)",
+                                                                          elapsed_secs,
+                                                                          elapsed_secs),
                                                 errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
 
                        /*
index 0439c4c1d1821b35aed6c6a154671fe8c1d8e5a7..d93aaeb54d6e4f4eef36158cfe7474d2d5d3b70a 100644 (file)
@@ -42,7 +42,7 @@
  *
  *
  * 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)
                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
@@ -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
  */
@@ -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
  */
index 6c9069656960aaa16b27134b72b8c227fe38e8ae..81ddb9fc3779fc142de58e1d135fde132d1dadeb 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
        (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain) ? \
         (errfinish rest) : (void) 0)
 
-#define ereport(level, rest)   \
-       ereport_domain(level, TEXTDOMAIN, rest)
+#define ereport(elevel, rest)  \
+       ereport_domain(elevel, TEXTDOMAIN, rest)
 
 #define TEXTDOMAIN NULL
 
@@ -132,6 +132,14 @@ errmsg_internal(const char *fmt,...)
    the supplied arguments. */
 __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
 errdetail(const char *fmt,...)
 /* This extension allows gcc to check the format string for consistency with
@@ -144,6 +152,14 @@ errdetail_log(const char *fmt,...)
    the supplied arguments. */
 __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
 errhint(const char *fmt,...)
 /* This extension allows gcc to check the format string for consistency with
index 8eaf6dd773fe39a85bacbea3cef2a2e07524f016..030886c0bb9d4efd2aa6bc43feaad1b5dfe33b00 100644 (file)
@@ -1,5 +1,5 @@
-# $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
 AVAIL_LANGUAGES        := de es fr pt_BR tr
 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
index 7f17b88eece43df16d6aabe95681bd37c23099d3..ffebf4b21e91f31cb13874f093ba4c99ebd5df9c 100644 (file)
@@ -1,8 +1,8 @@
-# $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
 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_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
 gettext-files: distprep
index e436e053b1b75d4950b3d6d67ca814436a377734..b5f000691886414b9673f9ab3f3f32f4537ea64f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,9 +4083,11 @@ exec_eval_expr(PLpgSQL_execstate *estate,
        if (estate->eval_tuptable->tupdesc->natts != 1)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg(dngettext(TEXTDOMAIN, "query \"%s\" returned %d column", "query \"%s\" returned %d columns", estate->eval_tuptable->tupdesc->natts),
-                                               expr->query,
-                                               estate->eval_tuptable->tupdesc->natts)));
+                                errmsg_plural("query \"%s\" returned %d column",
+                                                          "query \"%s\" returned %d columns",
+                                                          estate->eval_tuptable->tupdesc->natts,
+                                                          expr->query,
+                                                          estate->eval_tuptable->tupdesc->natts)));
 
        /*
         * Return the result and its type
index 5721dbf445e7508609c97791433d3882d6389175..6e54a35312097b109b55620a4ae0a1589cfa7cf7 100644 (file)
@@ -1,5 +1,5 @@
-# $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
 AVAIL_LANGUAGES        := de es fr pt_BR tr
 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
index 61e4772e8bcb0d6b2e94bb9c46b740149a78a7ce..345f3f65236619d6cd431fde70a9aaef4eabe709 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * 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);
 static void PLy_init_plpy(void);
 
 /* call PyErr_SetString with a vprint interface and translation support */
-static void
-PLy_exception_set(PyObject *, const char *,...)
+static void PLy_exception_set(PyObject *, const char *,...)
 __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 */
 static char *PLy_procedure_name(PLyProcedure *);
@@ -2525,9 +2529,11 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
                        PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
                                         PLy_procedure_name(PLy_curr_procedure));
                sv = PyString_AsString(so);
-               PLy_exception_set(PLy_exc_spi_error,
-                                                 dngettext(TEXTDOMAIN, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs),
-                                                 plan->nargs, nargs, sv);
+               PLy_exception_set_plural(PLy_exc_spi_error,
+                                                                "Expected sequence of %d argument, got %d: %s",
+                                                                "Expected sequence of %d arguments, got %d: %s",
+                                                                plan->nargs,
+                                                                plan->nargs, nargs, sv);
                Py_DECREF(so);
 
                return NULL;
@@ -2941,8 +2947,8 @@ PLy_procedure_name(PLyProcedure * proc)
        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
 PLy_exception_set(PyObject * exc, const char *fmt,...)
@@ -2957,6 +2963,26 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
        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
  * current Python error.  This should be used to propagate Python errors
  * into PG.
index 9188911f7629b639c262df4dd2fee0af093ff06c..f331e658a988e864f457c593a7bd79e7e2dcbacb 100644 (file)
@@ -1,5 +1,5 @@
-# $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
 AVAIL_LANGUAGES        := de es fr pt_BR tr
 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