added patch from Guillermo to cleanup in/between,
[dbsrgits/SQL-Abstract.git] / lib / SQL / Abstract.pm
CommitLineData
32eab2da 1
2package SQL::Abstract;
3
4=head1 NAME
5
6SQL::Abstract - Generate SQL from Perl data structures
7
8=head1 SYNOPSIS
9
10 use SQL::Abstract;
11
12 my $sql = SQL::Abstract->new;
13
14 my($stmt, @bind) = $sql->select($table, \@fields, \%where, \@order);
15
16 my($stmt, @bind) = $sql->insert($table, \%fieldvals || \@values);
17
18 my($stmt, @bind) = $sql->update($table, \%fieldvals, \%where);
19
20 my($stmt, @bind) = $sql->delete($table, \%where);
21
22 # Then, use these in your DBI statements
23 my $sth = $dbh->prepare($stmt);
24 $sth->execute(@bind);
25
26 # Just generate the WHERE clause
abe72f94 27 my($stmt, @bind) = $sql->where(\%where, \@order);
32eab2da 28
29 # Return values in the same order, for hashed queries
30 # See PERFORMANCE section for more details
31 my @bind = $sql->values(\%fieldvals);
32
33=head1 DESCRIPTION
34
35This module was inspired by the excellent L<DBIx::Abstract>.
36However, in using that module I found that what I really wanted
37to do was generate SQL, but still retain complete control over my
38statement handles and use the DBI interface. So, I set out to
39create an abstract SQL generation module.
40
41While based on the concepts used by L<DBIx::Abstract>, there are
42several important differences, especially when it comes to WHERE
43clauses. I have modified the concepts used to make the SQL easier
44to generate from Perl data structures and, IMO, more intuitive.
45The underlying idea is for this module to do what you mean, based
46on the data structures you provide it. The big advantage is that
47you don't have to modify your code every time your data changes,
48as this module figures it out.
49
50To begin with, an SQL INSERT is as easy as just specifying a hash
51of C<key=value> pairs:
52
53 my %data = (
54 name => 'Jimbo Bobson',
55 phone => '123-456-7890',
56 address => '42 Sister Lane',
57 city => 'St. Louis',
58 state => 'Louisiana',
59 );
60
61The SQL can then be generated with this:
62
63 my($stmt, @bind) = $sql->insert('people', \%data);
64
65Which would give you something like this:
66
67 $stmt = "INSERT INTO people
68 (address, city, name, phone, state)
69 VALUES (?, ?, ?, ?, ?)";
70 @bind = ('42 Sister Lane', 'St. Louis', 'Jimbo Bobson',
71 '123-456-7890', 'Louisiana');
72
73These are then used directly in your DBI code:
74
75 my $sth = $dbh->prepare($stmt);
76 $sth->execute(@bind);
77
78In addition, you can apply SQL functions to elements of your C<%data>
79by specifying an arrayref for the given hash value. For example, if
80you need to execute the Oracle C<to_date> function on a value, you
81can say something like this:
82
83 my %data = (
84 name => 'Bill',
85 date_entered => ["to_date(?,'MM/DD/YYYY')", "03/02/2003"],
86 );
87
88The first value in the array is the actual SQL. Any other values are
89optional and would be included in the bind values array. This gives
90you:
91
92 my($stmt, @bind) = $sql->insert('people', \%data);
93
94 $stmt = "INSERT INTO people (name, date_entered)
95 VALUES (?, to_date(?,'MM/DD/YYYY'))";
96 @bind = ('Bill', '03/02/2003');
97
98An UPDATE is just as easy, all you change is the name of the function:
99
100 my($stmt, @bind) = $sql->update('people', \%data);
101
102Notice that your C<%data> isn't touched; the module will generate
103the appropriately quirky SQL for you automatically. Usually you'll
104want to specify a WHERE clause for your UPDATE, though, which is
105where handling C<%where> hashes comes in handy...
106
107This module can generate pretty complicated WHERE statements
108easily. For example, simple C<key=value> pairs are taken to mean
109equality, and if you want to see if a field is within a set
110of values, you can use an arrayref. Let's say we wanted to
111SELECT some data based on this criteria:
112
113 my %where = (
114 requestor => 'inna',
115 worker => ['nwiger', 'rcwe', 'sfz'],
116 status => { '!=', 'completed' }
117 );
118
119 my($stmt, @bind) = $sql->select('tickets', '*', \%where);
120
121The above would give you something like this:
122
123 $stmt = "SELECT * FROM tickets WHERE
124 ( requestor = ? ) AND ( status != ? )
125 AND ( worker = ? OR worker = ? OR worker = ? )";
126 @bind = ('inna', 'completed', 'nwiger', 'rcwe', 'sfz');
127
128Which you could then use in DBI code like so:
129
130 my $sth = $dbh->prepare($stmt);
131 $sth->execute(@bind);
132
133Easy, eh?
134
135=head1 FUNCTIONS
136
137The functions are simple. There's one for each major SQL operation,
138and a constructor you use first. The arguments are specified in a
139similar order to each function (table, then fields, then a where
140clause) to try and simplify things.
141
142=cut
143
144use Carp;
145use strict;
146
69f2a249 147our $VERSION = '1.22';
148our $REVISION = '$Id$';
32eab2da 149our $AUTOLOAD;
150
151# Fix SQL case, if so requested
152sub _sqlcase {
153 my $self = shift;
154 return $self->{case} ? $_[0] : uc($_[0]);
155}
156
157# Anon copies of arrays/hashes
158# Based on deep_copy example by merlyn
159# http://www.stonehenge.com/merlyn/UnixReview/col30.html
160sub _anoncopy {
161 my $orig = shift;
162 return (ref $orig eq 'HASH') ? +{map { $_ => _anoncopy($orig->{$_}) } keys %$orig}
163 : (ref $orig eq 'ARRAY') ? [map _anoncopy($_), @$orig]
164 : $orig;
165}
166
167# Debug
168sub _debug {
169 return unless $_[0]->{debug}; shift; # a little faster
170 my $func = (caller(1))[3];
171 warn "[$func] ", @_, "\n";
172}
173
174sub belch (@) {
175 my($func) = (caller(1))[3];
176 carp "[$func] Warning: ", @_;
177}
178
179sub puke (@) {
180 my($func) = (caller(1))[3];
181 croak "[$func] Fatal: ", @_;
182}
183
184# Utility functions
185sub _table {
186 my $self = shift;
187 my $tab = shift;
188 if (ref $tab eq 'ARRAY') {
189 return join ', ', map { $self->_quote($_) } @$tab;
190 } else {
191 return $self->_quote($tab);
192 }
193}
194
195sub _quote {
196 my $self = shift;
197 my $label = shift;
198
199 return $label
200 if $label eq '*';
201
202 return $self->{quote_char} . $label . $self->{quote_char}
203 if !defined $self->{name_sep};
204
205 return join $self->{name_sep},
206 map { $self->{quote_char} . $_ . $self->{quote_char} }
207 split /\Q$self->{name_sep}\E/, $label;
208}
209
210# Conversion, if applicable
211sub _convert ($) {
212 my $self = shift;
213 return @_ unless $self->{convert};
214 my $conv = $self->_sqlcase($self->{convert});
215 my @ret = map { $conv.'('.$_.')' } @_;
216 return wantarray ? @ret : $ret[0];
217}
218
219# And bindtype
220sub _bindtype (@) {
221 my $self = shift;
222 my($col,@val) = @_;
223 return $self->{bindtype} eq 'columns' ? [ @_ ] : @val;
224}
225
226# Modified -logic or -nest
227sub _modlogic ($) {
228 my $self = shift;
229 my $sym = @_ ? lc(shift) : $self->{logic};
230 $sym =~ tr/_/ /;
231 $sym = $self->{logic} if $sym eq 'nest';
232 return $self->_sqlcase($sym); # override join
233}
234
235=head2 new(option => 'value')
236
237The C<new()> function takes a list of options and values, and returns
238a new B<SQL::Abstract> object which can then be used to generate SQL
239through the methods below. The options accepted are:
240
241=over
242
243=item case
244
245If set to 'lower', then SQL will be generated in all lowercase. By
246default SQL is generated in "textbook" case meaning something like:
247
248 SELECT a_field FROM a_table WHERE some_field LIKE '%someval%'
249
250=item cmp
251
252This determines what the default comparison operator is. By default
253it is C<=>, meaning that a hash like this:
254
255 %where = (name => 'nwiger', email => 'nate@wiger.org');
256
257Will generate SQL like this:
258
259 WHERE name = 'nwiger' AND email = 'nate@wiger.org'
260
261However, you may want loose comparisons by default, so if you set
262C<cmp> to C<like> you would get SQL such as:
263
264 WHERE name like 'nwiger' AND email like 'nate@wiger.org'
265
266You can also override the comparsion on an individual basis - see
267the huge section on L</"WHERE CLAUSES"> at the bottom.
268
269=item logic
270
271This determines the default logical operator for multiple WHERE
272statements in arrays. By default it is "or", meaning that a WHERE
273array of the form:
274
275 @where = (
276 event_date => {'>=', '2/13/99'},
277 event_date => {'<=', '4/24/03'},
278 );
279
280Will generate SQL like this:
281
282 WHERE event_date >= '2/13/99' OR event_date <= '4/24/03'
283
284This is probably not what you want given this query, though (look
285at the dates). To change the "OR" to an "AND", simply specify:
286
287 my $sql = SQL::Abstract->new(logic => 'and');
288
289Which will change the above C<WHERE> to:
290
291 WHERE event_date >= '2/13/99' AND event_date <= '4/24/03'
292
293=item convert
294
295This will automatically convert comparisons using the specified SQL
296function for both column and value. This is mostly used with an argument
297of C<upper> or C<lower>, so that the SQL will have the effect of
298case-insensitive "searches". For example, this:
299
300 $sql = SQL::Abstract->new(convert => 'upper');
301 %where = (keywords => 'MaKe iT CAse inSeNSItive');
302
303Will turn out the following SQL:
304
305 WHERE upper(keywords) like upper('MaKe iT CAse inSeNSItive')
306
307The conversion can be C<upper()>, C<lower()>, or any other SQL function
308that can be applied symmetrically to fields (actually B<SQL::Abstract> does
309not validate this option; it will just pass through what you specify verbatim).
310
311=item bindtype
312
313This is a kludge because many databases suck. For example, you can't
314just bind values using DBI's C<execute()> for Oracle C<CLOB> or C<BLOB> fields.
315Instead, you have to use C<bind_param()>:
316
317 $sth->bind_param(1, 'reg data');
318 $sth->bind_param(2, $lots, {ora_type => ORA_CLOB});
319
320The problem is, B<SQL::Abstract> will normally just return a C<@bind> array,
321which loses track of which field each slot refers to. Fear not.
322
323If you specify C<bindtype> in new, you can determine how C<@bind> is returned.
324Currently, you can specify either C<normal> (default) or C<columns>. If you
325specify C<columns>, you will get an array that looks like this:
326
327 my $sql = SQL::Abstract->new(bindtype => 'columns');
328 my($stmt, @bind) = $sql->insert(...);
329
330 @bind = (
331 [ 'column1', 'value1' ],
332 [ 'column2', 'value2' ],
333 [ 'column3', 'value3' ],
334 );
335
336You can then iterate through this manually, using DBI's C<bind_param()>.
337
338 $sth->prepare($stmt);
339 my $i = 1;
340 for (@bind) {
341 my($col, $data) = @$_;
342 if ($col eq 'details' || $col eq 'comments') {
343 $sth->bind_param($i, $data, {ora_type => ORA_CLOB});
344 } elsif ($col eq 'image') {
345 $sth->bind_param($i, $data, {ora_type => ORA_BLOB});
346 } else {
347 $sth->bind_param($i, $data);
348 }
349 $i++;
350 }
351 $sth->execute; # execute without @bind now
352
353Now, why would you still use B<SQL::Abstract> if you have to do this crap?
354Basically, the advantage is still that you don't have to care which fields
355are or are not included. You could wrap that above C<for> loop in a simple
356sub called C<bind_fields()> or something and reuse it repeatedly. You still
357get a layer of abstraction over manual SQL specification.
358
359=item quote_char
360
361This is the character that a table or column name will be quoted
362with. By default this is an empty string, but you could set it to
363the character C<`>, to generate SQL like this:
364
365 SELECT `a_field` FROM `a_table` WHERE `some_field` LIKE '%someval%'
366
367This is useful if you have tables or columns that are reserved words
368in your database's SQL dialect.
369
370=item name_sep
371
372This is the character that separates a table and column name. It is
373necessary to specify this when the C<quote_char> option is selected,
374so that tables and column names can be individually quoted like this:
375
376 SELECT `table`.`one_field` FROM `table` WHERE `table`.`other_field` = 1
377
378=back
379
380=cut
381
382sub new {
383 my $self = shift;
384 my $class = ref($self) || $self;
385 my %opt = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_;
386
387 # choose our case by keeping an option around
388 delete $opt{case} if $opt{case} && $opt{case} ne 'lower';
389
390 # override logical operator
391 $opt{logic} = uc $opt{logic} if $opt{logic};
392
393 # how to return bind vars
394 $opt{bindtype} ||= delete($opt{bind_type}) || 'normal';
395
396 # default comparison is "=", but can be overridden
397 $opt{cmp} ||= '=';
398
399 # default quotation character around tables/columns
400 $opt{quote_char} ||= '';
401
402 return bless \%opt, $class;
403}
404
405=head2 insert($table, \@values || \%fieldvals)
406
407This is the simplest function. You simply give it a table name
408and either an arrayref of values or hashref of field/value pairs.
409It returns an SQL INSERT statement and a list of bind values.
410
411=cut
412
413sub insert {
414 my $self = shift;
415 my $table = $self->_table(shift);
416 my $data = shift || return;
417
418 my $sql = $self->_sqlcase('insert into') . " $table ";
419 my(@sqlf, @sqlv, @sqlq) = ();
420
421 my $ref = ref $data;
422 if ($ref eq 'HASH') {
423 for my $k (sort keys %$data) {
424 my $v = $data->{$k};
425 my $r = ref $v;
426 # named fields, so must save names in order
427 push @sqlf, $self->_quote($k);
428 if ($r eq 'ARRAY') {
429 # SQL included for values
430 my @val = @$v;
431 push @sqlq, shift @val;
432 push @sqlv, $self->_bindtype($k, @val);
433 } elsif ($r eq 'SCALAR') {
434 # embedded literal SQL
435 push @sqlq, $$v;
436 } else {
437 push @sqlq, '?';
438 push @sqlv, $self->_bindtype($k, $v);
439 }
440 }
441 $sql .= '(' . join(', ', @sqlf) .') '. $self->_sqlcase('values') . ' ('. join(', ', @sqlq) .')';
442 } elsif ($ref eq 'ARRAY') {
443 # just generate values(?,?) part
444 # no names (arrayref) so can't generate bindtype
445 carp "Warning: ",__PACKAGE__,"->insert called with arrayref when bindtype set"
446 if $self->{bindtype} ne 'normal';
447 for my $v (@$data) {
448 my $r = ref $v;
449 if ($r eq 'ARRAY') {
450 my @val = @$v;
451 push @sqlq, shift @val;
452 push @sqlv, @val;
453 } elsif ($r eq 'SCALAR') {
454 # embedded literal SQL
455 push @sqlq, $$v;
456 } else {
457 push @sqlq, '?';
458 push @sqlv, $v;
459 }
460 }
461 $sql .= $self->_sqlcase('values') . ' ('. join(', ', @sqlq) .')';
462 } elsif ($ref eq 'SCALAR') {
463 # literal SQL
464 $sql .= $$data;
465 } else {
466 puke "Unsupported data type specified to \$sql->insert";
467 }
468
469 return wantarray ? ($sql, @sqlv) : $sql;
470}
471
472=head2 update($table, \%fieldvals, \%where)
473
474This takes a table, hashref of field/value pairs, and an optional
475hashref WHERE clause. It returns an SQL UPDATE function and a list
476of bind values.
477
478=cut
479
480sub update {
481 my $self = shift;
482 my $table = $self->_table(shift);
483 my $data = shift || return;
484 my $where = shift;
485
486 my $sql = $self->_sqlcase('update') . " $table " . $self->_sqlcase('set ');
487 my(@sqlf, @sqlv) = ();
488
489 puke "Unsupported data type specified to \$sql->update"
490 unless ref $data eq 'HASH';
491
492 for my $k (sort keys %$data) {
493 my $v = $data->{$k};
494 my $r = ref $v;
495 my $label = $self->_quote($k);
496 if ($r eq 'ARRAY') {
497 # SQL included for values
498 my @bind = @$v;
499 my $sql = shift @bind;
500 push @sqlf, "$label = $sql";
501 push @sqlv, $self->_bindtype($k, @bind);
502 } elsif ($r eq 'SCALAR') {
503 # embedded literal SQL
504 push @sqlf, "$label = $$v";
505 } else {
506 push @sqlf, "$label = ?";
507 push @sqlv, $self->_bindtype($k, $v);
508 }
509 }
510
511 $sql .= join ', ', @sqlf;
512
513 if ($where) {
514 my($wsql, @wval) = $self->where($where);
515 $sql .= $wsql;
516 push @sqlv, @wval;
517 }
518
519 return wantarray ? ($sql, @sqlv) : $sql;
520}
521
522=head2 select($table, \@fields, \%where, \@order)
523
524This takes a table, arrayref of fields (or '*'), optional hashref
525WHERE clause, and optional arrayref order by, and returns the
526corresponding SQL SELECT statement and list of bind values.
527
528=cut
529
530sub select {
531 my $self = shift;
532 my $table = $self->_table(shift);
533 my $fields = shift || '*';
534 my $where = shift;
535 my $order = shift;
536
537 my $f = (ref $fields eq 'ARRAY') ? join ', ', map { $self->_quote($_) } @$fields : $fields;
538 my $sql = join ' ', $self->_sqlcase('select'), $f, $self->_sqlcase('from'), $table;
539
540 my(@sqlf, @sqlv) = ();
541 my($wsql, @wval) = $self->where($where, $order);
542 $sql .= $wsql;
543 push @sqlv, @wval;
544
545 return wantarray ? ($sql, @sqlv) : $sql;
546}
547
548=head2 delete($table, \%where)
549
550This takes a table name and optional hashref WHERE clause.
551It returns an SQL DELETE statement and list of bind values.
552
553=cut
554
555sub delete {
556 my $self = shift;
557 my $table = $self->_table(shift);
558 my $where = shift;
559
560 my $sql = $self->_sqlcase('delete from') . " $table";
561 my(@sqlf, @sqlv) = ();
562
563 if ($where) {
564 my($wsql, @wval) = $self->where($where);
565 $sql .= $wsql;
566 push @sqlv, @wval;
567 }
568
569 return wantarray ? ($sql, @sqlv) : $sql;
570}
571
572=head2 where(\%where, \@order)
573
574This is used to generate just the WHERE clause. For example,
575if you have an arbitrary data structure and know what the
576rest of your SQL is going to look like, but want an easy way
577to produce a WHERE clause, use this. It returns an SQL WHERE
578clause and list of bind values.
579
580=cut
581
582# Finally, a separate routine just to handle WHERE clauses
583sub where {
584 my $self = shift;
585 my $where = shift;
586 my $order = shift;
587
588 # Need a separate routine to properly wrap w/ "where"
589 my $sql = '';
590 my @ret = $self->_recurse_where($where);
591 if (@ret) {
592 my $wh = shift @ret;
593 $sql .= $self->_sqlcase(' where ') . $wh if $wh;
594 }
595
596 # order by?
597 if ($order) {
598 $sql .= $self->_order_by($order);
599 }
600
601 return wantarray ? ($sql, @ret) : $sql;
602}
603
604
605sub _recurse_where {
606 local $^W = 0; # really, you've gotta be fucking kidding me
607 my $self = shift;
608 my $where = _anoncopy(shift); # prevent destroying original
609 my $ref = ref $where || '';
610 my $join = shift || $self->{logic} ||
611 ($ref eq 'ARRAY' ? $self->_sqlcase('or') : $self->_sqlcase('and'));
612
613 # For assembling SQL fields and values
614 my(@sqlf, @sqlv) = ();
615
616 # If an arrayref, then we join each element
617 if ($ref eq 'ARRAY') {
618 # need to use while() so can shift() for arrays
619 my $subjoin;
620 while (my $el = shift @$where) {
621
622 # skip empty elements, otherwise get invalid trailing AND stuff
623 if (my $ref2 = ref $el) {
624 if ($ref2 eq 'ARRAY') {
625 next unless @$el;
626 } elsif ($ref2 eq 'HASH') {
627 next unless %$el;
628 $subjoin ||= $self->_sqlcase('and');
629 } elsif ($ref2 eq 'SCALAR') {
630 # literal SQL
631 push @sqlf, $$el;
632 next;
633 }
634 $self->_debug("$ref2(*top) means join with $subjoin");
635 } else {
636 # top-level arrayref with scalars, recurse in pairs
637 $self->_debug("NOREF(*top) means join with $subjoin");
638 $el = {$el => shift(@$where)};
639 }
640 my @ret = $self->_recurse_where($el, $subjoin);
641 push @sqlf, shift @ret;
642 push @sqlv, @ret;
643 }
644 }
645 elsif ($ref eq 'HASH') {
646 # Note: during recursion, the last element will always be a hashref,
647 # since it needs to point a column => value. So this be the end.
648 for my $k (sort keys %$where) {
649 my $v = $where->{$k};
650 my $label = $self->_quote($k);
61fdf29c 651
32eab2da 652 if ($k =~ /^-(\D+)/) {
653 # special nesting, like -and, -or, -nest, so shift over
654 my $subjoin = $self->_modlogic($1);
655 $self->_debug("OP(-$1) means special logic ($subjoin), recursing...");
656 my @ret = $self->_recurse_where($v, $subjoin);
657 push @sqlf, shift @ret;
658 push @sqlv, @ret;
659 } elsif (! defined($v)) {
660 # undef = null
661 $self->_debug("UNDEF($k) means IS NULL");
662 push @sqlf, $label . $self->_sqlcase(' is null');
663 } elsif (ref $v eq 'ARRAY') {
664 my @v = @$v;
665
666 # multiple elements: multiple options
667 $self->_debug("ARRAY($k) means multiple elements: [ @v ]");
668
669 # special nesting, like -and, -or, -nest, so shift over
670 my $subjoin = $self->_sqlcase('or');
671 if ($v[0] =~ /^-(\D+)/) {
672 $subjoin = $self->_modlogic($1); # override subjoin
673 $self->_debug("OP(-$1) means special logic ($subjoin), shifting...");
674 shift @v;
675 }
676
677 # map into an array of hashrefs and recurse
678 my @ret = $self->_recurse_where([map { {$k => $_} } @v], $subjoin);
679
680 # push results into our structure
681 push @sqlf, shift @ret;
682 push @sqlv, @ret;
683 } elsif (ref $v eq 'HASH') {
684 # modified operator { '!=', 'completed' }
685 for my $f (sort keys %$v) {
686 my $x = $v->{$f};
61fdf29c 687
688 # do the right thing for single -in values
689 $x = [$x] if ($f =~ /^-?\s*(not[\s_]+)?in\s*$/i && ref $x ne 'ARRAY');
690
32eab2da 691 $self->_debug("HASH($k) means modified operator: { $f }");
692
693 # check for the operator being "IN" or "BETWEEN" or whatever
694 if (ref $x eq 'ARRAY') {
695 if ($f =~ /^-?\s*(not[\s_]+)?(in|between)\s*$/i) {
696 my $u = $self->_modlogic($1 . $2);
697 $self->_debug("HASH($f => $x) uses special operator: [ $u ]");
698 if ($u =~ /between/i) {
699 # SQL sucks
61fdf29c 700 # Throw an exception if you try to use between with
701 # anything other than 2 values
702 $self->puke("You need two values to use between") unless @$x == 2;
32eab2da 703 push @sqlf, join ' ', $self->_convert($label), $u, $self->_convert('?'),
704 $self->_sqlcase('and'), $self->_convert('?');
61fdf29c 705 } elsif (@$x) {
706 # DWIM for empty arrayrefs
32eab2da 707 push @sqlf, join ' ', $self->_convert($label), $u, '(',
708 join(', ', map { $self->_convert('?') } @$x),
709 ')';
61fdf29c 710 } elsif(@$x == 0){
711 # Empty IN defaults to 0=1 and empty NOT IN to 1=1
712 push(@sqlf, ($u =~ /not/i ? "1=1" : "0=1"));
32eab2da 713 }
714 push @sqlv, $self->_bindtype($k, @$x);
715 } else {
716 # multiple elements: multiple options
717 $self->_debug("ARRAY($x) means multiple elements: [ @$x ]");
718
719 # map into an array of hashrefs and recurse
720 my @ret = $self->_recurse_where([map { {$k => {$f, $_}} } @$x]);
721
722 # push results into our structure
723 push @sqlf, shift @ret;
724 push @sqlv, @ret;
725 }
726 } elsif (! defined($x)) {
727 # undef = NOT null
728 my $not = ($f eq '!=' || $f eq 'not like') ? ' not' : '';
729 push @sqlf, $label . $self->_sqlcase(" is$not null");
730 } else {
731 # regular ol' value
732 $f =~ s/^-//; # strip leading -like =>
733 $f =~ s/_/ /; # _ => " "
734 push @sqlf, join ' ', $self->_convert($label), $self->_sqlcase($f), $self->_convert('?');
735 push @sqlv, $self->_bindtype($k, $x);
736 }
737 }
738 } elsif (ref $v eq 'SCALAR') {
739 # literal SQL
740 $self->_debug("SCALAR($k) means literal SQL: $$v");
741 push @sqlf, "$label $$v";
742 } else {
743 # standard key => val
744 $self->_debug("NOREF($k) means simple key=val: $k $self->{cmp} $v");
745 push @sqlf, join ' ', $self->_convert($label), $self->_sqlcase($self->{cmp}), $self->_convert('?');
746 push @sqlv, $self->_bindtype($k, $v);
747 }
748 }
749 }
750 elsif ($ref eq 'SCALAR') {
751 # literal sql
752 $self->_debug("SCALAR(*top) means literal SQL: $$where");
753 push @sqlf, $$where;
754 }
755 elsif (defined $where) {
756 # literal sql
757 $self->_debug("NOREF(*top) means literal SQL: $where");
758 push @sqlf, $where;
759 }
760
761 # assemble and return sql
762 my $wsql = @sqlf ? '( ' . join(" $join ", @sqlf) . ' )' : '';
763 return wantarray ? ($wsql, @sqlv) : $wsql;
764}
765
766sub _order_by {
767 my $self = shift;
768 my $ref = ref $_[0];
769
770 my @vals = $ref eq 'ARRAY' ? @{$_[0]} :
771 $ref eq 'SCALAR' ? ${$_[0]} :
772 $ref eq '' ? $_[0] :
773 puke "Unsupported data struct $ref for ORDER BY";
774
775 my $val = join ', ', map { $self->_quote($_) } @vals;
776 return $val ? $self->_sqlcase(' order by')." $val" : '';
777}
778
779=head2 values(\%data)
780
781This just returns the values from the hash C<%data>, in the same
782order that would be returned from any of the other above queries.
783Using this allows you to markedly speed up your queries if you
784are affecting lots of rows. See below under the L</"PERFORMANCE"> section.
785
786=cut
787
788sub values {
789 my $self = shift;
790 my $data = shift || return;
791 puke "Argument to ", __PACKAGE__, "->values must be a \\%hash"
792 unless ref $data eq 'HASH';
793 return map { $self->_bindtype($_, $data->{$_}) } sort keys %$data;
794}
795
796=head2 generate($any, 'number', $of, \@data, $struct, \%types)
797
798Warning: This is an experimental method and subject to change.
799
800This returns arbitrarily generated SQL. It's a really basic shortcut.
801It will return two different things, depending on return context:
802
803 my($stmt, @bind) = $sql->generate('create table', \$table, \@fields);
804 my $stmt_and_val = $sql->generate('create table', \$table, \@fields);
805
806These would return the following:
807
808 # First calling form
809 $stmt = "CREATE TABLE test (?, ?)";
810 @bind = (field1, field2);
811
812 # Second calling form
813 $stmt_and_val = "CREATE TABLE test (field1, field2)";
814
815Depending on what you're trying to do, it's up to you to choose the correct
816format. In this example, the second form is what you would want.
817
818By the same token:
819
820 $sql->generate('alter session', { nls_date_format => 'MM/YY' });
821
822Might give you:
823
824 ALTER SESSION SET nls_date_format = 'MM/YY'
825
826You get the idea. Strings get their case twiddled, but everything
827else remains verbatim.
828
829=cut
830
831sub generate {
832 my $self = shift;
833
834 my(@sql, @sqlq, @sqlv);
835
836 for (@_) {
837 my $ref = ref $_;
838 if ($ref eq 'HASH') {
839 for my $k (sort keys %$_) {
840 my $v = $_->{$k};
841 my $r = ref $v;
842 my $label = $self->_quote($k);
843 if ($r eq 'ARRAY') {
844 # SQL included for values
845 my @bind = @$v;
846 my $sql = shift @bind;
847 push @sqlq, "$label = $sql";
848 push @sqlv, $self->_bindtype($k, @bind);
849 } elsif ($r eq 'SCALAR') {
850 # embedded literal SQL
851 push @sqlq, "$label = $$v";
852 } else {
853 push @sqlq, "$label = ?";
854 push @sqlv, $self->_bindtype($k, $v);
855 }
856 }
857 push @sql, $self->_sqlcase('set'), join ', ', @sqlq;
858 } elsif ($ref eq 'ARRAY') {
859 # unlike insert(), assume these are ONLY the column names, i.e. for SQL
860 for my $v (@$_) {
861 my $r = ref $v;
862 if ($r eq 'ARRAY') {
863 my @val = @$v;
864 push @sqlq, shift @val;
865 push @sqlv, @val;
866 } elsif ($r eq 'SCALAR') {
867 # embedded literal SQL
868 push @sqlq, $$v;
869 } else {
870 push @sqlq, '?';
871 push @sqlv, $v;
872 }
873 }
874 push @sql, '(' . join(', ', @sqlq) . ')';
875 } elsif ($ref eq 'SCALAR') {
876 # literal SQL
877 push @sql, $$_;
878 } else {
879 # strings get case twiddled
880 push @sql, $self->_sqlcase($_);
881 }
882 }
883
884 my $sql = join ' ', @sql;
885
886 # this is pretty tricky
887 # if ask for an array, return ($stmt, @bind)
888 # otherwise, s/?/shift @sqlv/ to put it inline
889 if (wantarray) {
890 return ($sql, @sqlv);
891 } else {
892 1 while $sql =~ s/\?/my $d = shift(@sqlv);
893 ref $d ? $d->[1] : $d/e;
894 return $sql;
895 }
896}
897
898sub DESTROY { 1 }
899sub AUTOLOAD {
900 # This allows us to check for a local, then _form, attr
901 my $self = shift;
902 my($name) = $AUTOLOAD =~ /.*::(.+)/;
903 return $self->generate($name, @_);
904}
905
9061;
907
908__END__
909
910=head1 WHERE CLAUSES
911
912This module uses a variation on the idea from L<DBIx::Abstract>. It
913is B<NOT>, repeat I<not> 100% compatible. B<The main logic of this
914module is that things in arrays are OR'ed, and things in hashes
915are AND'ed.>
916
917The easiest way to explain is to show lots of examples. After
918each C<%where> hash shown, it is assumed you used:
919
920 my($stmt, @bind) = $sql->where(\%where);
921
922However, note that the C<%where> hash can be used directly in any
923of the other functions as well, as described above.
924
925So, let's get started. To begin, a simple hash:
926
927 my %where = (
928 user => 'nwiger',
929 status => 'completed'
930 );
931
932Is converted to SQL C<key = val> statements:
933
934 $stmt = "WHERE user = ? AND status = ?";
935 @bind = ('nwiger', 'completed');
936
937One common thing I end up doing is having a list of values that
938a field can be in. To do this, simply specify a list inside of
939an arrayref:
940
941 my %where = (
942 user => 'nwiger',
943 status => ['assigned', 'in-progress', 'pending'];
944 );
945
946This simple code will create the following:
947
948 $stmt = "WHERE user = ? AND ( status = ? OR status = ? OR status = ? )";
949 @bind = ('nwiger', 'assigned', 'in-progress', 'pending');
950
951If you want to specify a different type of operator for your comparison,
952you can use a hashref for a given column:
953
954 my %where = (
955 user => 'nwiger',
956 status => { '!=', 'completed' }
957 );
958
959Which would generate:
960
961 $stmt = "WHERE user = ? AND status != ?";
962 @bind = ('nwiger', 'completed');
963
964To test against multiple values, just enclose the values in an arrayref:
965
966 status => { '!=', ['assigned', 'in-progress', 'pending'] };
967
968Which would give you:
969
970 "WHERE status != ? OR status != ? OR status != ?"
971
972But, this is probably not what you want in this case (look at it). So
973the hashref can also contain multiple pairs, in which case it is expanded
974into an C<AND> of its elements:
975
976 my %where = (
977 user => 'nwiger',
978 status => { '!=', 'completed', -not_like => 'pending%' }
979 );
980
981 # Or more dynamically, like from a form
982 $where{user} = 'nwiger';
983 $where{status}{'!='} = 'completed';
984 $where{status}{'-not_like'} = 'pending%';
985
986 # Both generate this
987 $stmt = "WHERE user = ? AND status != ? AND status NOT LIKE ?";
988 @bind = ('nwiger', 'completed', 'pending%');
989
990To get an OR instead, you can combine it with the arrayref idea:
991
992 my %where => (
993 user => 'nwiger',
994 priority => [ {'=', 2}, {'!=', 1} ]
995 );
996
997Which would generate:
998
999 $stmt = "WHERE user = ? AND priority = ? OR priority != ?";
1000 @bind = ('nwiger', '2', '1');
1001
1002However, there is a subtle trap if you want to say something like
1003this (notice the C<AND>):
1004
1005 WHERE priority != ? AND priority != ?
1006
1007Because, in Perl you I<can't> do this:
1008
1009 priority => { '!=', 2, '!=', 1 }
1010
1011As the second C<!=> key will obliterate the first. The solution
1012is to use the special C<-modifier> form inside an arrayref:
1013
1014 priority => [ -and => {'!=', 2}, {'!=', 1} ]
1015
1016Normally, these would be joined by C<OR>, but the modifier tells it
1017to use C<AND> instead. (Hint: You can use this in conjunction with the
1018C<logic> option to C<new()> in order to change the way your queries
1019work by default.) B<Important:> Note that the C<-modifier> goes
1020B<INSIDE> the arrayref, as an extra first element. This will
1021B<NOT> do what you think it might:
1022
1023 priority => -and => [{'!=', 2}, {'!=', 1}] # WRONG!
1024
1025Here is a quick list of equivalencies, since there is some overlap:
1026
1027 # Same
1028 status => {'!=', 'completed', 'not like', 'pending%' }
1029 status => [ -and => {'!=', 'completed'}, {'not like', 'pending%'}]
1030
1031 # Same
1032 status => {'=', ['assigned', 'in-progress']}
1033 status => [ -or => {'=', 'assigned'}, {'=', 'in-progress'}]
1034 status => [ {'=', 'assigned'}, {'=', 'in-progress'} ]
1035
1036In addition to C<-and> and C<-or>, there is also a special C<-nest>
1037operator which adds an additional set of parens, to create a subquery.
1038For example, to get something like this:
1039
1040 $stmt = WHERE user = ? AND ( workhrs > ? OR geo = ? )
1041 @bind = ('nwiger', '20', 'ASIA');
1042
1043You would do:
1044
1045 my %where = (
1046 user => 'nwiger',
1047 -nest => [ workhrs => {'>', 20}, geo => 'ASIA' ],
1048 );
1049
1050You can also use the hashref format to compare a list of fields using the
1051C<IN> comparison operator, by specifying the list as an arrayref:
1052
1053 my %where = (
1054 status => 'completed',
1055 reportid => { -in => [567, 2335, 2] }
1056 );
1057
1058Which would generate:
1059
1060 $stmt = "WHERE status = ? AND reportid IN (?,?,?)";
1061 @bind = ('completed', '567', '2335', '2');
1062
1063You can use this same format to use other grouping functions, such
1064as C<BETWEEN>, C<SOME>, and so forth. For example:
1065
1066 my %where = (
1067 user => 'nwiger',
1068 completion_date => {
1069 -not_between => ['2002-10-01', '2003-02-06']
1070 }
1071 );
1072
1073Would give you:
1074
1075 WHERE user = ? AND completion_date NOT BETWEEN ( ? AND ? )
1076
1077So far, we've seen how multiple conditions are joined with a top-level
1078C<AND>. We can change this by putting the different conditions we want in
1079hashes and then putting those hashes in an array. For example:
1080
1081 my @where = (
1082 {
1083 user => 'nwiger',
1084 status => { -like => ['pending%', 'dispatched'] },
1085 },
1086 {
1087 user => 'robot',
1088 status => 'unassigned',
1089 }
1090 );
1091
1092This data structure would create the following:
1093
1094 $stmt = "WHERE ( user = ? AND ( status LIKE ? OR status LIKE ? ) )
1095 OR ( user = ? AND status = ? ) )";
1096 @bind = ('nwiger', 'pending', 'dispatched', 'robot', 'unassigned');
1097
1098This can be combined with the C<-nest> operator to properly group
1099SQL statements:
1100
1101 my @where = (
1102 -and => [
1103 user => 'nwiger',
1104 -nest => [
1105 -and => [workhrs => {'>', 20}, geo => 'ASIA' ],
1106 -and => [workhrs => {'<', 50}, geo => 'EURO' ]
1107 ],
1108 ],
1109 );
1110
1111That would yield:
1112
1113 WHERE ( user = ? AND
1114 ( ( workhrs > ? AND geo = ? )
1115 OR ( workhrs < ? AND geo = ? ) ) )
1116
1117Finally, sometimes only literal SQL will do. If you want to include
1118literal SQL verbatim, you can specify it as a scalar reference, namely:
1119
1120 my $inn = 'is Not Null';
1121 my %where = (
1122 priority => { '<', 2 },
1123 requestor => \$inn
1124 );
1125
1126This would create:
1127
1128 $stmt = "WHERE priority < ? AND requestor is Not Null";
1129 @bind = ('2');
1130
1131Note that in this example, you only get one bind parameter back, since
1132the verbatim SQL is passed as part of the statement.
1133
1134Of course, just to prove a point, the above can also be accomplished
1135with this:
1136
1137 my %where = (
1138 priority => { '<', 2 },
1139 requestor => { '!=', undef },
1140 );
1141
1142TMTOWTDI.
1143
1144These pages could go on for a while, since the nesting of the data
1145structures this module can handle are pretty much unlimited (the
1146module implements the C<WHERE> expansion as a recursive function
1147internally). Your best bet is to "play around" with the module a
1148little to see how the data structures behave, and choose the best
1149format for your data based on that.
1150
1151And of course, all the values above will probably be replaced with
1152variables gotten from forms or the command line. After all, if you
1153knew everything ahead of time, you wouldn't have to worry about
1154dynamically-generating SQL and could just hardwire it into your
1155script.
1156
1157=head1 PERFORMANCE
1158
1159Thanks to some benchmarking by Mark Stosberg, it turns out that
1160this module is many orders of magnitude faster than using C<DBIx::Abstract>.
1161I must admit this wasn't an intentional design issue, but it's a
1162byproduct of the fact that you get to control your C<DBI> handles
1163yourself.
1164
1165To maximize performance, use a code snippet like the following:
1166
1167 # prepare a statement handle using the first row
1168 # and then reuse it for the rest of the rows
1169 my($sth, $stmt);
1170 for my $href (@array_of_hashrefs) {
1171 $stmt ||= $sql->insert('table', $href);
1172 $sth ||= $dbh->prepare($stmt);
1173 $sth->execute($sql->values($href));
1174 }
1175
1176The reason this works is because the keys in your C<$href> are sorted
1177internally by B<SQL::Abstract>. Thus, as long as your data retains
1178the same structure, you only have to generate the SQL the first time
1179around. On subsequent queries, simply use the C<values> function provided
1180by this module to return your values in the correct order.
1181
1182=head1 FORMBUILDER
1183
1184If you use my C<CGI::FormBuilder> module at all, you'll hopefully
1185really like this part (I do, at least). Building up a complex query
1186can be as simple as the following:
1187
1188 #!/usr/bin/perl
1189
1190 use CGI::FormBuilder;
1191 use SQL::Abstract;
1192
1193 my $form = CGI::FormBuilder->new(...);
1194 my $sql = SQL::Abstract->new;
1195
1196 if ($form->submitted) {
1197 my $field = $form->field;
1198 my $id = delete $field->{id};
1199 my($stmt, @bind) = $sql->update('table', $field, {id => $id});
1200 }
1201
1202Of course, you would still have to connect using C<DBI> to run the
1203query, but the point is that if you make your form look like your
1204table, the actual query script can be extremely simplistic.
1205
1206If you're B<REALLY> lazy (I am), check out C<HTML::QuickTable> for
1207a fast interface to returning and formatting data. I frequently
1208use these three modules together to write complex database query
1209apps in under 50 lines.
1210
1211=head1 NOTES
1212
1213There is not (yet) any explicit support for SQL compound logic
1214statements like "AND NOT". Instead, just do the de Morgan's
1215law transformations yourself. For example, this:
1216
1217 "lname LIKE '%son%' AND NOT ( age < 10 OR age > 20 )"
1218
1219Becomes:
1220
1221 "lname LIKE '%son%' AND ( age >= 10 AND age <= 20 )"
1222
1223With the corresponding C<%where> hash:
1224
1225 %where = (
1226 lname => {like => '%son%'},
1227 age => [-and => {'>=', 10}, {'<=', 20}],
1228 );
1229
1230Again, remember that the C<-and> goes I<inside> the arrayref.
1231
1232=head1 ACKNOWLEDGEMENTS
1233
1234There are a number of individuals that have really helped out with
1235this module. Unfortunately, most of them submitted bugs via CPAN
1236so I have no idea who they are! But the people I do know are:
1237
1238 Mark Stosberg (benchmarking)
1239 Chas Owens (initial "IN" operator support)
1240 Philip Collins (per-field SQL functions)
1241 Eric Kolve (hashref "AND" support)
1242 Mike Fragassi (enhancements to "BETWEEN" and "LIKE")
1243 Dan Kubb (support for "quote_char" and "name_sep")
abe72f94 1244 Matt Trout (DBIx::Class support)
32eab2da 1245
1246Thanks!
1247
1248=head1 BUGS
1249
1250If found, please DO NOT submit anything via C<rt.cpan.org> - that
1251just causes me a ton of work. Email me a patch (or script demonstrating
abe72f94 1252the problem) to the below address, and include the VERSION you're using.
32eab2da 1253
1254=head1 SEE ALSO
1255
1256L<DBIx::Abstract>, L<DBI|DBI>, L<CGI::FormBuilder>, L<HTML::QuickTable>
1257
32eab2da 1258=head1 AUTHOR
1259
6be9cea3 1260Copyright (c) 2001-2006 Nathan Wiger <nwiger@cpan.org>. All Rights Reserved.
32eab2da 1261
abe72f94 1262For support, your best bet is to try the C<DBIx::Class> users mailing list.
1263While not an official support venue, C<DBIx::Class> makes heavy use of
1264C<SQL::Abstract>, and as such list members there are very familiar with
1265how to create queries.
1266
32eab2da 1267This module is free software; you may copy this under the terms of
1268the GNU General Public License, or the Artistic License, copies of
1269which should have accompanied your Perl kit.
1270
1271=cut
1272