POD.
# Deprecated/internal modules need no exposure
no_index directory => $_ for (qw|
lib/DBIx/Class/Admin
- lib/DBIx/Class/SQLAHacks
lib/DBIx/Class/PK/Auto
lib/DBIx/Class/CDBICompat
|);
no_index package => $_ for (qw/
- DBIx::Class::SQLAHacks DBIx::Class::Storage::DBIHacks
+ DBIx::Class::Storage::DBIHacks
/);
-
WriteAll();
# Re-write META.yml to _exclude_ all forced requires (we do not want to ship this)
return $rv;
}
-=head2 Setting limit dialect for SQL::Abstract::Limit
-
-In some cases, SQL::Abstract::Limit cannot determine the dialect of
-the remote SQL server by looking at the database handle. This is a
-common problem when using the DBD::JDBC, since the DBD-driver only
-know that in has a Java-driver available, not which JDBC driver the
-Java component has loaded. This specifically sets the limit_dialect
-to Microsoft SQL-server (See more names in SQL::Abstract::Limit
--documentation.
-
- __PACKAGE__->storage->sql_maker->limit_dialect('mssql');
-
-The JDBC bridge is one way of getting access to a MSSQL server from a platform
-that Microsoft doesn't deliver native client libraries for. (e.g. Linux)
-
-The limit dialect can also be set at connect time by specifying a
-C<limit_dialect> key in the final hash as shown above.
-
=head2 Working with PostgreSQL array types
You can also assign values to PostgreSQL array columns by passing array
-package # Hide from PAUSE
- DBIx::Class::SQLAHacks;
+package DBIx::Class::SQLMaker;
-# This module is a subclass of SQL::Abstract and includes a number of
-# DBIC-specific workarounds, not yet suitable for inclusion into the
-# SQLA core.
-# It also provides all (and more than) the functionality of
-# SQL::Abstract::Limit, which proved to be very hard to keep updated
+=head1 NAME
+
+DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class
+
+=head1 DESCRIPTION
+
+This module is a subclass of L<SQL::Abstract> and includes a number of
+DBIC-specific workarounds, not yet suitable for inclusion into the
+L<SQL::Abstract> core. It also provides all (and more than) the functionality
+of L<SQL::Abstract::Limit>, see L<DBIx::Class::SQLMaker::LimitDialects> for
+more info.
+
+Currently the enhancements to L<SQL::Abstract> are:
+
+=over
+
+=item * Support for C<JOIN> statements (via extended C<table/from> support)
+
+=item * Support of functions in C<SELECT> lists
+
+=item * C<GROUP BY>/C<HAVING> support (via extensions to the order_by parameter)
+
+=item * Support of C<...FOR UPDATE> type of select statement modifiers
+
+=back
+
+=cut
use base qw/
- DBIx::Class::SQLAHacks::LimitDialects
+ DBIx::Class::SQLMaker::LimitDialects
SQL::Abstract
Class::Accessor::Grouped
/;
my $clan_import = \&{$f};
*{"SQL::Abstract::$f"} = subname "SQL::Abstract::$f" =>
sub {
- if (Carp::longmess() =~ /DBIx::Class::SQLAHacks::[\w]+ .+? called \s at/x) {
+ if (Carp::longmess() =~ /DBIx::Class::SQLMaker::[\w]+ .+? called \s at/x) {
$clan_import->(@_);
}
else {
my $dialect = $self->limit_dialect
or croak "Unable to generate SQL-limit - no limit dialect specified on $self, and no emulate_limit method found";
$self->can ("_$dialect")
- or croak "SQLAHacks does not implement the requested dialect '$dialect'";
+ or croak (__PACKAGE__ . " does not implement the requested dialect '$dialect'");
}
;
}
1;
+
+=head1 AUTHORS
+
+See L<DBIx::Class/CONTRIBUTORS>.
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
-package DBIx::Class::SQLAHacks::LimitDialects;
+package DBIx::Class::SQLMaker::LimitDialects;
use warnings;
use strict;
}
### end-of-FIXME
-# PostgreSQL and SQLite
+=head1 NAME
+
+DBIx::Class::SQLMaker::LimitDialects - SQL::Abstract::Limit-like functionality for DBIx::Class::SQLMaker
+
+=head1 DESCRIPTION
+
+This module replicates a lot of the functionality originally found in
+L<SQL::Abstract::Limit>. While simple limits would work as-is, the more
+complex dialects that require e.g. subqueries could not be reliably
+implemented without taking full advantage of the metadata locked within
+L<DBIx::Class::ResultSource> classes. After reimplementation of close to
+80% of the L<SQL::Abstract::Limit> functionality it was deemed more
+practical to simply make an independent DBIx::Class-specific limit-dialect
+provider.
+
+=head1 SQL LIMIT DIALECTS
+
+Note that the actual implementations listed below never use C<*> literally.
+Instead proper re-aliasing of selectors and order criteria is done, so that
+the limit dialect are safe to use on joined resultsets with clashing column
+names.
+
+Currently the provided dialects are:
+
+=cut
+
+=head2 LimitOffset
+
+ SELECT ... LIMIT $limit OFFSET $offset
+
+Supported by B<PostgreSQL> and B<SQLite>
+
+=cut
sub _LimitOffset {
my ( $self, $sql, $order, $rows, $offset ) = @_;
$sql .= $self->_order_by( $order ) . " LIMIT $rows";
return $sql;
}
-# MySQL and any SQL::Statement based DBD
+=head2 LimitXY
+
+ SELECT ... LIMIT $offset $limit
+
+Supported by B<MySQL> and any L<SQL::Statement> based DBD
+
+=cut
sub _LimitXY {
my ( $self, $sql, $order, $rows, $offset ) = @_;
$sql .= $self->_order_by( $order ) . " LIMIT ";
$sql .= $rows;
return $sql;
}
-# ANSI standard Limit/Offset implementation. DB2 and MSSQL >= 2005 use this
+
+=head2 RowNumberOver
+
+ SELECT * FROM (
+ SELECT *, ROW_NUMBER() OVER( ORDER BY ... ) AS RNO__ROW__INDEX FROM (
+ SELECT ...
+ )
+ ) WHERE RNO__ROW__INDEX BETWEEN ($offset+1) AND ($limit+$offset)
+
+
+ANSI standard Limit/Offset implementation. Supported by B<DB2> and
+B<< MSSQL >= 2005 >>.
+
+=cut
sub _RowNumberOver {
my ($self, $sql, $rs_attrs, $rows, $offset ) = @_;
return undef;
}
-# Informix specific limit, almost like LIMIT/OFFSET
-# According to SQLA::Limit informix also supports
-# SKIP X LIMIT Y (in addition to SKIP X FIRST Y)
+=head2 SkipFirst
+
+ SELECT SKIP $offset FIRST $limit * FROM ...
+
+Suported by B<Informix>, almost like LimitOffset. According to
+L<SQL::Abstract::Limit> C<... SKIP $offset LIMIT $limit ...> is also supported.
+
+=cut
sub _SkipFirst {
my ($self, $sql, $rs_attrs, $rows, $offset) = @_;
);
}
-# Firebird specific limit, reverse of _SkipFirst for Informix
-# According to SQLA::Limit firebird/interbase also supports
-# ROWS X TO Y (in addition to FIRST X SKIP Y)
+=head2 FirstSkip
+
+ SELECT FIRST $limit SKIP $offset * FROM ...
+
+Supported by B<Firebird/Interbase>, reverse of SkipFirst. According to
+L<SQL::Abstract::Limit> C<... ROWS $limit TO $offset ...> is also supported.
+
+=cut
sub _FirstSkip {
my ($self, $sql, $rs_attrs, $rows, $offset) = @_;
);
}
-# WhOracle limits
+=head2 RowNum
+
+ SELECT * FROM (
+ SELECT *, ROWNUM rownum__index FROM (
+ SELECT ...
+ )
+ ) WHERE rownum__index BETWEEN ($offset+1) AND ($limit+$offset)
+
+Supported by B<Oracle>.
+
+=cut
sub _RowNum {
my ( $self, $sql, $rs_attrs, $rows, $offset ) = @_;
return $sql;
}
-# Crappy Top based Limit/Offset support. Legacy for MSSQL < 2005
+=head2 Top
+
+ SELECT * FROM
+
+ SELECT TOP $limit FROM (
+ SELECT TOP $limit FROM (
+ SELECT TOP ($limit+$offset) ...
+ ) ORDER BY $reversed_original_order
+ ) ORDER BY $original_order
+
+Unreliable Top-based implementation, supported by B<< MSSQL < 2005 >>.
+
+=head3 CAVEAT
+
+Due to its implementation, this limit dialect returns B<incorrect results>
+when $limit+$offset > total amount of rows in the resultset.
+
+=cut
sub _Top {
my ( $self, $sql, $rs_attrs, $rows, $offset ) = @_;
return $sql;
}
-# This for Sybase ASE, to use SET ROWCOUNT when there is no offset, and
-# GenericSubQ otherwise.
+=head2 RowCountOrGenericSubQ
+
+This is not exactly a limit dialect, but more of a proxy for B<Sybase ASE>.
+If no $offset is supplied the limit is simply performed as:
+
+ SET ROWCOUNT $limit
+ SELECT ...
+ SET ROWCOUNT 0
+
+Otherwise we fall back to L</GenericSubQ>
+
+=cut
sub _RowCountOrGenericSubQ {
my $self = shift;
my ($sql, $rs_attrs, $rows, $offset) = @_;
EOF
}
-# This is the most evil limit "dialect" (more of a hack) for *really*
-# stupid databases. It works by ordering the set by some unique column,
-# and calculating amount of rows that have a less-er value (thus
-# emulating a RowNum-like index). Of course this implies the set can
-# only be ordered by a single unique columns.
+=head2 GenericSubQ
+
+ SELECT * FROM (
+ SELECT ...
+ )
+ WHERE (
+ SELECT COUNT(*) FROM $original_table cnt WHERE cnt.id < $original_table.id
+ ) BETWEEN $offset AND ($offset+$rows-1)
+
+This is the most evil limit "dialect" (more of a hack) for I<really> stupid
+databases. It works by ordering the set by some unique column, and calculating
+the amount of rows that have a less-er value (thus emulating a L</RowNum>-like
+index). Of course this implies the set can only be ordered by a single unique
+column. Also note that this technique can be and often is B<excruciatingly
+slow>.
+
+Currently used by B<Sybase ASE>, due to lack of any other option.
+
+=cut
sub _GenericSubQ {
my ($self, $sql, $rs_attrs, $rows, $offset) = @_;
}
1;
+
+=head1 AUTHORS
+
+See L<DBIx::Class/CONTRIBUTORS>.
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
package # Hide from PAUSE
- DBIx::Class::SQLAHacks::MSSQL;
+ DBIx::Class::SQLMaker::MSSQL;
-use base qw( DBIx::Class::SQLAHacks );
+use base qw( DBIx::Class::SQLMaker );
use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
#
package # Hide from PAUSE
- DBIx::Class::SQLAHacks::MySQL;
+ DBIx::Class::SQLMaker::MySQL;
-use base qw( DBIx::Class::SQLAHacks );
+use base qw( DBIx::Class::SQLMaker );
use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
#
package # Hide from PAUSE
- DBIx::Class::SQLAHacks::Oracle;
+ DBIx::Class::SQLMaker::Oracle;
use warnings;
use strict;
-use base qw( DBIx::Class::SQLAHacks );
+use base qw( DBIx::Class::SQLMaker );
use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
sub new {
}
1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-DBIx::Class::SQLAHacks::Oracle - adds hierarchical query support for Oracle to SQL::Abstract
-
-=head1 DESCRIPTION
-
-See L<DBIx::Class::Storage::DBI::Oracle::Generic> for more information about
-how to use hierarchical queries with DBIx::Class.
-
-=cut
package # Hide from PAUSE
- DBIx::Class::SQLAHacks::OracleJoins;
+ DBIx::Class::SQLMaker::OracleJoins;
-use base qw( DBIx::Class::SQLAHacks );
+use base qw( DBIx::Class::SQLMaker );
use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
sub select {
=head1 NAME
-DBIx::Class::SQLAHacks::OracleJoins - Pre-ANSI Joins-via-Where-Clause Syntax
+DBIx::Class::SQLMaker::OracleJoins - Pre-ANSI Joins-via-Where-Clause Syntax
=head1 PURPOSE
=item select ($\@$;$$@)
-Replaces DBIx::Class::SQLAHacks's select() method, which calls _oracle_joins()
+Replaces DBIx::Class::SQLMaker's select() method, which calls _oracle_joins()
to modify the column and table list before calling SUPER::select().
=item _recurse_from ($$\@)
=item L<DBIx::Class::Storage::DBI::Oracle::WhereJoins> - Storage class using this
-=item L<DBIx::Class::SQLAHacks> - Parent module
+=item L<DBIx::Class::SQLMaker> - Parent module
=item L<DBIx::Class> - Duh
package # Hide from PAUSE
- DBIx::Class::SQLAHacks::SQLite;
+ DBIx::Class::SQLMaker::SQLite;
-use base qw( DBIx::Class::SQLAHacks );
+use base qw( DBIx::Class::SQLMaker );
use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
#
=head2 sql_maker
Returns a C<sql_maker> object - normally an object of class
-C<DBIx::Class::SQLAHacks>.
+C<DBIx::Class::SQLMaker>.
=cut
__PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor');
__PACKAGE__->mk_group_accessors('inherited' => qw/sql_maker_class sql_limit_dialect/);
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker');
__PACKAGE__->mk_group_accessors('simple' => qw/
_connect_info _dbi_connect_info _dbic_connect_attributes _driver_determined
=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
-driver alone. See also L<SQL::Abstract::Limit>.
+Sets a specific SQL::Abstract::Limit-style limit dialect, overriding the
+default L</sql_limit_dialect> setting of the storage (if any). For a list
+of available limit dialects see L<DBIx::Class::SQLMaker::LimitDialects>.
=item quote_char
}
}
- # Sanity check the attributes (SQLAHacks does it too, but
+ # Sanity check the attributes (SQLMaker does it too, but
# in case of a software_limit we'll never reach there)
if (defined $attrs->{offset}) {
$self->throw_exception('A supplied offset attribute must be a non-negative integer')
return @row;
}
+=head2 sql_limit_dialect
+
+This is an accessor for the default SQL limit dialect used by a particular
+storage driver. Can be overriden by supplying an explicit L</limit_dialect>
+to L<DBIx::Class::Schema/connect>. For a list of available limit dialects
+see L<DBIx::Class::SQLMaker::LimitDialects>.
+
=head2 sth
=over 4
=head1 DESCRIPTION
This class implements autoincrements for Firebird using C<RETURNING> as well as
-L<auto_nextval|DBIx::Class::ResultSource/auto_nextval> sets the limit dialect to
-C<FIRST X SKIP X> and provides L<DBIx::Class::InflateColumn::DateTime> support.
+L<auto_nextval|DBIx::Class::ResultSource/auto_nextval> and provides
+L<DBIx::Class::InflateColumn::DateTime> support.
You need to use either the
L<disable_sth_caching|DBIx::Class::Storage::DBI/disable_sth_caching> option or
_identity _identity_method
/);
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::MSSQL');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::MSSQL');
sub _set_identity_insert {
my ($self, $table) = @_;
use base qw/DBIx::Class::Storage::DBI/;
use mro 'c3';
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::Oracle');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::Oracle');
sub deployment_statements {
my $self = shift;;
use base qw( DBIx::Class::Storage::DBI::Oracle::Generic );
use mro 'c3';
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::OracleJoins');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::OracleJoins');
1;
=head1 METHODS
-See L<DBIx::Class::SQLAHacks::OracleJoins> for implementation details.
+See L<DBIx::Class::SQLMaker::OracleJoins> for implementation details.
=head1 BUGS
=over
-=item L<DBIx::Class::SQLAHacks>
+=item L<DBIx::Class::SQLMaker>
-=item L<DBIx::Class::SQLAHacks::OracleJoins>
+=item L<DBIx::Class::SQLMaker::OracleJoins>
=item L<DBIx::Class::Storage::DBI::Oracle::Generic>
=head1 DESCRIPTION
-This class implements autoincrements for Sybase SQL Anywhere, selects the
-RowNumberOver limit implementation and provides
+This class implements autoincrements for Sybase SQL Anywhere and provides
L<DBIx::Class::InflateColumn::DateTime> support.
You need the C<DBD::SQLAnywhere> driver that comes with the SQL Anywhere
use File::Copy;
use File::Spec;
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::SQLite');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::SQLite');
__PACKAGE__->sql_limit_dialect ('LimitOffset');
sub backup
=item *
-Adaptive Server Anywhere (ASA) support, with possible SQLA::Limit support.
+Adaptive Server Anywhere (ASA) support
=item *
/;
use mro 'c3';
-__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::MySQL');
+__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::MySQL');
__PACKAGE__->sql_limit_dialect ('LimitXY');
sub with_deferred_fk_checks {
my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
$schema2->resultset("Artist")->find(4);
-isa_ok($schema2->storage->sql_maker, 'DBIx::Class::SQLAHacks::MySQL');
+isa_ok($schema2->storage->sql_maker, 'DBIx::Class::SQLMaker::MySQL');
done_testing;
my $orig_debug = $schema->storage->debug;
# test the abstract join => SQL generator
-my $sa = new DBIx::Class::SQLAHacks;
+my $sa = new DBIx::Class::SQLMaker;
my @j = (
{ child => 'person' },
);
}
-# Make sure the carp/croak override in SQLA works (via SQLAHacks)
+# Make sure the carp/croak override in SQLA works (via SQLMaker)
my $file = quotemeta (__FILE__);
throws_ok (sub {
$schema->resultset ('Artist')->search ({}, { order_by => { -asc => 'stuff', -desc => 'staff' } } )->as_query;
--- /dev/null
+use strict;
+use warnings;
+
+use Test::More;
+
+use lib qw(t/lib);
+use DBICTest::Schema;
+use DBIC::SqlMakerTest;
+
+# This is legacy stuff from SQL::Absract::Limit
+# Keep it around just in case someone is using it
+
+{
+ package DBICTest::SQLMaker::CustomDialect;
+ use base qw/DBIx::Class::SQLMaker/;
+ sub emulate_limit {
+ my ($self, $sql, $rs_attrs, $limit, $offset) = @_;
+ return sprintf ('shiny sproc ((%s), %d, %d)',
+ $sql,
+ $limit || 0,
+ $offset || 0,
+ );
+ }
+}
+
+my $s = DBICTest::Schema->connect ('dbi:SQLite::memory:');
+$s->storage->sql_maker_class ('DBICTest::SQLMaker::CustomDialect');
+
+my $rs = $s->resultset ('CD');
+is_same_sql_bind (
+ $rs->search ({}, { rows => 1, offset => 3,columns => [
+ { id => 'foo.id' },
+ { 'bar.id' => 'bar.id' },
+ { bleh => \ 'TO_CHAR (foo.womble, "blah")' },
+ ]})->as_query,
+ '(
+ shiny sproc (
+ (
+ SELECT foo.id, bar.id, TO_CHAR (foo.womble, "blah")
+ FROM cd me
+ ),
+ 1,
+ 3
+ )
+ )',
+ [],
+ 'Rownum subsel aliasing works correctly'
+);
+
+done_testing;
use Data::Dumper::Concise;
use lib qw(t/lib);
use DBIC::SqlMakerTest;
-use DBIx::Class::SQLAHacks::Oracle;
+use DBIx::Class::SQLMaker::Oracle;
#
# Offline test for connect_by
# TODO: NOCYCLE parameter doesn't work
);
-my $sqla_oracle = DBIx::Class::SQLAHacks::Oracle->new( quote_char => '"', name_sep => '.' );
-isa_ok($sqla_oracle, 'DBIx::Class::SQLAHacks::Oracle');
+my $sqla_oracle = DBIx::Class::SQLMaker::Oracle->new( quote_char => '"', name_sep => '.' );
+isa_ok($sqla_oracle, 'DBIx::Class::SQLMaker::Oracle');
for my $case (@handle_tests) {
use Test::More;
use lib qw(t/lib);
-use DBIx::Class::SQLAHacks::OracleJoins;
+use DBIx::Class::SQLMaker::OracleJoins;
use DBICTest;
use DBIC::SqlMakerTest;
-my $sa = new DBIx::Class::SQLAHacks::OracleJoins;
+my $sa = new DBIx::Class::SQLMaker::OracleJoins;
# search with undefined or empty $cond
# test some specific components whose parents are exempt below
'DBIx::Class::Relationship::Base' => {},
+ 'DBIx::Class::SQLMaker::LimitDialects' => {},
# internals
- 'DBIx::Class::SQLAHacks*' => { skip => 1 },
+ 'DBIx::Class::SQLMaker*' => { skip => 1 },
'DBIx::Class::Storage::DBI*' => { skip => 1 },
'SQL::Translator::*' => { skip => 1 },