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