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();