X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=3adfc01b6dc5d72896c60106f24776e3cf080e79;hb=78536e8b84aa4fdab20f50fb2ca65d17319099af;hp=61d476200b1166cc9a246225e1a1c5e72d2f73c7;hpb=3ae1c5e20cfd486c0a0def29832f05977d0092a0;p=dbsrgits%2FSQL-Abstract.git
diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm
index 61d4762..3adfc01 100644
--- a/lib/SQL/Abstract.pm
+++ b/lib/SQL/Abstract.pm
@@ -27,7 +27,7 @@ BEGIN {
# GLOBALS
#======================================================================
-our $VERSION = '1.78';
+our $VERSION = '1.81';
# This would confuse some packagers
$VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
@@ -78,11 +78,6 @@ sub puke (@) {
sub is_literal_value ($) {
ref $_[0] eq 'SCALAR' ? [ ${$_[0]} ]
: ( ref $_[0] eq 'REF' and ref ${$_[0]} eq 'ARRAY' ) ? [ @${ $_[0] } ]
- : (
- ref $_[0] eq 'HASH' and keys %{$_[0]} == 1
- and
- defined $_[0]->{-ident} and ! length ref $_[0]->{-ident}
- ) ? [ $_[0]->{-ident} ]
: undef;
}
@@ -232,7 +227,10 @@ sub insert {
return wantarray ? ($sql, @bind) : $sql;
}
-sub _insert_returning {
+# Used by DBIx::Class::SQLMaker->insert
+sub _insert_returning { shift->_returning(@_) }
+
+sub _returning {
my ($self, $options) = @_;
my $f = $options->{returning};
@@ -353,10 +351,11 @@ sub _insert_values {
sub update {
- my $self = shift;
- my $table = $self->_table(shift);
- my $data = shift || return;
- my $where = shift;
+ my $self = shift;
+ my $table = $self->_table(shift);
+ my $data = shift || return;
+ my $where = shift;
+ my $options = shift;
# first build the 'SET' part of the sql statement
my (@set, @all_bind);
@@ -419,6 +418,12 @@ sub update {
push @all_bind, @where_bind;
}
+ if ($options->{returning}) {
+ my ($returning_sql, @returning_bind) = $self->_returning ($options);
+ $sql .= $returning_sql;
+ push @all_bind, @returning_bind;
+ }
+
return wantarray ? ($sql, @all_bind) : $sql;
}
@@ -497,9 +502,15 @@ sub _recurse_where {
my ($sql, @bind) = $self->$method($where, $logic);
- # DBIx::Class directly calls _recurse_where in scalar context, so
- # we must implement it, even if not in the official API
- return wantarray ? ($sql, @bind) : $sql;
+ # DBIx::Class used to call _recurse_where in scalar context
+ # something else might too...
+ if (wantarray) {
+ return ($sql, @bind);
+ }
+ else {
+ belch "Calling _recurse_where in scalar context is deprecated and will go away before 2.0";
+ return $sql;
+ }
}
@@ -519,7 +530,10 @@ sub _where_ARRAYREF {
my (@sql_clauses, @all_bind);
# need to use while() so can shift() for pairs
- while (my $el = shift @clauses) {
+ while (@clauses) {
+ my $el = shift @clauses;
+
+ $el = undef if (defined $el and ! length $el);
# switch according to kind of $el and get corresponding ($sql, @bind)
my ($sql, @bind) = $self->_SWITCH_refkind($el, {
@@ -537,10 +551,12 @@ sub _where_ARRAYREF {
SCALARREF => sub { ($$el); },
- SCALAR => sub {# top-level arrayref with scalars, recurse in pairs
- $self->_recurse_where({$el => shift(@clauses)})},
+ SCALAR => sub {
+ # top-level arrayref with scalars, recurse in pairs
+ $self->_recurse_where({$el => shift(@clauses)})
+ },
- UNDEF => sub {puke "not supported : UNDEF in arrayref" },
+ UNDEF => sub {puke "Supplying an empty left hand side argument is not supported in array-pairs" },
});
if ($sql) {
@@ -594,11 +610,20 @@ sub _where_HASHREF {
$s = "($s)" unless (
List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}}
or
- defined($self->{_nested_func_lhs}) && ($self->{_nested_func_lhs} eq $k)
+ ( defined $self->{_nested_func_lhs} and $self->{_nested_func_lhs} eq $k )
);
($s, @b);
}
else {
+ if (! length $k) {
+ if (is_literal_value ($v) ) {
+ belch 'Hash-pairs consisting of an empty string with a literal are deprecated, and will be removed in 2.0: use -and => [ $literal ] instead';
+ }
+ else {
+ puke "Supplying an empty left hand side argument is not supported in hash-pairs";
+ }
+ }
+
my $method = $self->_METHOD_FOR_refkind("_where_hashpair", $v);
$self->$method($k, $v);
}
@@ -614,6 +639,11 @@ sub _where_HASHREF {
sub _where_unary_op {
my ($self, $op, $rhs) = @_;
+ # top level special ops are illegal in general
+ # this includes the -ident/-value ops (dual purpose unary and special)
+ puke "Illegal use of top-level '-$op'"
+ if ! defined $self->{_nested_func_lhs} and List::Util::first {$op =~ $_->{regex}} @{$self->{special_ops}};
+
if (my $op_entry = List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}}) {
my $handler = $op_entry->{handler};
@@ -638,8 +668,8 @@ sub _where_unary_op {
my ($sql, @bind) = $self->_SWITCH_refkind ($rhs, {
SCALAR => sub {
- puke "Illegal use of top-level '$op'"
- unless $self->{_nested_func_lhs};
+ puke "Illegal use of top-level '-$op'"
+ unless defined $self->{_nested_func_lhs};
return (
$self->_convert('?'),
@@ -771,7 +801,7 @@ sub _where_op_VALUE {
# special-case NULL
if (! defined $rhs) {
- return $lhs
+ return defined $lhs
? $self->_convert($self->_quote($lhs)) . ' IS NULL'
: undef
;
@@ -779,7 +809,7 @@ sub _where_op_VALUE {
my @bind =
$self->_bindtype (
- ($lhs || $self->{_nested_func_lhs}),
+ ( defined $lhs ? $lhs : $self->{_nested_func_lhs} ),
$rhs,
)
;
@@ -830,7 +860,10 @@ sub _where_hashpair_HASHREF {
my ($self, $k, $v, $logic) = @_;
$logic ||= 'and';
- local $self->{_nested_func_lhs} = $self->{_nested_func_lhs};
+ local $self->{_nested_func_lhs} = defined $self->{_nested_func_lhs}
+ ? $self->{_nested_func_lhs}
+ : $k
+ ;
my ($all_sql, @all_bind);
@@ -909,10 +942,6 @@ sub _where_hashpair_HASHREF {
},
FALLBACK => sub { # CASE: col => {op/func => $stuff}
-
- # retain for proper column type bind
- $self->{_nested_func_lhs} ||= $k;
-
($sql, @bind) = $self->_where_unary_op ($op, $val);
$sql = join (' ',
@@ -1108,7 +1137,6 @@ sub _where_field_BETWEEN {
my ($func, $arg, @rest) = %$val;
puke ("Only simple { -func => arg } functions accepted as sub-arguments to BETWEEN")
if (@rest or $func !~ /^ \- (.+)/x);
- local $self->{_nested_func_lhs} = $k;
$self->_where_unary_op ($1 => $arg);
},
FALLBACK => sub {
@@ -1166,7 +1194,6 @@ sub _where_field_IN {
my ($func, $arg, @rest) = %$val;
puke ("Only simple { -func => arg } functions accepted as sub-arguments to IN")
if (@rest or $func !~ /^ \- (.+)/x);
- local $self->{_nested_func_lhs} = $k;
$self->_where_unary_op ($1 => $arg);
},
UNDEF => sub {
@@ -1225,8 +1252,29 @@ sub _where_field_IN {
# adding them back in the corresponding method
sub _open_outer_paren {
my ($self, $sql) = @_;
- $sql = $1 while $sql =~ /^ \s* \( (.*) \) \s* $/xs;
- return $sql;
+
+ while ( my ($inner) = $sql =~ /^ \s* \( (.*) \) \s* $/xs ) {
+
+ # there are closing parens inside, need the heavy duty machinery
+ # to reevaluate the extraction starting from $sql (full reevaluation)
+ if ( $inner =~ /\)/ ) {
+ require Text::Balanced;
+
+ my (undef, $remainder) = do {
+ # idiotic design - writes to $@ but *DOES NOT* throw exceptions
+ local $@;
+ Text::Balanced::extract_bracketed( $sql, '()', qr/\s*/ );
+ };
+
+ # the entire expression needs to be a balanced bracketed thing
+ # (after an extract no remainder sans trailing space)
+ last if defined $remainder and $remainder =~ /\S/;
+ }
+
+ $sql = $inner;
+ }
+
+ $sql;
}
@@ -1772,9 +1820,9 @@ Easy, eh?
=head1 METHODS
-The methods are simple. There's one for each major SQL operation,
+The methods are simple. There's one for every major SQL operation,
and a constructor you use first. The arguments are specified in a
-similar order to each method (table, then fields, then a where
+similar order for each method (table, then fields, then a where
clause) to try and simplify things.
=head2 new(option => 'value')
@@ -1946,7 +1994,7 @@ words in your database's SQL dialect.
This is the character that will be used to escape Ls appearing
in an identifier before it has been quoted.
-The paramter default in case of a single L character is the quote
+The parameter default in case of a single L character is the quote
character itself.
When opening-closing-style quoting is used (L is an arrayref)
@@ -2032,7 +2080,7 @@ be supported by all database engines.
=back
-=head2 update($table, \%fieldvals, \%where)
+=head2 update($table, \%fieldvals, \%where, \%options)
This takes a table, hashref of field/value pairs, and an optional
hashref L. It returns an SQL UPDATE function and a list
@@ -2041,6 +2089,19 @@ See the sections on L"Inserting and Updating Arrays"> and
L"Inserting and Updating SQL"> for information on how to insert
with those data types.
+The optional C<\%options> hash reference may contain additional
+options to generate the update SQL. Currently supported options
+are:
+
+=over 4
+
+=item returning
+
+See the C option to
+L.
+
+=back
+
=head2 select($source, $fields, $where, $order)
This returns a SQL SELECT statement and associated list of bind values, as
@@ -2190,7 +2251,7 @@ or perhaps even
If you fall victim to the above - please attempt to reduce the problem
to something that could be sent to the L
+|DBIx::Class/GETTING HELP/SUPPORT>
(either publicly or privately). As a workaround in the meantime you can
set C<$ENV{SQLA_ISVALUE_IGNORE_AUTOGENERATED_STRINGIFICATION}> to a true
value, which will most likely eliminate your problem (at the expense of
@@ -2213,8 +2274,6 @@ module:
=item * C<\[ $sql_string, @bind_values ]>
-=item * C<< { -ident => $plain_defined_string } >>
-
=back
On failure returns C, on sucess returns an B reference
@@ -2344,7 +2403,7 @@ Which would generate:
@bind = ('2', '5', 'nwiger');
If you want to include literal SQL (with or without bind values), just use a
-scalar reference or array reference as the value:
+scalar reference or reference to an arrayref as the value:
my %where = (
date_entered => { '>' => \["to_date(?, 'MM/DD/YYYY')", "11/26/2008"] },
@@ -2353,7 +2412,7 @@ scalar reference or array reference as the value:
Which would generate:
- $stmt = "WHERE date_entered > "to_date(?, 'MM/DD/YYYY') AND date_expires < now()";
+ $stmt = "WHERE date_entered > to_date(?, 'MM/DD/YYYY') AND date_expires < now()";
@bind = ('11/26/2008');
@@ -2367,7 +2426,7 @@ this (notice the C):
Because, in Perl you I do this:
- priority => { '!=', 2, '!=', 1 }
+ priority => { '!=' => 2, '!=' => 1 }
As the second C key will obliterate the first. The solution
is to use the special C<-modifier> form inside an arrayref:
@@ -2559,10 +2618,10 @@ to change the logic inside :
That would yield:
- WHERE ( user = ? AND (
- ( workhrs > ? AND geo = ? )
- OR ( workhrs < ? OR geo = ? )
- ) )
+ $stmt = "WHERE ( user = ?
+ AND ( ( workhrs > ? AND geo = ? )
+ OR ( workhrs < ? OR geo = ? ) ) )";
+ @bind = ('nwiger', '20', 'ASIA', '50', 'EURO');
=head3 Algebraic inconsistency, for historical reasons
@@ -2587,10 +2646,16 @@ This difference in syntax is unfortunate but must be preserved for
historical reasons. So be careful : the two examples below would
seem algebraically equivalent, but they are not
- {col => [-and => {-like => 'foo%'}, {-like => '%bar'}]}
+ { col => [ -and =>
+ { -like => 'foo%' },
+ { -like => '%bar' },
+ ] }
# yields : WHERE ( ( col LIKE ? AND col LIKE ? ) )
- [-and => {col => {-like => 'foo%'}, {col => {-like => '%bar'}}]]
+ [ -and =>
+ { col => { -like => 'foo%' } },
+ { col => { -like => '%bar' } },
+ ]
# yields : WHERE ( ( col LIKE ? OR col LIKE ? ) )
@@ -2692,12 +2757,13 @@ This would create:
@bind = ('10');
Note that you must pass the bind values in the same format as they are returned
-by L. That means that if you set L to C, you must
-provide the bind values in the C<< [ column_meta => value ] >> format, where
-C is an opaque scalar value; most commonly the column name, but
-you can use any scalar value (including references and blessed references),
-L will simply pass it through intact. So if C is set
-to C the above example will look like:
+by L. This means that if you set L
+to C, you must provide the bind values in the
+C<< [ column_meta => value ] >> format, where C is an opaque
+scalar value; most commonly the column name, but you can use any scalar value
+(including references and blessed references), L will simply
+pass it through intact. So if C is set to C the above
+example will look like:
my %where = (
date_column => \[ "= date '2008-09-30' - ?::integer", [ {} => 10 ] ]
@@ -2907,14 +2973,14 @@ Either a coderef or a plain scalar method name. In both cases
the expected return is C<< ($sql, @bind) >>.
When supplied with a method name, it is simply called on the
-L object as:
+L object as:
$self->$method_name ($field, $op, $arg)
Where:
- $op is the part that matched the handler regex
$field is the LHS of the operator
+ $op is the part that matched the handler regex
$arg is the RHS
When supplied with a coderef, it is called as:
@@ -2983,7 +3049,7 @@ Either a coderef or a plain scalar method name. In both cases
the expected return is C<< $sql >>.
When supplied with a method name, it is simply called on the
-L object as:
+L object as:
$self->$method_name ($op, $arg)
@@ -3064,13 +3130,27 @@ a fast interface to returning and formatting data. I frequently
use these three modules together to write complex database query
apps in under 50 lines.
-=head1 REPO
+=head1 HOW TO CONTRIBUTE
+
+Contributions are always welcome, in all usable forms (we especially
+welcome documentation improvements). The delivery methods include git-
+or unified-diff formatted patches, GitHub pull requests, or plain bug
+reports either via RT or the Mailing list. Contributors are generally
+granted full access to the official repository after their first several
+patches pass successful review.
+
+This project is maintained in a git repository. The code and related tools are
+accessible at the following locations:
=over
-=item * gitweb: L
+=item * Official repo: L
+
+=item * Official gitweb: L
+
+=item * GitHub mirror: L
-=item * git: L
+=item * Authorized committers: L
=back