]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Document the always-true-but-previously-undocumented fact that PQfnumber()
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Oct 2003 21:05:21 +0000 (21:05 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Oct 2003 21:05:21 +0000 (21:05 +0000)
will downcase the supplied field name unless it is double-quoted.  Also,
upgrade the routine's handling of double quotes to match the backend,
in particular support doubled double quotes within quoted identifiers.
Per pgsql-interfaces discussion a couple weeks ago.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/fe-exec.c

index 37e1fffb5fe85f3ccdb7418b8ea02e88279c4e4b..3bbad0c45999cfa2dc74ad9940001343a6eda9c2 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.138 2003/10/03 18:26:14 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.139 2003/10/04 21:05:20 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1553,8 +1553,7 @@ NULL is returned if the column number is out of range.
 <term><function>PQfnumber</function><indexterm><primary>PQfnumber</></></term>
 <listitem>
 <para>
-            Returns  the  column number
-          associated with the given column name.
+          Returns the column number associated with the given column name.
 <synopsis>
 int PQfnumber(const PGresult *res,
               const char *column_name);
@@ -1564,6 +1563,24 @@ int PQfnumber(const PGresult *res,
 <para>
         -1 is returned if the given name does not match any column.
 </para>
+
+<para>
+        The given name is treated like an identifier in an SQL command,
+       that is, it is downcased unless double-quoted.  For example,
+       given a query result generated from the SQL command
+<programlisting>
+select 1 as FOO, 2 as "BAR";
+</programlisting>
+        we would have the results:
+<programlisting>
+PQfname(res, 0)              <lineannotation>foo</lineannotation>
+PQfname(res, 1)              <lineannotation>BAR</lineannotation>
+PQfnumber(res, "FOO")        <lineannotation>0</lineannotation>
+PQfnumber(res, "foo")        <lineannotation>0</lineannotation>
+PQfnumber(res, "BAR")        <lineannotation>-1</lineannotation>
+PQfnumber(res, "\"BAR\"")    <lineannotation>1</lineannotation>
+</programlisting>
+</para>
 </listitem>
 </varlistentry>
 
index 6d159a9a35a5569a770525edddf4f312b59d7a86..810a753ed3c7565e1dd7194daf7b60a5922708be 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.150 2003/10/03 18:26:14 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.151 2003/10/04 21:05:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1803,32 +1803,80 @@ PQfname(const PGresult *res, int field_num)
 }
 
 /*
- * returns -1 on a bad field name
+ * PQfnumber: find column number given column name
+ *
+ * The column name is parsed as if it were in a SQL statement, including
+ * case-folding and double-quote processing.  But note a possible gotcha:
+ * downcasing in the frontend might follow different locale rules than
+ * downcasing in the backend...
+ *
+ * Returns -1 if no match.  In the present backend it is also possible
+ * to have multiple matches, in which case the first one is found.
  */
 int
 PQfnumber(const PGresult *res, const char *field_name)
 {
-       int                     i;
        char       *field_case;
+       bool            in_quotes;
+       char       *iptr;
+       char       *optr;
+       int                     i;
 
        if (!res)
                return -1;
 
+       /*
+        * Note: it is correct to reject a zero-length input string; the proper
+        * input to match a zero-length field name would be "".
+        */
        if (field_name == NULL ||
                field_name[0] == '\0' ||
                res->attDescs == NULL)
                return -1;
 
+       /*
+        * Note: this code will not reject partially quoted strings, eg
+        * foo"BAR"foo will become fooBARfoo when it probably ought to be
+        * an error condition.
+        */
        field_case = strdup(field_name);
-       if (*field_case == '"')
+       if (field_case == NULL)
+               return -1;                              /* grotty */
+
+       in_quotes = false;
+       optr = field_case;
+       for (iptr = field_case; *iptr; iptr++)
        {
-               strcpy(field_case, field_case + 1);
-               *(field_case + strlen(field_case) - 1) = '\0';
+               char    c = *iptr;
+
+               if (in_quotes)
+               {
+                       if (c == '"')
+                       {
+                               if (iptr[1] == '"')
+                               {
+                                       /* doubled quotes become a single quote */
+                                       *optr++ = '"';
+                                       iptr++;
+                               }
+                               else
+                                       in_quotes = false;
+                       }
+                       else
+                               *optr++ = c;
+               }
+               else if (c == '"')
+               {
+                       in_quotes = true;
+               }
+               else
+               {
+                       if (isupper((unsigned char) c))
+                               c = tolower((unsigned char) c);
+                       *optr++ = c;
+               }
        }
-       else
-               for (i = 0; field_case[i]; i++)
-                       if (isupper((unsigned char) field_case[i]))
-                               field_case[i] = tolower((unsigned char) field_case[i]);
+       *optr = '\0';
 
        for (i = 0; i < res->numAttributes; i++)
        {