View | Details | Raw Unified | Return to bug 27584
Collapse All | Expand All

(-)a/Koha/OAI/Server/ListBase.pm (-61 / +73 lines)
Lines 45-57 sub GetRecords { Link Here
45
    if ( defined $token->{'set'} ) {
45
    if ( defined $token->{'set'} ) {
46
        $set = GetOAISetBySpec($token->{'set'});
46
        $set = GetOAISetBySpec($token->{'set'});
47
    }
47
    }
48
    my $offset = $token->{offset};
49
    my $deleted = defined $token->{deleted} ? $token->{deleted} : 0;
48
    my $deleted = defined $token->{deleted} ? $token->{deleted} : 0;
50
    my $deleted_count = defined $token->{deleted_count} ? $token->{deleted_count} : 0;
51
    my $max = $repository->{koha_max_count};
49
    my $max = $repository->{koha_max_count};
52
    my $count = 0;
50
    my $count = 0;
53
    my $format = $args{metadataPrefix} || $token->{metadata_prefix};
51
    my $format = $args{metadataPrefix} || $token->{metadata_prefix};
54
    my $include_items = $repository->items_included( $format );
52
    my $include_items = $repository->items_included( $format );
53
    my $from = $token->{from_arg};
54
    my $until = $token->{until_arg};
55
    my $next_id = $token->{next_id};
55
56
56
    # Since creating a union of normal and deleted record tables would be a heavy
57
    # Since creating a union of normal and deleted record tables would be a heavy
57
    # operation in a large database, build results in two stages:
58
    # operation in a large database, build results in two stages:
Lines 59-129 sub GetRecords { Link Here
59
    STAGELOOP:
60
    STAGELOOP:
60
    for ( ; $deleted >= 0; $deleted-- ) {
61
    for ( ; $deleted >= 0; $deleted-- ) {
61
        my $table = $deleted ? 'deletedbiblio_metadata' : 'biblio_metadata';
62
        my $table = $deleted ? 'deletedbiblio_metadata' : 'biblio_metadata';
63
64
        my @part_bind_params = ($next_id);
65
66
        my $where = "biblionumber >= ?";
67
        if ( $from ) {
68
            $where .= " AND timestamp >= ?";
69
            push @part_bind_params, $from;
70
        }
71
        if ( $until ) {
72
            $where .= " AND timestamp <= ?";
73
            push @part_bind_params, $until;
74
        }
75
        if ( defined $set ) {
76
            $where .= " AND biblionumber in (SELECT osb.biblionumber FROM oai_sets_biblios osb WHERE osb.set_id = ?)";
77
            push @part_bind_params, $set->{'id'};
78
        }
79
80
        my @bind_params = @part_bind_params;
81
82
        my $criteria = "WHERE $where ORDER BY biblionumber LIMIT " . ($max + 1);
83
62
        my $sql = "
84
        my $sql = "
63
            SELECT biblionumber
85
            SELECT biblionumber
64
            FROM $table
86
            FROM $table
65
            WHERE (timestamp >= ? AND timestamp <= ?)
87
            $criteria
66
        ";
88
        ";
67
        my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'});
68
89
69
        if ($include_items) {
90
        # If items are included, fetch a set of potential biblionumbers from items tables as well.
70
            $sql .= "
91
        # Then merge them, sort them and take the required number of them from the resulting list.
71
                OR biblionumber IN (SELECT biblionumber from deleteditems WHERE timestamp >= ? AND timestamp <= ?)
92
        # This may seem counter-intuitive as in worst case we fetch 3 times the biblionumbers needed,
72
            ";
93
        # but avoiding joins or subqueries makes this so much faster that it does not matter.
73
            push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'});
94
        if ( $include_items )  {
95
            $sql = "($sql) UNION (SELECT DISTINCT(biblionumber) FROM deleteditems $criteria)";
96
            push @bind_params, @part_bind_params;
74
            if (!$deleted) {
97
            if (!$deleted) {
75
                $sql .= "
98
                $sql .= " UNION (SELECT DISTINCT(biblionumber) FROM items $criteria)";
76
                    OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ?)
99
                push @bind_params, @part_bind_params;
77
                ";
78
                push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'});
79
            }
100
            }
101
            $sql = "SELECT biblionumber FROM ($sql) bibnos ORDER BY biblionumber LIMIT " . ($max + 1);
80
        }
102
        }
81
103
82
        $sql .= "
83
            ORDER BY biblionumber
84
        ";
85
86
        # Use a subquery for sets since it allows us to use an index in
87
        # biblioitems table and is quite a bit faster than a join.
88
        if (defined $set) {
89
            $sql = "
90
                SELECT bi.* FROM ($sql) bi
91
                  WHERE bi.biblionumber in (SELECT osb.biblionumber FROM oai_sets_biblios osb WHERE osb.set_id = ?)
92
            ";
93
            push @bind_params, $set->{'id'};
94
        }
95
96
        $sql .= "
97
            LIMIT " . ($max + 1) . "
98
            OFFSET " . ($offset - $deleted_count);
99
100
        my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
104
        my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
105
        $sth->execute( @bind_params ) || die( 'Could not execute statement: ' . $sth->errstr );
101
106
102
        if ( $deleted ) {
107
        my $ts_sql;
103
            $sql = "
108
        if ( $include_items ) {
104
                SELECT MAX(timestamp)
109
            if ( $deleted ) {
105
                FROM (
110
                $ts_sql = "
106
                    SELECT timestamp FROM deletedbiblio_metadata WHERE biblionumber = ?
111
                    SELECT MAX(timestamp)
107
                    UNION
112
                    FROM (
108
                    SELECT timestamp FROM deleteditems WHERE biblionumber = ?
113
                        SELECT timestamp FROM deletedbiblio_metadata WHERE biblionumber = ?
109
                ) bis
114
                        UNION
110
            ";
115
                        SELECT timestamp FROM deleteditems WHERE biblionumber = ?
116
                    ) bis
117
                ";
118
            } else {
119
                $ts_sql = "
120
                    SELECT MAX(timestamp)
121
                    FROM (
122
                        SELECT timestamp FROM biblio_metadata WHERE biblionumber = ?
123
                        UNION
124
                        SELECT timestamp FROM deleteditems WHERE biblionumber = ?
125
                        UNION
126
                        SELECT timestamp FROM items WHERE biblionumber = ?
127
                    ) bi
128
                ";
129
            }
111
        } else {
130
        } else {
112
            $sql = "
131
            $ts_sql = "SELECT timestamp FROM $table WHERE biblionumber = ?";
113
                SELECT MAX(timestamp)
114
                FROM (
115
                    SELECT timestamp FROM biblio_metadata WHERE biblionumber = ?
116
                    UNION
117
                    SELECT timestamp FROM deleteditems WHERE biblionumber = ?
118
                    UNION
119
                    SELECT timestamp FROM items WHERE biblionumber = ?
120
                ) bi
121
            ";
122
        }
132
        }
123
        my $record_sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
133
        my $ts_sth = $dbh->prepare( $ts_sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
124
134
125
        $sth->execute( @bind_params ) || die( 'Could not execute statement: ' . $sth->errstr );
135
        foreach my $row (@{ $sth->fetchall_arrayref() }) {
126
        while ( my ($biblionumber) = $sth->fetchrow ) {
136
            my $biblionumber = $row->[0];
127
            $count++;
137
            $count++;
128
            if ( $count > $max ) {
138
            if ( $count > $max ) {
129
                $self->resumptionToken(
139
                $self->resumptionToken(
Lines 131-148 sub GetRecords { Link Here
131
                        metadataPrefix  => $token->{metadata_prefix},
141
                        metadataPrefix  => $token->{metadata_prefix},
132
                        from            => $token->{from},
142
                        from            => $token->{from},
133
                        until           => $token->{until},
143
                        until           => $token->{until},
134
                        offset          => $token->{offset} + $max,
144
                        cursor          => $token->{cursor} + $max,
135
                        set             => $token->{set},
145
                        set             => $token->{set},
136
                        deleted         => $deleted,
146
                        deleted         => $deleted,
137
                        deleted_count   => $deleted_count
147
                        next_id         => $biblionumber
138
                    )
148
                    )
139
                );
149
                );
140
                last STAGELOOP;
150
                last STAGELOOP;
141
            }
151
            }
142
            my @params = $deleted ? ( $biblionumber, $biblionumber ) : ( $biblionumber, $biblionumber, $biblionumber );
152
            my @params = ($biblionumber);
143
            $record_sth->execute( @params ) || die( 'Could not execute statement: ' . $sth->errstr );
153
            if ( $include_items ) {
154
                push @params, $deleted ? ( $biblionumber ) : ( $biblionumber, $biblionumber );
155
            }
156
            $ts_sth->execute( @params ) || die( 'Could not execute statement: ' . $ts_sth->errstr );
144
157
145
            my ($timestamp) = $record_sth->fetchrow;
158
            my ($timestamp) = $ts_sth->fetchrow;
146
159
147
            my $oai_sets = GetOAISetsBiblio($biblionumber);
160
            my $oai_sets = GetOAISetsBiblio($biblionumber);
148
            my @setSpecs;
161
            my @setSpecs;
Lines 172-180 sub GetRecords { Link Here
172
                ) );
185
                ) );
173
            }
186
            }
