aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/libpq.sgml23
-rw-r--r--src/interfaces/libpq/fe-exec.c68
2 files changed, 78 insertions, 13 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 37e1fffb5fe..3bbad0c4599 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 6d159a9a35a..810a753ed3c 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -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++)
{