Author: turnstep
Date: Fri Dec 12 08:46:21 2008
New Revision: 12153

Modified:
   DBD-Pg/trunk/Changes
   DBD-Pg/trunk/dbdimp.c
   DBD-Pg/trunk/t/12placeholders.t

Log:
Fix placeholder parsing, per bug 41582


Modified: DBD-Pg/trunk/Changes
==============================================================================
--- DBD-Pg/trunk/Changes        (original)
+++ DBD-Pg/trunk/Changes        Fri Dec 12 08:46:21 2008
@@ -1,5 +1,9 @@
 ('GSM' is Greg Sabino Mullane, [email protected])
 
+2.11.7
+
+  - Fix placeholder parsing logic (CPAN bug #41582)
+
 2.11.6 Released November 30, 2008 (subversion r12126)
 
   - Only set UTF8 flag on array items after UTF8 test. (CPAN bug #41253)

Modified: DBD-Pg/trunk/dbdimp.c
==============================================================================
--- DBD-Pg/trunk/dbdimp.c       (original)
+++ DBD-Pg/trunk/dbdimp.c       Fri Dec 12 08:46:21 2008
@@ -1500,6 +1500,8 @@
 
        /* Builds the "segment" and "placeholder" structures for a statement 
handle */
 
+       D_imp_dbh_from_sth;
+
        STRLEN currpos; /* Where we currently are in the statement string */
 
        STRLEN sectionstart, sectionstop; /* Borders of current section */
@@ -1524,6 +1526,8 @@
 
        char * dollarstring = NULL; /* Dynamic string between $$ in dollar 
quoting */
 
+       char standard_conforming_strings = 1; /* Status 0=on 1=unknown -1=off */
+
        STRLEN xlen; /* Because "x" is too hard to search for */
 
        int xint;
@@ -1602,15 +1606,24 @@
                if ('\'' == ch || '"' == ch) {
                        quote = ch;
                        backslashes = 0;
+                       if ('\'' == ch && 1 == standard_conforming_strings) {
+                               const char * scs = 
PQparameterStatus(imp_dbh->conn,"standard_conforming_strings");
+                               standard_conforming_strings = (NULL==scs ? 1 : 
strncmp(scs,"on",2));
+                       }
+
                        /* Go until ending quote character (unescaped) or end 
of string */
                        while (quote && ++currpos && (ch = *statement++)) {
                                /* 1.1 : single quotes have no meaning in 
double-quoted sections and vice-versa */
                                /* 1.2 : backslashed quotes do not end the 
section */
-                               if (ch == quote && (0==(backslashes&1))) {
+                               /* 1.2.1 : backslashes have no meaning in 
double quoted sections */
+                               /* 1.2.2 : if standard_confirming_strings is 
set, ignore backslashes in single quotes */
+                               if (ch == quote && (quote == '"' || 
0==(backslashes&1))) {
                                        quote = 0;
                                }
-                               else if ('\\' == ch) 
-                                       backslashes++;
+                               else if ('\\' == ch) {
+                                       if (quote == '"' || 
standard_conforming_strings)
+                                               backslashes++;
+                               }
                                else
                                        backslashes = 0;
                        }
@@ -1627,14 +1640,13 @@
 
                /* 2: A comment block: */
                if (('-' == ch && '-' == *statement) ||
-                       ('/' == ch && '/' == *statement) ||
                        ('/' == ch && '*' == *statement)
                        ) {
                        quote = *statement;
                        /* Go until end of comment (may be newline) or end of 
the string */
                        while (quote && ++currpos && (ch = *statement++)) {
-                               /* 2.1: dashdash and slashslash only terminate 
at newline */
-                               if (('-' == quote || '/' == quote) && '\n' == 
ch) {
+                               /* 2.1: dashdash only terminates at newline */
+                               if ('-' == quote && '\n' == ch) {
                                        quote=0;
                                }
                                /* 2.2: slashstar ends with a matching 
starslash */
@@ -1655,8 +1667,16 @@
                } /* end comment section */
 
                /* 3: advanced dollar quoting - only if the backend is version 
8 or higher */
-               if (version >= 80000 && '$' == ch && (*statement == '$' || 
*statement >= 'A')) {
-                       /* Unlike PG, we allow a little more latitude in legal 
characters - anything >= 65 can be used */
+               if (version >= 80000 && '$' == ch && 
+                       (*statement == '$' 
+                        || *statement == '_'
+                        || (*statement >= 'A' && *statement <= 'Z') 
+                        || (*statement >= 'a' && *statement <= 'z'))) {
+                       /* "SQL identifiers must begin with a letter (a-z, but 
also letters with diacritical marks and non-Latin letters) 
+                or an underscore (_). Subsequent characters in an identifier 
or key word can be letters, underscores, 
+                digits (0-9), or dollar signs ($)
+                       */
+                       /* Postgres technically allows \200-\377 as well, but 
we don't */
                        sectionsize = 0; /* How far from the first dollar sign 
are we? */
                        found = 0; /* Have we found the end of the dollarquote? 
*/
 
@@ -1664,14 +1684,19 @@
                        while ((ch = *statement++)) {
 
                                sectionsize++;
-                               /* If we hit an invalid character, bail out */
-                               if (ch <= 32 || (ch >= '0' && ch <= '9')) {
-                                       break;
-                               }
                                if ('$' == ch) {
                                        found = DBDPG_TRUE;
                                        break;
                                }
+
+                               /* If we hit an invalid character, bail out */
+                               if (ch <= 47 
+                                       || (ch >= 58 && ch <= 64)
+                                       || (ch >= 91 && ch <= 94)
+                                       || ch == 96
+                                       || (ch >= 123)) {
+                                       break;
+                               }
                        } /* end first scan */
 
                        /* Not found? Move to the next letter after the 
dollarsign and move on */

Modified: DBD-Pg/trunk/t/12placeholders.t
==============================================================================
--- DBD-Pg/trunk/t/12placeholders.t     (original)
+++ DBD-Pg/trunk/t/12placeholders.t     Fri Dec 12 08:46:21 2008
@@ -15,7 +15,7 @@
 if (! defined $dbh) {
        plan skip_all => 'Connection to database failed, cannot continue 
testing';
 }
-plan tests => 28;
+plan tests => 45;
 
 my $t='Connect to database for placeholder testing';
 isnt ($dbh, undef, $t);
@@ -232,10 +232,74 @@
 };
 is ($@, q{}, $t);
 
-## Begin custom type testing
+$t='Prepare works with placeholders after double slashes';
+eval {
+       $dbh->do(q{CREATE OPERATOR // ( PROCEDURE=bit, LEFTARG=int, 
RIGHTARG=int )});
+       $sth = $dbh->prepare(q{SELECT ? // ?});
+       $sth->execute(1,2);
+       $sth->finish();
+};
+is ($@, q{}, $t);
+
+$t='Dollar quotes starting with a number are not treated as valid identifiers';
+eval {
+       $sth = $dbh->prepare(q{SELECT $123$  $123$});
+       $sth->execute(1);
+       $sth->finish();
+};
+like ($@, qr{Invalid placeholders}, $t);
+
+$t='Dollar quotes with invalid characters are not parsed as identifiers';
+for my $char (qw!+ / : @ [ `!) {
+       eval {
+               $sth = $dbh->prepare(qq{SELECT \$abc${char}\$ 123 
\$abc${char}\$});
+               $sth->execute();
+               $sth->finish();
+       };
+       like ($@, qr{syntax error}, $t);
+}
+
+$t='Dollar quotes with valid characters are parsed as identifiers';
+$dbh->rollback();
+for my $char (qw{0 9 A Z a z}) {
+       eval {
+               $sth = $dbh->prepare(qq{SELECT \$abc${char}\$ 123 
\$abc${char}\$});
+               $sth->execute();
+               $sth->finish();
+       };
+       is ($@, q{}, $t);
+}
+
+$t='Backslash quoting inside double quotes is parsed correctly';
+eval {
+       $sth = $dbh->prepare(q{SELECT * FROM "\" WHERE a=?});
+       $sth->execute(1);
+       $sth->finish();
+};
+like ($@, qr{relation ".*" does not exist}, $t);
+$dbh->rollback();
 
+$t='Backslash quoting inside single quotes is parsed correctly with 
standard_conforming_strings off';
+eval {
+       $dbh->do(q{SET standard_conforming_strings = 'off'});
+       $sth = $dbh->prepare(q{SELECT '\', ?});
+       $sth->execute();
+       $sth->finish();
+};
+like ($@, qr{unterminated quoted string}, $t);
+$dbh->rollback();
+
+$t='Backslash quoting inside single quotes is parsed correctly with 
standard_conforming_strings on';
+eval {
+       $dbh->do(q{SET standard_conforming_strings = 'on'});
+       $sth = $dbh->prepare(q{SELECT '\', ?::int});
+       $sth->execute(1);
+       $sth->finish();
+};
+is ($@, q{}, $t);
 
 
+## Begin custom type testing
 
 $dbh->rollback();
 

Reply via email to