use base 'DBIx::Class::Storage';
-use strict;
+use strict;
use warnings;
use Carp::Clan qw/^DBIx::Class/;
use DBI;
-use DBIx::Class::SQLAHacks;
use DBIx::Class::Storage::DBI::Cursor;
use DBIx::Class::Storage::Statistics;
-use Scalar::Util qw/blessed weaken/;
+use Scalar::Util();
+use List::Util();
__PACKAGE__->mk_group_accessors('simple' =>
qw/_connect_info _dbi_connect_info _dbh _sql_maker _sql_maker_opts
=item *
-A single code reference which returns a connected
-L<DBI database handle|DBI/connect> optionally followed by
+A single code reference which returns a connected
+L<DBI database handle|DBI/connect> optionally followed by
L<extra attributes|/DBIx::Class specific connection attributes> recognized
by DBIx::Class:
%extra_attributes,
}];
-This is particularly useful for L<Catalyst> based applications, allowing the
+This is particularly useful for L<Catalyst> based applications, allowing the
following config (L<Config::General> style):
<Model::DB>
set C<AutoCommit> to either I<0> or I<1>. L<DBIx::Class> further
recommends that it be set to I<1>, and that you perform transactions
via our L<DBIx::Class::Schema/txn_do> method. L<DBIx::Class> will set it
-to I<1> if you do not do explicitly set it to zero. This is the default
+to I<1> if you do not do explicitly set it to zero. This is the default
for most DBDs. See L</DBIx::Class and AutoCommit> for details.
=head3 DBIx::Class specific connection attributes
=over
+=item a scalar
+
+This contains one SQL statement to execute.
+
=item an array reference
This contains SQL statements to execute in order. Each element contains
If set to a true value, this option will disable the caching of
statement handles via L<DBI/prepare_cached>.
-=item limit_dialect
+=item limit_dialect
Sets the limit dialect. This is useful for JDBC-bridge among others
where the remote SQL-dialect cannot be determined by the name of the
=item quote_char
-Specifies what characters to use to quote table and column names. If
+Specifies what characters to use to quote table and column names. If
you use this you will want to specify L</name_sep> as well.
C<quote_char> expects either a single character, in which case is it
=item name_sep
-This only needs to be used in conjunction with C<quote_char>, and is used to
-specify the charecter that seperates elements (schemas, tables, columns) from
+This only needs to be used in conjunction with C<quote_char>, and is used to
+specify the charecter that seperates elements (schemas, tables, columns) from
each other. In most cases this is simply a C<.>.
The consequences of not supplying this value is that L<SQL::Abstract>
sub _sql_maker_args {
my ($self) = @_;
-
+
return ( bindtype=>'columns', array_datatypes => 1, limit_dialect => $self->dbh, %{$self->_sql_maker_opts} );
}
my ($self) = @_;
unless ($self->_sql_maker) {
my $sql_maker_class = $self->sql_maker_class;
+ $self->ensure_class_loaded ($sql_maker_class);
$self->_sql_maker($sql_maker_class->new( $self->_sql_maker_args ));
}
return $self->_sql_maker;
my @info = @{$self->_dbi_connect_info || []};
$self->_dbh($self->_connect(@info));
+ $self->_conn_pid($$);
+ $self->_conn_tid(threads->tid) if $INC{'threads.pm'};
+
+ $self->_determine_driver;
+
# Always set the transaction depth on connect, since
# there is no transaction in progress by definition
$self->{transaction_depth} = $self->_dbh_autocommit ? 0 : 1;
- if(ref $self eq 'DBIx::Class::Storage::DBI') {
- my $driver = $self->_dbh->{Driver}->{Name};
+ my $connection_do = $self->on_connect_do;
+ $self->_do_connection_actions($connection_do) if $connection_do;
+}
+
+sub _determine_driver {
+ my ($self) = @_;
+
+ if (ref $self eq 'DBIx::Class::Storage::DBI') {
+ my $driver;
+
+ if ($self->_dbh) { # we are connected
+ $driver = $self->_dbh->{Driver}{Name};
+ } else {
+ # try to use dsn to not require being connected, the driver may still
+ # force a connection in _rebless to determine version
+ ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i;
+ }
+
if ($self->load_optional_class("DBIx::Class::Storage::DBI::${driver}")) {
bless $self, "DBIx::Class::Storage::DBI::${driver}";
$self->_rebless();
}
}
-
- $self->_conn_pid($$);
- $self->_conn_tid(threads->tid) if $INC{'threads.pm'};
-
- my $connection_do = $self->on_connect_do;
- $self->_do_connection_actions($connection_do) if ref($connection_do);
}
sub _do_connection_actions {
my $self = shift;
my $connection_do = shift;
- if (ref $connection_do eq 'ARRAY') {
+ if (!ref $connection_do) {
+ $self->_do_query($connection_do);
+ }
+ elsif (ref $connection_do eq 'ARRAY') {
$self->_do_query($_) foreach @$connection_do;
}
elsif (ref $connection_do eq 'CODE') {
$connection_do->($self);
}
+ else {
+ $self->throw_exception (sprintf ("Don't know how to process conection actions of type '%s'", ref $connection_do) );
+ }
return $self;
}
if($dbh && !$self->unsafe) {
my $weak_self = $self;
- weaken($weak_self);
+ Scalar::Util::weaken($weak_self);
$dbh->{HandleError} = sub {
if ($weak_self) {
$weak_self->throw_exception("DBI Exception: $_[0]");
$self->throw_exception ("Your Storage implementation doesn't support savepoints")
unless $self->can('_svp_begin');
-
+
push @{ $self->{savepoints} }, $name;
$self->debugobj->svp_begin($name) if $self->debug;
-
+
return $self->_svp_begin($name);
}
}
$self->debugobj->svp_rollback($name) if $self->debug;
-
+
return $self->_svp_rollback($name);
}
sub _prep_for_execute {
my ($self, $op, $extra_bind, $ident, $args) = @_;
- if( blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
+ if( Scalar::Util::blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
$ident = $ident->from();
}
return ($sql, \@bind);
}
+
sub _fix_bind_params {
my ($self, @bind) = @_;
if ( $self->debug ) {
@bind = $self->_fix_bind_params(@bind);
-
+
$self->debugobj->query_start( $sql, @bind );
}
}
my $sth = $self->sth($sql,$op);
- my $placeholder_index = 1;
+ my $placeholder_index = 1;
foreach my $bound (@$bind) {
my $attributes = {};
sub insert {
my ($self, $source, $to_insert) = @_;
-
- my $ident = $source->from;
+
+ my $ident = $source->from;
my $bind_attributes = $self->source_bind_attributes($source);
my $updated_cols = {};
}
## Still not quite perfect, and EXPERIMENTAL
-## Currently it is assumed that all values passed will be "normal", i.e. not
+## Currently it is assumed that all values passed will be "normal", i.e. not
## scalar refs, or at least, all the same type as the first set, the statement is
## only prepped once.
sub insert_bulk {
my $table = $source->from;
@colvalues{@$cols} = (0..$#$cols);
my ($sql, @bind) = $self->sql_maker->insert($table, \%colvalues);
-
+
$self->_query_start( $sql, @bind );
my $sth = $self->sth($sql);
my $bind_attributes = $self->source_bind_attributes($source);
## Bind the values and execute
- my $placeholder_index = 1;
+ my $placeholder_index = 1;
foreach my $bound (@bind) {
$sth->bind_param_array( $placeholder_index, [@data], $attributes );
$placeholder_index++;
}
- my $rv = $sth->execute_array({ArrayTupleStatus => $tuple_status});
+ my $rv = eval { $sth->execute_array({ArrayTupleStatus => $tuple_status}) };
+ if (my $err = $@) {
+ my $i = 0;
+ ++$i while $i <= $#$tuple_status && !ref $tuple_status->[$i];
+
+ $self->throw_exception($sth->errstr || "Unexpected populate error: $err")
+ if ($i > $#$tuple_status);
+
+ require Data::Dumper;
+ local $Data::Dumper::Terse = 1;
+ local $Data::Dumper::Indent = 1;
+ local $Data::Dumper::Useqq = 1;
+ local $Data::Dumper::Quotekeys = 0;
+
+ $self->throw_exception(sprintf "%s for populate slice:\n%s",
+ $tuple_status->[$i][1],
+ Data::Dumper::Dumper(
+ { map { $cols->[$_] => $data->[$i][$_] } (0 .. $#$cols) }
+ ),
+ );
+ }
$self->throw_exception($sth->errstr) if !$rv;
$self->_query_end( $sql, @bind );
my $self = shift @_;
my $source = shift @_;
my $bind_attributes = $self->source_bind_attributes($source);
-
+
return $self->_execute('update' => [], $source, $bind_attributes, @_);
}
sub delete {
my $self = shift @_;
my $source = shift @_;
-
- my $bind_attrs = {}; ## If ever it's needed...
-
+
+ my $bind_attrs = $self->source_bind_attributes($source);
+
return $self->_execute('delete' => [], $source, $bind_attrs, @_);
}
#
# Genarating a single PK column subquery is trivial and supported
# by all RDBMS. However if we have a multicolumn PK, things get ugly.
-# Look at multipk_update_delete()
-sub subq_update_delete {
+# Look at _multipk_update_delete()
+sub _subq_update_delete {
my $self = shift;
my ($rs, $op, $values) = @_;
- if ($rs->result_source->primary_columns == 1) {
- return $self->_onepk_update_delete (@_);
+ my $rsrc = $rs->result_source;
+
+ # we already check this, but double check naively just in case. Should be removed soon
+ my $sel = $rs->_resolved_attrs->{select};
+ $sel = [ $sel ] unless ref $sel eq 'ARRAY';
+ my @pcols = $rsrc->primary_columns;
+ if (@$sel != @pcols) {
+ $self->throw_exception (
+ 'Subquery update/delete can not be called on resultsets selecting a'
+ .' number of columns different than the number of primary keys'
+ );
}
+
+ if (@pcols == 1) {
+ return $self->$op (
+ $rsrc,
+ $op eq 'update' ? $values : (),
+ { $pcols[0] => { -in => $rs->as_query } },
+ );
+ }
+
else {
return $self->_multipk_update_delete (@_);
}
}
-# Generally a single PK resultset operation is trivially expressed
-# with PK IN (subquery). However some databases (mysql) do not support
-# modification of a table mentioned in the subselect. This method
-# should be overriden in the appropriate storage class to be smarter
-# in such situations
-sub _onepk_update_delete {
-
- my $self = shift;
- my ($rs, $op, $values) = @_;
-
- my $rsrc = $rs->result_source;
- my $attrs = $rs->_resolved_attrs;
- my @pcols = $rsrc->primary_columns;
-
- $self->throw_exception ('_onepk_update_delete can not be called on resultsets selecting multiple columns')
- if (ref $attrs->{select} eq 'ARRAY' and @{$attrs->{select}} > 1);
-
- return $self->$op (
- $rsrc,
- $op eq 'update' ? $values : (),
- { $pcols[0] => { -in => $rs->as_query } },
- );
+# ANSI SQL does not provide a reliable way to perform a multicol-PK
+# resultset update/delete involving subqueries. So by default resort
+# to simple (and inefficient) delete_all style per-row opearations,
+# while allowing specific storages to override this with a faster
+# implementation.
+#
+sub _multipk_update_delete {
+ return shift->_per_row_update_delete (@_);
}
-# ANSI SQL does not provide a reliable way to perform a multicol-PK
-# resultset update/delete involving subqueries. So resort to simple
-# (and inefficient) delete_all style per-row opearations, while allowing
-# specific storages to override this with a faster implementation.
+# This is the default loop used to delete/update rows for multi PK
+# resultsets, and used by mysql exclusively (because it can't do anything
+# else).
#
# We do not use $row->$op style queries, because resultset update/delete
# is not expected to cascade (this is what delete_all/update_all is for).
#
# There should be no race conditions as the entire operation is rolled
# in a transaction.
-sub _multipk_update_delete {
+#
+sub _per_row_update_delete {
my $self = shift;
my ($rs, $op, $values) = @_;
my $rsrc = $rs->result_source;
my @pcols = $rsrc->primary_columns;
- my $attrs = $rs->_resolved_attrs;
-
- $self->throw_exception ('Number of columns selected by supplied resultset does not match number of primary keys')
- if ( ref $attrs->{select} ne 'ARRAY' or @{$attrs->{select}} != @pcols );
my $guard = $self->txn_scope_guard;
+ # emulate the return value of $sth->execute for non-selects
+ my $row_cnt = '0E0';
+
my $subrs_cur = $rs->cursor;
while (my @pks = $subrs_cur->next) {
$op eq 'update' ? $values : (),
$cond,
);
+
+ $row_cnt++;
}
$guard->commit;
- return 1;
+ return $row_cnt;
}
-
sub _select {
my $self = shift;
+
+ # localization is neccessary as
+ # 1) there is no infrastructure to pass this around before SQLA2
+ # 2) _select_args sets it and _prep_for_execute consumes it
my $sql_maker = $self->sql_maker;
- local $sql_maker->{for};
+ local $sql_maker->{_dbic_rs_attrs};
+
return $self->_execute($self->_select_args(@_));
}
+sub _select_args_to_query {
+ my $self = shift;
+
+ # localization is neccessary as
+ # 1) there is no infrastructure to pass this around before SQLA2
+ # 2) _select_args sets it and _prep_for_execute consumes it
+ my $sql_maker = $self->sql_maker;
+ local $sql_maker->{_dbic_rs_attrs};
+
+ # my ($op, $bind, $ident, $bind_attrs, $select, $cond, $order, $rows, $offset)
+ # = $self->_select_args($ident, $select, $cond, $attrs);
+ my ($op, $bind, $ident, $bind_attrs, @args) =
+ $self->_select_args(@_);
+
+ # my ($sql, $prepared_bind) = $self->_prep_for_execute($op, $bind, $ident, [ $select, $cond, $order, $rows, $offset ]);
+ my ($sql, $prepared_bind) = $self->_prep_for_execute($op, $bind, $ident, \@args);
+ $prepared_bind ||= [];
+
+ return wantarray
+ ? ($sql, $prepared_bind, $bind_attrs)
+ : \[ "($sql)", @$prepared_bind ]
+ ;
+}
+
sub _select_args {
my ($self, $ident, $select, $condition, $attrs) = @_;
- my $order = $attrs->{order_by};
- my $for = delete $attrs->{for};
my $sql_maker = $self->sql_maker;
- $sql_maker->{for} = $for;
+ $sql_maker->{_dbic_rs_attrs} = $attrs;
- if (exists $attrs->{group_by} || $attrs->{having}) {
- $order = {
- group_by => $attrs->{group_by},
- having => $attrs->{having},
- ($order ? (order_by => $order) : ())
- };
+ my $order = { map
+ { $attrs->{$_} ? ( $_ => $attrs->{$_} ) : () }
+ (qw/order_by group_by having _virtual_order_by/ )
+ };
+
+
+ my $bind_attrs = {};
+
+ my $alias2source = $self->_resolve_ident_sources ($ident);
+
+ for my $alias (keys %$alias2source) {
+ my $bindtypes = $self->source_bind_attributes ($alias2source->{$alias}) || {};
+ for my $col (keys %$bindtypes) {
+
+ my $fqcn = join ('.', $alias, $col);
+ $bind_attrs->{$fqcn} = $bindtypes->{$col} if $bindtypes->{$col};
+
+ # so that unqualified searches can be bound too
+ $bind_attrs->{$col} = $bind_attrs->{$fqcn} if $alias eq 'me';
+ }
}
- my $bind_attrs = {}; ## Future support
+
+ # This would be the point to deflate anything found in $condition
+ # (and leave $attrs->{bind} intact). Problem is - inflators historically
+ # expect a row object. And all we have is a resultsource (it is trivial
+ # to extract deflator coderefs via $alias2source above).
+ #
+ # I don't see a way forward other than changing the way deflators are
+ # invoked, and that's just bad...
+
my @args = ('select', $attrs->{bind}, $ident, $bind_attrs, $select, $condition, $order);
if ($attrs->{software_limit} ||
- $self->sql_maker->_default_limit_syntax eq "GenericSubQ") {
+ $sql_maker->_default_limit_syntax eq "GenericSubQ") {
$attrs->{software_limit} = 1;
} else {
$self->throw_exception("rows attribute must be positive if present")
return @args;
}
+sub _resolve_ident_sources {
+ my ($self, $ident) = @_;
+
+ my $alias2source = {};
+
+ # 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") ) {
+ # this is compat mode for insert/update/delete which do not deal with aliases
+ $alias2source->{me} = $ident;
+ }
+ elsif (ref $ident eq 'ARRAY') {
+
+ for (@$ident) {
+ my $tabinfo;
+ if (ref $_ eq 'HASH') {
+ $tabinfo = $_;
+ }
+ if (ref $_ eq 'ARRAY' and ref $_->[0] eq 'HASH') {
+ $tabinfo = $_->[0];
+ }
+
+ $alias2source->{$tabinfo->{-alias}} = $tabinfo->{-source_handle}->resolve
+ if ($tabinfo->{-source_handle});
+ }
+ }
+
+ return $alias2source;
+}
+
+# Takes $ident, \@column_names
+#
+# returns { $column_name => \%column_info, ... }
+# also note: this adds -result_source => $rsrc to the column info
+#
+# usage:
+# my $col_sources = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
+sub _resolve_column_info {
+ my ($self, $ident, $colnames) = @_;
+ my $alias2src = $self->_resolve_ident_sources($ident);
+
+ my $sep = $self->_sql_maker_opts->{name_sep} || '.';
+ $sep = "\Q$sep\E";
+
+ my %return;
+ foreach my $col (@{$colnames}) {
+ $col =~ m/^ (?: ([^$sep]+) $sep)? (.+) $/x;
+
+ my $alias = $1 || 'me';
+ my $colname = $2;
+
+ my $rsrc = $alias2src->{$alias};
+ $return{$col} = $rsrc && { %{$rsrc->column_info($colname)}, -result_source => $rsrc };
+ }
+ return \%return;
+}
+
+sub count {
+ my ($self, $source, $attrs) = @_;
+
+ my $tmp_attrs = { %$attrs };
+
+ # take off any pagers, record_filter is cdbi, and no point of ordering a count
+ delete $tmp_attrs->{$_} for (qw/select as rows offset page order_by record_filter/);
+
+ # overwrite the selector
+ $tmp_attrs->{select} = { count => '*' };
+
+ my $tmp_rs = $source->resultset_class->new($source, $tmp_attrs);
+ my ($count) = $tmp_rs->cursor->next;
+
+ # if the offset/rows attributes are still present, we did not use
+ # a subquery, so we need to make the calculations in software
+ $count -= $attrs->{offset} if $attrs->{offset};
+ $count = $attrs->{rows} if $attrs->{rows} and $attrs->{rows} < $count;
+ $count = 0 if ($count < 0);
+
+ return $count;
+}
+
+sub count_grouped {
+ my ($self, $source, $attrs) = @_;
+
+ # copy for the subquery, we need to do some adjustments to it too
+ my $sub_attrs = { %$attrs };
+
+ # these can not go in the subquery, and there is no point of ordering it
+ delete $sub_attrs->{$_} for qw/prefetch collapse select as order_by/;
+
+ # if we prefetch, we group_by primary keys only as this is what we would get out of the rs via ->next/->all
+ # simply deleting group_by suffices, as the code below will re-fill it
+ # Note: we check $attrs, as $sub_attrs has collapse deleted
+ if (ref $attrs->{collapse} and keys %{$attrs->{collapse}} ) {
+ delete $sub_attrs->{group_by};
+ }
+
+ $sub_attrs->{group_by} ||= [ map { "$attrs->{alias}.$_" } ($source->primary_columns) ];
+ $sub_attrs->{select} = $self->_grouped_count_select ($source, $sub_attrs);
+
+ $attrs->{from} = [{
+ count_subq => $source->resultset_class->new ($source, $sub_attrs )->as_query
+ }];
+
+ # the subquery replaces this
+ delete $attrs->{$_} for qw/where bind prefetch collapse group_by having having_bind rows offset page pager/;
+
+ return $self->count ($source, $attrs);
+}
+
+#
+# Returns a SELECT to go with a supplied GROUP BY
+# (caled by count_grouped so a group_by is present)
+# Most databases expect them to match, but some
+# choke in various ways.
+#
+sub _grouped_count_select {
+ my ($self, $source, $rs_args) = @_;
+ return $rs_args->{group_by};
+}
+
sub source_bind_attributes {
my ($self, $source) = @_;
-
+
my $bind_attributes;
foreach my $column ($source->columns) {
-
+
my $data_type = $source->column_info($column)->{data_type} || '';
$bind_attributes->{$column} = $self->bind_attribute_by_data_type($data_type)
if $data_type;
return;
}
-=head2 create_ddl_dir
+=head2 is_datatype_numeric
+
+Given a datatype from column_info, returns a boolean value indicating if
+the current RDBMS considers it a numeric value. This controls how
+L<DBIx::Class::Row/set_column> decides whether to mark the column as
+dirty - when the datatype is deemed numeric a C<< != >> comparison will
+be performed instead of the usual C<eq>.
+
+=cut
+
+sub is_datatype_numeric {
+ my ($self, $dt) = @_;
+
+ return 0 unless $dt;
+
+ return $dt =~ /^ (?:
+ numeric | int(?:eger)? | (?:tiny|small|medium|big)int | dec(?:imal)? | real | float | double (?: \s+ precision)? | (?:big)?serial
+ ) $/ix;
+}
+
+
+=head2 create_ddl_dir (EXPERIMENTAL)
=over 4
=back
Creates a SQL file based on the Schema, for each of the specified
-database types, in the given directory.
+database engines in C<\@databases> in the given directory.
+(note: specify L<SQL::Translator> names, not L<DBI> driver names).
+
+Given a previous version number, this will also create a file containing
+the ALTER TABLE statements to transform the previous schema into the
+current one. Note that these statements may contain C<DROP TABLE> or
+C<DROP COLUMN> statements that can potentially destroy data.
+
+The file names are created using the C<ddl_filename> method below, please
+override this method in your schema if you would like a different file
+name format. For the ALTER file, the same format is used, replacing
+$version in the name with "$preversion-$version".
+
+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>.
+
+If no arguments are passed, then the following default values are assumed:
+
+=over 4
+
+=item databases - ['MySQL', 'SQLite', 'PostgreSQL']
+
+=item version - $schema->schema_version
+
+=item directory - './'
+
+=item preversion - <none>
+
+=back
By default, C<\%sqlt_args> will have
{ add_drop_table => 1, ignore_constraint_names => 1, ignore_index_names => 1 }
-merged with the hash passed in. To disable any of those features, pass in a
+merged with the hash passed in. To disable any of those features, pass in a
hashref like the following
{ ignore_constraint_names => 0, # ... other options }
+
+Note that this feature is currently EXPERIMENTAL and may not work correctly
+across all databases, or fully handle complex relationships.
+
+WARNING: Please check all SQL files created, before applying them.
+
=cut
sub create_ddl_dir {
$version ||= $schema_version;
$sqltargs = {
- add_drop_table => 1,
+ add_drop_table => 1,
ignore_constraint_names => 1,
ignore_index_names => 1,
%{$sqltargs || {}}
}
print $file $output;
close($file);
-
+
next unless ($preversion);
require SQL::Translator::Diff;
carp("Overwriting existing diff file - $difffile");
unlink($difffile);
}
-
+
my $source_schema;
{
my $t = SQL::Translator->new($sqltargs);
unless ( $source_schema->name );
}
- # The "new" style of producers have sane normalization and can support
+ # The "new" style of producers have sane normalization and can support
# diffing a SQL file against a DBIC->SQLT schema. Old style ones don't
# And we have to diff parsed SQL against parsed SQL.
my $dest_schema = $sqlt_schema;
$dest_schema->name( $filename )
unless $dest_schema->name;
}
-
+
my $diff = SQL::Translator::Diff::schema_diff($source_schema, $db,
$dest_schema, $db,
$sqltargs
);
- if(!open $file, ">$difffile") {
+ if(!open $file, ">$difffile") {
$self->throw_exception("Can't write to $difffile ($!)");
next;
}
=back
Returns the statements used by L</deploy> and L<DBIx::Class::Schema/deploy>.
-The database driver name is given by C<$type>, though the value from
-L</sqlt_type> is used if it is not specified.
+
+The L<SQL::Translator> (not L<DBI>) database driver name can be explicitly
+provided in C<$type>, otherwise the result of L</sqlt_type> is used as default.
C<$directory> is used to return statements from files in a previously created
L</create_ddl_dir> directory and is optional. The filenames are constructed
if(-f $filename)
{
my $file;
- open($file, "<$filename")
+ open($file, "<$filename")
or $self->throw_exception("Can't open $filename ($!)");
my @rows = <$file>;
close($file);
eval qq{use SQL::Translator::Producer::${type}};
$self->throw_exception($@) if $@;
- # sources needs to be a parser arg, but for simplicty allow at top level
+ # sources needs to be a parser arg, but for simplicty allow at top level
# coming in
$sqltargs->{parser_args}{sources} = delete $sqltargs->{sources}
if exists $sqltargs->{sources};
}
$self->_query_end($line);
};
- my @statements = $self->deployment_statements($schema, $type, undef, $dir, { no_comments => 1, %{ $sqltargs || {} } } );
+ my @statements = $self->deployment_statements($schema, $type, undef, $dir, { %{ $sqltargs || {} }, no_comments => 1 } );
if (@statements > 1) {
foreach my $statement (@statements) {
$deploy->( $statement );
sub is_replicating {
return;
-
+
}
=head2 lag_behind_master