1 package DBIx::Class::SQLMaker;
8 DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class
12 This module is a subclass of L<SQL::Abstract> and includes a number of
13 DBIC-specific workarounds, not yet suitable for inclusion into the
14 L<SQL::Abstract> core. It also provides all (and more than) the functionality
15 of L<SQL::Abstract::Limit>, see L<DBIx::Class::SQLMaker::LimitDialects> for
18 Currently the enhancements to L<SQL::Abstract> are:
22 =item * Support for C<JOIN> statements (via extended C<table/from> support)
24 =item * Support of functions in C<SELECT> lists
26 =item * C<GROUP BY>/C<HAVING> support (via extensions to the order_by parameter)
28 =item * Support of C<...FOR UPDATE> type of select statement modifiers
30 =item * The L</-ident> operator
32 =item * The L</-value> operator
40 DBIx::Class::SQLMaker::LimitDialects
44 use Module::Runtime qw(use_module);
45 use Sub::Name 'subname';
46 use DBIx::Class::Carp;
47 use DBIx::Class::Exception;
51 has limit_dialect => (
52 is => 'rw', default => sub { 'LimitOffset' },
53 trigger => sub { shift->clear_renderer_class }
57 if ($_[0]->can('emulate_limit')) {
59 The ancient and horrible emulate_limit method was deprecated for many moons.
60 Now, it is no more. Time to rewrite the code in ${\ref($_[0])}
65 our %LIMIT_DIALECT_MAP = (
66 'GenericSubQ' => 'GenericSubquery',
69 sub mapped_limit_dialect {
71 my $unmapped = $self->limit_dialect;
72 $LIMIT_DIALECT_MAP{$unmapped}||$unmapped;
75 around _build_renderer_roles => sub {
76 my ($orig, $self) = (shift, shift);
79 'Data::Query::Renderer::SQL::Slice::'.$self->mapped_limit_dialect
83 # for when I need a normalized l/r pair
86 { defined $_ ? $_ : '' }
87 ( ref $_[0]->{quote_char} ? (@{$_[0]->{quote_char}}) : ( ($_[0]->{quote_char}) x 2 ) )
91 sub _build_converter_class {
92 Module::Runtime::use_module('DBIx::Class::SQLMaker::Converter')
95 # FIXME when we bring in the storage weaklink, check its schema
96 # weaklink and channel through $schema->throw_exception
97 sub throw_exception { DBIx::Class::Exception->throw($_[1]) }
100 # reinstall the belch()/puke() functions of SQL::Abstract with custom versions
101 # that use DBIx::Class::Carp/DBIx::Class::Exception instead of plain Carp
102 no warnings qw/redefine/;
104 *SQL::Abstract::belch = subname 'SQL::Abstract::belch' => sub (@) {
105 my($func) = (caller(1))[3];
106 carp "[$func] Warning: ", @_;
109 *SQL::Abstract::puke = subname 'SQL::Abstract::puke' => sub (@) {
110 my($func) = (caller(1))[3];
111 __PACKAGE__->throw_exception("[$func] Fatal: " . join ('', @_));
114 # Current SQLA pollutes its namespace - clean for the time being
115 namespace::clean->clean_subroutines(qw/SQL::Abstract carp croak confess/);
118 # the "oh noes offset/top without limit" constant
119 # limited to 31 bits for sanity (and consistency,
120 # since it may be handed to the like of sprintf %u)
122 # Also *some* builds of SQLite fail the test
123 # some_column BETWEEN ? AND ?: 1, 4294967295
124 # with the proper integer bind attrs
126 # Implemented as a method, since ::Storage::DBI also
127 # refers to it (i.e. for the case of software_limit or
128 # as the value to abuse with MSSQL ordered subqueries)
129 sub __max_int () { 0x7FFFFFFF };
131 # poor man's de-qualifier
133 $_[0]->next::method( ( $_[0]{_dequalify_idents} and ! ref $_[1] )
134 ? $_[1] =~ / ([^\.]+) $ /x
140 carp_unique ("-nest in search conditions is deprecated, you most probably wanted:\n"
141 .q|{..., -and => [ \%cond0, \@cond1, \'cond2', \[ 'cond3', [ col => bind ] ], etc. ], ... }|
144 shift->next::method(@_);
147 # Handle limit-dialect selection
149 my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
151 if (defined $offset) {
152 $self->throw_exception('A supplied offset must be a non-negative integer')
153 if ( $offset =~ /\D/ or $offset < 0 );
157 if (defined $limit) {
158 $self->throw_exception('A supplied limit must be a positive integer')
159 if ( $limit =~ /\D/ or $limit <= 0 );
162 $limit = $self->__max_int;
165 my %final_attrs = (%{$rs_attrs||{}}, limit => $limit, offset => $offset);
167 if ($limit or $offset) {
168 my %slice_stability = $self->renderer->slice_stability;
170 if (my $stability = $slice_stability{$offset ? 'offset' : 'limit'}) {
171 my $source = $rs_attrs->{_rsroot_rsrc};
173 $final_attrs{order_is_stable}
174 = $final_attrs{preserve_order}
175 = $source->schema->storage
176 ->_order_by_is_stable(
177 @final_attrs{qw(from order_by where)}
180 if ($stability eq 'requires') {
181 if ($self->converter->_order_by_to_dq($final_attrs{order_by})) {
182 $self->throw_exception(
183 $self->limit_dialect.' limit/offset implementation requires a stable order for offset'
186 if (my $ident_cols = $source->_identifying_column_set) {
187 $final_attrs{order_by} = [
188 map "$final_attrs{alias}.$_", @$ident_cols
190 $final_attrs{order_is_stable} = 1;
192 $self->throw_exception(sprintf(
193 'Unable to auto-construct stable order criteria for "skimming type"
195 . "dialect based on source '%s'", $source->name) );
202 my %slice_subquery = $self->renderer->slice_subquery;
204 if (my $subquery = $slice_subquery{$offset ? 'offset' : 'limit'}) {
206 my $f = $fields->[$_];
208 $f = { '' => $f } unless ref($f) eq 'HASH';
209 ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
210 } elsif ($f !~ /^\Q$final_attrs{alias}${\$self->name_sep}/) {
212 ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
219 my ($sql, @bind) = $self->next::method ($table, $fields, $where, $final_attrs{order_by}, \%final_attrs );
221 $sql .= $self->_lock_select ($rs_attrs->{for})
224 return wantarray ? ($sql, @bind) : $sql;
227 sub _assemble_binds {
229 return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/pre_select select from where group having order limit/);
233 update => 'FOR UPDATE',
234 shared => 'FOR SHARE',
237 my ($self, $type) = @_;
240 if (ref($type) eq 'SCALAR') {
244 $sql = $for_syntax->{$type} || $self->throw_exception( "Unknown SELECT .. FO
245 R type '$type' requested" );
252 scalar shift->_render_sqla(table => \@_);
261 Used to explicitly specify an SQL identifier. Takes a plain string as value
262 which is then invariably treated as a column name (and is being properly
263 quoted if quoting has been requested). Most useful for comparison of two
267 priority => { '<', 2 },
268 requestor => { -ident => 'submitter' }
273 $stmt = 'WHERE "priority" < ? AND "requestor" = "submitter"';
278 The -value operator signals that the argument to the right is a raw bind value.
279 It will be passed straight to DBI, without invoking any of the SQL::Abstract
280 condition-parsing logic. This allows you to, for example, pass an array as a
281 column value for databases that support array datatypes, e.g.:
284 array => { -value => [1, 2, 3] }
289 $stmt = 'WHERE array = ?';
294 See L<DBIx::Class/CONTRIBUTORS>.
298 You may distribute this code under the same terms as Perl itself.