tons of storage server changes... see diffs
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Driver / Pg.pm
1 { # Based on the change to Class::DBI in OpenILS::Application::Storage.  This will
2   # allow us to use TSearch2 via a simple cdbi "search" interface.
3         #-------------------------------------------------------------------------------
4         use Class::DBI;
5         package Class::DBI;
6
7         sub search_fti {
8                 my $self = shift;
9                 my @args = @_;
10                 if (ref($args[-1]) eq 'HASH') {
11                         $args[-1]->{_placeholder} = "to_tsquery('default',?)";
12                 } else {
13                         push @args, {_placeholder => "to_tsquery('default',?)"};
14                 }
15                 $self->_do_search("@@"  => @args);
16         }
17 }
18
19 { # Every driver needs to provide a 'compile()' method to OpenILS::Application::Storage::FTS.
20   # If that driver wants to support FTI, that is...
21         #-------------------------------------------------------------------------------
22         package OpenILS::Application::Storage::FTS;
23         use OpenSRF::Utils::Logger qw/:level/;
24         my $log = 'OpenSRF::Utils::Logger';
25
26         sub compile {
27                 my $self = shift;
28                 my $term = shift;
29
30                 $self = ref($self) || $self;
31                 $self = bless {} => $self;
32
33                 $self->decompose($term);
34
35                 my $newterm = join('&', $self->words);
36
37                 if (@{$self->nots}) {
38                         $newterm = '('.$newterm.')&('. join('|', $self->nots) . ')';
39                 }
40
41                 $log->debug("Compiled term is [$newterm]", DEBUG);
42                 $newterm = OpenILS::Application::Storage::Driver::Pg->quote($newterm);
43                 $log->debug("Quoted term is [$newterm]", DEBUG);
44
45                 $self->{fts_query} = ["to_tsquery('default',$newterm)"];
46                 $self->{fts_query_nots} = [];
47                 $self->{fts_op} = '@@';
48                 $self->{text_col} = shift;
49                 $self->{fts_col} = shift;
50
51                 return $self;
52         }
53
54         sub sql_where_clause {
55                 my $self = shift;
56                 my $column = $self->fts_col;
57                 my @output;
58         
59                 my @ranks;
60                 for my $fts ( $self->fts_query ) {
61                         push @output, join(' ', $self->fts_col, $self->{fts_op}, $fts);
62                         push @ranks, "rank($column, $fts)";
63                 }
64                 $self->{fts_rank} = \@ranks;
65         
66                 my $phrase_match = $self->sql_exact_phrase_match();
67                 return join(' AND ', @output) . $phrase_match;
68         }
69
70 }
71
72
73 { # The driver package itself just needs a db_Main method (or db_Slaves if
74   #Class::DBI::Replication is in use) for Class::DBI to call.
75   #
76   # Any other fixups can go in here too... Also, the drivers should subclass the
77   # DBI driver that they are wrapping, or provide a 'quote()' method that calls
78   # the DBD::xxx::quote() method on FTI's behalf.
79   #
80   # The dirver MUST be a subclass of Class::DBI(::Replication) and
81   # OpenILS::Application::Storage.
82   #-------------------------------------------------------------------------------
83         package OpenILS::Application::Storage::Driver::Pg;
84         use Class::DBI;
85         use base qw/Class::DBI OpenILS::Application::Storage/;
86         use DBI;
87         use OpenSRF::EX qw/:try/;
88         use OpenSRF::DomainObject::oilsResponse;
89         use OpenSRF::Utils::Logger qw/:level/;
90         my $log = 'OpenSRF::Utils::Logger';
91
92         __PACKAGE__->set_sql( retrieve_limited => 'SELECT * FROM __TABLE__ ORDER BY id LIMIT ?' );
93         __PACKAGE__->set_sql( copy_start => 'COPY %s (%s) FROM STDIN;' );
94         __PACKAGE__->set_sql( copy_end => '\.' );
95
96         my $master_db;
97         my @slave_dbs;
98         my $_db_params;
99         sub child_init {
100                 my $self = shift;
101                 $_db_params = shift;
102
103                 $log->debug("Running child_init inside ".__PACKAGE__, INTERNAL);
104
105                 $_db_params = [ $_db_params ] unless (ref($_db_params) eq 'ARRAY');
106
107                 my %attrs = (   %{$self->_default_attributes},
108                                 RootClass => 'DBIx::ContextualFetch',
109                                 ShowErrorStatement => 1,
110                                 RaiseError => 1,
111                                 AutoCommit => 1,
112                                 PrintError => 1,
113                                 Taint => 1,
114                                 pg_enable_utf8 => 1,
115                                 FetchHashKeyName => 'NAME_lc',
116                                 ChopBlanks => 1,
117                 );
118
119                 my $master = shift @$_db_params;
120                 $master_db = DBI->connect("dbi:Pg:host=$$master{host};dbname=$$master{db}",$$master{user},$$master{pw}, \%attrs);
121                 $master_db->do("SET NAMES '$$master{client_encoding}';") if ($$master{client_encoding});
122
123                 $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
124                 
125                 for my $db (@$_db_params) {
126                         push @slave_dbs, DBI->connect("dbi:Pg:host=$$db{host};dbname=$$db{db}",$$db{user},$$db{pw}, \%attrs);
127                         $slave_dbs[-1]->do("SET NAMES '$$db{client_encoding}';") if ($$master{client_encoding});
128
129                         $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
130                 }
131
132                 $log->debug("All is well on the western front", INTERNAL);
133         }
134
135         sub db_Main {
136                 my $self = shift;
137                 return $master_db if ($self->current_xact_session);
138                 return $master_db unless (@slave_dbs);
139                 return ($master_db, @slave_dbs)[rand(scalar(@slave_dbs))];
140         }
141
142         sub quote {
143                 my $self = shift;
144                 return $self->db_Main->quote(@_)
145         }
146
147 #       sub tsearch2_trigger {
148 #               my $self = shift;
149 #               return unless ($self->value);
150 #               $self->index_vector(
151 #                       $self->db_Slaves->selectrow_array(
152 #                               "SELECT to_tsvector('default',?);",
153 #                               {},
154 #                               $self->value
155 #                       )
156 #               );
157 #       }
158
159         my $_xact_session;
160
161         sub current_xact_session {
162                 my $self = shift;
163                 if (defined($_xact_session)) {
164                         return $_xact_session;
165                 }
166                 return undef;
167         }
168
169         sub current_xact_is_auto {
170                 my $self = shift;
171                 my $auto = shift;
172                 if (defined($_xact_session) and ref($_xact_session)) {
173                         if (defined $auto) {
174                                 $_xact_session->session_data(autocommit => $auto);
175                         }
176                         return $_xact_session->session_data('autocommit'); 
177                 }
178         }
179
180         sub current_xact_id {
181                 my $self = shift;
182                 if (defined($_xact_session) and ref($_xact_session)) {
183                         return $_xact_session->session_id;
184                 }
185                 return undef;
186         }
187
188         sub set_xact_session {
189                 my $self = shift;
190                 my $ses = shift;
191                 if (!defined($ses)) {
192                         return undef;
193                 }
194                 $_xact_session = $ses;
195                 return $_xact_session;
196         }
197
198         sub unset_xact_session {
199                 my $self = shift;
200                 my $ses = $_xact_session;
201                 undef $_xact_session;
202                 return $ses;
203         }
204
205 }
206
207
208 {
209         package OpenILS::Application::Storage;
210         use OpenSRF::Utils::Logger;
211         my $log = 'OpenSRF::Utils::Logger';
212
213         my $pg = 'OpenILS::Application::Storage::Driver::Pg';
214
215         sub pg_begin_xaction {
216                 my $self = shift;
217                 my $client = shift;
218
219                 if (my $old_xact = $pg->current_xact_session) {
220                         if ($pg->current_xact_is_auto) {
221                                 $log->debug("Commiting old autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
222                                 $self->pg_commit_xaction($client);
223                         } else {
224                                 $log->debug("Rolling back old NON-autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
225                                 $self->pg_rollback_xaction($client);
226                                 throw OpenSRF::DomainObject::oilsException->new(
227                                                 statusCode => 500,
228                                                 status => "Previous transaction rolled back!",
229                                 );
230                         }
231                 }
232                 
233                 $pg->set_xact_session( $client->session );
234                 my $xact_id = $pg->current_xact_id;
235
236                 $log->debug("Beginning a new trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
237
238                 my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
239                 
240                 try {
241                         $dbh->begin_work;
242
243                 } catch Error with {
244                         my $e = shift;
245                         $log->debug("Failed to begin a new trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
246                         throw $e;
247                 };
248
249
250                 my $death_cb = $client->session->register_callback(
251                         death => sub {
252                                 __PACKAGE__->pg_rollback_xaction;
253                         }
254                 );
255
256                 $log->debug("Registered 'death' callback [$death_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
257
258                 $client->session->session_data( death_cb => $death_cb );
259
260                 if ($self->api_name =~ /autocommit$/o) {
261                         $pg->current_xact_is_auto(1);
262                         my $dc_cb = $client->session->register_callback(
263                                 disconnect => sub {
264                                         my $ses = shift;
265                                         $ses->unregister_callback(death => $death_cb);
266                                         __PACKAGE__->pg_commit_xaction;
267                                 }
268                         );
269                         $log->debug("Registered 'disconnect' callback [$dc_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
270                         if ($client and $client->session) {
271                                 $client->session->session_data( disconnect_cb => $dc_cb );
272                         }
273                 }
274
275                 return 1;
276
277         }
278         __PACKAGE__->register_method(
279                 method          => 'pg_begin_xaction',
280                 api_name        => 'open-ils.storage.transaction.begin',
281                 api_level       => 1,
282                 argc            => 0,
283         );
284         __PACKAGE__->register_method(
285                 method          => 'pg_begin_xaction',
286                 api_name        => 'open-ils.storage.transaction.begin.autocommit',
287                 api_level       => 1,
288                 argc            => 0,
289         );
290
291         sub pg_commit_xaction {
292                 my $self = shift;
293
294                 my $xact_id = $pg->current_xact_id;
295
296                 try {
297                         $log->debug("Committing trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
298                         my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
299                         $dbh->commit;
300
301                 } catch Error with {
302                         my $e = shift;
303                         $log->debug("Failed to commit trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
304                         return 0;
305                 };
306                 
307                 $pg->current_xact_session->unregister_callback( death => 
308                         $pg->current_xact_session->session_data( 'death_cb' )
309                 ) if ($pg->current_xact_session);
310
311                 if ($pg->current_xact_is_auto) {
312                         $pg->current_xact_session->unregister_callback( disconnect => 
313                                 $pg->current_xact_session->session_data( 'disconnect_cb' )
314                         );
315                 }
316
317                 $pg->unset_xact_session;
318
319                 return 1;
320                 
321         }
322         __PACKAGE__->register_method(
323                 method          => 'pg_commit_xaction',
324                 api_name        => 'open-ils.storage.transaction.commit',
325                 api_level       => 1,
326                 argc            => 0,
327         );
328
329         sub pg_rollback_xaction {
330                 my $self = shift;
331
332                 my $xact_id = $pg->current_xact_id;
333                 try {
334                         my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
335                         $log->debug("Rolling back a trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
336                         $dbh->rollback;
337
338                 } catch Error with {
339                         my $e = shift;
340                         $log->debug("Failed to roll back trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
341                         return 0;
342                 };
343         
344                 $pg->current_xact_session->unregister_callback( death =>
345                         $pg->current_xact_session->session_data( 'death_cb' )
346                 ) if ($pg->current_xact_session);
347
348                 if ($pg->current_xact_is_auto) {
349                         $pg->current_xact_session->unregister_callback( disconnect =>
350                                 $pg->current_xact_session->session_data( 'disconnect_cb' )
351                         );
352                 }
353
354                 $pg->unset_xact_session;
355
356                 return 1;
357         }
358         __PACKAGE__->register_method(
359                 method          => 'pg_rollback_xaction',
360                 api_name        => 'open-ils.storage.transaction.rollback',
361                 api_level       => 1,
362                 argc            => 0,
363         );
364
365         sub copy_create {
366                 my $self = shift;
367                 my $client = shift;
368                 my @fm_nodes = @_;
369
370                 warn 'Inside copy_create...';
371
372                 return undef unless ($pg->current_xact_session);
373
374                 my $cdbi = $self->{cdbi};
375
376                 my $pri = $cdbi->columns('Primary');
377
378                 my @cols = grep {$_ ne $pri} $cdbi->columns('All');
379
380                 my $col_list = join ',', @cols;
381
382                 $log->debug('Starting COPY import for '.$cdbi->table." ($col_list)", DEBUG);
383                 $cdbi->sql_copy_start($cdbi->table, $col_list)->execute;
384
385                 my $dbh = $cdbi->db_Main;
386                 for my $node ( @fm_nodes ) {
387                         next unless ($node);
388                         my $line = join("\t", map { defined($node->$_()) ? $node->$_() : '\N' } @cols);
389                         $log->debug("COPY line: [$line]",DEBUG);
390                         $dbh->func($line."\n", 'putline');
391                 }
392
393                 $dbh->func('endcopy');
394
395                 return scalar(@fm_nodes);
396         }
397
398 }
399
400 {
401         #---------------------------------------------------------------------
402         package action::survey;
403         
404         action::survey->table( 'action.survey' );
405         action::survey->sequence( 'action.survey_id_seq' );
406         
407         #---------------------------------------------------------------------
408         package action::survey_question;
409         
410         action::survey_question->table( 'action.survey_question' );
411         action::survey_question->sequence( 'action.survey_question_id_seq' );
412         
413         #---------------------------------------------------------------------
414         package action::survey_answer;
415         
416         action::survey_answer->table( 'action.survey_answer' );
417         action::survey_answer->sequence( 'action.survey_answer_id_seq' );
418         
419         #---------------------------------------------------------------------
420         package action::survey_response;
421         
422         action::survey_response->table( 'action.survey_response' );
423         action::survey_response->sequence( 'action.survey_response_id_seq' );
424         
425         #---------------------------------------------------------------------
426         package config::metabib_field;
427         
428         config::metabib_field->table( 'config.metabib_field' );
429         config::metabib_field->sequence( 'config.metabib_field_id_seq' );
430         
431         #---------------------------------------------------------------------
432         package config::bib_source;
433         
434         config::bib_source->table( 'config.bib_source' );
435         config::bib_source->sequence( 'config.bib_source_id_seq' );
436         
437         #---------------------------------------------------------------------
438         package config::identification_type;
439         
440         config::identification_type->table( 'config.identification_type' );
441         config::identification_type->sequence( 'config.identification_type_id_seq' );
442         
443         #---------------------------------------------------------------------
444         package asset::call_number_note;
445         
446         asset::call_number->table( 'asset.call_number_note' );
447         asset::call_number->sequence( 'asset.call_number_note_id_seq' );
448         
449         #---------------------------------------------------------------------
450         package asset::copy_note;
451         
452         asset::copy->table( 'asset.copy_note' );
453         asset::copy->sequence( 'asset.copy_note_id_seq' );
454
455         #---------------------------------------------------------------------
456         package asset::call_number;
457         
458         asset::call_number->table( 'asset.call_number' );
459         asset::call_number->sequence( 'asset.call_number_id_seq' );
460         
461         #---------------------------------------------------------------------
462         package asset::copy;
463         
464         asset::copy->table( 'asset.copy' );
465         asset::copy->sequence( 'asset.copy_id_seq' );
466
467         #---------------------------------------------------------------------
468         package asset::stat_cat;
469         
470         asset::stat_cat->table( 'asset.stat_cat' );
471         asset::stat_cat->sequence( 'asset.stat_cat_id_seq' );
472         
473         #---------------------------------------------------------------------
474         package asset::stat_cat_entry;
475         
476         asset::stat_cat_entry->table( 'asset.stat_cat_entry' );
477         asset::stat_cat_entry->sequence( 'asset.stat_cat_entry_id_seq' );
478         
479         #---------------------------------------------------------------------
480         package asset::stat_cat_entry_copy_map;
481         
482         asset::stat_cat_entry_copy_map->table( 'asset.stat_cat_entry_copy_map' );
483         asset::stat_cat_entry_copy_map->sequence( 'asset.stat_cat_entry_copy_map_id_seq' );
484         
485         #---------------------------------------------------------------------
486         package biblio::record_entry;
487         
488         biblio::record_entry->table( 'biblio.record_entry' );
489         biblio::record_entry->sequence( 'biblio.record_entry_id_seq' );
490
491         #---------------------------------------------------------------------
492         package biblio::record_marc;
493         
494         biblio::record_marc->table( 'biblio.record_marc' );
495         biblio::record_marc->sequence( 'biblio.record_marc_id_seq' );
496
497         #---------------------------------------------------------------------
498         package biblio::record_note;
499         
500         biblio::record_note->table( 'biblio.record_note' );
501         biblio::record_note->sequence( 'biblio.record_note_id_seq' );
502         
503         #---------------------------------------------------------------------
504         package actor::user;
505         
506         actor::user->table( 'actor.usr' );
507         actor::user->sequence( 'actor.usr_id_seq' );
508         
509         #---------------------------------------------------------------------
510         package actor::org_unit_type;
511         
512         actor::org_unit_type->table( 'actor.org_unit_type' );
513         actor::org_unit_type->sequence( 'actor.org_unit_type_id_seq' );
514
515         #---------------------------------------------------------------------
516         package actor::org_unit;
517         
518         actor::org_unit->table( 'actor.org_unit' );
519         actor::org_unit->sequence( 'actor.org_unit_id_seq' );
520
521         #---------------------------------------------------------------------
522         package actor::stat_cat;
523         
524         actor::stat_cat->table( 'actor.stat_cat' );
525         actor::stat_cat->sequence( 'actor.stat_cat_id_seq' );
526         
527         #---------------------------------------------------------------------
528         package actor::stat_cat_entry;
529         
530         actor::stat_cat_entry->table( 'actor.stat_cat_entry' );
531         actor::stat_cat_entry->sequence( 'actor.stat_cat_entry_id_seq' );
532         
533         #---------------------------------------------------------------------
534         package actor::stat_cat_entry_user_map;
535         
536         actor::stat_cat_entry_user_map->table( 'actor.stat_cat_entry_copy_map' );
537         actor::stat_cat_entry_user_map->sequence( 'actor.stat_cat_entry_usr_map_id_seq' );
538         
539         #---------------------------------------------------------------------
540         package actor::card;
541         
542         actor::card->table( 'actor.card' );
543         actor::card->sequence( 'actor.card_id_seq' );
544         
545
546         #---------------------------------------------------------------------
547
548         #-------------------------------------------------------------------------------
549         package metabib::metarecord;
550
551         metabib::metarecord->table( 'metabib.metarecord' );
552         metabib::metarecord->sequence( 'metabib.metarecord_id_seq' );
553
554         #-------------------------------------------------------------------------------
555
556         #-------------------------------------------------------------------------------
557         package metabib::title_field_entry;
558
559         metabib::title_field_entry->table( 'metabib.title_field_entry' );
560         metabib::title_field_entry->sequence( 'metabib.title_field_entry_id_seq' );
561         metabib::title_field_entry->columns( 'FTS' => 'index_vector' );
562
563 #       metabib::title_field_entry->add_trigger(
564 #               before_create => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
565 #       );
566 #       metabib::title_field_entry->add_trigger(
567 #               before_update => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
568 #       );
569
570         OpenILS::Application::Storage->register_method(
571                 api_name        => 'open-ils.storage.direct.metabib.title_field_entry.batch.create',
572                 method          => 'copy_create',
573                 api_level       => 1,
574                 'package'       => 'OpenILS::Application::Storage',
575                 cdbi            => 'metabib::title_field_entry',
576         );
577
578         #-------------------------------------------------------------------------------
579
580         #-------------------------------------------------------------------------------
581         package metabib::author_field_entry;
582
583         metabib::author_field_entry->table( 'metabib.author_field_entry' );
584         metabib::author_field_entry->sequence( 'metabib.author_field_entry_id_seq' );
585         metabib::author_field_entry->columns( 'FTS' => 'index_vector' );
586
587         OpenILS::Application::Storage->register_method(
588                 api_name        => 'open-ils.storage.direct.metabib.author_field_entry.batch.create',
589                 method          => 'copy_create',
590                 api_level       => 1,
591                 'package'       => 'OpenILS::Application::Storage',
592                 cdbi            => 'metabib::author_field_entry',
593         );
594
595         #-------------------------------------------------------------------------------
596
597         #-------------------------------------------------------------------------------
598         package metabib::subject_field_entry;
599
600         metabib::subject_field_entry->table( 'metabib.subject_field_entry' );
601         metabib::subject_field_entry->sequence( 'metabib.subject_field_entry_id_seq' );
602         metabib::subject_field_entry->columns( 'FTS' => 'index_vector' );
603
604         OpenILS::Application::Storage->register_method(
605                 api_name        => 'open-ils.storage.direct.metabib.subject_field_entry.batch.create',
606                 method          => 'copy_create',
607                 api_level       => 1,
608                 'package'       => 'OpenILS::Application::Storage',
609                 cdbi            => 'metabib::subject_field_entry',
610         );
611
612         #-------------------------------------------------------------------------------
613
614         #-------------------------------------------------------------------------------
615         package metabib::keyword_field_entry;
616
617         metabib::keyword_field_entry->table( 'metabib.keyword_field_entry' );
618         metabib::keyword_field_entry->sequence( 'metabib.keyword_field_entry_id_seq' );
619         metabib::keyword_field_entry->columns( 'FTS' => 'index_vector' );
620
621         OpenILS::Application::Storage->register_method(
622                 api_name        => 'open-ils.storage.direct.metabib.keyword_field_entry.batch.create',
623                 method          => 'copy_create',
624                 api_level       => 1,
625                 'package'       => 'OpenILS::Application::Storage',
626                 cdbi            => 'metabib::keyword_field_entry',
627         );
628
629         #-------------------------------------------------------------------------------
630
631         #-------------------------------------------------------------------------------
632         #package metabib::title_field_entry_source_map;
633
634         #metabib::title_field_entry_source_map->table( 'metabib.title_field_entry_source_map' );
635
636         #-------------------------------------------------------------------------------
637
638         #-------------------------------------------------------------------------------
639         #package metabib::author_field_entry_source_map;
640
641         #metabib::author_field_entry_source_map->table( 'metabib.author_field_entry_source_map' );
642
643         #-------------------------------------------------------------------------------
644
645         #-------------------------------------------------------------------------------
646         #package metabib::subject_field_entry_source_map;
647
648         #metabib::subject_field_entry_source_map->table( 'metabib.subject_field_entry_source_map' );
649
650         #-------------------------------------------------------------------------------
651
652         #-------------------------------------------------------------------------------
653         #package metabib::keyword_field_entry_source_map;
654
655         #metabib::keyword_field_entry_source_map->table( 'metabib.keyword_field_entry_source_map' );
656
657         #-------------------------------------------------------------------------------
658
659         #-------------------------------------------------------------------------------
660         package metabib::metarecord_source_map;
661
662         metabib::metarecord_source_map->table( 'metabib.metarecord_source_map' );
663
664         #-------------------------------------------------------------------------------
665         package metabib::record_descriptor;
666
667         metabib::record_descriptor->table( 'metabib.rec_descriptor' );
668         metabib::record_descriptor->sequence( 'metabib.rec_descriptor_id_seq' );
669
670         OpenILS::Application::Storage->register_method(
671                 api_name        => 'open-ils.storage.direct.metabib.record_descriptor.batch.create',
672                 method          => 'copy_create',
673                 api_level       => 1,
674                 'package'       => 'OpenILS::Application::Storage',
675                 cdbi            => 'metabib::record_descriptor',
676         );
677
678         #-------------------------------------------------------------------------------
679
680
681         #-------------------------------------------------------------------------------
682         package metabib::full_rec;
683
684         metabib::full_rec->table( 'metabib.full_rec' );
685         metabib::full_rec->sequence( 'metabib.full_rec_id_seq' );
686         metabib::full_rec->columns( 'FTS' => 'index_vector' );
687
688         OpenILS::Application::Storage->register_method(
689                 api_name        => 'open-ils.storage.direct.metabib.full_rec.batch.create',
690                 method          => 'copy_create',
691                 api_level       => 1,
692                 'package'       => 'OpenILS::Application::Storage',
693                 cdbi            => 'metabib::full_rec',
694         );
695
696
697         #-------------------------------------------------------------------------------
698 }
699
700 1;