BEGIN {
$Fey::SQL::Update::VERSION = '0.38';
}
use strict;
use Fey::Exceptions qw( param_error );
qw( ArrayRef CanQuote ColumnWithTable NonNullableUpdateValue
NullableUpdateValue Table );
use overload ();
use Scalar::Util qw( blessed );
use Moose;
use MooseX::Params::Validate qw( pos_validated_list );
with 'Fey::Role::SQL::HasOrderByClause', 'Fey::Role::SQL::HasLimitClause';
-excludes => 'bind_params',
-alias => { bind_params => '_where_clause_bind_params' },
};
-excludes => 'bind_params',
-alias => { bind_params => '_update_bind_params' },
};
has '_update' => (
is => 'rw',
isa => ArrayRef,
default => sub { [] },
init_arg => undef,
);
has '_set_pairs' => (
traits => ['Array'],
is => 'bare',
isa => ArrayRef[ArrayRef],
default => sub { [] },
handles => {
_add_set_pair => 'push',
_set_pairs => 'elements',
},
init_arg => undef,
);
sub update {
my $self = shift;
my $count = @_ ? @_ : 1;
my (@tables) = pos_validated_list(
\@_,
( ( { isa => Table } ) x $count ),
MX_PARAMS_VALIDATE_NO_CACHE => 1,
);
$self->_set_update( \@tables );
return $self;
}
sub set {
my $self = shift;
if ( !@_ || @_ % 2 ) {
my $count = @_;
param_error
"The set method expects a list of paired column objects and values but you passed $count parameters";
}
my @spec;
for ( my $x = 0; $x < @_; $x += 2 ) {
push @spec, { isa => ColumnWithTable };
push @spec,
blessed $_[$x] && $_[$x]->is_nullable()
? { isa => NullableUpdateValue }
: { isa => NonNullableUpdateValue };
}
my @set
= pos_validated_list( \@_, @spec, MX_PARAMS_VALIDATE_NO_CACHE => 1 );
for ( my $x = 0; $x < @_; $x += 2 ) {
my $val = $_[ $x + 1 ];
$val .= ''
if blessed $val && overload::Overloaded($val);
if ( !blessed $val ) {
if ( defined $val && $self->auto_placeholders() ) {
$self->_add_bind_param($val);
$val = Fey::Placeholder->new();
}
else {
$val = Fey::Literal->new_from_scalar($val);
}
}
$self->_add_set_pair( [ $_[$x], $val ] );
}
return $self;
}
sub sql {
my $self = shift;
my ($dbh) = pos_validated_list( \@_, { isa => CanQuote } );
return (
join ' ',
$self->update_clause($dbh),
$self->set_clause($dbh),
$self->where_clause($dbh),
$self->order_by_clause($dbh),
$self->limit_clause($dbh),
);
}
sub update_clause {
return 'UPDATE ' . $_[0]->_tables_subclause( $_[1] );
}
sub _tables_subclause {
return (
join ', ',
map { $_[1]->quote_identifier( $_->name() ) } @{ $_[0]->_update() }
);
}
sub set_clause {
my $self = shift;
my $dbh = shift;
# SQLite objects when the table name is provided ("User"."email")
# on the LHS of the set. I'm hoping that a DBMS which allows a
# multi-table update also allows the table name in the LHS.
my $col_quote = @{ $self->_update() } > 1 ? '_name_and_table' : '_name';
return (
'SET ' . (
join ', ',
map {
$self->$col_quote( $_->[0], $dbh ) . ' = '
. $_->[1]->sql($dbh)
} $self->_set_pairs()
)
);
}
sub _name_and_table {
return $_[1]->sql( $_[2] );
}
sub _name {
return $_[2]->quote_identifier( $_[1]->name() );
}
sub bind_params {
my $self = shift;
return (
$self->_update_bind_params(),
$self->_where_clause_bind_params(),
);
}
__PACKAGE__->meta()->make_immutable();
1;
# ABSTRACT: Represents a UPDATE query
=pod
=head1 NAME
Fey::SQL::Update - Represents a UPDATE query
=head1 VERSION
version 0.38
=head1 SYNOPSIS
my $sql = Fey::SQL->new_update();
# UPDATE Part
# SET quantity = 10
# WHERE part_id IN (1, 5)
$sql->update($Part);
$sql->set( $quantity, 10 );
$sql->where( $part_id, 'IN', 1, 5 );
print $sql->sql($dbh);
=head1 DESCRIPTION
This class represents a C<UPDATE> query.
=head1 METHODS
This class provides the following methods:
=head2 Constructor
To construct an object of this class, call C<< $query->update() >> on
a C<Fey::SQL> object.
=head2 $update->update()
This method specifies the C<UPDATE> clause of the query. It expects
one or more L<Fey::Table> objects (not aliases). Most RDBMS
implementations only allow for a single table here, but some (like
MySQL) do allow for multi-table updates.
=head2 $update->set(...)
This method takes a list of key/value pairs. The keys should be column
objects, and the value can be one of the following:
=over 4
=item * a plain scalar, including undef
This will be passed to C<< Fey::Literal->new_from_scalar() >>.
=item * C<Fey::Literal> object
=item * C<Fey::Column> object
A column alias cannot be used.
=item * C<Fey::Placeholder> object
=back
=head2 $update->where(...)
See the L<Fey::SQL section on WHERE Clauses|Fey::SQL/WHERE Clauses>
for more details.
=head2 $update->order_by(...)
See the L<Fey::SQL section on ORDER BY Clauses|Fey::SQL/ORDER BY
Clauses> for more details.
=head2 $update->limit(...)
See the L<Fey::SQL section on LIMIT Clauses|Fey::SQL/LIMIT Clauses>
for more details.
=head2 $update->sql($dbh)
Returns the full SQL statement which this object represents. A DBI
handle must be passed so that identifiers can be properly quoted.
=head2 $update->bind_params()
See the L<Fey::SQL section on Bind Parameters|Fey::SQL/Bind
Parameters> for more details.
=head2 $update->update_clause()
Returns the C<UPDATE> clause portion of the SQL statement as a string.
=head2 $update->set_clause()
Returns the C<SET> clause portion of the SQL statement as a string.
=head2 $update->where_clause()
Returns the C<WHERE> clause portion of the SQL statement as a string.
=head2 $update->order_by_clause()
Returns the C<ORDER BY> clause portion of the SQL statement as a
string.
=head2 $update->limit_clause()
Returns the C<LIMIT> clause portion of the SQL statement as a string.
=head1 ROLES
=over 4
=item * L<Fey::Role::SQL::HasBindParams>
=item * L<Fey::Role::SQL::HasWhereClause>
=item * L<Fey::Role::SQL::HasOrderByClause>
=item * L<Fey::Role::SQL::HasLimitClause>
=item * L<Fey::Role::SQL::Cloneable>
=back
=head1 BUGS
See L<Fey> for details on how to report bugs.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2010 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0
=cut
__END__