fix stupid mistake in _extract_order_criteria
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / SQLMaker.pm
CommitLineData
d5dedbd6 1package DBIx::Class::SQLMaker;
6f4ddea1 2
a697fa31 3use strict;
4use warnings;
5
d5dedbd6 6=head1 NAME
7
8DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class
9
10=head1 DESCRIPTION
11
12This module is a subclass of L<SQL::Abstract> and includes a number of
13DBIC-specific workarounds, not yet suitable for inclusion into the
14L<SQL::Abstract> core. It also provides all (and more than) the functionality
15of L<SQL::Abstract::Limit>, see L<DBIx::Class::SQLMaker::LimitDialects> for
16more info.
17
18Currently the enhancements to L<SQL::Abstract> are:
19
20=over
21
22=item * Support for C<JOIN> statements (via extended C<table/from> support)
23
24=item * Support of functions in C<SELECT> lists
25
26=item * C<GROUP BY>/C<HAVING> support (via extensions to the order_by parameter)
27
28=item * Support of C<...FOR UPDATE> type of select statement modifiers
29
10cef607 30=item * The L</-ident> operator
31
32=item * The L</-value> operator
33
d5dedbd6 34=back
35
36=cut
6a247f33 37
38use base qw/
39 SQL::Abstract
10cef607 40 DBIx::Class::SQLMaker::LimitDialects
6a247f33 41/;
42use mro 'c3';
a697fa31 43
10cef607 44use Module::Runtime qw(use_module);
6298a324 45use Sub::Name 'subname';
70c28808 46use DBIx::Class::Carp;
10cef607 47use DBIx::Class::Exception;
48use Moo;
e8fc51c7 49use namespace::clean;
b2b22cd6 50
10cef607 51has limit_dialect => (
52 is => 'rw', default => sub { 'LimitOffset' },
53 trigger => sub { shift->clear_renderer_class }
54);
55
56our %LIMIT_DIALECT_MAP = (
57 'GenericSubQ' => 'GenericSubquery',
58 'RowCountOrGenericSubQ' => 'RowCountOrGenericSubquery',
59);
60
61sub mapped_limit_dialect {
62 my ($self) = @_;
63 my $unmapped = $self->limit_dialect;
64 $LIMIT_DIALECT_MAP{$unmapped}||$unmapped;
65}
66
67around _build_renderer_roles => sub {
68 my ($orig, $self) = (shift, shift);
69 return (
70 $self->$orig(@_),
71 'Data::Query::Renderer::SQL::Slice::'.$self->mapped_limit_dialect
72 );
73};
6a247f33 74
3f5b99fe 75# for when I need a normalized l/r pair
76sub _quote_chars {
77 map
78 { defined $_ ? $_ : '' }
79 ( ref $_[0]->{quote_char} ? (@{$_[0]->{quote_char}}) : ( ($_[0]->{quote_char}) x 2 ) )
80 ;
81}
82
10cef607 83sub _build_converter_class {
84 Module::Runtime::use_module('DBIx::Class::SQLMaker::Converter')
85}
86
70c28808 87# FIXME when we bring in the storage weaklink, check its schema
88# weaklink and channel through $schema->throw_exception
89sub throw_exception { DBIx::Class::Exception->throw($_[1]) }
90
b2b22cd6 91BEGIN {
2ea6032a 92 # reinstall the belch()/puke() functions of SQL::Abstract with custom versions
70c28808 93 # that use DBIx::Class::Carp/DBIx::Class::Exception instead of plain Carp
b2b22cd6 94 no warnings qw/redefine/;
2ea6032a 95
96 *SQL::Abstract::belch = subname 'SQL::Abstract::belch' => sub (@) {
97 my($func) = (caller(1))[3];
98 carp "[$func] Warning: ", @_;
99 };
100
101 *SQL::Abstract::puke = subname 'SQL::Abstract::puke' => sub (@) {
102 my($func) = (caller(1))[3];
70c28808 103 __PACKAGE__->throw_exception("[$func] Fatal: " . join ('', @_));
2ea6032a 104 };
10cef607 105
106 # Current SQLA pollutes its namespace - clean for the time being
107 namespace::clean->clean_subroutines(qw/SQL::Abstract carp croak confess/);
b2b22cd6 108}
6f4ddea1 109
e9657379 110# the "oh noes offset/top without limit" constant
fcb7fcbb 111# limited to 31 bits for sanity (and consistency,
112# since it may be handed to the like of sprintf %u)
113#
114# Also *some* builds of SQLite fail the test
115# some_column BETWEEN ? AND ?: 1, 4294967295
116# with the proper integer bind attrs
117#
6a247f33 118# Implemented as a method, since ::Storage::DBI also
119# refers to it (i.e. for the case of software_limit or
120# as the value to abuse with MSSQL ordered subqueries)
fcb7fcbb 121sub __max_int () { 0x7FFFFFFF };
e9657379 122
e39f188a 123# poor man's de-qualifier
124sub _quote {
125 $_[0]->next::method( ( $_[0]{_dequalify_idents} and ! ref $_[1] )
126 ? $_[1] =~ / ([^\.]+) $ /x
127 : $_[1]
128 );
129}
130
b1d821de 131sub _where_op_NEST {
70c28808 132 carp_unique ("-nest in search conditions is deprecated, you most probably wanted:\n"
b1d821de 133 .q|{..., -and => [ \%cond0, \@cond1, \'cond2', \[ 'cond3', [ col => bind ] ], etc. ], ... }|
70c28808 134 );
b1d821de 135
136 shift->next::method(@_);
137}
138
6a247f33 139# Handle limit-dialect selection
6f4ddea1 140sub select {
6a247f33 141 my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
142
6a247f33 143 if (defined $offset) {
70c28808 144 $self->throw_exception('A supplied offset must be a non-negative integer')
6a247f33 145 if ( $offset =~ /\D/ or $offset < 0 );
146 }
147 $offset ||= 0;
1cbd3034 148
6a247f33 149 if (defined $limit) {
70c28808 150 $self->throw_exception('A supplied limit must be a positive integer')
6a247f33 151 if ( $limit =~ /\D/ or $limit <= 0 );
152 }
153 elsif ($offset) {
154 $limit = $self->__max_int;
6f4ddea1 155 }
c2b7c5dc 156
10cef607 157 my %final_attrs = (%{$rs_attrs||{}}, limit => $limit, offset => $offset);
158
159 if ($limit or $offset) {
160 my %slice_stability = $self->renderer->slice_stability;
161
162 if (my $stability = $slice_stability{$offset ? 'offset' : 'limit'}) {
163 my $source = $rs_attrs->{_rsroot_rsrc};
164 unless (
165 $final_attrs{order_is_stable}
166 = $final_attrs{preserve_order}
167 = $source->schema->storage
168 ->_order_by_is_stable(
169 @final_attrs{qw(from order_by where)}
170 )
171 ) {
172 if ($stability eq 'requires') {
173 if ($self->converter->_order_by_to_dq($final_attrs{order_by})) {
174 $self->throw_exception(
175 $self->limit_dialect.' limit/offset implementation requires a stable order for offset'
176 );
177 }
178 if (my $ident_cols = $source->_identifying_column_set) {
179 $final_attrs{order_by} = [
180 map "$final_attrs{alias}.$_", @$ident_cols
181 ];
182 $final_attrs{order_is_stable} = 1;
183 } else {
184 $self->throw_exception(sprintf(
185 'Unable to auto-construct stable order criteria for "skimming type"
186 limit '
187 . "dialect based on source '%s'", $source->name) );
188 }
189 }
190 }
a6b68a60 191
67341081 192 }
67341081 193
10cef607 194 my %slice_subquery = $self->renderer->slice_subquery;
195
196 if (my $subquery = $slice_subquery{$offset ? 'offset' : 'limit'}) {
197 $fields = [ map {
198 my $f = $fields->[$_];
199 if (ref $f) {
200 $f = { '' => $f } unless ref($f) eq 'HASH';
201 ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
202 } elsif ($f !~ /^\Q$final_attrs{alias}${\$self->name_sep}/) {
203 $f = { '' => $f };
204 ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
205 }
206 $f;
207 } 0 .. $#$fields ];
67341081 208 }
6a247f33 209 }
583a0c65 210
10cef607 211 my ($sql, @bind) = $self->next::method ($table, $fields, $where, $final_attrs{order_by}, \%final_attrs );
583a0c65 212
e5372da4 213 $sql .= $self->_lock_select ($rs_attrs->{for})
214 if $rs_attrs->{for};
215
10cef607 216 return wantarray ? ($sql, @bind) : $sql;
583a0c65 217}
218
219sub _assemble_binds {
220 my $self = shift;
8b31f62e 221 return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/pre_select select from where group having order limit/);
6f4ddea1 222}
223
e5372da4 224my $for_syntax = {
225 update => 'FOR UPDATE',
226 shared => 'FOR SHARE',
227};
228sub _lock_select {
229 my ($self, $type) = @_;
8249c09b 230
231 my $sql;
232 if (ref($type) eq 'SCALAR') {
233 $sql = "FOR $$type";
234 }
235 else {
10cef607 236 $sql = $for_syntax->{$type} || $self->throw_exception( "Unknown SELECT .. FO
237R type '$type' requested" );
8249c09b 238 }
239
e5372da4 240 return " $sql";
241}
242
10cef607 243sub _recurse_from {
244 scalar shift->_render_sqla(table => \@_);
6f4ddea1 245}
246
10cef607 2471;
50136dd9 248
10cef607 249=head1 OPERATORS
81446c4f 250
10cef607 251=head2 -ident
50136dd9 252
10cef607 253Used to explicitly specify an SQL identifier. Takes a plain string as value
254which is then invariably treated as a column name (and is being properly
255quoted if quoting has been requested). Most useful for comparison of two
256columns:
83e09b5b 257
10cef607 258 my %where = (
259 priority => { '<', 2 },
260 requestor => { -ident => 'submitter' }
50136dd9 261 );
262
10cef607 263which results in:
6f4ddea1 264
10cef607 265 $stmt = 'WHERE "priority" < ? AND "requestor" = "submitter"';
266 @bind = ('2');
a6b68a60 267
10cef607 268=head2 -value
1cbd3034 269
10cef607 270The -value operator signals that the argument to the right is a raw bind value.
271It will be passed straight to DBI, without invoking any of the SQL::Abstract
272condition-parsing logic. This allows you to, for example, pass an array as a
273column value for databases that support array datatypes, e.g.:
15827712 274
10cef607 275 my %where = (
276 array => { -value => [1, 2, 3] }
b8391c87 277 );
6f4ddea1 278
10cef607 279which results in:
aa82ce29 280
10cef607 281 $stmt = 'WHERE array = ?';
282 @bind = ([1, 2, 3]);
d5dedbd6 283
284=head1 AUTHORS
285
286See L<DBIx::Class/CONTRIBUTORS>.
287
288=head1 LICENSE
289
290You may distribute this code under the same terms as Perl itself.
291
292=cut