Skip Menu |

This queue is for tickets about the SQL-Abstract CPAN distribution.

Report information
The Basics
Id: 55189
Status: rejected
Priority: 0/
Queue: SQL-Abstract

People
Owner: Nobody in particular
Requestors: DMLOND [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 1.61
Fixed in: (no value)



Subject: incomplete use of $self->_bindtype when passing an ARRAYREF or ARRAYREFREF value to a column and bindtypes => 'columns' is in use
Hi, I was trying to make the following code work: use SQL::Abstract; use Data::Dumper; my $sql = SQL::Abstract->new( bindtype => 'columns' ); my %data = ( name => 'Jimbo Bobson', phone => '123-456-7890', address => '42 Sister Lane', city => 'St. Louis', state => 'Louisiana', blaz => \["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] ); my($stmt, @bind) = $sql->insert('people', \%data); print "STMT ${stmt}\n"; print "BIND ".Dumper(@bind)."\n"; exit; using SQL::Abstract version 1.61 on a Linux 2.6.18-164.11.1.el5 #1 SMP Wed Jan 20 07:32:21 EST 2010 x86_64 x86_64 x86_64 GNU/Linux system with perl v5.8.8 built for x86_64-linux-thread-multi. I received the error :bindtype 'columns' selected, you need to pass: [column_name => bind_value] at /usr/lib/perl5/site_perl/5.8.8/SQL/Abstract.pm line 1108. I manually edited Abstract.pm and determined that it needed to use the $self->_bindtype function on each element in the @bind when the @bind is passed in as an ARRAYREF or ARRAYREFREF. It now works correctly against our Oracle database. I am not sure whether this works for all databases, and all cases of passing in ARRAYREF(REF) values. Here is the diff Abstract.pm.new Abstract.pm.orig 195d194 < @bind = map { $self->_bindtype($column, $_) } @bind; 204,205d202 < @bind = map { $self->_bindtype($column, $_) } @bind; < 274d270 < @bind = map { $self->_bindtype($k, $_) } @bind; 1223d1218 < @bind = map { $self->_bindtype($k, $_) } @bind; 1230d1224 < @bind = map { $self->_bindtype($k, $_) } @bind; I am happy to release this code for whatever use you deem appropriate. Cheers, Darin London
On Wed Mar 03 12:55:41 2010, DMLOND wrote: Show quoted text
> Hi, > I was trying to make the following code work: > use SQL::Abstract; > use Data::Dumper; > > my $sql = SQL::Abstract->new( bindtype => 'columns' ); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => \["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] > ); > > my($stmt, @bind) = $sql->insert('people', \%data); > print "STMT ${stmt}\n"; > print "BIND ".Dumper(@bind)."\n"; > exit; > > using SQL::Abstract version 1.61 on a > Linux 2.6.18-164.11.1.el5 #1 SMP Wed Jan 20 07:32:21 EST 2010 x86_64 > x86_64 x86_64 GNU/Linux system with perl v5.8.8 built for > x86_64-linux-thread-multi. > > I received the error :bindtype 'columns' selected, you need to pass: > [column_name => bind_value] at > /usr/lib/perl5/site_perl/5.8.8/SQL/Abstract.pm line 1108.
I am not entirely sure why you believe this is not correct behavior. You (or dbix-class or something else) requests a specific bindtype. It is only logical that the arrayrefref is expected to come with a matching bindtype. Please elaborate on your reasoning.
All I know is if you copy the provided code, which is pulled directly from the perldoc for SQL::Abstract 1.61 (so it should work IMHO), into a script, and run it, you get an error. It isnt DBIx class or DBI that is producing the error, as I am not even using these modules in the provided code example, it is pure SQL::Abstract. Here is the actual code in Abstract.pm where I see an inconsistency: ARRAYREF => sub { if ($self->{array_datatypes}) { # if array datatype are activated push @values, '?'; push @all_bind, $self->_bindtype($column, $v); } else { # else literal SQL with bind my ($sql, @bind) = @$v; $self->_assert_bindval_matches_bindtype(@bind); push @values, $sql; push @all_bind, @bind; } }, ARRAYREFREF => sub { # literal SQL with bind my ($sql, @bind) = @${$v}; $self->_assert_bindval_matches_bindtype(@bind); push @values, $sql; push @all_bind, @bind; }, # THINK : anything useful to do with a HASHREF ? HASHREF => sub { # (nothing, but old SQLA passed it through) #TODO in SQLA >= 2.0 it will die instead belch "HASH ref as bind value in insert is not supported"; push @values, '?'; push @all_bind, $self->_bindtype($column, $v); }, SCALARREF => sub { # literal SQL without bind push @values, $$v; }, SCALAR_or_UNDEF => sub { push @values, '?'; push @all_bind, $self->_bindtype($column, $v); }, }); What I see is that, for simple scalar key => value pairs, your dispatch table code (SCALAR_or_UNDEF) calls $self->_bindtype. If you look at the behavior of the provided example code, and follow it through the perl debugger, you can see that the ARRAYREFREF dispatch table code (and ARRAYREF when array_datatypes is false) is attempting to produce an inconsistent @bind array, where all of the key => value pairs for simple scalar values are producing the correct [ 'field' => value ] entries in @bind (as it is supposed to do based on the perldoc for bindtypes => 'column'), but the one with the ARRAYREFREF value is attempting to produce a @bind entry which is simply 'value'. If I add the @bind = map { $self->_bindtype($column, $_) } @bind; process before calling $self->_assert_bindval_matches_bindtype(@bind); not only does this assertion pass, but the returned value for @bindtype is in the correct [ 'field' => 'value' ] format required to iterate over @bind to call the bind_param on the prepared statement handle later on.
On Wed Mar 03 20:54:52 2010, DMLOND wrote: Show quoted text
> All I know is if you copy the provided code, which is pulled directly > from the perldoc for SQL::Abstract 1.61 (so it should work IMHO), into a > script, and run it, you get an error. It isnt DBIx class or DBI that is > producing the error, as I am not even using these modules in the > provided code example, it is pure SQL::Abstract.
Right I didn't see you select the column bindtype on the very top. Even better: Show quoted text
> > What I see is that, for simple scalar key => value pairs, your dispatch > table code (SCALAR_or_UNDEF) calls $self->_bindtype. If you look at > the behavior of the provided example code, and follow it through the > perl debugger, you can see that the ARRAYREFREF dispatch table code (and > ARRAYREF when array_datatypes is false) is attempting to produce an > inconsistent @bind array, where all of the key => value pairs for simple > scalar values are producing the correct [ 'field' => value ] entries in > @bind (as it is supposed to do based on the perldoc for bindtypes => > 'column'), but the one with the ARRAYREFREF value is attempting to > produce a @bind entry which is simply 'value'.
That's the source of your confusion. \ in SQLA means "take this verbatim, do not do *anything* with it, just pass along". This applies equally to the SQL and to the bindvalues supplied in case of \[]. To have the supplied bind doctored in any way would be a violation of this assumption. Since you (the user) know the correct bindtype, it is your responsibility to provide the correct bindtype in \[].
I think the source of the confusion is that the perldoc does not provide enough information to help users know how to use both 'bindtype => columns' and apply an oracle (or postgresql) function to a value as it is being inserted into the database. In order to use SQL::Abstract as it is currently distributed to do this, you can do the following (note, no 'bindtype => columns'): use SQL::Abstract; use Data::Dumper; my $sql = SQL::Abstract->new(); my %data = ( name => 'Jimbo Bobson', phone => '123-456-7890', address => '42 Sister Lane', city => 'St. Louis', state => 'Louisiana', blaz => ["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] ); my($stmt, @bind) = $sql->insert('people', \%data); print "STMT ${stmt}\n"; print "BIND ".Dumper(@bind)."\n"; exit; But in order to do the same with 'bindtype => columns' you have to manually pass the [ 'column' => 'value' ] as the bind variable to be associated with the function, e.g. : use SQL::Abstract; use Data::Dumper; #my $sql = SQL::Abstract->new( bindtype => 'columns' ); my %data = ( name => 'Jimbo Bobson', phone => '123-456-7890', address => '42 Sister Lane', city => 'St. Louis', state => 'Louisiana', blaz => ["to_date(?, 'MM/DD/YYYY')", ['blaz' => "03/03/2003"] ); my($stmt, @bind) = $sql->insert('people', \%data); print "STMT ${stmt}\n"; print "BIND ".Dumper(@bind)."\n"; exit; Which, to me, is a bit unintuitive, and certainly doesnt behave the same way as inserting a simple scalar value without applying a function to it does when used in the context of bindtype => columns. It is also not clear from the perldocs that this is required, as it states to pass ['function(?)', $value ] as the value for the field in the hash passed into insert.
On Thu Mar 04 08:55:35 2010, DMLOND wrote: Show quoted text
> I think the source of the confusion is that the perldoc does not provide > enough information to help users know how to use both 'bindtype => > columns' and apply an oracle (or postgresql) function to a value as it > is being inserted into the database. In order to use SQL::Abstract as > it is currently distributed to do this, you can do the following (note, > no 'bindtype => columns'): > > use SQL::Abstract; > use Data::Dumper; > > my $sql = SQL::Abstract->new(); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => ["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] > ); > > my($stmt, @bind) = $sql->insert('people', \%data); > print "STMT ${stmt}\n"; > print "BIND ".Dumper(@bind)."\n"; > exit; > > But in order to do the same with 'bindtype => columns' you have to > manually pass the [ 'column' => 'value' ] as the bind variable to be > associated with the function, e.g. : > use SQL::Abstract; > use Data::Dumper; > > #my $sql = SQL::Abstract->new( bindtype => 'columns' ); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => ["to_date(?, 'MM/DD/YYYY')", ['blaz' => > "03/03/2003"] > > ); > > my($stmt, @bind) = $sql->insert('people', \%data); > print "STMT ${stmt}\n"; > print "BIND ".Dumper(@bind)."\n"; > exit; > > Which, to me, is a bit unintuitive, and certainly doesnt behave the same > way as inserting a simple scalar value without applying a function to it > does when used in the context of bindtype => columns. It is also not > clear from the perldocs that this is required, as it states to pass > ['function(?)', $value ] as the value for the field in the hash passed > into insert.
Right. The crucial difference between [] and \[] is the \, which implies "AS-IS". Patches to improve the docs to make this more obvious would be great.
but [] doesnt work either when 'bindtypes => columns' is in use. It throws the exact same exception unless you manually pass in the ['field' => $value] instead of ['field' => $value]. I can see what you are trying to say, and maybe the docs and the code could be changed to better reflect this difference, e.g. if you pass in [] it should apply the $self->_bindtype function to each value in @bind, but if \[] it leaves stuff alone.
On Thu Mar 04 08:55:35 2010, DMLOND wrote: Show quoted text
> I think the source of the confusion is that the perldoc does not provide > enough information to help users know how to use both 'bindtype => > columns' and apply an oracle (or postgresql) function to a value as it > is being inserted into the database. In order to use SQL::Abstract as > it is currently distributed to do this, you can do the following (note, > no 'bindtype => columns'): > > use SQL::Abstract; > use Data::Dumper; > > my $sql = SQL::Abstract->new(); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => ["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] > );
I am sorry I overlooked a crucial error. col => [ ... ] is undefined in insert(). It was left in as an unfinished array-support for databases which know how to do that (e.g. Pg). The only way to supply a function is either via \'...' or via \[ ..., @bind ]. There is no non-double-ref syntax to invoke raw sql. Patches to make SQLA die louder/documentation clarifications are welcome.
I still do not understand what you are trying to say. If you are saying that I should be able to do the following to apply an oracle function using bindtype => 'columns' (as the perldocs currently state I should be able to do): use SQL::Abstract; use Data::Dumper; my $sql = SQL::Abstract->new( bindtype => 'columns' ); my %data = ( name => 'Jimbo Bobson', phone => '123-456-7890', address => '42 Sister Lane', city => 'St. Louis', state => 'Louisiana', blaz => \["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] ); my($stmt, @bind) = $sql->insert('people', \%data); print "STMT ${stmt}\n"; print "BIND ".Dumper(@bind)."\n"; exit; Then you are wrong. SQL::Abstract throws an exception. In order to get it to work, I have to do: use SQL::Abstract; use Data::Dumper; my $sql = SQL::Abstract->new( bindtype => 'columns' ); my %data = ( name => 'Jimbo Bobson', phone => '123-456-7890', address => '42 Sister Lane', city => 'St. Louis', state => 'Louisiana', blaz => \["to_date(?, 'MM/DD/YYYY')", ['blaz' => "03/02/2003"]] ); my($stmt, @bind) = $sql->insert('people', \%data); print "STMT ${stmt}\n"; print "BIND ".Dumper(@bind)."\n"; exit; Which is completely unintuitive, and not documented in the perldocs. I would be happy to provide a patch to the perldoc stating that this is the preferred way of applying a function using bindtype => 'columns' if that is what you are saying needs to be done. If you would prefer that the former code example work as-is whether or not bindtype => 'columns' is passed in the constructor (matching the current perldoc API), you will need to apply the patch that I supplied earlier to the code.
On Tue Mar 09 10:47:16 2010, DMLOND wrote: Show quoted text
> I still do not understand what you are trying to say. If you are saying > that I should be able to do the following to apply an oracle function > using bindtype => 'columns' (as the perldocs currently state I should be > able to do): > > use SQL::Abstract; > use Data::Dumper; > my $sql = SQL::Abstract->new( bindtype => 'columns' ); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => \["to_date(?, 'MM/DD/YYYY')", "03/02/2003"] > ); > my($stmt, @bind) = $sql->insert('people', \%data); > print "STMT ${stmt}\n"; > print "BIND ".Dumper(@bind)."\n"; > exit;
No, it should die as it does now. Show quoted text
> use SQL::Abstract; > use Data::Dumper; > my $sql = SQL::Abstract->new( bindtype => 'columns' ); > my %data = ( > name => 'Jimbo Bobson', > phone => '123-456-7890', > address => '42 Sister Lane', > city => 'St. Louis', > state => 'Louisiana', > blaz => \["to_date(?, 'MM/DD/YYYY')", ['blaz' => "03/02/2003"]] > ); > my($stmt, @bind) = $sql->insert('people', \%data); > print "STMT ${stmt}\n"; > print "BIND ".Dumper(@bind)."\n"; > exit;
This is correct. Show quoted text
> Which is completely unintuitive, and not documented in the perldocs. I > would be happy to provide a patch to the perldoc stating that this is > the preferred way of applying a function using bindtype => 'columns' if > that is what you are saying needs to be done.
Yes please. I grepped through the POD and find the \[] syntax adecvately explained in several places, but maybe I am missing the point where you started looking from. So yes - more docs are in order and welcome. Cheers
Closing bugreport - waiting on docpatch from submitter. Feel free to reopen at your convenience.