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 #-------------------------------------------------------------------------------
10 if (ref($args[-1]) eq 'HASH') {
11 $args[-1]->{_placeholder} = "to_tsquery('default',?)";
13 push @args, {_placeholder => "to_tsquery('default',?)"};
15 $self->_do_search("@@" => @args);
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';
30 $self = ref($self) || $self;
31 $self = bless {} => $self;
33 $self->decompose($term);
35 my $newterm = join('&', $self->words);
38 $newterm = '('.$newterm.')&('. join('|', $self->nots) . ')';
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);
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;
54 sub sql_where_clause {
56 my $column = $self->fts_col;
60 for my $fts ( $self->fts_query ) {
61 push @output, join(' ', $self->fts_col, $self->{fts_op}, $fts);
62 push @ranks, "rank($column, $fts)";
64 $self->{fts_rank} = \@ranks;
66 my $phrase_match = $self->sql_exact_phrase_match();
67 return join(' AND ', @output) . $phrase_match;
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.
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.
80 # The dirver MUST be a subclass of Class::DBI(::Replication) and
81 # OpenILS::Application::Storage.
82 #-------------------------------------------------------------------------------
83 package OpenILS::Application::Storage::Driver::Pg;
85 use base qw/Class::DBI OpenILS::Application::Storage/;
87 use OpenSRF::EX qw/:try/;
88 use OpenSRF::DomainObject::oilsResponse;
89 use OpenSRF::Utils::Logger qw/:level/;
90 my $log = 'OpenSRF::Utils::Logger';
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 => '\.' );
103 $log->debug("Running child_init inside ".__PACKAGE__, INTERNAL);
105 $_db_params = [ $_db_params ] unless (ref($_db_params) eq 'ARRAY');
107 my %attrs = ( %{$self->_default_attributes},
108 RootClass => 'DBIx::ContextualFetch',
109 ShowErrorStatement => 1,
115 FetchHashKeyName => 'NAME_lc',
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});
123 $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
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});
129 $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
132 $log->debug("All is well on the western front", INTERNAL);
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))];
144 return $self->db_Main->quote(@_)
147 # sub tsearch2_trigger {
149 # return unless ($self->value);
150 # $self->index_vector(
151 # $self->db_Slaves->selectrow_array(
152 # "SELECT to_tsvector('default',?);",
161 sub current_xact_session {
163 if (defined($_xact_session)) {
164 return $_xact_session;
169 sub current_xact_is_auto {
172 if (defined($_xact_session) and ref($_xact_session)) {
174 $_xact_session->session_data(autocommit => $auto);
176 return $_xact_session->session_data('autocommit');
180 sub current_xact_id {
182 if (defined($_xact_session) and ref($_xact_session)) {
183 return $_xact_session->session_id;
188 sub set_xact_session {
191 if (!defined($ses)) {
194 $_xact_session = $ses;
195 return $_xact_session;
198 sub unset_xact_session {
200 my $ses = $_xact_session;
201 undef $_xact_session;
209 package OpenILS::Application::Storage;
210 use OpenSRF::Utils::Logger;
211 my $log = 'OpenSRF::Utils::Logger';
213 my $pg = 'OpenILS::Application::Storage::Driver::Pg';
219 return $pg->current_xact_id;
221 __PACKAGE__->register_method(
222 method => 'current_xact',
223 api_name => 'open-ils.storage.transaction.current',
229 sub pg_begin_xaction {
233 if (my $old_xact = $pg->current_xact_session) {
234 if ($pg->current_xact_is_auto) {
235 $log->debug("Commiting old autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
236 $self->pg_commit_xaction($client);
238 $log->debug("Rolling back old NON-autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
239 $self->pg_rollback_xaction($client);
240 throw OpenSRF::DomainObject::oilsException->new(
242 status => "Previous transaction rolled back!",
247 $pg->set_xact_session( $client->session );
248 my $xact_id = $pg->current_xact_id;
250 $log->debug("Beginning a new trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
252 my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
259 $log->debug("Failed to begin a new trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
264 my $death_cb = $client->session->register_callback(
266 __PACKAGE__->pg_rollback_xaction;
270 $log->debug("Registered 'death' callback [$death_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
272 $client->session->session_data( death_cb => $death_cb );
274 if ($self->api_name =~ /autocommit$/o) {
275 $pg->current_xact_is_auto(1);
276 my $dc_cb = $client->session->register_callback(
279 $ses->unregister_callback(death => $death_cb);
280 __PACKAGE__->pg_commit_xaction;
283 $log->debug("Registered 'disconnect' callback [$dc_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
284 if ($client and $client->session) {
285 $client->session->session_data( disconnect_cb => $dc_cb );
292 __PACKAGE__->register_method(
293 method => 'pg_begin_xaction',
294 api_name => 'open-ils.storage.transaction.begin',
298 __PACKAGE__->register_method(
299 method => 'pg_begin_xaction',
300 api_name => 'open-ils.storage.transaction.begin.autocommit',
305 sub pg_commit_xaction {
308 my $xact_id = $pg->current_xact_id;
311 $log->debug("Committing trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
312 my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
317 $log->debug("Failed to commit trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
321 $pg->current_xact_session->unregister_callback( death =>
322 $pg->current_xact_session->session_data( 'death_cb' )
323 ) if ($pg->current_xact_session);
325 if ($pg->current_xact_is_auto) {
326 $pg->current_xact_session->unregister_callback( disconnect =>
327 $pg->current_xact_session->session_data( 'disconnect_cb' )
331 $pg->unset_xact_session;
336 __PACKAGE__->register_method(
337 method => 'pg_commit_xaction',
338 api_name => 'open-ils.storage.transaction.commit',
343 sub pg_rollback_xaction {
346 my $xact_id = $pg->current_xact_id;
348 my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
349 $log->debug("Rolling back a trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
354 $log->debug("Failed to roll back trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
358 $pg->current_xact_session->unregister_callback( death =>
359 $pg->current_xact_session->session_data( 'death_cb' )
360 ) if ($pg->current_xact_session);
362 if ($pg->current_xact_is_auto) {
363 $pg->current_xact_session->unregister_callback( disconnect =>
364 $pg->current_xact_session->session_data( 'disconnect_cb' )
368 $pg->unset_xact_session;
372 __PACKAGE__->register_method(
373 method => 'pg_rollback_xaction',
374 api_name => 'open-ils.storage.transaction.rollback',
384 return undef unless ($pg->current_xact_session);
386 my $cdbi = $self->{cdbi};
388 my $pri = $cdbi->columns('Primary');
390 my @cols = grep {$_ ne $pri} $cdbi->columns('All');
392 my $col_list = join ',', @cols;
394 $log->debug('Starting COPY import for '.$cdbi->table." ($col_list)", DEBUG);
395 $cdbi->sql_copy_start($cdbi->table, $col_list)->execute;
397 my $dbh = $cdbi->db_Main;
398 for my $node ( @fm_nodes ) {
400 my $line = join("\t", map { defined($node->$_()) ? $node->$_() : '\N' } @cols);
401 $log->debug("COPY line: [$line]",DEBUG);
402 $dbh->func($line."\n", 'putline');
405 $dbh->func('endcopy');
407 return scalar(@fm_nodes);
413 #---------------------------------------------------------------------
414 package action::survey;
416 action::survey->table( 'action.survey' );
417 action::survey->sequence( 'action.survey_id_seq' );
419 #---------------------------------------------------------------------
420 package action::survey_question;
422 action::survey_question->table( 'action.survey_question' );
423 action::survey_question->sequence( 'action.survey_question_id_seq' );
425 #---------------------------------------------------------------------
426 package action::survey_answer;
428 action::survey_answer->table( 'action.survey_answer' );
429 action::survey_answer->sequence( 'action.survey_answer_id_seq' );
431 #---------------------------------------------------------------------
432 package action::survey_response;
434 action::survey_response->table( 'action.survey_response' );
435 action::survey_response->sequence( 'action.survey_response_id_seq' );
437 #---------------------------------------------------------------------
438 package config::copy_status;
440 config::standing->table( 'config.copy_status' );
441 config::standing->sequence( 'config.copy_status_id_seq' );
443 #---------------------------------------------------------------------
444 package config::net_access_level;
446 config::standing->table( 'config.net_access_level' );
447 config::standing->sequence( 'config.net_access_level_id_seq' );
449 #---------------------------------------------------------------------
450 package config::standing;
452 config::standing->table( 'config.standing' );
453 config::standing->sequence( 'config.standing_id_seq' );
455 #---------------------------------------------------------------------
456 package config::metabib_field;
458 config::metabib_field->table( 'config.metabib_field' );
459 config::metabib_field->sequence( 'config.metabib_field_id_seq' );
461 #---------------------------------------------------------------------
462 package config::bib_source;
464 config::bib_source->table( 'config.bib_source' );
465 config::bib_source->sequence( 'config.bib_source_id_seq' );
467 #---------------------------------------------------------------------
468 package config::identification_type;
470 config::identification_type->table( 'config.identification_type' );
471 config::identification_type->sequence( 'config.identification_type_id_seq' );
473 #---------------------------------------------------------------------
474 package asset::call_number_note;
476 asset::call_number->table( 'asset.call_number_note' );
477 asset::call_number->sequence( 'asset.call_number_note_id_seq' );
479 #---------------------------------------------------------------------
480 package asset::copy_note;
482 asset::copy->table( 'asset.copy_note' );
483 asset::copy->sequence( 'asset.copy_note_id_seq' );
485 #---------------------------------------------------------------------
486 package asset::call_number;
488 asset::call_number->table( 'asset.call_number' );
489 asset::call_number->sequence( 'asset.call_number_id_seq' );
491 #---------------------------------------------------------------------
492 package asset::copy_location;
494 asset::copy->table( 'asset.copy_location' );
495 asset::copy->sequence( 'asset.copy_location_id_seq' );
497 #---------------------------------------------------------------------
500 asset::copy->table( 'asset.copy' );
501 asset::copy->sequence( 'asset.copy_id_seq' );
503 #---------------------------------------------------------------------
504 package asset::stat_cat;
506 asset::stat_cat->table( 'asset.stat_cat' );
507 asset::stat_cat->sequence( 'asset.stat_cat_id_seq' );
509 #---------------------------------------------------------------------
510 package asset::stat_cat_entry;
512 asset::stat_cat_entry->table( 'asset.stat_cat_entry' );
513 asset::stat_cat_entry->sequence( 'asset.stat_cat_entry_id_seq' );
515 #---------------------------------------------------------------------
516 package asset::stat_cat_entry_copy_map;
518 asset::stat_cat_entry_copy_map->table( 'asset.stat_cat_entry_copy_map' );
519 asset::stat_cat_entry_copy_map->sequence( 'asset.stat_cat_entry_copy_map_id_seq' );
521 #---------------------------------------------------------------------
522 package biblio::record_entry;
524 biblio::record_entry->table( 'biblio.record_entry' );
525 biblio::record_entry->sequence( 'biblio.record_entry_id_seq' );
527 #---------------------------------------------------------------------
528 #package biblio::record_marc;
530 #biblio::record_marc->table( 'biblio.record_marc' );
531 #biblio::record_marc->sequence( 'biblio.record_marc_id_seq' );
533 #---------------------------------------------------------------------
534 package biblio::record_note;
536 biblio::record_note->table( 'biblio.record_note' );
537 biblio::record_note->sequence( 'biblio.record_note_id_seq' );
539 #---------------------------------------------------------------------
542 actor::user->table( 'actor.usr' );
543 actor::user->sequence( 'actor.usr_id_seq' );
545 #---------------------------------------------------------------------
546 package actor::user_address;
548 actor::user_address->table( 'actor.usr_address' );
549 actor::user_address->sequence( 'actor.usr_address_id_seq' );
551 #---------------------------------------------------------------------
552 package actor::org_address;
554 actor::org_address->table( 'actor.org_address' );
555 actor::org_address->sequence( 'actor.org_address_id_seq' );
557 #---------------------------------------------------------------------
558 package actor::profile;
560 actor::profile->table( 'actor.profile' );
561 actor::profile->sequence( 'actor.profile_id_seq' );
563 #---------------------------------------------------------------------
564 package actor::org_unit_type;
566 actor::org_unit_type->table( 'actor.org_unit_type' );
567 actor::org_unit_type->sequence( 'actor.org_unit_type_id_seq' );
569 #---------------------------------------------------------------------
570 package actor::org_unit;
572 actor::org_unit->table( 'actor.org_unit' );
573 actor::org_unit->sequence( 'actor.org_unit_id_seq' );
575 #---------------------------------------------------------------------
576 package actor::stat_cat;
578 actor::stat_cat->table( 'actor.stat_cat' );
579 actor::stat_cat->sequence( 'actor.stat_cat_id_seq' );
581 #---------------------------------------------------------------------
582 package actor::stat_cat_entry;
584 actor::stat_cat_entry->table( 'actor.stat_cat_entry' );
585 actor::stat_cat_entry->sequence( 'actor.stat_cat_entry_id_seq' );
587 #---------------------------------------------------------------------
588 package actor::stat_cat_entry_user_map;
590 actor::stat_cat_entry_user_map->table( 'actor.stat_cat_entry_copy_map' );
591 actor::stat_cat_entry_user_map->sequence( 'actor.stat_cat_entry_usr_map_id_seq' );
593 #---------------------------------------------------------------------
596 actor::card->table( 'actor.card' );
597 actor::card->sequence( 'actor.card_id_seq' );
599 #---------------------------------------------------------------------
601 #-------------------------------------------------------------------------------
602 package metabib::metarecord;
604 metabib::metarecord->table( 'metabib.metarecord' );
605 metabib::metarecord->sequence( 'metabib.metarecord_id_seq' );
607 OpenILS::Application::Storage->register_method(
608 api_name => 'open-ils.storage.direct.metabib.metarecord.batch.create',
609 method => 'copy_create',
611 'package' => 'OpenILS::Application::Storage',
612 cdbi => 'metabib::metarecord',
616 #-------------------------------------------------------------------------------
618 #-------------------------------------------------------------------------------
619 package metabib::title_field_entry;
621 metabib::title_field_entry->table( 'metabib.title_field_entry' );
622 metabib::title_field_entry->sequence( 'metabib.title_field_entry_id_seq' );
623 metabib::title_field_entry->columns( 'FTS' => 'index_vector' );
625 # metabib::title_field_entry->add_trigger(
626 # before_create => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
628 # metabib::title_field_entry->add_trigger(
629 # before_update => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
632 OpenILS::Application::Storage->register_method(
633 api_name => 'open-ils.storage.direct.metabib.title_field_entry.batch.create',
634 method => 'copy_create',
636 'package' => 'OpenILS::Application::Storage',
637 cdbi => 'metabib::title_field_entry',
640 #-------------------------------------------------------------------------------
642 #-------------------------------------------------------------------------------
643 package metabib::author_field_entry;
645 metabib::author_field_entry->table( 'metabib.author_field_entry' );
646 metabib::author_field_entry->sequence( 'metabib.author_field_entry_id_seq' );
647 metabib::author_field_entry->columns( 'FTS' => 'index_vector' );
649 OpenILS::Application::Storage->register_method(
650 api_name => 'open-ils.storage.direct.metabib.author_field_entry.batch.create',
651 method => 'copy_create',
653 'package' => 'OpenILS::Application::Storage',
654 cdbi => 'metabib::author_field_entry',
657 #-------------------------------------------------------------------------------
659 #-------------------------------------------------------------------------------
660 package metabib::subject_field_entry;
662 metabib::subject_field_entry->table( 'metabib.subject_field_entry' );
663 metabib::subject_field_entry->sequence( 'metabib.subject_field_entry_id_seq' );
664 metabib::subject_field_entry->columns( 'FTS' => 'index_vector' );
666 OpenILS::Application::Storage->register_method(
667 api_name => 'open-ils.storage.direct.metabib.subject_field_entry.batch.create',
668 method => 'copy_create',
670 'package' => 'OpenILS::Application::Storage',
671 cdbi => 'metabib::subject_field_entry',
674 #-------------------------------------------------------------------------------
676 #-------------------------------------------------------------------------------
677 package metabib::keyword_field_entry;
679 metabib::keyword_field_entry->table( 'metabib.keyword_field_entry' );
680 metabib::keyword_field_entry->sequence( 'metabib.keyword_field_entry_id_seq' );
681 metabib::keyword_field_entry->columns( 'FTS' => 'index_vector' );
683 OpenILS::Application::Storage->register_method(
684 api_name => 'open-ils.storage.direct.metabib.keyword_field_entry.batch.create',
685 method => 'copy_create',
687 'package' => 'OpenILS::Application::Storage',
688 cdbi => 'metabib::keyword_field_entry',
691 #-------------------------------------------------------------------------------
693 #-------------------------------------------------------------------------------
694 #package metabib::title_field_entry_source_map;
696 #metabib::title_field_entry_source_map->table( 'metabib.title_field_entry_source_map' );
698 #-------------------------------------------------------------------------------
700 #-------------------------------------------------------------------------------
701 #package metabib::author_field_entry_source_map;
703 #metabib::author_field_entry_source_map->table( 'metabib.author_field_entry_source_map' );
705 #-------------------------------------------------------------------------------
707 #-------------------------------------------------------------------------------
708 #package metabib::subject_field_entry_source_map;
710 #metabib::subject_field_entry_source_map->table( 'metabib.subject_field_entry_source_map' );
712 #-------------------------------------------------------------------------------
714 #-------------------------------------------------------------------------------
715 #package metabib::keyword_field_entry_source_map;
717 #metabib::keyword_field_entry_source_map->table( 'metabib.keyword_field_entry_source_map' );
719 #-------------------------------------------------------------------------------
721 #-------------------------------------------------------------------------------
722 package metabib::metarecord_source_map;
724 metabib::metarecord_source_map->table( 'metabib.metarecord_source_map' );
725 OpenILS::Application::Storage->register_method(
726 api_name => 'open-ils.storage.direct.metabib.metarecord_source_map.batch.create',
727 method => 'copy_create',
729 'package' => 'OpenILS::Application::Storage',
730 cdbi => 'metabib::metarecord_source_map',
734 #-------------------------------------------------------------------------------
735 package metabib::record_descriptor;
737 metabib::record_descriptor->table( 'metabib.rec_descriptor' );
738 metabib::record_descriptor->sequence( 'metabib.rec_descriptor_id_seq' );
740 OpenILS::Application::Storage->register_method(
741 api_name => 'open-ils.storage.direct.metabib.record_descriptor.batch.create',
742 method => 'copy_create',
744 'package' => 'OpenILS::Application::Storage',
745 cdbi => 'metabib::record_descriptor',
748 #-------------------------------------------------------------------------------
751 #-------------------------------------------------------------------------------
752 package metabib::full_rec;
754 metabib::full_rec->table( 'metabib.full_rec' );
755 metabib::full_rec->sequence( 'metabib.full_rec_id_seq' );
756 metabib::full_rec->columns( 'FTS' => 'index_vector' );
758 OpenILS::Application::Storage->register_method(
759 api_name => 'open-ils.storage.direct.metabib.full_rec.batch.create',
760 method => 'copy_create',
762 'package' => 'OpenILS::Application::Storage',
763 cdbi => 'metabib::full_rec',
767 #-------------------------------------------------------------------------------