174
        }
187
        }
175
        # Store offset and deleted record count
188
        # Reset $next_id for the next stage
176
        $offset += $count;
189
        $next_id = 0;
177
        $deleted_count = $offset if ($deleted);
178
    }
190
    }
179
    return $count;
191
    return $count;
180
}
192
}
(-)a/Koha/OAI/Server/ResumptionToken.pm (-17 / +18 lines)
Lines 1-6 Link Here
1
# Copyright Tamil s.a.r.l. 2008-2015
1
# Copyright Tamil s.a.r.l. 2008-2015
2
# Copyright Biblibre 2008-2015
2
# Copyright Biblibre 2008-2015
3
# Copyright The National Library of Finland, University of Helsinki 2016-2017
3
# Copyright The National Library of Finland, University of Helsinki 2016-2021
4
#
4
#
5
# This file is part of Koha.
5
# This file is part of Koha.
6
#
6
#
Lines 31-79 use base ("HTTP::OAI::ResumptionToken"); Link Here
31
# - metadataPrefix
31
# - metadataPrefix
32
# - from
32
# - from
33
# - until
33
# - until
34
# - offset
34
# - cursor
35
# - deleted
35
# - deleted
36
# - deleted count (not used anymore, but preserved for back-compatibility)
37
# - nextId
36
38
37
sub new {
39
sub new {
38
    my ($class, %args) = @_;
40
    my ($class, %args) = @_;
39
41
40
    my $self = $class->SUPER::new(%args);
42
    my $self = $class->SUPER::new(%args);
41
43
42
    my ($metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count);
44
    my ($metadata_prefix, $cursor, $from, $until, $set, $deleted, $next_id);
43
    if ( $args{ resumptionToken } ) {
45
    if ( $args{ resumptionToken } ) {
44
        ($metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count)
46
        # Note: undef in place of deleted record count for back-compatibility
47
        ($metadata_prefix, $cursor, $from, $until, $set, $deleted, undef, $next_id)
45
            = split( '/', $args{resumptionToken} );
48
            = split( '/', $args{resumptionToken} );
49
        $next_id = 0 unless $next_id;
46
    }
50
    }
47
    else {
51
    else {
48
        $metadata_prefix = $args{ metadataPrefix };
52
        $metadata_prefix = $args{ metadataPrefix };
49
        $from = $args{ from } || '1970-01-01';
53
        $from = $args{ from } || '';
50
        $until = $args{ until };
54
        $until = $args{ until } || '';
51
        unless ( $until) {
55
        # Add times to the arguments, when necessary, so they correctly match against the DB timestamps
52
            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime( time );
53
            $until = sprintf( "%.4d-%.2d-%.2d", $year+1900, $mon+1,$mday );
54
        }
55
        #Add times to the arguments, when necessary, so they correctly match against the DB timestamps
56
        $from .= 'T00:00:00Z' if length($from) == 10;
56
        $from .= 'T00:00:00Z' if length($from) == 10;
57
        $until .= 'T23:59:59Z' if length($until) == 10;
57
        $until .= 'T23:59:59Z' if length($until) == 10;
58
        $offset = $args{ offset } || 0;
58
        $cursor = $args{ cursor } // 0;
59
        $set = $args{ set } || '';
59
        $set = $args{ set } || '';
60
        $deleted = defined $args{ deleted } ? $args{ deleted } : 1;
60
        $deleted = defined $args{ deleted } ? $args{ deleted } : 1;
61
        $deleted_count = defined $args{ deleted_count } ? $args{ deleted_count } : 0;
61
        $next_id = $args{ next_id } // 0;
62
    }
62
    }
63
63
64
    $self->{ metadata_prefix } = $metadata_prefix;
64
    $self->{ metadata_prefix } = $metadata_prefix;
65
    $self->{ offset          } = $offset;
65
    $self->{ cursor          } = $cursor;
66
    $self->{ from            } = $from;
66
    $self->{ from            } = $from;
67
    $self->{ until           } = $until;
67
    $self->{ until           } = $until;
68
    $self->{ set             } = $set;
68
    $self->{ set             } = $set;
69
    $self->{ from_arg        } = _strip_UTC_designators($from);
69
    $self->{ from_arg        } = _strip_UTC_designators($from);
70
    $self->{ until_arg       } = _strip_UTC_designators($until);
70
    $self->{ until_arg       } = _strip_UTC_designators($until);
71
    $self->{ deleted         } = $deleted;
71
    $self->{ deleted         } = $deleted;
72
    $self->{ deleted_count   } = $deleted_count;
72
    $self->{ next_id         } = $next_id;
73
73
74
    # Note: put zero where deleted record count used to be for back-compatibility
74
    $self->resumptionToken(
75
    $self->resumptionToken(
75
        join( '/', $metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count ) );
76
        join( '/', $metadata_prefix, $cursor, $from, $until, $set, $deleted, 0, $next_id ) );
76
    $self->cursor( $offset );
77
    $self->cursor( $cursor );
77
78
78
    return $self;
79
    return $self;
79
}
80
}
(-)a/t/db_dependent/OAI/Server.t (-23 / +25 lines)
Lines 18-23 Link Here
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use Test::Deep qw( cmp_deeply re );
21
use Test::MockTime qw/set_fixed_time restore_time/;
22
use Test::MockTime qw/set_fixed_time restore_time/;
22
23
23
use Test::More tests => 31;
24
use Test::More tests => 31;
Lines 74-85 $dbh->do('DELETE FROM oai_sets'); Link Here
74
75
75
set_fixed_time(CORE::time());
76
set_fixed_time(CORE::time());
76
77
77
my $base_datetime = dt_from_string();
78
my $base_datetime = dt_from_string(undef, undef, 'UTC');
78
my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
79
my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
79
my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
80
my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
80
my (@header, @marcxml, @oaidc, @marcxml_transformed);
81
my (@header, @marcxml, @oaidc, @marcxml_transformed);
81
my $sth = $dbh->prepare('UPDATE biblioitems     SET timestamp=? WHERE biblionumber=?');
82
my $sth = $dbh->prepare('UPDATE biblioitems     SET timestamp=? WHERE biblionumber=?');
82
my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
83
my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
84
my $first_bn = 0;
83
85
84
# Add biblio records
86
# Add biblio records
85
foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
87
foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
Lines 94-99 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) { Link Here
94
        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
96
        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
95
    }
