]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher.pm
Post-2.5-m1 whitespace fixup
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Storage / Publisher.pm
1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
3 our $VERSION = 1;
4
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;
7 use OpenSRF::Utils;
8 use OpenSRF::Utils::Logger qw/:level/;
9 use OpenILS::Utils::Fieldmapper;
10
11 my $log = 'OpenSRF::Utils::Logger';
12
13
14 sub register_method {
15     my $class = shift;
16     my %args = @_;
17     my %dup_args = %args;
18
19     $class = ref($class) || $class;
20
21     $args{package} ||= $class;
22     __PACKAGE__->SUPER::register_method( %args );
23
24     if (exists($dup_args{cachable}) and $dup_args{cachable}) {
25         (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
26         if ($name ne $dup_args{api_name}) {
27             $dup_args{real_api_name} = $dup_args{api_name};
28             $dup_args{method} = 'cachable_wrapper';
29             $dup_args{api_name} = $name;
30             $dup_args{package} = __PACKAGE__;
31             __PACKAGE__->SUPER::register_method( %dup_args );
32         }
33     }
34
35     if ($dup_args{real_api_name} =~ /^open-ils\.storage\.direct\..+\.search.+/o ||
36         $dup_args{api_name} =~ /^open-ils\.storage\.direct\..+\.search.+/o) {
37         $dup_args{api_name} = $dup_args{real_api_name} if ($dup_args{real_api_name});
38
39         (my $name = $dup_args{api_name}) =~ s/\.direct\./.id_list./o;
40
41         $dup_args{notes} = $dup_args{real_api_name};
42         $dup_args{real_api_name} = $dup_args{api_name};
43         $dup_args{method} = 'search_ids';
44         $dup_args{api_name} = $name;
45         $dup_args{package} = __PACKAGE__;
46
47         __PACKAGE__->SUPER::register_method( %dup_args );
48     }
49 }
50
51 sub cachable_wrapper {
52     my $self = shift;
53     my $client = shift;
54     my @args = @_;
55
56     my %cache_args = (
57         limit       => 100,
58         offset      => 0,
59         timeout     => 7200,
60         cache_page_size => 1000,
61     );
62
63     my @real_args;
64     my $key_string = $self->api_name;
65     for (my $ind = 0; $ind < scalar(@args); $ind++) {
66         if (    $args[$ind] eq 'limit' ||
67             $args[$ind] eq 'offset' ||
68             $args[$ind] eq 'cache_page_size' ||
69             $args[$ind] eq 'timeout' ) {
70
71             my $key_ind = $ind;
72             $ind++;
73             my $value_ind = $ind;
74             $cache_args{$args[$key_ind]} = $args[$value_ind];
75             $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
76             next;
77         }
78         $key_string .= $args[$ind];
79         $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
80         push @real_args, $args[$ind];
81     }
82
83     my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
84     my $cache_key;
85     {   use bytes;
86         $cache_key = md5_hex($key_string.$cache_page);
87     }
88
89     $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
90     $log->debug("Cache page is $cache_page", DEBUG);
91
92     my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
93     if (defined $cached_res) {
94         $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
95         $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
96         my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
97         my $end = int($start + $cache_args{limit} - 1);
98         $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
99             $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
100         return undef;
101     }
102
103     my $method = $self->method_lookup($self->{real_api_name});
104     my @res = $method->run(@real_args);
105
106
107         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
108
109     $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
110         int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
111     try {
112         OpenSRF::Utils::Cache->new->put_cache(
113             $cache_key =>
114             [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
115             OpenSRF::Utils->interval_to_seconds( $cache_args{timeout} )
116         );
117     } catch Error with {
118         my $e = shift;
119         $log->error("Cache seems to be down, $e");
120     };
121
122     return undef;
123 }
124
125 sub random_object {
126     my $self = shift;
127     my $client = shift;
128
129     my $cdbi = $self->{cdbi};
130     my $table = $cdbi->table;
131     my $sql = <<"    SQL";
132         SELECT  id
133           FROM  $table
134           WHERE id IN (( SELECT (RANDOM() * (SELECT MAX(id) FROM $table))::INT LIMIT 1 ));
135     SQL
136
137     my $trys = 100;
138     while ($trys--) {
139
140         my $id = $cdbi->db_Main->selectcol_arrayref($sql);
141         next unless (@$id);
142
143         return ($cdbi->fast_fieldmapper(@$id))[0];
144     }
145     return undef;
146 }
147
148 sub retrieve_node {
149     my $self = shift;
150     my $client = shift;
151     my @ids = @_;
152
153     my $cdbi = $self->{cdbi};
154
155     for my $id ( @ids ) {
156         next unless ($id);
157
158         my ($rec) = $cdbi->fast_fieldmapper($id);
159         if ($self->api_name !~ /batch/o) {
160             return $rec if ($rec);
161         }
162         $client->respond($rec);
163     }
164     return undef;
165 }
166
167 sub search_ids {
168     my $self = shift;
169     my $client = shift;
170     my @args = @_;
171
172     my @res = $self->method_lookup($self->{real_api_name})->run(@args);
173
174     if (ref($res[0]) eq 'ARRAY') {
175         return [ map { $_->id } @{ $res[0] } ];
176     }
177
178     $client->respond($_) for ( map { $_->id } @res );
179     return undef;
180 }
181
182 sub search_where {
183     my $self = shift;
184     my $client = shift;
185     my @args = @_;
186
187     if (ref($args[0]) eq 'HASH') {
188         if ($args[1]) {
189             $args[1]{limit_dialect} = $self->{cdbi}->db_Main;
190         } else {
191             $args[1] = {limit_dialect => $self->{cdbi}->db_Main };
192         }
193     } else {
194         $args[0] = { @args };
195         $args[1] = {limit_dialect => $self->{cdbi} };
196     }
197
198     my $cdbi = $self->{cdbi};
199
200     for my $obj ($cdbi->search_where(@args)) {
201         next unless ref($obj);
202         $client->respond( $obj->to_fieldmapper );
203     }
204     return undef;
205 }
206
207 sub search {
208     my $self = shift;
209     my $client = shift;
210     my @args = @_;
211
212     my $cdbi = $self->{cdbi};
213
214     (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
215
216     for my $obj ($cdbi->$search_type(@args)) {
217         next unless ref($obj);
218         $client->respond( $obj->to_fieldmapper );
219     }
220     return undef;
221 }
222
223 sub search_one_field {
224     my $self = shift;
225     my $client = shift;
226     my @args = @_;
227
228     (my $field = $self->api_name) =~ s/.*\.([^\.]+)$/$1/o;
229
230     return search( $self, $client, $field, @args );
231 }
232
233 sub old_search_one_field {
234     my $self = shift;
235     my $client = shift;
236     my @terms = @_;
237
238     (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
239     (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
240     my $cdbi = $self->{cdbi};
241
242     my $like = 0;
243     $like = 1 if ($search_type =~ /like$/o);
244     $like = 2 if ($search_type =~ /fts$/o);
245     $like = 3 if ($search_type =~ /regex$/o);
246
247     for my $term (@terms) {
248         $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
249         if (@terms == 1) {
250             return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
251         }
252         $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
253     }
254     return undef;
255 }
256
257
258 sub create_node {
259     my $self = shift;
260     my $client = shift;
261     my $node = shift;
262
263     local $OpenILS::Application::Storage::WRITE = 1;
264
265     my $cdbi = $self->{cdbi};
266
267     my $success;
268     try {
269         my $rec = $cdbi->create($node);
270         $success = $rec->id if ($rec);
271     } catch Error with {
272         $success = 0;
273     };
274
275     return $success;
276 }
277
278 sub update_node {
279     my $self = shift;
280     my $client = shift;
281     my $node = shift;
282
283     local $OpenILS::Application::Storage::WRITE = 1;
284
285     my $cdbi = $self->{cdbi};
286
287     return $cdbi->update($node);
288 }
289
290 sub mass_delete {
291     my $self = shift;
292     my $client = shift;
293     my $search = shift;
294
295     local $OpenILS::Application::Storage::WRITE = 1;
296
297     my $where = 'WHERE ';
298
299     my $cdbi = $self->{cdbi};
300     my $table = $cdbi->table;
301
302     my @keys = sort keys %$search;
303     
304     my @binds;
305     my @wheres;
306     for my $col ( @keys ) {
307         if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
308             push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
309             push @binds, map { "$_" } @{ $$search{$col} };
310         } else {
311             push @wheres, "$col = ?";
312             push @binds, $$search{$col};
313         }
314     }
315     $where .= join ' AND ', @wheres;
316
317     my $delete = "DELETE FROM $table $where";
318
319     $log->debug("Performing MASS deletion : $delete",DEBUG);
320
321     my $dbh = $cdbi->db_Main;
322     my $success = 1;
323     try {
324         my $sth = $dbh->prepare($delete);
325         $sth->execute( @binds );
326         $sth->finish;
327         $log->debug("MASS Delete succeeded",DEBUG);
328     } catch Error with {
329         $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
330         $success = 0;
331     };
332     return $success;
333 }
334
335 sub remote_update_node {
336     my $self = shift;
337     my $client = shift;
338     my $keys = shift;
339     my $vals = shift;
340
341     local $OpenILS::Application::Storage::WRITE = 1;
342
343     my $cdbi = $self->{cdbi};
344
345     my $success = 1;
346     try {
347         $success = $cdbi->remote_update($keys,$vals);
348     } catch Error with {
349         $success = 0;
350     };
351     return $success;
352 }
353
354 sub merge_node {
355     my $self = shift;
356     my $client = shift;
357     my $keys = shift;
358     my $vals = shift;
359
360     local $OpenILS::Application::Storage::WRITE = 1;
361
362     my $cdbi = $self->{cdbi};
363
364     my $success = 1;
365     try {
366         $success = $cdbi->merge($keys,$vals)->id;
367     } catch Error with {
368         $success = 0;
369     };
370     return $success;
371 }
372
373 sub delete_node {
374     my $self = shift;
375     my $client = shift;
376     my $node = shift;
377
378     local $OpenILS::Application::Storage::WRITE = 1;
379
380     my $cdbi = $self->{cdbi};
381
382     my $success = 1;
383     try {
384         $success = $cdbi->delete($node);
385     } catch Error with {
386         $success = 0;
387     };
388     return $success;
389 }
390
391 sub batch_call {
392     my $self = shift;
393     my $client = shift;
394     my @nodes = @_;
395
396     my $unwrap = $self->{unwrap};
397
398     my $cdbi = $self->{cdbi};
399     my $api_name = $self->api_name;
400     (my $single_call_api_name = $api_name) =~ s/batch\.//o;
401
402     $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
403     my $method = $self->method_lookup($single_call_api_name);
404
405     my @success;
406     while ( my $node = shift(@nodes) ) {
407         my ($res) = $method->run( ($unwrap ? (@$node) : ($node)) ); 
408         push(@success, 1) if ($res >= 0);
409     }
410
411     my $insert_total = 0;
412     $insert_total += $_ for (@success);
413
414     return $insert_total;
415 }
416
417
418 # --------------------- End of generic methods -----------------------
419
420
421 for my $pkg ( qw/actor action asset biblio config metabib authority money permission container/ ) {
422     "OpenILS::Application::Storage::Publisher::$pkg"->use;
423     if ($@) {
424         $log->debug("ARG! Couldn't load $pkg class Publisher: $@", ERROR);
425         throw OpenSRF::EX::ERROR ("ARG! Couldn't load $pkg class Publisher: $@");
426     }
427 }
428
429 for my $fmclass ( (Fieldmapper->classes) ) {
430
431     $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
432
433     next if ($fmclass->is_virtual);
434
435     (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
436     (my $class = $cdbi) =~ s/::.*//o;
437     (my $api_class = $cdbi) =~ s/::/./go;
438     my $registration_class = __PACKAGE__ . "::$class";
439     my $api_prefix = 'open-ils.storage.direct.'.$api_class;
440
441     # Create the search methods
442     unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
443         __PACKAGE__->register_method(
444             api_name    => $api_prefix.'.search',
445             method      => 'search',
446             api_level   => 1,
447             argc        => 2,
448             stream      => 1,
449             cdbi        => $cdbi,
450             cachable    => 1,
451         );
452     }
453
454     unless ( __PACKAGE__->is_registered( $api_prefix.'.search_where' ) ) {
455         __PACKAGE__->register_method(
456             api_name    => $api_prefix.'.search_where',
457             method      => 'search_where',
458             api_level   => 1,
459             stream      => 1,
460             argc        => 1,
461             cdbi        => $cdbi,
462             cachable    => 1,
463         );
464     }
465
466 =head1 comment
467
468     unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like' ) ) {
469         __PACKAGE__->register_method(
470             api_name    => $api_prefix.'.search_like',
471             method      => 'search',
472             api_level   => 1,
473             stream      => 1,
474             cdbi        => $cdbi,
475             cachable    => 1,
476             argc        => 2,
477         );
478     }
479
480     if (\&Class::DBI::search_fts and $cdbi->columns('FTS')) {
481         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts' ) ) {
482             __PACKAGE__->register_method(
483                 api_name    => $api_prefix.'.search_fts',
484                 method      => 'search',
485                 api_level   => 1,
486                 stream      => 1,
487                 cdbi        => $cdbi,
488                 cachable    => 1,
489                 argc        => 2,
490             );
491         }
492     }
493
494     if (\&Class::DBI::search_regex) {
495         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex' ) ) {
496             __PACKAGE__->register_method(
497                 api_name    => $api_prefix.'.search_regex',
498                 method      => 'search',
499                 api_level   => 1,
500                 stream      => 1,
501                 cdbi        => $cdbi,
502                 cachable    => 1,
503                 argc        => 2,
504             );
505         }
506     }
507
508     if (\&Class::DBI::search_ilike) {
509         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike' ) ) {
510             __PACKAGE__->register_method(
511                 api_name    => $api_prefix.'.search_ilike',
512                 method      => 'search',
513                 api_level   => 1,
514                 stream      => 1,
515                 cdbi        => $cdbi,
516                 cachable    => 1,
517                 argc        => 2,
518             );
519         }
520     }
521
522 =cut
523
524     # Create the random method
525     unless ( __PACKAGE__->is_registered( $api_prefix.'.random' ) ) {
526         __PACKAGE__->register_method(
527             api_name    => $api_prefix.'.random',
528             method      => 'random_object',
529             api_level   => 1,
530             cdbi        => $cdbi,
531             argc        => 0,
532         );
533     }
534
535     # Create the retrieve method
536     unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
537         __PACKAGE__->register_method(
538             api_name    => $api_prefix.'.retrieve',
539             method      => 'retrieve_node',
540             api_level   => 1,
541             cdbi        => $cdbi,
542             cachable    => 1,
543             argc        => 1,
544         );
545     }
546
547     # Create the batch retrieve method
548     unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
549         __PACKAGE__->register_method(
550             api_name    => $api_prefix.'.batch.retrieve',
551             method      => 'retrieve_node',
552             api_level   => 1,
553             stream      => 1,
554             cdbi        => $cdbi,
555             cachable    => 1,
556             argc        => 1,
557         );
558     }
559
560     for my $field ($fmclass->real_fields) {
561         unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
562             __PACKAGE__->register_method(
563                 api_name    => $api_prefix.'.search.'.$field,
564                 method      => 'search_one_field',
565                 api_level   => 1,
566                 cdbi        => $cdbi,
567                 cachable    => 1,
568                 stream      => 1,
569                 argc        => 1,
570             );
571         }
572
573 =head1 comment
574
575         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
576             __PACKAGE__->register_method(
577                 api_name    => $api_prefix.'.search_like.'.$field,
578                 method      => 'search_one_field',
579                 api_level   => 1,
580                 cdbi        => $cdbi,
581                 cachable    => 1,
582                 stream      => 1,
583                 argc        => 1,
584             );
585         }
586         if (\&Class::DBI::search_fts and grep { $field eq $_ } $cdbi->columns('FTS')) {
587             unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts.'.$field ) ) {
588                 __PACKAGE__->register_method(
589                     api_name    => $api_prefix.'.search_fts.'.$field,
590                     method      => 'search_one_field',
591                     api_level   => 1,
592                     cdbi        => $cdbi,
593                     cachable    => 1,
594                     stream      => 1,
595                     argc        => 1,
596                 );
597             }
598         }
599         if (\&Class::DBI::search_regex) {
600             unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex.'.$field ) ) {
601                 __PACKAGE__->register_method(
602                     api_name    => $api_prefix.'.search_regex.'.$field,
603                     method      => 'search_one_field',
604                     api_level   => 1,
605                     cdbi        => $cdbi,
606                     cachable    => 1,
607                     stream      => 1,
608                     argc        => 1,
609                 );
610             }
611         }
612         if (\&Class::DBI::search_ilike) {
613             unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike.'.$field ) ) {
614                 __PACKAGE__->register_method(
615                     api_name    => $api_prefix.'.search_ilike.'.$field,
616                     method      => 'search_one_field',
617                     api_level   => 1,
618                     cdbi        => $cdbi,
619                     cachable    => 1,
620                     stream      => 1,
621                     argc        => 1,
622                 );
623             }
624         }
625
626 =cut
627
628     }
629
630
631     unless ($fmclass->is_readonly) {
632         # Create the create method
633         unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
634             __PACKAGE__->register_method(
635                 api_name    => $api_prefix.'.create',
636                 method      => 'create_node',
637                 api_level   => 1,
638                 cdbi        => $cdbi,
639                 argc        => 1,
640             );
641         }
642
643         # Create the batch create method
644         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
645             __PACKAGE__->register_method(
646                 api_name    => $api_prefix.'.batch.create',
647                 method      => 'batch_call',
648                 api_level   => 1,
649                 cdbi        => $cdbi,
650                 argc        => 1,
651             );
652         }
653
654         # Create the update method
655         unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
656             __PACKAGE__->register_method(
657                 api_name    => $api_prefix.'.update',
658                 method      => 'update_node',
659                 api_level   => 1,
660                 cdbi        => $cdbi,
661                 argc        => 1,
662             );
663         }
664
665         # Create the batch update method
666         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
667             __PACKAGE__->register_method(
668                 api_name    => $api_prefix.'.batch.update',
669                 method      => 'batch_call',
670                 api_level   => 1,
671                 cdbi        => $cdbi,
672                 argc        => 1,
673             );
674         }
675
676         # Create the delete method
677         unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
678             __PACKAGE__->register_method(
679                 api_name    => $api_prefix.'.delete',
680                 method      => 'delete_node',
681                 api_level   => 1,
682                 cdbi        => $cdbi,
683                 argc        => 1,
684             );
685         }
686
687         # Create the batch delete method
688         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
689             __PACKAGE__->register_method(
690                 api_name    => $api_prefix.'.batch.delete',
691                 method      => 'batch_call',
692                 api_level   => 1,
693                 cdbi        => $cdbi,
694                 argc        => 1,
695             );
696         }
697
698         # Create the merge method
699         unless ( __PACKAGE__->is_registered( $api_prefix.'.merge' ) ) {
700             __PACKAGE__->register_method(
701                 api_name    => $api_prefix.'.merge',
702                 method      => 'merge_node',
703                 api_level   => 1,
704                 cdbi        => $cdbi,
705                 argc        => 1,
706             );
707         }
708
709         # Create the batch merge method
710         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.merge' ) ) {
711             __PACKAGE__->register_method(
712                 api_name    => $api_prefix.'.batch.merge',
713                 method      => 'batch_call',
714                 unwrap      => 1,
715                 api_level   => 1,
716                 cdbi        => $cdbi,
717                 argc        => 1,
718             );
719         }
720
721         # Create the remote_update method
722         unless ( __PACKAGE__->is_registered( $api_prefix.'.remote_update' ) ) {
723             __PACKAGE__->register_method(
724                 api_name    => $api_prefix.'.remote_update',
725                 method      => 'remote_update_node',
726                 api_level   => 1,
727                 cdbi        => $cdbi,
728                 argc        => 1,
729             );
730         }
731
732         # Create the batch remote_update method
733         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.remote_update' ) ) {
734             __PACKAGE__->register_method(
735                 api_name    => $api_prefix.'.batch.remote_update',
736                 method      => 'batch_call',
737                 api_level   => 1,
738                 unwrap      => 1,
739                 cdbi        => $cdbi,
740                 argc        => 1,
741             );
742         }
743
744         # Create the search-based mass delete method
745         unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
746             __PACKAGE__->register_method(
747                 api_name    => $api_prefix.'.mass_delete',
748                 method      => 'mass_delete',
749                 api_level   => 1,
750                 cdbi        => $cdbi,
751                 argc        => 1,
752             );
753         }
754     }
755 }
756
757 1;