use strict;
use warnings;
- use overload
- '0+' => "count",
- 'bool' => "_bool",
- fallback => 1;
+ use base qw/DBIx::Class/;
use Carp::Clan qw/^DBIx::Class/;
use DBIx::Class::Exception;
use Data::Page;
use DBIx::Class::ResultSetColumn;
use DBIx::Class::ResultSourceHandle;
use List::Util ();
- use Scalar::Util ();
- use base qw/DBIx::Class/;
+ use Scalar::Util 'blessed';
+ use namespace::clean;
+
+ use overload
+ '0+' => "count",
+ 'bool' => "_bool",
+ fallback => 1;
__PACKAGE__->mk_group_accessors('simple' => qw/_result_class _source_handle/);
my $rsrc = $self->result_source;
$attrs ||= $self->_resolved_attrs;
- # only take pieces we need for a simple count
- my $tmp_attrs = { map
- { $_ => $attrs->{$_} }
- qw/ alias from where bind join /
- };
+ my $tmp_attrs = { %$attrs };
+ # take off any limits, record_filter is cdbi, and no point of ordering nor locking a count
+ delete @{$tmp_attrs}{qw/rows offset order_by record_filter for/};
# overwrite the selector (supplied by the storage)
- $tmp_attrs->{select} = $rsrc->storage->_count_select ($rsrc, $tmp_attrs);
+ $tmp_attrs->{select} = $rsrc->storage->_count_select ($rsrc, $attrs);
$tmp_attrs->{as} = 'count';
my $tmp_rs = $rsrc->resultset_class->new($rsrc, $tmp_attrs)->get_column ('count');
my $rsrc = $self->result_source;
$attrs ||= $self->_resolved_attrs;
- my $sub_attrs = { map
- { $_ => $attrs->{$_} }
- qw/ alias from where bind join group_by having rows offset /
- };
+ my $sub_attrs = { %$attrs };
+ # extra selectors do not go in the subquery and there is no point of ordering it, nor locking it
+ delete @{$sub_attrs}{qw/collapse select _prefetch_select as order_by for/};
# if we multi-prefetch we group_by primary keys only as this is what we would
# get out of the rs via ->next/->all. We *DO WANT* to clobber old group_by regardless
->new ($rsrc, $sub_attrs)
->as_subselect_rs
->search ({}, { columns => { count => $rsrc->storage->_count_select ($rsrc, $attrs) } })
- -> get_column ('count');
+ ->get_column ('count');
}
sub _bool {
my $value = shift;
my $ref_type = ref $value;
return 1 if $ref_type eq '' || $ref_type eq 'SCALAR';
- return 1 if Scalar::Util::blessed($value);
+ return 1 if blessed $value;
return 0;
}
use base qw/SQL::Abstract::Limit/;
use strict;
use warnings;
-use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
+use Carp::Clan qw/^DBIx::Class|^SQL::Abstract|^Try::Tiny/;
- use Sub::Name();
+ use List::Util 'first';
+ use Sub::Name 'subname';
+ use namespace::clean;
BEGIN {
# reinstall the carp()/croak() functions imported into SQL::Abstract
for my $f (qw/carp croak/) {
my $orig = \&{"SQL::Abstract::$f"};
- *{"SQL::Abstract::$f"} = Sub::Name::subname "SQL::Abstract::$f" =>
+ *{"SQL::Abstract::$f"} = subname "SQL::Abstract::$f" =>
sub {
if (Carp::longmess() =~ /DBIx::Class::SQLAHacks::[\w]+ .+? called \s at/x) {
__PACKAGE__->can($f)->(@_);
# for possible further chaining)
my (@in_sel, @out_sel, %renamed);
for my $node (@sel) {
- if (List::Util::first { $_ =~ / (?<! $re_alias ) $re_sep /x } ($node->{as}, $node->{unquoted_sql}) ) {
+ if (first { $_ =~ / (?<! $re_alias ) $re_sep /x } ($node->{as}, $node->{unquoted_sql}) ) {
$node->{as} =~ s/ $re_sep /__/xg;
my $quoted_as = $self->_quote($node->{as});
push @in_sel, sprintf '%s AS %s', $node->{sql}, $quoted_as;
sub select {
my ($self, $table, $fields, $where, $rs_attrs, @rest) = @_;
- $self->{"${_}_bind"} = [] for (qw/having from order/);
-
if (not ref($table) or ref($table) eq 'SCALAR') {
$table = $self->_quote($table);
}
croak "LIMIT 0 Does Not Compute" if $rest[0] == 0;
# and anyway, SQL::Abstract::Limit will cause a barf if we don't first
- my ($sql, @where_bind) = $self->SUPER::select(
+ my ($sql, @bind) = $self->SUPER::select(
$table, $self->_recurse_fields($fields), $where, $rs_attrs, @rest
);
- return wantarray ? ($sql, @{$self->{from_bind}}, @where_bind, @{$self->{having_bind}}, @{$self->{order_bind}} ) : $sql;
+ push @{$self->{where_bind}}, @bind;
+
+# this *must* be called, otherwise extra binds will remain in the sql-maker
+ my @all_bind = $self->_assemble_binds;
+
+ return wantarray ? ($sql, @all_bind) : $sql;
+}
+
+sub _assemble_binds {
+ my $self = shift;
+ return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/from where having order/);
}
# Quotes table names, and handles default inserts
use strict;
use warnings;
use Scope::Guard ();
- use Context::Preserve ();
+ use Context::Preserve 'preserve_context';
use Try::Tiny;
use namespace::clean;
__PACKAGE__->set_primary_key('id');
__PACKAGE__->sequence('mysequence');
+ # Somewhere in your Code
+ # add some data to a table with a hierarchical relationship
+ $schema->resultset('Person')->create ({
+ firstname => 'foo',
+ lastname => 'bar',
+ children => [
+ {
+ firstname => 'child1',
+ lastname => 'bar',
+ children => [
+ {
+ firstname => 'grandchild',
+ lastname => 'bar',
+ }
+ ],
+ },
+ {
+ firstname => 'child2',
+ lastname => 'bar',
+ },
+ ],
+ });
+
+ # select from the hierarchical relationship
+ my $rs = $schema->resultset('Person')->search({},
+ {
+ 'start_with' => { 'firstname' => 'foo', 'lastname' => 'bar' },
+ 'connect_by' => { 'parentid' => { '-prior' => \'persionid' },
+ 'order_siblings_by' => { -asc => 'name' },
+ };
+ );
+
+ # this will select the whole tree starting from person "foo bar", creating
+ # following query:
+ # SELECT
+ # me.persionid me.firstname, me.lastname, me.parentid
+ # FROM
+ # person me
+ # START WITH
+ # firstname = 'foo' and lastname = 'bar'
+ # CONNECT BY
+ # parentid = prior persionid
+ # ORDER SIBLINGS BY
+ # firstname ASC
+
=head1 DESCRIPTION
This class implements base Oracle support. The subclass
use base qw/DBIx::Class::Storage::DBI/;
use mro 'c3';
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::Oracle');
+
sub deployment_statements {
my $self = shift;;
my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
$self->_do_query('alter session set constraints = immediate');
});
- return Context::Preserve::preserve_context(sub { $sub->() },
- after => sub { $txn_scope_guard->commit });
+ return
+ preserve_context { $sub->() } after => sub { $txn_scope_guard->commit };
}
+=head1 ATTRIBUTES
+
+Following additional attributes can be used in resultsets.
+
+=head2 connect_by or connect_by_nocycle
+
+=over 4
+
+=item Value: \%connect_by
+
+=back
+
+A hashref of conditions used to specify the relationship between parent rows
+and child rows of the hierarchy.
+
+
+ connect_by => { parentid => 'prior personid' }
+
+ # adds a connect by statement to the query:
+ # SELECT
+ # me.persionid me.firstname, me.lastname, me.parentid
+ # FROM
+ # person me
+ # CONNECT BY
+ # parentid = prior persionid
+
+
+ connect_by_nocycle => { parentid => 'prior personid' }
+
+ # adds a connect by statement to the query:
+ # SELECT
+ # me.persionid me.firstname, me.lastname, me.parentid
+ # FROM
+ # person me
+ # CONNECT BY NOCYCLE
+ # parentid = prior persionid
+
+
+=head2 start_with
+
+=over 4
+
+=item Value: \%condition
+
+=back
+
+A hashref of conditions which specify the root row(s) of the hierarchy.
+
+It uses the same syntax as L<DBIx::Class::ResultSet/search>
+
+ start_with => { firstname => 'Foo', lastname => 'Bar' }
+
+ # SELECT
+ # me.persionid me.firstname, me.lastname, me.parentid
+ # FROM
+ # person me
+ # START WITH
+ # firstname = 'foo' and lastname = 'bar'
+ # CONNECT BY
+ # parentid = prior persionid
+
+=head2 order_siblings_by
+
+=over 4
+
+=item Value: ($order_siblings_by | \@order_siblings_by)
+
+=back
+
+Which column(s) to order the siblings by.
+
+It uses the same syntax as L<DBIx::Class::ResultSet/order_by>
+
+ 'order_siblings_by' => 'firstname ASC'
+
+ # SELECT
+ # me.persionid me.firstname, me.lastname, me.parentid
+ # FROM
+ # person me
+ # CONNECT BY
+ # parentid = prior persionid
+ # ORDER SIBLINGS BY
+ # firstname ASC
+
=head1 AUTHOR
See L<DBIx::Class/CONTRIBUTORS>.
use DBD::Pg qw(:pg_types);
use Scope::Guard ();
- use Context::Preserve ();
+ use Context::Preserve 'preserve_context';
+ use namespace::clean;
# Ask for a DBD::Pg with array support
warn __PACKAGE__.": DBD::Pg 2.9.2 or greater is strongly recommended\n"
$self->_do_query('SET CONSTRAINTS ALL IMMEDIATE');
});
- return Context::Preserve::preserve_context(sub { $sub->() },
- after => sub { $txn_scope_guard->commit });
+ return preserve_context { $sub->() } after => sub { $txn_scope_guard->commit };
}
# only used when INSERT ... RETURNING is disabled
$self->_get_dbh->pg_rollback_to($name);
}
+sub deployment_statements {
+ my $self = shift;;
+ my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
+
+ $sqltargs ||= {};
+
+ if (my $version = $self->_server_info->{normalized_dbms_version}) {
+ $sqltargs->{producer_args}{postgres_version} = $version;
+ }
+
+ $self->next::method($schema, $type, $version, $dir, $sqltargs, @rest);
+}
+
1;
__END__
use mro 'c3';
use Carp::Clan qw/^DBIx::Class/;
+ use List::Util 'first';
+ use Scalar::Util 'blessed';
+ use namespace::clean;
#
# This code will remove non-selecting/non-restricting joins from
# if a multi-type join was needed in the subquery - add a group_by to simulate the
# collapse in the subq
$inner_attrs->{group_by} ||= $inner_select
- if List::Util::first
- { ! $_->[0]{-is_single} }
- (@{$inner_from}[1 .. $#$inner_from])
- ;
+ if first { ! $_->[0]{-is_single} } (@{$inner_from}[1 .. $#$inner_from]);
# generate the subquery
my $subq = $self->_select_args_to_query (
local $sql_maker->{quote_char} = "\x00"; # so that we can regex away
# generate sql chunks
+ local $sql_maker->{having_bind}; # these are throw away results
my $to_scan = {
restricting => [
$sql_maker->_recurse_where ($where),
- $sql_maker->_order_by({
+ $sql_maker->_parse_rs_attrs ({
map { $_ => $attrs->{$_} } (qw/group_by having/)
}),
],
# the reason this is so contrived is that $ident may be a {from}
# structure, specifying multiple tables to join
- if ( Scalar::Util::blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
+ if ( blessed $ident && $ident->isa("DBIx::Class::ResultSource") ) {
# this is compat mode for insert/update/delete which do not deal with aliases
$alias2source->{me} = $ident;
$rs_alias = 'me';