Changeset: b0ac51c36919 for monetdb-perl
URL: https://dev.monetdb.org/hg/monetdb-perl/rev/b0ac51c36919
Added Files:
        DBD/t/12bindplaceholder.t
Modified Files:
        DBD/monetdb.pm
        DBD/t/12bind.t
Branch: default
Log Message:

Properly handle bound parameters with question marks in them


diffs (266 lines):

diff --git a/DBD/monetdb.pm b/DBD/monetdb.pm
--- a/DBD/monetdb.pm
+++ b/DBD/monetdb.pm
@@ -118,16 +118,50 @@ sub quote {
 }
 
 
-sub _count_param {
-    my $statement = shift;
-    my $num = 0;
+sub _split_query_params {
+       my $query = shift;
+
+       # print STDERR "QUERY «$query»\n";
 
-    $statement =~ s{
-        ' (?: \\. | [^\\']++ )*+ ' |
-        " (?: \\. | [^\\"]++ )*+ '
-    }{}gx;
+       my @parts;
+       my $expect_placeholder = 0;
+       while ($query =~ m {(
+               [?]
+               | (?:
+                       -- [^\n]*
+                       | [^?'eErR"]
+                       | [eE]? ' (?: \\. | [^\\']++ )*+ '
+                       |  [rR] ' (?: '' | [^']++ )*+ '
+                       |       " (?: "" | [^"]++ )*+ "
+                       | [eE]? ' (?: \\. | [^\\']++ )*+ $
+                       |  [rR] ' (?: '' | [^']++ )*+ $
+                       |       " (?: "" | [^"]++ )*+ $
+                       | \w+
+               )++
+       )}gsx) {
+               # print STDERR " $expect_placeholder TOK  «$1»\n";
+               if ($1 eq '?') {
+                       push @parts, '' unless $expect_placeholder;
+                       $expect_placeholder = 0;
+               } else {
+                       die "internal error: expecting placeholder" if 
$expect_placeholder;
+                       push @parts, $1;
+                       $expect_placeholder = 1;
+               }
+       }
 
-    return $statement =~ tr/?/?/;
+       my $tail;
+       if ($expect_placeholder) {
+               $tail = pop @parts;
+       } else {
+               $tail = '';
+       }
+
+       # for (@parts) {
+       #       print STDERR " PART «$_»\n";
+       # }
+       # print STDERR " TAIL «$tail»\n";
+       return \@parts, $tail;
 }
 
 
@@ -140,12 +174,16 @@ sub prepare {
 
     my ($outer, $sth) = DBI::_new_sth($dbh, { Statement => $statement });
 
-    $sth->STORE('NUM_OF_PARAMS', _count_param($statement));
+       my ($parts, $tail) = _split_query_params($statement);
+
+    $sth->STORE('NUM_OF_PARAMS', scalar(@{$parts}));
 
     $sth->{monetdb_hdl} = $hdl;
     $sth->{monetdb_params} = [];
     $sth->{monetdb_types} = [];
     $sth->{monetdb_rows} = -1;
+       $sth->{monetdb_parts} = $parts;
+       $sth->{monetdb_tail} = $tail;
 
     return $outer;
 }
@@ -541,7 +579,6 @@ sub bind_param {
 
 sub execute {
     my($sth, @bind_values) = @_;
-    my $statement = $sth->{Statement};
     my $dbh = $sth->{Database};
 
     $sth->STORE('Active', 0 );  # we don't need to call $sth->finish because
@@ -549,15 +586,30 @@ sub execute {
 
     $sth->bind_param($_, $bind_values[$_-1]) or return for 1 .. @bind_values;
 
+       my $parts = $sth->{monetdb_parts};
+       my $nparts = @$parts;
+       my $tail = $sth->{monetdb_tail};
+
     my $params = $sth->{monetdb_params};
-    my $num_of_params = $sth->FETCH('NUM_OF_PARAMS');
-    return $sth->set_err(-1, @$params ." values bound when $num_of_params 
expected")
-        unless @$params == $num_of_params;
+       my $nparams = @$params;
+       if ($nparams != $nparts) {
+               return $sth->set_err(-1, "$nparams value bound while $nparts 
expected");
+       }
 
-    for ( 1 .. $num_of_params ) {
-        my $quoted_param = $dbh->quote($params->[$_-1], 
$sth->{monetdb_types}[$_-1]);
-        $statement =~ s/\?/$quoted_param/;  # TODO: '?' inside quotes/comments
-    }
+       my $statement;
+       if ($nparts > 0) {
+               my @stmt = ();
+               for (1 .. $nparts) {
+               my $quoted_param = $dbh->quote($params->[$_-1], 
$sth->{monetdb_types}[$_-1]);
+                       push @stmt, $parts->[$_ - 1], $quoted_param;
+               }
+               push @stmt, $tail;
+               $statement = join('', @stmt);
+       } else {
+               $statement = $tail;
+       }
+
+       # print STDERR "# $statement\n";
     $sth->trace_msg("    -- Statement: $statement\n", 5);
 
     my $hdl = $sth->{monetdb_hdl};
diff --git a/DBD/t/12bind.t b/DBD/t/12bind.t
--- a/DBD/t/12bind.t
+++ b/DBD/t/12bind.t
@@ -16,7 +16,7 @@ use DBD_TEST();
 use Test::More;
 
 if (defined $ENV{DBI_DSN}) {
-  plan tests => 6;
+  plan tests => 7;
 } else {
   plan skip_all => 'Cannot test without DB info';
 }
@@ -38,6 +38,8 @@ ok( tab_insert( $dbh, $data ),'Insert te
 
 ok( tab_select( $dbh ),'Select test data');
 
+ok( tab_bind_question_marks( $dbh ), 'Bind data with question marks');
+
 ok( DBD_TEST::tab_delete( $dbh ),'Drop test table');
 
 ok( $dbh->disconnect,'Disconnect');
@@ -64,6 +66,43 @@ sub tab_select
   return 1;
 }
 
+sub tab_bind_question_marks
+{
+       my $dbh = shift;
+
+       my $sth = $dbh->prepare("SELECT ? AS x, ? AS y");
+       unless ( $sth ) {
+               print $DBI::errstr;
+               return 0;
+       }
+
+       # without question marks
+       $sth->execute("foo", "bar");
+       my($x, $y) = $sth->fetchrow_array;
+       if ($x ne "foo") {
+               print "# when binding foo and bar, expected foo, got '$x'";
+               return undef;
+       }
+       if ($y ne "bar") {
+               print "# when binding foo and bar, expected bar, got '$y'";
+               return undef;
+       }
+
+       # with question marks
+       $sth->execute("foo?", "bar?");
+       ($x, $y) = $sth->fetchrow_array;
+       if ($x ne "foo?") {
+               print "# when binding foo? and bar?, expected foo?, got '$x'";
+               return undef;
+       }
+       if ($y ne "bar?") {
+               print "# when binding foo? and bar?, expected bar?, got '$y'";
+               return undef;
+       }
+
+       return 1;
+}
+
 sub tab_insert
 {
   my $dbh  = shift;
diff --git a/DBD/t/12bindplaceholder.t b/DBD/t/12bindplaceholder.t
new file mode 100644
--- /dev/null
+++ b/DBD/t/12bindplaceholder.t
@@ -0,0 +1,70 @@
+#!perl -I./t
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0.  If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
+
+$| = 1;
+
+use strict;
+use warnings;
+use DBI();
+use DBD_TEST();
+
+use Test::More;
+
+if (defined $ENV{DBI_DSN}) {
+  plan tests => 9;
+} else {
+  plan skip_all => 'Cannot test without DB info';
+}
+
+my $dbh = DBI->connect or die "Connect failed: $DBI::errstr\n";
+ok ( defined $dbh, 'Connection');
+
+my $do_execute = 0;
+
+sub process
+{
+       my $query = shift;
+       my $sth = $dbh->prepare($query);
+
+       my $expected = $sth->{NUM_OF_PARAMS};
+       my @params = @_;
+       my $nparams = @params;
+       if ($nparams != $expected) {
+               print "# expected $expected parameters, got $nparams\n";
+               return undef;
+       }
+
+       return 1 unless $do_execute;
+
+       print("# EXECUTE $query");
+       print("# PARMS ", join('|', @params)) if @params;
+       $sth->execute(@params);
+       my @row;
+       while (@row = $sth->fetchrow_array()) {
+               print("# ROW ", join(' | ', @row), '\n');
+       }
+
+       return 1;
+}
+
+
+ok( process("SELECT 42"), 'no placeholders');
+
+ok( process("SELECT ?", 42), 'one placeholder');
+
+ok( process("-- '?\nSELECT 42"), 'not a real placeholder, is in a comment');
+
+ok( process("-- '?\nSELECT ?", 42), 'commented placeholder and real 
placeholder');
+
+ok( process("SELECT 42 -- ?"), 'commented placeholder at end');
+
+ok( process("SELECT 42 --?\nWHERE TRUE"), 'commented placeholder, then more 
query');
+
+ok( process("SELECT R'\\' ?", 'foo'), 'sdf');
+
+ok( process("SELECT '\\' ?'"), 'not fooled by the backslash escape');
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to