my ($sql, $bind) = $self->_prep_for_execute($op, $ident, \@args);
- shift->dbh_do( # retry over disconnects
- '_dbh_execute',
+ # not even a PID check - we do not care about the state of the _dbh.
+ # All we need is to get the appropriate drivers loaded if they aren't
+ # already so that the assumption in ad7c50fc26e holds
+ $self->_populate_dbh unless $self->_dbh;
+
+ $self->dbh_do( _dbh_execute => # retry over disconnects
$sql,
$bind,
- $ident,
+ $self->_dbi_attrs_for_bind($ident, $bind),
);
}
sub _dbh_execute {
- my ($self, undef, $sql, $bind, $ident) = @_;
+ my ($self, $dbh, $sql, $bind, $bind_attrs) = @_;
$self->_query_start( $sql, $bind );
- my $bind_attrs = $self->_dbi_attrs_for_bind($ident, $bind);
+ my $sth = $self->_bind_sth_params(
+ $self->_prepare_sth($dbh, $sql),
+ $bind,
+ $bind_attrs,
+ );
+
+ # Can this fail without throwing an exception anyways???
+ my $rv = $sth->execute();
+ $self->throw_exception(
+ $sth->errstr || $sth->err || 'Unknown error: execute() returned false, but error flags were not set...'
+ ) if !$rv;
+
+ $self->_query_end( $sql, $bind );
+
+ return (wantarray ? ($rv, $sth, @$bind) : $rv);
+}
+
+sub _prepare_sth {
+ my ($self, $dbh, $sql) = @_;
- my $sth = $self->_sth($sql);
+ # 3 is the if_active parameter which avoids active sth re-use
+ my $sth = $self->disable_sth_caching
+ ? $dbh->prepare($sql)
+ : $dbh->prepare_cached($sql, {}, 3);
+
+ # XXX You would think RaiseError would make this impossible,
+ # but apparently that's not true :(
+ $self->throw_exception(
+ $dbh->errstr
+ ||
+ sprintf( "\$dbh->prepare() of '%s' through %s failed *silently* without "
+ .'an exception and/or setting $dbh->errstr',
+ length ($sql) > 20
+ ? substr($sql, 0, 20) . '...'
+ : $sql
+ ,
+ 'DBD::' . $dbh->{Driver}{Name},
+ )
+ ) if !$sth;
+
+ $sth;
+}
+
+sub _bind_sth_params {
+ my ($self, $sth, $bind, $bind_attrs) = @_;
for my $i (0 .. $#$bind) {
if (ref $bind->[$i][1] eq 'SCALAR') { # any scalarrefs are assumed to be bind_inouts
);
}
else {
+ # FIXME SUBOPTIMAL - most likely this is not necessary at all
+ # confirm with dbi-dev whether explicit stringification is needed
+ my $v = ( length ref $bind->[$i][1] and overload::Method($bind->[$i][1], '""') )
+ ? "$bind->[$i][1]"
+ : $bind->[$i][1]
+ ;
$sth->bind_param(
$i + 1,
- (ref $bind->[$i][1] and overload::Method($bind->[$i][1], '""'))
- ? "$bind->[$i][1]"
- : $bind->[$i][1]
- ,
+ $v,
$bind_attrs->[$i],
);
}
}
- # Can this fail without throwing an exception anyways???
- my $rv = $sth->execute();
- $self->throw_exception(
- $sth->errstr || $sth->err || 'Unknown error: execute() returned false, but error flags were not set...'
- ) if !$rv;
-
- $self->_query_end( $sql, $bind );
-
- return (wantarray ? ($rv, $sth, @$bind) : $rv);
+ $sth;
}
sub _prefetch_autovalues {
my @col_range = (0..$#$cols);
- # FIXME - perhaps this is not even needed? does DBI stringify?
+ # FIXME SUBOPTIMAL - most likely this is not necessary at all
+ # confirm with dbi-dev whether explicit stringification is needed
#
# forcibly stringify whatever is stringifiable
# ResultSet::populate() hands us a copy - safe to mangle
for my $r (0 .. $#$data) {
for my $c (0 .. $#{$data->[$r]}) {
$data->[$r][$c] = "$data->[$r][$c]"
- if ( ref $data->[$r][$c] and overload::Method($data->[$r][$c], '""') );
+ if ( length ref $data->[$r][$c] and overload::Method($data->[$r][$c], '""') );
}
}
my $guard = $self->txn_scope_guard;
$self->_query_start( $sql, @$proto_bind ? [[undef => '__BULK_INSERT__' ]] : () );
- my $sth = $self->_sth($sql);
+ my $sth = $self->_prepare_sth($self->_dbh, $sql);
my $rv = do {
if (@$proto_bind) {
# proto bind contains the information on which pieces of $data to pull
$self->_select_args(@_);
# my ($sql, $prepared_bind) = $self->_gen_sql_bind($op, $ident, [ $select, $cond, $rs_attrs, $rows, $offset ]);
- my ($sql, $prepared_bind) = $self->_gen_sql_bind($op, $ident, \@args);
- $prepared_bind ||= [];
+ my ($sql, $bind) = $self->_gen_sql_bind($op, $ident, \@args);
- return wantarray
- ? ($sql, $prepared_bind)
- : \[ "($sql)", @$prepared_bind ]
- ;
+ # reuse the bind arrayref
+ unshift @{$bind}, "($sql)";
+ \$bind;
}
sub _select_args {
my ($self, $ident, $select, $where, $attrs) = @_;
my $sql_maker = $self->sql_maker;
- my ($alias2source, $rs_alias) = $self->_resolve_ident_sources ($ident);
+ my $alias2source = $self->_resolve_ident_sources ($ident);
$attrs = {
%$attrs,
select => $select,
from => $ident,
where => $where,
- $rs_alias && $alias2source->{$rs_alias}
- ? ( _rsroot_rsrc => $alias2source->{$rs_alias} )
+
+ # limit dialects use this stuff
+ # yes, some CDBICompat crap does not supply an {alias} >.<
+ ( $attrs->{alias} and $alias2source->{$attrs->{alias}} )
+ ? ( _rsroot_rsrc => $alias2source->{$attrs->{alias}} )
: ()
,
};
$attrs->{rows} = $sql_maker->__max_int;
}
- my @limit;
-
- # see if we need to tear the prefetch apart otherwise delegate the limiting to the
- # storage, unless software limit was requested
- if (
- # limited collapsing has_many
- ( $attrs->{rows} && $attrs->{collapse} )
- ||
- # grouped prefetch (to satisfy group_by == select)
- ( $attrs->{group_by}
- &&
- @{$attrs->{group_by}}
- &&
- $attrs->{_prefetch_selector_range}
- )
+ # see if we will need to tear the prefetch apart to satisfy group_by == select
+ # this is *extremely tricky* to get right, I am still not sure I did
+ #
+ my ($prefetch_needs_subquery, @limit_args);
+
+ if ( $attrs->{_grouped_by_distinct} and $attrs->{collapse} ) {
+ # we already know there is a valid group_by and we know it is intended
+ # to be based *only* on the main result columns
+ # short circuit the group_by parsing below
+ $prefetch_needs_subquery = 1;
+ }
+ elsif (
+ # The rationale is that even if we do *not* have collapse, we still
+ # need to wrap the core grouped select/group_by in a subquery
+ # so that databases that care about group_by/select equivalence
+ # are happy (this includes MySQL in strict_mode)
+ # If any of the other joined tables are referenced in the group_by
+ # however - the user is on their own
+ ( $prefetch_needs_subquery or $attrs->{_related_results_construction} )
+ and
+ $attrs->{group_by}
+ and
+ @{$attrs->{group_by}}
+ and
+ my $grp_aliases = try { # try{} because $attrs->{from} may be unreadable
+ $self->_resolve_aliastypes_from_select_args( $attrs->{from}, undef, undef, { group_by => $attrs->{group_by} } )
+ }
) {
- ($ident, $select, $where, $attrs)
- = $self->_adjust_select_args_for_complex_prefetch ($ident, $select, $where, $attrs);
+ # no aliases other than our own in group_by
+ # if there are - do not allow subquery even if limit is present
+ $prefetch_needs_subquery = ! scalar grep { $_ ne $attrs->{alias} } keys %{ $grp_aliases->{grouping} || {} };
+ }
+ elsif ( $attrs->{rows} && $attrs->{collapse} ) {
+ # active collapse with a limit - that one is a no-brainer unless
+ # overruled by a group_by above
+ $prefetch_needs_subquery = 1;
+ }
+
+ if ($prefetch_needs_subquery) {
+ ($ident, $select, $where, $attrs) =
+ $self->_adjust_select_args_for_complex_prefetch ($ident, $select, $where, $attrs);
}
elsif (! $attrs->{software_limit} ) {
- push @limit, (
+ push @limit_args, (
$attrs->{rows} || (),
$attrs->{offset} || (),
);
# try to simplify the joinmap further (prune unreferenced type-single joins)
if (
+ ! $prefetch_needs_subquery # already pruned
+ and
ref $ident
and
reftype $ident eq 'ARRAY'
# invoked, and that's just bad...
###
- return ('select', $ident, $select, $where, $attrs, @limit);
+ return ('select', $ident, $select, $where, $attrs, @limit_args);
}
# Returns a counting SELECT for a simple count
=cut
-sub _dbh_sth {
- my ($self, $dbh, $sql) = @_;
-
- # 3 is the if_active parameter which avoids active sth re-use
- my $sth = $self->disable_sth_caching
- ? $dbh->prepare($sql)
- : $dbh->prepare_cached($sql, {}, 3);
-
- # XXX You would think RaiseError would make this impossible,
- # but apparently that's not true :(
- $self->throw_exception(
- $dbh->errstr
- ||
- sprintf( "\$dbh->prepare() of '%s' through %s failed *silently* without "
- .'an exception and/or setting $dbh->errstr',
- length ($sql) > 20
- ? substr($sql, 0, 20) . '...'
- : $sql
- ,
- 'DBD::' . $dbh->{Driver}{Name},
- )
- ) if !$sth;
-
- $sth;
-}
-
-sub sth {
- carp_unique 'sth was mistakenly marked/documented as public, stop calling it (will be removed before DBIC v0.09)';
- shift->_sth(@_);
-}
-
-sub _sth {
- my ($self, $sql) = @_;
- $self->dbh_do('_dbh_sth', $sql); # retry over disconnects
-}
-
sub _dbh_columns_info_for {
my ($self, $dbh, $table) = @_;
See L<SQL::Translator/METHODS> for a list of values for C<\%sqlt_args>.
The most common value for this would be C<< { add_drop_table => 1 } >>
to have the SQL produced include a C<DROP TABLE> statement for each table
-created. For quoting purposes supply C<quote_table_names> and
-C<quote_field_names>.
+created. For quoting purposes supply C<quote_identifiers>.
If no arguments are passed, then the following default values are assumed: