X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSource.pm;h=e7ab22d80cf2d6de18987fe5627ab50c7c122f93;hb=52416317a26986602098ffe2ea6aa64a05925b6f;hp=171218bd72c5633980ebaf3fdec1615f4a03c4e1;hpb=ed7ab0f4ce1a9118ea6285ee562ef003085a6b64;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 171218b..e7ab22d 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -9,6 +9,8 @@ use DBIx::Class::ResultSourceHandle; use DBIx::Class::Exception; use Carp::Clan qw/^DBIx::Class/; use Try::Tiny; +use List::Util 'first'; +use namespace::clean; use base qw/DBIx::Class/; @@ -143,7 +145,7 @@ by supplying an L in the column_info hash. If a column name beginning with a plus sign ('+col1') is provided, the attributes provided will be merged with any existing attributes for the column, with the new attributes taking precedence in the case that an -attribute already exists. Using this without a hashref +attribute already exists. Using this without a hashref (C<< $source->add_columns(qw/+col1 +col2/) >>) is legal, but useless -- it does the same thing it would do without the plus. @@ -175,7 +177,7 @@ the name of the column will be used. This contains the column type. It is automatically filled if you use the L producer, or the -L module. +L module. Currently there is no standard set of values for the data_type. Use whatever your database supports. @@ -359,31 +361,31 @@ sub column_info { my ($self, $column) = @_; $self->throw_exception("No such column $column") unless exists $self->_columns->{$column}; - #warn $self->{_columns_info_loaded}, "\n"; + if ( ! $self->_columns->{$column}{data_type} - and $self->column_info_from_storage and ! $self->{_columns_info_loaded} - and $self->schema and $self->storage ) + and $self->column_info_from_storage + and $self->schema and my $stor = $self->storage ) { $self->{_columns_info_loaded}++; - my $info = {}; - my $lc_info = {}; + # try for the case of storage without table - my $caught; - try { $info = $self->storage->columns_info_for( $self->from ) } - catch { $caught = 1 }; - unless ($caught) { - for my $realcol ( keys %{$info} ) { - $lc_info->{lc $realcol} = $info->{$realcol}; - } + try { + my $info = $stor->columns_info_for( $self->from ); + my $lc_info = { map + { (lc $_) => $info->{$_} } + ( keys %$info ) + }; + foreach my $col ( keys %{$self->_columns} ) { $self->_columns->{$col} = { %{ $self->_columns->{$col} }, %{ $info->{$col} || $lc_info->{lc $col} || {} } }; } - } + }; } + return $self->_columns->{$column}; } @@ -411,6 +413,82 @@ sub columns { return @{$self->{_ordered_columns}||[]}; } +=head2 columns_info + +=over + +=item Arguments: \@colnames ? + +=item Return value: Hashref of column name/info pairs + +=back + + my $columns_info = $source->columns_info; + +Like L but returns information for the requested columns. If +the optional column-list arrayref is ommitted it returns info on all columns +currently defined on the ResultSource via L. + +=cut + +sub columns_info { + my ($self, $columns) = @_; + + my $colinfo = $self->_columns; + + if ( + first { ! $_->{data_type} } values %$colinfo + and + ! $self->{_columns_info_loaded} + and + $self->column_info_from_storage + and + $self->schema + and + my $stor = $self->storage + ) { + $self->{_columns_info_loaded}++; + + # try for the case of storage without table + try { + my $info = $stor->columns_info_for( $self->from ); + my $lc_info = { map + { (lc $_) => $info->{$_} } + ( keys %$info ) + }; + + foreach my $col ( keys %$colinfo ) { + $colinfo->{$col} = { + %{ $colinfo->{$col} }, + %{ $info->{$col} || $lc_info->{lc $col} || {} } + }; + } + }; + } + + my %ret; + + if ($columns) { + for (@$columns) { + if (my $inf = $colinfo->{$_}) { + $ret{$_} = $inf; + } + else { + $self->throw_exception( sprintf ( + "No such column '%s' on source %s", + $_, + $self->source_name, + )); + } + } + } + else { + %ret = %$colinfo; + } + + return \%ret; +} + =head2 remove_columns =over @@ -486,7 +564,7 @@ named C. Note: you normally do want to define a primary key on your sources B. See -L +L for more info. =cut @@ -530,11 +608,40 @@ sub _pri_cols { my @pcols = $self->primary_columns or $self->throw_exception (sprintf( "Operation requires a primary key to be declared on '%s' via set_primary_key", - $self->source_name, + # source_name is set only after schema-registration + $self->source_name || $self->result_class || $self->name || 'Unknown source...?', )); return @pcols; } +=head2 sequence + +Manually define the correct sequence for your table, to avoid the overhead +associated with looking up the sequence automatically. The supplied sequence +will be applied to the L of each L + +=over 4 + +=item Arguments: $sequence_name + +=item Return value: undefined + +=back + +=cut + +sub sequence { + my ($self,$seq) = @_; + + my $rsrc = $self->result_source; + my @pks = $rsrc->primary_columns + or next; + + $_->{sequence} = $seq + for values %{ $rsrc->columns_info (\@pks) }; +} + + =head2 add_unique_constraint =over 4 @@ -572,8 +679,22 @@ the result source. sub add_unique_constraint { my $self = shift; + + if (@_ > 2) { + $self->throw_exception( + 'add_unique_constraint() does not accept multiple constraints, use ' + . 'add_unique_constraints() instead' + ); + } + my $cols = pop @_; - my $name = shift; + if (ref $cols ne 'ARRAY') { + $self->throw_exception ( + 'Expecting an arrayref of constraint columns, got ' . ($cols||'NOTHING') + ); + } + + my $name = shift @_; $name ||= $self->name_unique_constraint($cols); @@ -587,18 +708,70 @@ sub add_unique_constraint { $self->_unique_constraints(\%unique_constraints); } +=head2 add_unique_constraints + +=over 4 + +=item Arguments: @constraints + +=item Return value: undefined + +=back + +Declare multiple unique constraints on this source. + + __PACKAGE__->add_unique_constraints( + constraint_name1 => [ qw/column1 column2/ ], + constraint_name2 => [ qw/column2 column3/ ], + ); + +Alternatively, you can specify only the columns: + + __PACKAGE__->add_unique_constraints( + [ qw/column1 column2/ ], + [ qw/column3 column4/ ] + ); + +This will result in unique constraints named C and +C, where C is replaced with the table name. + +Throws an error if any of the given column names do not yet exist on +the result source. + +See also L. + +=cut + +sub add_unique_constraints { + my $self = shift; + my @constraints = @_; + + if ( !(@constraints % 2) && first { ref $_ ne 'ARRAY' } @constraints ) { + # with constraint name + while (my ($name, $constraint) = splice @constraints, 0, 2) { + $self->add_unique_constraint($name => $constraint); + } + } + else { + # no constraint name + foreach my $constraint (@constraints) { + $self->add_unique_constraint($constraint); + } + } +} + =head2 name_unique_constraint =over 4 -=item Arguments: @colnames +=item Arguments: \@colnames =item Return value: Constraint name =back $source->table('mytable'); - $source->name_unique_constraint('col1', 'col2'); + $source->name_unique_constraint(['col1', 'col2']); # returns 'mytable_col1_col2' @@ -900,7 +1073,7 @@ clause contents. my $schema = $source->schema(); -Returns the L object that this result source +Returns the L object that this result source belongs to. =head2 storage @@ -1025,7 +1198,7 @@ sub add_relationship { return $self; - # XXX disabled. doesn't work properly currently. skip in tests. +# XXX disabled. doesn't work properly currently. skip in tests. my $f_source = $self->schema->source($f_source_name); unless ($f_source) { @@ -1041,10 +1214,11 @@ sub add_relationship { try { $self->_resolve_join($rel, 'me', {}, []) } catch { # If the resolve failed, back out and re-throw the error - delete $rels{$rel}; # + delete $rels{$rel}; $self->_relationships(\%rels); $self->throw_exception("Error creating relationship $rel: $_"); }; + 1; } @@ -1291,7 +1465,7 @@ sub _resolve_join { -is_single => ( $rel_info->{attrs}{accessor} && - List::Util::first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) + first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) ), -alias => $as, -relation_chain_depth => $seen->{-relation_chain_depth} || 0, @@ -1476,13 +1650,33 @@ sub _resolve_prefetch { # in ResultSet->_collapse_result my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); } keys %{$rel_info->{cond}}; - my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' - ? @{$rel_info->{attrs}{order_by}} - - : (defined $rel_info->{attrs}{order_by} - ? ($rel_info->{attrs}{order_by}) - : ())); - push(@$order, map { "${as}.$_" } (@key, @ord)); + push @$order, map { "${as}.$_" } @key; + + if (my $rel_order = $rel_info->{attrs}{order_by}) { + # this is kludgy and incomplete, I am well aware + # but the parent method is going away entirely anyway + # so sod it + my $sql_maker = $self->storage->sql_maker; + my ($orig_ql, $orig_qr) = $sql_maker->_quote_chars; + my $sep = $sql_maker->name_sep; + + # install our own quoter, so we can catch unqualified stuff + local $sql_maker->{quote_char} = ["\x00", "\xFF"]; + + my $quoted_prefix = "\x00${as}\xFF"; + + for my $chunk ( $sql_maker->_order_by_chunks ($rel_order) ) { + my @bind; + ($chunk, @bind) = @$chunk if ref $chunk; + + $chunk = "${quoted_prefix}${sep}${chunk}" + unless $chunk =~ /\Q$sep/; + + $chunk =~ s/\x00/$orig_ql/g; + $chunk =~ s/\xFF/$orig_qr/g; + push @$order, \[$chunk, @bind]; + } + } } return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } @@ -1536,7 +1730,7 @@ sub related_class { =head2 handle -Obtain a new handle to this source. Returns an instance of a +Obtain a new handle to this source. Returns an instance of a L. =cut