Fey
view release on metacpan or search on metacpan
lib/Fey/SQL/Update.pm view on Meta::CPAN
package Fey::SQL::Update;
use strict;
use warnings;
use namespace::autoclean;
our $VERSION = '0.44';
use Fey::Exceptions qw( param_error );
use Fey::Literal;
use Fey::Types qw( ArrayRef CanQuote ColumnWithTable NonNullableUpdateValue
NullableUpdateValue Table );
use overload ();
use Scalar::Util qw( blessed );
use Moose 2.1200;
use MooseX::Params::Validate 0.21 qw( pos_validated_list );
use MooseX::SemiAffordanceAccessor 0.03;
use MooseX::StrictConstructor 0.13;
with 'Fey::Role::SQL::HasOrderByClause', 'Fey::Role::SQL::HasLimitClause';
with 'Fey::Role::SQL::HasWhereClause' => {
-excludes => 'bind_params',
-alias => { bind_params => '_where_clause_bind_params' },
};
with 'Fey::Role::SQL::HasBindParams' => {
-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,
);
with 'Fey::Role::SQL::Cloneable';
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 {
my $val = $_->[1];
my $val_sql = $val->sql($dbh);
$val_sql = "($val_sql)"
if blessed $val
&& $val->can('does')
&& $val->does('Fey::Role::SQL::ReturnsData');
$self->$col_quote( $_->[0], $dbh ) . ' = ' . $val_sql;
} $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
__END__
( run in 0.606 second using v1.01-cache-2.11-cpan-39bf76dae61 )