97
    }
96
    my ($biblionumber) = AddBiblio($record, '');
98
    my ($biblionumber) = AddBiblio($record, '');
99
    $first_bn = $biblionumber unless $first_bn;
97
    my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
100
    my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
98
    $sth->execute($timestamp,$biblionumber);
101
    $sth->execute($timestamp,$biblionumber);
99
    $sth2->execute($timestamp,$biblionumber);
102
    $sth2->execute($timestamp,$biblionumber);
Lines 174-180 sub test_query { Link Here
174
    }
177
    }
175
178
176
    delete $response->{responseDate};
179
    delete $response->{responseDate};
177
    unless (is_deeply($response, \%full_expected, $test)) {
180
    unless (cmp_deeply($response, \%full_expected, $test)) {
178
        diag
181
        diag
179
            "PARAM:" . YAML::XS::Dump($param) .
182
            "PARAM:" . YAML::XS::Dump($param) .
180
            "EXPECTED:" . YAML::XS::Dump(\%full_expected) .
183
            "EXPECTED:" . YAML::XS::Dump(\%full_expected) .
Lines 215-221 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
215
    ListIdentifiers => {
218
    ListIdentifiers => {
216
        header => [ @header[0..2] ],
219
        header => [ @header[0..2] ],
217
        resumptionToken => {
220
        resumptionToken => {
218
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
221
            content => re( qr{^marcxml/3////0/0/\d+$} ),
219
            cursor  => 3,
222
            cursor  => 3,
220
        },
223
        },
221
    },
224
    },
Lines 225-231 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
225
    ListIdentifiers => {
228
    ListIdentifiers => {
226
        header => [ @header[0..2] ],
229
        header => [ @header[0..2] ],
227
        resumptionToken => {
230
        resumptionToken => {
228
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
231
            content => re( qr{^marcxml/3////0/0/\d+$} ),
229
            cursor  => 3,
232
            cursor  => 3,
230
        },
233
        },
231
    },
234
    },
Lines 233-244 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
233
236
234
test_query(
237
test_query(
235
    'ListIdentifiers with resumptionToken 1',
238
    'ListIdentifiers with resumptionToken 1',
236
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
239
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
237
    {
240
    {
238
        ListIdentifiers => {
241
        ListIdentifiers => {
239
            header => [ @header[3..5] ],
242
            header => [ @header[3..5] ],
240
            resumptionToken => {
243
            resumptionToken => {
241
              content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
244
              content => re( qr{^marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
242
              cursor  => 6,
245
              cursor  => 6,
243
            },
246
            },
244
          },
247
          },
Lines 247-258 test_query( Link Here
247
250
248
test_query(
251
test_query(
249
    'ListIdentifiers with resumptionToken 2',
252
    'ListIdentifiers with resumptionToken 2',
250
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
253
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
251
    {
254
    {
252
        ListIdentifiers => {
255
        ListIdentifiers => {
253
            header => [ @header[6..8] ],
256
            header => [ @header[6..8] ],
254
            resumptionToken => {
257
            resumptionToken => {
255
              content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
258
              content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
256
              cursor  => 9,
259
              cursor  => 9,
257
            },
260
            },
258
          },
261
          },
Lines 261-267 test_query( Link Here
261
264
262
test_query(
265
test_query(
263
    'ListIdentifiers with resumptionToken 3, response without resumption',
266
    'ListIdentifiers with resumptionToken 3, response without resumption',
264
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
267
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
265
    {
268
    {
266
        ListIdentifiers => {
269
        ListIdentifiers => {
267
            header => $header[9],
270
            header => $header[9],
Lines 280-286 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar Link Here
280
    ListRecords => {
283
    ListRecords => {
281
        record => [ @marcxml[0..2] ],
284
        record => [ @marcxml[0..2] ],
282
        resumptionToken => {
285
        resumptionToken => {
283
          content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
286
          content => re( qr{^marcxml/3////0/0/\d+$} ),
284
          cursor  => 3,
287
          cursor  => 3,
285
        },
288
        },
286
    },
289
    },
Lines 288-298 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar Link Here
288
291
289
test_query(
292
test_query(
290
    'ListRecords marcxml with resumptionToken 1',
293
    'ListRecords marcxml with resumptionToken 1',
291
    { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
294
    { verb => 'ListRecords', resumptionToken => "marcxml/3////0/0/" . ($first_bn + 3) },
292
    { ListRecords => {
295
    { ListRecords => {
293
        record => [ @marcxml[3..5] ],
296
        record => [ @marcxml[3..5] ],
294
        resumptionToken => {
297
        resumptionToken => {
295
          content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
298
          content => re( qr{^marcxml/6////0/0/\d+$} ),
296
          cursor  => 6,
299
          cursor  => 6,
297
        },
300
        },
298
    },
301
    },
Lines 300-310 test_query( Link Here
300
303
301
test_query(
304
test_query(
302
    'ListRecords marcxml with resumptionToken 2',
305
    'ListRecords marcxml with resumptionToken 2',
303
    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
306
    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
304
    { ListRecords => {
307
    { ListRecords => {
305
        record => [ @marcxml[6..8] ],
308
        record => [ @marcxml[6..8] ],
306
        resumptionToken => {
309
        resumptionToken => {
307
          content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
310
          content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
308
          cursor  => 9,
311
          cursor  => 9,
309
        },
312
        },
310
    },
313
    },
Lines 313-319 test_query( Link Here
313
# Last record, so no resumption token
316
# Last record, so no resumption token
314
test_query(
317
test_query(
315
    'ListRecords marcxml with resumptionToken 3, response without resumption',
318
    'ListRecords marcxml with resumptionToken 3, response without resumption',
316
    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
319
    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
317
    { ListRecords => {
320
    { ListRecords => {
318
        record => $marcxml[9],
321
        record => $marcxml[9],
319
    },
322
    },
Lines 323-329 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_ Link Here
323
    ListRecords => {
326
    ListRecords => {
324
        record => [ @oaidc[0..2] ],
327
        record => [ @oaidc[0..2] ],
325
        resumptionToken => {
328
        resumptionToken => {
326
          content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
329
          content => re( qr{^oai_dc/3////0/0/\d+$} ),
327
          cursor  => 3,
330
          cursor  => 3,
328
        },
331
        },
329
    },
332
    },
Lines 331-341 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_ Link Here
331
334
332
test_query(
335
test_query(
333
    'ListRecords oai_dc with resumptionToken 1',
336
    'ListRecords oai_dc with resumptionToken 1',
334
    { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
337
    { verb => 'ListRecords', resumptionToken => "oai_dc/3////0/0/" . ($first_bn + 3) },
335
    { ListRecords => {
338
    { ListRecords => {
336
        record => [ @oaidc[3..5] ],
339
        record => [ @oaidc[3..5] ],
337
        resumptionToken => {
340
        resumptionToken => {
338
          content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
341
          content => re( qr{^oai_dc/6////0/0/\d+$} ),
339
          cursor  => 6,
342
          cursor  => 6,
340
        },
343
        },
341
    },
344
    },
Lines 343-353 test_query( Link Here
343
346
344
test_query(
347
test_query(
345
    'ListRecords oai_dc with resumptionToken 2',
348
    'ListRecords oai_dc with resumptionToken 2',
346
    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
349
    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
347
    { ListRecords => {
350
    { ListRecords => {
348
        record => [ @oaidc[6..8] ],
351
        record => [ @oaidc[6..8] ],
349
        resumptionToken => {
352
        resumptionToken => {
350
          content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
353
          content => re( qr{^oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
351
          cursor  => 9,
354
          cursor  => 9,
352
        },
355
        },
353
    },
356
    },
Lines 356-362 test_query( Link Here
356
# Last record, so no resumption token
359
# Last record, so no resumption token
357
test_query(
360
test_query(
358
    'ListRecords oai_dc with resumptionToken 3, response without resumption',
361
    'ListRecords oai_dc with resumptionToken 3, response without resumption',
359
    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
362
    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
360
    { ListRecords => {
363
    { ListRecords => {
361
        record => $oaidc[9],
364
        record => $oaidc[9],
362
    },
365
    },
Lines 369-375 test_query('ListRecords marcxml with xsl transformation', Link Here
369
    { ListRecords => {
372
    { ListRecords => {
370
        record => [ @marcxml_transformed[0..2] ],
373
        record => [ @marcxml_transformed[0..2] ],
371
        resumptionToken => {
374
        resumptionToken => {
372
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
375
            content => re( qr{^marcxml/3////0/0/\d+$} ),
373
            cursor => 3,
376
            cursor => 3,
374
        }
377
        }
375
    },
378
    },
376
- 

Return to bug 27584