view release on metacpan or search on metacpan
0.89 2016-02-21T03:17:18Z
- fixed pod syntax
0.88 2016-02-19T08:42:25Z
- [BUGFIX] fixed `last_insert_id` on MySQL. (`DBIx::Handler#dbh` send a ping to mysql. But, It removes `$dbh->{mysql_insertid}`.)
0.87 2016-02-02T01:33:39Z
- Allow to enable `suppress_(row|result)_objects` on select_by_sql and select_named.
0.86 2016-01-26T02:39:43Z
- Split Aniki::Plugin::PagerInjector
- Added Aniki::Plugin::SQLPager
0.85 2015-12-10T10:31:33Z
- Fixed no disconnect if caught not a connect error at retring in Aniki::Handler::RoundRobin
- switch to old style package declation syntax for some old static code analyzers
0.08 2015-10-26T12:39:36Z
- Split handler class from main class.
- Added WeightedRoundRobin plugin.
0.07 2015-10-13T07:59:29Z
- Fixed broken suppress_row_objects option of select method.
0.06 2015-10-13T00:39:45Z
- A little refactor. (no feature changes)
0.05 2015-10-12T18:17:51Z
- Renamed some methods/classes
- Performance improvements
- Support custom result class
- Keep inflated rows to keep relay cache
0.04_02 2015-09-28T08:26:21Z
- Plugin::SelectJoined is broken when outer joined.
0.04_01 2015-09-28T07:00:10Z
- Added Plugin::SelectJoined (inspired by Teng::Plugin::SearchJoined)
- Support to select specified columns by table
0.03_01 2015-09-22T04:58:09Z
- Support automatic pre-cache the inverse relational rows
0.02_09 2015-09-19T05:23:54Z
- Support the canonicalize of the `SELECT` SQL statement
- Support deep prefetch
minil.toml
script/install-aniki
t/00_compile.t
t/01_new.t
t/02_insert.t
t/03_insert_and_fetch_id.t
t/04_insert_and_fetch_row.t
t/05_insert_and_emulate_row.t
t/06_insert_multi.t
t/07_insert_on_duplicate.t
t/08_select.t
t/09_update.t
t/10_delete.t
t/11_relay.t
t/12_new_row_from_hashref.t
t/13_new_collection_from_arrayref.t
t/14_update_and_fetch_row.t
t/15_update_and_emulate_row.t
t/filter/declare/basic.t
t/filter/deflate/basic.t
t/filter/deflate/regex.t
t/lib/t/DB/Exception.pm
t/lib/t/DB/Filter.pm
t/lib/t/DB/Row.pm
t/lib/t/DB/Row/Author.pm
t/lib/t/DB/Schema/Common.pm
t/lib/t/DB/Schema/MySQL.pm
t/lib/t/DB/Schema/PostgreSQL.pm
t/lib/t/DB/Schema/SQLite.pm
t/lib/t/Util.pm
t/plugin/count/count.t
t/plugin/pager/select_with_pager.t
t/plugin/pager_injector/inject_pager_to_result.t
t/plugin/range_condition_maker/make_range_condition.t
t/plugin/select_joined/select_joined.t
t/plugin/sql_pager/select_by_sql_with_pager.t
t/plugin/sql_pager/select_named_with_pager.t
t/plugin/weighted_round_robin/weighted_round_robin.t
t/query_builder/canonical.t
t/row/can.t
t/row/joined/can.t
t/schema/00_has_many.t
t/schema/relationship/basic.t
t/schema/relationship/pluralize.t
t/schema/relationships/basic.t
META.yml
MANIFEST
$db->insert(module => {
name => 'DBIx::Schema::DSL',
author_id => $author_id,
});
$db->insert(module => {
name => 'Riji',
author_id => $author_id,
});
my $module = $db->select(module => {
name => 'Riji',
}, {
limit => 1,
})->first;
say '$module->name: ', $module->name; ## Riji
say '$module->author->name: ', $module->author->name; ## SONGMU
my $author = $db->select(author => {
name => 'songmu',
}, {
limit => 1,
prefetch => [qw/modules/],
})->first;
say '$author->name: ', $author->name; ## SONGMU
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
};
integer 'id', primary_key, auto_increment;
varchar 'name', unique;
};
};
```
A `author` has many `modules`.
So you can access `author` row object to `modules`.
```perl
my $author = $db->select(author => { name => 'songmu' })->first;
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
```
Also `module` has a `author`.
So you can access `module` row object to `author` also.
```perl
my $module = $db->select(module => { name => 'Riji' })->first;
say "Riji's author is ", $module->author->name; ## SONGMU
```
And you can pre-fetch related rows.
```perl
my @modules = $db->select(module => {}, { prefetch => [qw/author/] });
say $_->name, "'s author is ", $_->author->name for @modules;
```
# SETUP
Install Aniki from CPAN:
```
cpanm Aniki
```
If this option is true, no create row objects.
Aniki's methods returns hash reference instead of row object.
- `suppress_result_objects : Bool`
If this option is true, no create result objects.
Aniki's methods returns array reference instead of result object.
## INSTANCE METHODS
### `select($table_name, \%where, \%opt)`
Execute `SELECT` query by generated SQL, and returns result object.
```perl
my $result = $db->select(foo => { id => 1 }, { limit => 1 });
# stmt: SELECT FROM foo WHERE id = ? LIMIT 1
# bind: [1]
```
#### Options
There are the options of `SELECT` query.
See also [SQL::Maker](https://metacpan.org/pod/SQL::Maker#opt).
And you can use there options:
- `columns : ArrayRef[Str]`
List for retrieving columns from database.
- `prefetch : ArrayRef|HashRef`
Pre-fetch specified related rows.
See also ["RELATIONSHIP"](#relationship) section.
### `select_named($sql, \%bind, \%opt)`
### `select_by_sql($sql, \@bind, \%opt)`
Execute `SELECT` query by specified SQL, and returns result object.
```perl
my $result = $db->select_by_sql('SELECT FROM foo WHERE id = ? LIMIT 1', [1]);
# stmt: SELECT FROM foo WHERE id = ? LIMIT 1
# bind: [1]
```
#### Options
You can use there options:
- `table_name: Str`
my $count = $db->update(foo => { bar => 2 }, { id => 1 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
```
### `update($row, \%set)`
Execute `UPDATE` query, and returns changed rows count.
```perl
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $count = $db->update($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
```
### `update_and_fetch_row($row, \%set)`
Execute `UPDATE` query, and `SELECT` it, and returns row object.
```perl
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $new_row = $db->update_and_fetch_row($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
```
### `update_and_emulate_row($row, \%set)`
Execute `UPDATE` query, and returns row object created by `$row` and `%set`.
```perl
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $new_row = $db->update_and_emulate_row($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
```
This method is faster than `update_and_fetch_row`.
#### WARNING
If you use SQL `TRIGGER` or `AutoCommit`, this method don't return the correct value, maybe.
my $count = $db->delete(foo => { id => 1 });
# stmt: DELETE FROM foo WHERE id = ?
# bind: [1]
```
### `delete($row)`
Execute `DELETE` query, and returns changed rows count.
```perl
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $count = $db->delete($row);
# stmt: DELETE foo WHERE id = ?
# bind: [1]
```
## ACCESSORS
- `schema : Aniki::Schema`
- `filter : Aniki::Filter`
- `query_builder : Aniki::QueryBuilder`
author/benchmark.pl view on Meta::CPAN
say '=============== SELECT ===============';
cmpthese timethese 20000 => {
dbic => sub {
my @rows = $dbic->resultset('Author')->search({}, { rows => 10, order_by => { -asc => 'id' } })->all;
},
teng => sub {
my @rows = $teng->search('author' => {}, { limit => 10, order_by => { id => 'ASC' } })->all;
},
aniki => sub {
my @rows = $aniki->select('author' => {}, { limit => 10, order_by => { id => 'ASC' } })->all;
},
};
say '=============== UPDATE ===============';
cmpthese timethese 20000 => {
dbic => sub {
my $row = $dbic->resultset('Author')->single({ id => 1 });
$row->update({ message => 'good morning' });
},
'teng(row)' => sub {
my $row = $teng->single('author' => { id => 1 });
$row->update({ message => 'good morning' });
},
teng => sub {
$teng->update('author' => { message => 'good morning' }, { id => 1 });
},
'aniki(row)' => sub {
my $row = $aniki->select('author' => { id => 1 }, { limit => 1 })->first;
$aniki->update($row => { message => 'good morning' });
},
aniki => sub {
$aniki->update('author' => { message => 'good morning' }, { id => 1 });
},
};
say '=============== DELETE ===============';
my ($dbic_delete_id, $teng_delete_id, $aniki_delete_id) = (0, 0, 0);
cmpthese {
author/benchmark.pl view on Meta::CPAN
timethese 20000 => {
dbic => sub {
my $row = $dbic->resultset('Author')->single({ id => ++$dbic_delete_id });
$row->delete;
},
'teng(row)' => sub {
my $row = $teng->single('author' => { id => ++$teng_delete_id });
$row->delete;
},
'aniki(row)' => sub {
my $row = $aniki->select('author' => { id => ++$aniki_delete_id }, { limit => 1 })->first;
$aniki->delete($row);
},
}
},
do {
($teng_delete_id, $aniki_delete_id) = (0, 0);
$aniki->dbh->do('DELETE FROM author');
$aniki->dbh->do('DELETE FROM sqlite_sequence WHERE name = ?', undef, 'author');
$teng->dbh->do('DELETE FROM author');
$teng->dbh->do('DELETE FROM sqlite_sequence WHERE name = ?', undef, 'author');
eg/synopsis.pl view on Meta::CPAN
$db->insert(module => {
name => 'DBIx::Schema::DSL',
author_id => $author_id,
});
$db->insert(module => {
name => 'Riji',
author_id => $author_id,
});
my $module = $db->select(module => {
name => 'Riji',
}, {
limit => 1,
})->first;
say '$module->name: ', $module->name; ## Riji
say '$module->author->name: ', $module->author->name; ## SONGMU
my $author = $db->select(author => {
name => 'songmu',
}, {
limit => 1,
prefetch => [qw/modules/],
})->first;
say '$author->name: ', $author->name; ## SONGMU
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
lib/Aniki.pm view on Meta::CPAN
}
sub update_and_fetch_row {
my ($self, $row, $set) = @_;
croak '(Aniki#update_and_fetch_row) condition must be a Aniki::Row object.'
unless blessed $row && $row->isa('Aniki::Row');
my $emulated_row_data = $self->_update_and_emulate_row_data($row, $set);
my $where = $self->_where_row_cond($row->table, $emulated_row_data);
return $self->select($row->table_name, $where, { limit => 1, suppress_result_objects => 1 })->[0];
}
sub update_and_emulate_row {
my ($self, $row, $set) = @_;
croak '(Aniki#update_and_emulate_row) condition must be a Aniki::Row object.' unless blessed $row && $row->isa('Aniki::Row');
my $emulated_row_data = $self->_update_and_emulate_row_data($row, $set);
return $emulated_row_data if $self->suppress_row_objects;
return $self->guess_row_class($row->table_name)->new(
lib/Aniki.pm view on Meta::CPAN
my $table_name = shift;
my $row_data = shift;
my $table = $self->schema->get_table($table_name) or croak "$table_name is not defined in schema.";
local $self->{_context} = $self->dbh;
$self->insert($table_name, $row_data, @_);
return unless defined wantarray;
my $row = $self->select($table_name, $self->_where_row_cond($table, $row_data), { limit => 1, suppress_result_objects => 1 })->[0];
return $row if $self->suppress_row_objects;
$row->is_new(1);
return $row;
}
sub insert_and_emulate_row {
my ($self, $table_name, $row, $opt) = @_;
my $table = $self->schema->get_table($table_name) or croak "$table_name is not defined in schema.";
lib/Aniki.pm view on Meta::CPAN
: $pk->is_auto_increment ? $self->last_insert_id($table->name, $pk->name)
: undef
;
}
return \%where;
}
my $WILDCARD_COLUMNS = ['*'];
sub select :method {
my ($self, $table_name, $where, $opt) = @_;
$where //= {};
$opt //= {};
croak '(Aniki#select) `where` condition must be a reference.' unless ref $where;
my $table = $self->schema->get_table($table_name);
my $columns = exists $opt->{columns} ? $opt->{columns}
: defined $table ? $table->field_names
: $WILDCARD_COLUMNS;
$where = $self->_bind_sql_type_to_args($table, $where) if defined $table;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
my ($sql, @bind) = $self->query_builder->select($table_name, $columns, $where, $opt);
return $self->select_by_sql($sql, \@bind, {
%$opt,
table_name => $table_name,
columns => $columns,
});
}
sub fetch_and_attach_relay_data {
my ($self, $table_name, $prefetch, $rows) = @_;
return unless @$rows;
lib/Aniki.pm view on Meta::CPAN
sub _fetch_and_attach_relay_data {
my ($self, $relationships, $rows, $key, $prefetch) = @_;
my $relationship = $relationships->get($key);
unless ($relationship) {
croak "'$key' is not defined as relationship. (maybe possible typo?)";
}
$relationship->fetcher->execute($self, $rows, $prefetch);
}
sub select_named {
my ($self, $sql, $bind, $opt) = @_;
return $self->select_by_sql(bind_named($sql, $bind), $opt);
}
sub select_by_sql {
my ($self, $sql, $bind, $opt) = @_;
$opt //= {};
local $self->{suppress_row_objects} = 1 if $opt->{suppress_row_objects};
local $self->{suppress_result_objects} = 1 if $opt->{suppress_result_objects};
my $table_name = exists $opt->{table_name} ? $opt->{table_name} : $self->_guess_table_name($sql);
my $columns = exists $opt->{columns} ? $opt->{columns} : undef;
my $prefetch = exists $opt->{prefetch} ? $opt->{prefetch} : [];
$prefetch = [$prefetch] if ref $prefetch eq 'HASH';
lib/Aniki.pm view on Meta::CPAN
$db->insert(module => {
name => 'DBIx::Schema::DSL',
author_id => $author_id,
});
$db->insert(module => {
name => 'Riji',
author_id => $author_id,
});
my $module = $db->select(module => {
name => 'Riji',
}, {
limit => 1,
})->first;
say '$module->name: ', $module->name; ## Riji
say '$module->author->name: ', $module->author->name; ## SONGMU
my $author = $db->select(author => {
name => 'songmu',
}, {
limit => 1,
prefetch => [qw/modules/],
})->first;
say '$author->name: ', $author->name; ## SONGMU
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
};
lib/Aniki.pm view on Meta::CPAN
create_table 'author' => columns {
integer 'id', primary_key, auto_increment;
varchar 'name', unique;
};
};
A C<author> has many C<modules>.
So you can access C<author> row object to C<modules>.
my $author = $db->select(author => { name => 'songmu' })->first;
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
Also C<module> has a C<author>.
So you can access C<module> row object to C<author> also.
my $module = $db->select(module => { name => 'Riji' })->first;
say "Riji's author is ", $module->author->name; ## SONGMU
And you can pre-fetch related rows.
my @modules = $db->select(module => {}, { prefetch => [qw/author/] });
say $_->name, "'s author is ", $_->author->name for @modules;
=head1 SETUP
Install Aniki from CPAN:
cpanm Aniki
And run C<install-aniki> command.
lib/Aniki.pm view on Meta::CPAN
=item C<suppress_result_objects : Bool>
If this option is true, no create result objects.
Aniki's methods returns array reference instead of result object.
=back
=head2 INSTANCE METHODS
=head3 C<select($table_name, \%where, \%opt)>
Execute C<SELECT> query by generated SQL, and returns result object.
my $result = $db->select(foo => { id => 1 }, { limit => 1 });
# stmt: SELECT FROM foo WHERE id = ? LIMIT 1
# bind: [1]
=head4 Options
There are the options of C<SELECT> query.
See also L<SQL::Maker|https://metacpan.org/pod/SQL::Maker#opt>.
And you can use there options:
lib/Aniki.pm view on Meta::CPAN
List for retrieving columns from database.
=item C<prefetch : ArrayRef|HashRef>
Pre-fetch specified related rows.
See also L</"RELATIONSHIP"> section.
=back
=head3 C<select_named($sql, \%bind, \%opt)>
=head3 C<select_by_sql($sql, \@bind, \%opt)>
Execute C<SELECT> query by specified SQL, and returns result object.
my $result = $db->select_by_sql('SELECT FROM foo WHERE id = ? LIMIT 1', [1]);
# stmt: SELECT FROM foo WHERE id = ? LIMIT 1
# bind: [1]
=head4 Options
You can use there options:
=over 4
=item C<table_name: Str>
lib/Aniki.pm view on Meta::CPAN
Execute C<UPDATE> query, and returns changed rows count.
my $count = $db->update(foo => { bar => 2 }, { id => 1 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
=head3 C<update($row, \%set)>
Execute C<UPDATE> query, and returns changed rows count.
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $count = $db->update($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
=head3 C<update_and_fetch_row($row, \%set)>
Execute C<UPDATE> query, and C<SELECT> it, and returns row object.
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $new_row = $db->update_and_fetch_row($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
=head3 C<update_and_emulate_row($row, \%set)>
Execute C<UPDATE> query, and returns row object created by C<$row> and C<%set>.
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $new_row = $db->update_and_emulate_row($row => { bar => 2 });
# stmt: UPDATE foo SET bar = ? WHERE id = ?
# bind: [2, 1]
This method is faster than C<update_and_fetch_row>.
=head4 WARNING
If you use SQL C<TRIGGER> or C<AutoCommit>, this method don't return the correct value, maybe.
In this case, you should use C<update_and_fetch_row> instead of this method.
lib/Aniki.pm view on Meta::CPAN
Execute C<DELETE> query, and returns changed rows count.
my $count = $db->delete(foo => { id => 1 });
# stmt: DELETE FROM foo WHERE id = ?
# bind: [1]
=head3 C<delete($row)>
Execute C<DELETE> query, and returns changed rows count.
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first;
my $count = $db->delete($row);
# stmt: DELETE foo WHERE id = ?
# bind: [1]
=head2 ACCESSORS
=over 4
=item C<schema : Aniki::Schema>
lib/Aniki/Plugin/Count.pm view on Meta::CPAN
my ($self, $table, $column, $where, $opt) = @_;
$where //= {};
$column //= '*';
croak '(Aniki::Plugin::Count#count) `where` condition must be a reference.' unless ref $where;
if (ref $column) {
croak 'Do not pass HashRef/ArrayRef to second argument. Usage: $db->count($table[, $column[, $where[, $opt]]])';
}
my ($sql, @binds) = $self->query_builder->select($table, [\"COUNT($column)"], $where, $opt);
my ($count) = $self->dbh->selectrow_array($sql, undef, @binds);
return $count;
}
1;
__END__
=pod
=encoding utf-8
lib/Aniki/Plugin/Pager.pm view on Meta::CPAN
package Aniki::Plugin::Pager;
use 5.014002;
use namespace::autoclean;
use Mouse::Role;
use Carp qw/croak/;
requires qw/select/;
with qw/Aniki::Plugin::PagerInjector/;
with qw/Aniki::Plugin::RangeConditionMaker/;
sub select_with_pager {
my ($self, $table_name, $where, $opt) = @_;
$where //= {};
$opt //= {};
croak '(Aniki::Plugin::Pager#select_with_pager) `where` condition must be a reference.' unless ref $where;
my $range_condition = $self->make_range_condition($opt);
if ($range_condition) {
ref $where eq 'HASH'
or croak "where condition *MUST* be HashRef when using range codition.";
for my $column (keys %$range_condition) {
croak "Conflict range condition and where condition for $table_name.$column"
if exists $where->{$column};
}
$where = {%$where, %$range_condition};
}
my $page = $opt->{page} or Carp::croak("required parameter: page");
my $rows = $opt->{rows} or Carp::croak("required parameter: rows");
my $result = $self->select($table_name => $where, {
%$opt,
limit => $rows + 1,
!$range_condition ? (
offset => $rows * ($page - 1),
) : (),
});
return $self->inject_pager_to_result($result => {
rows => $rows,
page => $page,
lib/Aniki/Plugin/Pager.pm view on Meta::CPAN
=head1 SYNOPSIS
package MyDB;
use Mouse v2.4.5;
extends qw/Aniki/;
with qw/Aniki::Plugin::Pager/;
package main;
my $db = MyDB->new(...);
my $result = $db->select_with_pager('user', { type => 2 }, { page => 1, rows => 10 }); # => Aniki::Result::Collection(+Aniki::Result::Role::Pager)
$result->pager; # => Data::Page::NoTotalEntries
=head1 SEE ALSO
L<perl>
=head1 LICENSE
Copyright (C) karupanerura.
lib/Aniki/Plugin/PagerInjector.pm view on Meta::CPAN
package MyDB;
use Mouse v2.4.5;
extends qw/Aniki/;
with qw/Aniki::Plugin::PagerInjector/;
package main;
my $db = MyDB->new(...);
my ($page, $rows) = (1, 10);
my ($limit, $offset) = ($rows + 1, ($page - 1) * $rows);
my $result = $db->select('user', { type => 2 }, { limit => $limit, offset => $offset }); # => Aniki::Result::Collection
$result = $db->inject_pager_to_result($result => { # => inject Aniki::Result::Role::Pager
table_name => 'user',
rows => $rows,
page => $page,
})
$result->pager; # => Data::Page::NoTotalEntries
=head1 SEE ALSO
L<perl>
lib/Aniki/Plugin/SQLPager.pm view on Meta::CPAN
package Aniki::Plugin::SQLPager;
use 5.014002;
use namespace::autoclean;
use Mouse::Role;
requires qw/select_by_sql select_named/;
with qw/Aniki::Plugin::PagerInjector/;
sub select_by_sql_with_pager {
my ($self, $sql, $bind, $opt) = @_;
$opt //= {};
my $page = $opt->{page} or Carp::croak("required parameter: page");
my $rows = $opt->{rows} or Carp::croak("required parameter: rows");
my $limit = $rows + 1;
my $offset = $rows * ($page - 1);
if ($opt->{no_offset}) {
$sql .= sprintf ' LIMIT %d', $limit;
}
else {
$sql .= sprintf ' LIMIT %d OFFSET %d', $limit, $offset;
}
my $result = $self->select_by_sql($sql, $bind, $opt);
return $self->inject_pager_to_result($result => $opt);
}
sub select_named_with_pager {
my ($self, $sql, $bind, $opt) = @_;
$opt //= {};
my $page = $opt->{page} or Carp::croak("required parameter: page");
my $rows = $opt->{rows} or Carp::croak("required parameter: rows");
my $limit = $rows + 1;
my $offset = $rows * ($page - 1);
if ($opt->{no_offset}) {
$sql .= sprintf ' LIMIT %d', $limit;
}
else {
$sql .= sprintf ' LIMIT %d OFFSET %d', $limit, $offset;
}
my $result = $self->select_named($sql, $bind, $opt);
return $self->inject_pager_to_result($result => $opt);
}
1;
__END__
=pod
=for stopwords sql
lib/Aniki/Plugin/SQLPager.pm view on Meta::CPAN
=head1 SYNOPSIS
package MyDB;
use Mouse v2.4.5;
extends qw/Aniki/;
with qw/Aniki::Plugin::Pager/;
package main;
my $db = MyDB->new(...);
my $result = $db->select_by_sql_with_pager('SELECT * FROM user WHERE type = ?', [ 2 ], { page => 1, rows => 10 }); # => Aniki::Result::Collection(+Aniki::Result::Role::Pager)
# ALSO OK: my $result = $db->select_named_with_pager('SELECT * FROM user WHERE type = :type', { type => 2 }, { page => 1, rows => 10 }); # => Aniki::Result::Collection(+Aniki::Result::Role::Pager)
$result->pager; # => Data::Page::NoTotalEntries
=head1 SEE ALSO
L<perl>
=head1 LICENSE
Copyright (C) karupanerura.
lib/Aniki/Plugin/SelectJoined.pm view on Meta::CPAN
use namespace::autoclean;
use Mouse::Role;
use Aniki::QueryBuilder;
use Aniki::Result::Collection::Joined;
use Carp qw/croak/;
requires qw/schema query_builder suppress_row_objects txn_manager execute/;
Aniki::QueryBuilder->load_plugin('JoinSelect');
sub select_joined {
my ($self, $base_table, $join_conditions, $where, $opt) = @_;
croak '(Aniki::Plugin::SelectJoined#select_joined) `where` condition must be a reference.' unless ref $where;
my @table_names = ($base_table);
for (my $i = 0; my $table = $join_conditions->[$i]; $i += 2) {
push @table_names => $table;
}
my @tables = map { $self->schema->get_table($_) } @table_names;
my $name_sep = $self->query_builder->name_sep;
my @columns;
for my $table (@tables) {
my $table_name = $table->name;
push @columns =>
map { "$table_name$name_sep$_" }
map { $_->name } $table->get_fields();
}
my ($sql, @bind) = $self->query_builder->join_select($base_table, $join_conditions, \@columns, $where, $opt);
return $self->select_joined_by_sql($sql, \@bind, {
table_names => \@table_names,
columns => \@columns,
%$opt,
});
}
sub select_joined_by_sql {
my ($self, $sql, $bind, $opt) = @_;
$opt //= {};
my $table_names = $opt->{table_names} or croak 'table_names is required';
my $columns = $opt->{columns} or croak 'columns is required';
my $prefetch = exists $opt->{prefetch} ? $opt->{prefetch} : {};
my $prefetch_enabled_fg = %$prefetch && !$self->suppress_row_objects;
if ($prefetch_enabled_fg) {
my $txn; $txn = $self->txn_scope unless $self->txn_manager->in_transaction;
lib/Aniki/Plugin/SelectJoined.pm view on Meta::CPAN
=head1 SYNOPSIS
package MyDB;
use Mouse v2.4.5;
extends qw/Aniki/;
with qw/Aniki::Plugin::SelectJoined/;
package main;
my $db = MyDB->new(...);
my $result = $db->select_joined(user_item => [
user => {'user_item.user_id' => 'user.id'},
item => {'user_item.item_id' => 'item.id'},
], {
'user.id' => 2,
}, {
order_by => 'user_item.item_id',
});
for my $row ($result->all) {
my $user_item = $row->user_item;
lib/Aniki/QueryBuilder/Canonical.pm view on Meta::CPAN
sub delete :method {
my ($self, $table, $where, $opt) = @_;
if (ref $where eq 'HASH') {
$where = [
map { $_ => $where->{$_} } sort keys %$where
];
}
return $self->SUPER::delete($table, $where, $opt);
}
sub select_query {
my ($self, $table, $fields, $where, $opt) = @_;
if (ref $where eq 'HASH') {
$where = [
map { $_ => $where->{$_} } sort keys %$where
];
}
return $self->SUPER::select_query($table, $fields, $where, $opt);
}
1;
__END__
lib/Aniki/Result.pm view on Meta::CPAN
=pod
=encoding utf-8
=head1 NAME
Aniki::Result - Result class
=head1 SYNOPSIS
my $result = $db->select(foo => { bar => 1 });
=head1 DESCRIPTION
This is abstract result class.
Aniki detect the collection class from root result class by table name.
Default root result class is C<MyApp::DB::Collection>.
You can use original result class:
lib/Aniki/Result/Collection.pm view on Meta::CPAN
=pod
=encoding utf-8
=head1 NAME
Aniki::Result::Collection - Rows as a collection
=head1 SYNOPSIS
my $result = $db->select(foo => { bar => 1 });
for my $row ($result->all) {
print $row->id, "\n";
}
=head1 DESCRIPTION
This is collection result class.
=head1 INSTANCE METHODS
lib/Aniki/Row.pm view on Meta::CPAN
}
return \%row;
}
sub refetch {
my ($self, $opts) = @_;
$opts //= +{};
$opts->{limit} = 1;
my $where = $self->handler->_where_row_cond($self->table, $self->row_data);
return $self->handler->select($self->table_name => $where, $opts)->first;
}
my %accessor_method_cache;
sub _accessor_method_cache {
my $self = shift;
return $accessor_method_cache{$self->table_name} //= {};
}
sub _guess_accessor_method {
my ($invocant, $method) = @_;
lib/Aniki/Row.pm view on Meta::CPAN
=pod
=encoding utf-8
=head1 NAME
Aniki::Row - Row class
=head1 SYNOPSIS
my $result = $db->select(foo => { bar => 1 });
for my $row ($result->all) {
print $row->id, "\n";
}
=head1 DESCRIPTION
This is row class.
=head1 INSTANCE METHODS
lib/Aniki/Schema/Relationship/Fetcher.pm view on Meta::CPAN
my @src_values = grep defined, map { $_->get_column($src_column) } @$rows;
unless (@src_values) {
# set empty value
for my $row (@$rows) {
$row->relay_data->{$name} = $has_many ? [] : undef;
}
return;
}
my @related_rows = $handler->select($table_name => {
%where,
$dest_column => sql_in(\@src_values),
}, { prefetch => $prefetch })->all;
my %related_rows_map = partition_by { $_->get_column($dest_column) } @related_rows;
for my $row (@$rows) {
my $src_value = $row->get_column($src_column);
unless (defined $src_value) {
# set empty value
$row->relay_data->{$name} = $has_many ? [] : undef;
lib/Aniki/Schema/Relationship/Fetcher.pm view on Meta::CPAN
my $related_rows = $related_rows_map{$src_value};
$row->relay_data->{$name} = $has_many ? $related_rows : $related_rows->[0];
}
$self->_execute_inverse(\@related_rows => $rows);
}
else {
# follow slow case...
for my $row (@$rows) {
next if notall { defined $row->get_column($_) } @src_columns;
my @related_rows = $handler->select($table_name => {
%where,
pairwise { $a => $row->get_column($b) } @dest_columns, @src_columns
}, { prefetch => $prefetch })->all;
$row->relay_data->{$name} = $has_many ? \@related_rows : $related_rows[0];
}
}
}
sub _execute_inverse {
my ($self, $src_rows, $dest_rows) = @_;
t/02_insert.t view on Meta::CPAN
use utf8;
use Test::More;
use File::Spec;
use lib File::Spec->catfile('t', 'lib');
use t::Util;
run_on_database {
db->insert(author => { name => 'MOZNION' });
is db->select(author => {}, { limit => 1 })->count, 1, 'created.';
};
done_testing();
t/03_insert_and_fetch_id.t view on Meta::CPAN
use Test::More;
use File::Spec;
use lib File::Spec->catfile('t', 'lib');
use t::Util;
run_on_database {
my $id = db->insert_and_fetch_id(author => { name => 'MOZNION' });
ok defined $id, 'id is defined.';
my $row = db->select(author => { id => $id }, { limit => 1 })->first;
is_deeply $row->get_columns, {
id => $id,
name => 'MOZNION',
message => 'hello',
inflate_message => 'hello',
deflate_message => 'hello',
}, 'Data is valid.';
};
done_testing();
t/06_insert_multi.t view on Meta::CPAN
run_on_database {
is query_count {
db->insert_multi(author => []);
}, 0, 'nothing to do if empty values';
db->insert_multi(author => [
{ name => 'MOZNION', message => 'hoge' },
{ name => 'PAPIX', message => 'fuga' },
]);
is db->select(author => {}, {})->count, 2, 'created.';
is db->select(author => { name => 'PAPIX' }, { limit => 1 })->first->message, 'fuga';
if (db->query_builder->driver eq 'mysql') {
db->insert_multi(author => [
{ name => 'PAPIX', message => 'hoge' },
{ name => 'KARUPA', message => 'fuga' },
], {
update => {
message => sql_raw('VALUES(message)'),
}
});
is db->select(author => {}, {})->count, 3, 'created.';
is db->select(author => { name => 'PAPIX' }, { limit => 1 })->first->message, 'hoge';
};
};
done_testing();
t/07_insert_on_duplicate.t view on Meta::CPAN
use SQL::QueryMaker qw/sql_raw/;
run_on_each_databases [qw/MySQL/] => sub {
db->insert_on_duplicate(author => {
name => 'PAPIX',
message => 'hoge',
}, {
message => sql_raw('VALUES(message)'),
});
is db->select(author => {}, {})->count, 1, 'created.';
is db->select(author => { name => 'PAPIX' }, { limit => 1 })->first->message, 'hoge';
db->insert_on_duplicate(author => {
name => 'PAPIX',
message => 'fuga',
}, {
message => sql_raw('VALUES(message)'),
});
is db->select(author => {}, {})->count, 1, 'updated.';
is db->select(author => { name => 'PAPIX' }, { limit => 1 })->first->message, 'fuga';
};
done_testing();
t/08_select.t view on Meta::CPAN
use Test::More;
use File::Spec;
use lib File::Spec->catfile('t', 'lib');
use t::Util;
run_on_database {
db->insert(author => { name => 'MOZNION' });
my $rows = db->select(author => {});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 1;
isa_ok $rows->first, 'Aniki::Row';
$rows = db->select(author => {
name => 'OBAKE'
});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 0;
$rows = db->select(author => {}, { suppress_row_objects => 1 });
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 1;
isa_ok $rows->first, 'HASH';
$rows = db->select(author => {}, { suppress_result_objects => 1 });
isa_ok $rows, 'ARRAY';
is @$rows, 1;
isa_ok $rows->[0], 'Aniki::Row';
$rows = db->select(author => {}, { suppress_result_objects => 1, suppress_row_objects => 1 });
isa_ok $rows, 'ARRAY';
is @$rows, 1;
isa_ok $rows->[0], 'HASH';
};
done_testing();
t/09_update.t view on Meta::CPAN
use Test::More;
use File::Spec;
use lib File::Spec->catfile('t', 'lib');
use t::Util;
run_on_database {
db->insert(author => { name => 'MOZNION' });
db->update(author => { name => 'MOZNION2' }, { name => 'MOZNION' });
my $rows = db->select(author => {});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 1;
is $rows->first->name, 'MOZNION2', 'updated';
my $row = $rows->first;
my $cnt = db->update($row => { name => 'MOZNION' });
is $row->name, 'MOZNION2', 'old value';
is $cnt, 1, 'a row is changed';
my $new_row = $row->refetch;
t/10_delete.t view on Meta::CPAN
use Test::More;
use File::Spec;
use lib File::Spec->catfile('t', 'lib');
use t::Util;
run_on_database {
db->insert(author => { name => 'MOZNION' });
db->insert(author => { name => 'MOZNION2' });
is db->select(author => {})->count, 2;
db->delete(author => { name => 'MOZNION' });
my $rows = db->select(author => {});
is $rows->count, 1;
my $row = $rows->first;
is $row->name, 'MOZNION2';
db->delete($row);
is db->select(author => {})->count, 0;
my ($line, $file);
eval { db->delete(author => 'id = 1') }; ($line, $file) = (__LINE__, __FILE__);
like $@, qr/^\Q(Aniki#delete) `where` condition must be a reference at $file line $line/, 'croak with no set parameters';
};
done_testing();
t/11_relay.t view on Meta::CPAN
my $mamimu_id = db->insert_and_fetch_id(author => { name => 'MAMIMU' });
db->insert_multi(version => [map {
+{ name => '0.01', module_id => $_ },
} @moznion_module_ids, @karupa_module_ids]);
subtest 'shallow' => sub {
subtest 'prefetch' => sub {
my $queries = query_count {
my $rows = db->select(author => {}, { prefetch => [qw/modules/] });
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
ok $_->is_prefetched('modules') for $rows->all;
my %modules = map { $_->name => [sort map { $_->name } $_->modules] } $rows->all;
is_deeply \%modules, {
MOZNION => [qw/Perl::Lint Regexp::Lexer Test::JsonAPI::Autodoc/],
KARUPA => [qw/Plack::App::Vhost TOML::Parser Test::SharedObject/],
MAMIMU => [],
};
};
is $queries, 2;
};
subtest 'lazy' => sub {
my $queries = query_count {
my $rows = db->select(author => {});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
ok !$_->is_prefetched('modules') for $rows->all;
my %modules = map { $_->name => [sort map { $_->name } $_->modules] } $rows->all;
is_deeply \%modules, {
MOZNION => [qw/Perl::Lint Regexp::Lexer Test::JsonAPI::Autodoc/],
KARUPA => [qw/Plack::App::Vhost TOML::Parser Test::SharedObject/],
MAMIMU => [],
};
};
is $queries, 4;
};
};
subtest 'deep' => sub {
subtest 'prefetch' => sub {
my $queries = query_count {
my $rows = db->select(author => {}, { prefetch => { modules => [qw/versions/] } });
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
for my $row ($rows->all) {
ok $row->is_prefetched('modules');
ok $_->is_prefetched('versions') for $row->modules;
}
my %modules = map {
$_->name => +{
map {
t/11_relay.t view on Meta::CPAN
},
MAMIMU => +{
},
};
};
is $queries, 3;
};
subtest 'lazy' => sub {
my $queries = query_count {
my $rows = db->select(author => {});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
for my $row ($rows->all) {
ok !$row->is_prefetched('modules');
ok !$_->is_prefetched('versions') for $row->modules;
}
my %modules = map {
$_->name => +{
map {
t/11_relay.t view on Meta::CPAN
},
};
};
is $queries, 10;
};
};
subtest 'inverse' => sub {
subtest 'prefetch' => sub {
my $queries = query_count {
my $rows = db->select(author => {}, { prefetch => { modules => [qw/versions/] } });
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
for my $row ($rows->all) {
ok $row->is_prefetched('modules');
ok $_->is_prefetched('versions') for $row->modules;
}
my %modules = map { $_->versions->[0]->module->name => [$_->author->name, map { $_->name } @{ $_->versions }] } map { $_->modules } $rows->all;
is_deeply \%modules, {
'Perl::Lint' => ['MOZNION', '0.01'],
t/11_relay.t view on Meta::CPAN
'Plack::App::Vhost' => ['KARUPA', '0.01'],
'TOML::Parser' => ['KARUPA', '0.01'],
'Test::SharedObject' => ['KARUPA', '0.01'],
} or diag explain \%modules;
};
is $queries, 3;
};
subtest 'lazy' => sub {
my $queries = query_count {
my $rows = db->select(author => {});
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
for my $row ($rows->all) {
ok !$row->is_prefetched('modules');
ok !$_->is_prefetched('versions') for $row->modules;
}
my %modules = map { $_->versions->[0]->module->name => [$_->author->name, map { $_->name } @{ $_->versions }] } map { $_->modules } $rows->all;
is_deeply \%modules, {
'Perl::Lint' => ['MOZNION', '0.01'],
t/11_relay.t view on Meta::CPAN
is $queries, 10;
};
};
db->insert_multi(version => [map {
+{ name => '0.02', module_id => $_ },
} @moznion_module_ids, @karupa_module_ids]);
subtest 'extra where conditions' => sub {
my $queries = query_count {
my $rows = db->select(author => {}, { prefetch => { modules => { versions => { '.name' => '0.02' } } } });
isa_ok $rows, 'Aniki::Result::Collection';
is $rows->count, 3;
for my $row ($rows->all) {
ok $row->is_prefetched('modules');
ok $_->is_prefetched('versions') for $row->modules;
}
my %modules = map {
$_->name => +{
map {
t/plugin/pager/select_with_pager.t view on Meta::CPAN
use t::Util;
run_on_database {
Mouse::Util::apply_all_roles(db, 'Aniki::Plugin::Pager');
db->insert_multi(author => [map {
+{ name => $_ }
} qw/MOZNION KARUPA PAPIX/]);
subtest 'ASC' => sub {
my $rows = db->select_with_pager(author => {}, { rows => 2, page => 1, order_by => { id => 'ASC' } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 2;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 1;
ok $rows->pager->has_next;
$rows = db->select_with_pager(author => {}, { rows => 2, page => 2, order_by => { id => 'ASC' } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
$rows = db->select_with_pager(author => {}, { rows => 2, page => 2, order_by => { id => 'ASC' }, lower => { id => 2 } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
};
subtest 'DESC' => sub {
my $rows = db->select_with_pager(author => {}, { rows => 2, page => 1, order_by => { id => 'DESC' } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 2;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 1;
ok $rows->pager->has_next;
$rows = db->select_with_pager(author => {}, { rows => 2, page => 2, order_by => { id => 'DESC' } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 1;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
$rows = db->select_with_pager(author => {}, { rows => 2, page => 2, order_by => { id => 'DESC' }, upper => { id => 2 } });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 1;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
};
};
t/plugin/pager_injector/inject_pager_to_result.t view on Meta::CPAN
use Aniki::Plugin::PagerInjector;
use t::Util;
run_on_database {
Mouse::Util::apply_all_roles(db, 'Aniki::Plugin::PagerInjector');
db->insert_multi(author => [map {
+{ name => $_ }
} qw/MOZNION KARUPA PAPIX/]);
my $rows = db->select(author => {}, { limit => 3, offset => 0 });
isa_ok $rows, 'Aniki::Result::Collection';
ok !$rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 3;
$rows = db->inject_pager_to_result($rows => { page => 1, rows => 2 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 2;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 1;
ok $rows->pager->has_next;
$rows = db->select(author => {}, { limit => 3, offset => 2 });
isa_ok $rows, 'Aniki::Result::Collection';
ok !$rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
$rows = db->inject_pager_to_result($rows => { page => 2, rows => 2 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
t/plugin/range_condition_maker/make_range_condition.t view on Meta::CPAN
Mouse::Util::apply_all_roles(db, 'Aniki::Plugin::RangeConditionMaker');
db->insert_multi(author => [map {
+{ name => $_ }
} qw/MOZNION KARUPA PAPIX MACKEE/]);
my ($where, $result);
for my $type (qw/lower gt/) {
$where = db->make_range_condition({ $type => { id => 2 } });
$result = db->select('author', $where);
is scalar (map { $_->{id} > 2 } @{ $result->row_datas }), 2;
}
for my $type (qw/upper lt/) {
$where = db->make_range_condition({ $type => { id => 4 } });
$result = db->select('author', $where);
is scalar (map { $_->{id} < 4 } @{ $result->row_datas }), 3;
}
$where = db->make_range_condition({ ge => { id => 2 } });
$result = db->select('author', $where);
is scalar (map { $_->{id} >= 2 } @{ $result->row_datas }), 3;
$where = db->make_range_condition({ le => { id => 4 } });
$result = db->select('author', $where);
is scalar (map { $_->{id} <= 4 } @{ $result->row_datas }), 4;
$where = db->make_range_condition({ lower => { id => 1 }, upper => { id => 3 } });
$result = db->select('author', $where);
is scalar @{$result->row_datas}, 1;
is $result->row_datas->[0]->{id}, 2;
};
done_testing();
t/plugin/select_joined/select_joined.t view on Meta::CPAN
db->insert_and_fetch_id(module => { name => 'TOML::Parser', author_id => $karupa_id }),
db->insert_and_fetch_id(module => { name => 'Plack::App::Vhost', author_id => $karupa_id }),
db->insert_and_fetch_id(module => { name => 'Test::SharedObject', author_id => $karupa_id }),
);
my $obake1_id = db->insert_and_fetch_id(author => { name => 'OBAKE1' });
my $obake2_id = db->insert_and_fetch_id(author => { name => 'OBAKE2' });
my $obake3_id = db->insert_and_fetch_id(author => { name => 'OBAKE3' });
subtest normal => sub {
my $result = db->select_joined(author => [
module => { 'module.author_id' => 'author.id' },
], {
'author.id' => $moznion_id,
}, {
order_by => 'module.id',
});
my @authors = $result->all('author');
my @modules = $result->all('module');
is scalar @authors, 1;
t/plugin/select_joined/select_joined.t view on Meta::CPAN
is query_count { $module->versions }, 1;
my $expected = shift @expected;
is $module->name, $expected;
}
};
};
subtest outer => sub {
my $result = db->select_joined(author => [
module => [LEFT => { 'module.author_id' => 'author.id' }],
], {
# anywhere
}, {
order_by => ['author.id', 'module.id'],
});
my @authors = $result->all('author');
my @modules = $result->all('module');
is scalar @authors, 5;
t/plugin/select_joined/select_joined.t view on Meta::CPAN
is $module->table_name, 'module';
my $expected = shift @expected;
is $author->name, $expected->{author};
is $module->name, $expected->{module};
}
};
};
subtest prefetch => sub {
my $result = db->select_joined(author => [
module => { 'module.author_id' => 'author.id' },
], {
'author.id' => $moznion_id,
}, {
order_by => 'module.id',
prefetch => {
module => [qw/versions/],
}
});
t/plugin/sql_pager/select_by_sql_with_pager.t view on Meta::CPAN
use Aniki::Plugin::SQLPager;
use t::Util;
run_on_database {
Mouse::Util::apply_all_roles(db, 'Aniki::Plugin::SQLPager');
db->insert_multi(author => [map {
+{ name => $_ }
} qw/MOZNION KARUPA PAPIX/]);
my $rows = db->select_by_sql_with_pager('SELECT * FROM author ORDER BY id', [], { rows => 2, page => 1 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 2;
is $rows->first->id, 1;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 1;
ok $rows->pager->has_next;
$rows = db->select_by_sql_with_pager('SELECT * FROM author ORDER BY id', [], { rows => 2, page => 2 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
$rows = db->select_by_sql_with_pager('SELECT * FROM author WHERE id > ? ORDER BY id', [2], { rows => 2, page => 2, no_offset => 1 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
};
t/plugin/sql_pager/select_named_with_pager.t view on Meta::CPAN
use Aniki::Plugin::SQLPager;
use t::Util;
run_on_database {
Mouse::Util::apply_all_roles(db, 'Aniki::Plugin::SQLPager');
db->insert_multi(author => [map {
+{ name => $_ }
} qw/MOZNION KARUPA PAPIX/]);
my $rows = db->select_named_with_pager('SELECT * FROM author ORDER BY id', {}, { rows => 2, page => 1 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 2;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 1;
ok $rows->pager->has_next;
$rows = db->select_named_with_pager('SELECT * FROM author ORDER BY id', {}, { rows => 2, page => 2 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
$rows = db->select_named_with_pager('SELECT * FROM author WHERE id > :id ORDER BY id', { id => 2 }, { rows => 2, page => 2, no_offset => 1 });
isa_ok $rows, 'Aniki::Result::Collection';
ok $rows->meta->does_role('Aniki::Result::Role::Pager');
is $rows->count, 1;
is $rows->first->id, 3;
isa_ok $rows->pager, 'Data::Page::NoTotalEntries';
is $rows->pager->current_page, 2;
ok !$rows->pager->has_next;
};