]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg.pm
95b88f6b6fadd4862cc1e0dd9b4c25ee02bf1292
[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
49                 return $self;
50         }
51
52         sub sql_where_clause {
53                 my $self = shift;
54                 my $column = shift;
55                 my @output;
56         
57                 my @ranks;
58                 for my $fts ( $self->fts_query ) {
59                         push @output, join(' ', $column, $self->{fts_op}, $fts);
60                         push @ranks, "rank($column, $fts)";
61                 }
62                 $self->{fts_rank} = \@ranks;
63         
64                 return join(' AND ', @output);
65         }
66
67 }
68
69
70 { # The driver package itself just needs a db_Main method (or db_Slaves if
71   #Class::DBI::Replication is in use) for Class::DBI to call.
72   #
73   # Any other fixups can go in here too... Also, the drivers should subclass the
74   # DBI driver that they are wrapping, or provide a 'quote()' method that calls
75   # the DBD::xxx::quote() method on FTI's behalf.
76   #
77   # The dirver MUST be a subclass of Class::DBI(::Replication) and
78   # OpenILS::Application::Storage.
79   #-------------------------------------------------------------------------------
80         package OpenILS::Application::Storage::Driver::Pg;
81         use Class::DBI;
82         use base qw/Class::DBI OpenILS::Application::Storage/;
83         use DBI;
84         use OpenSRF::EX qw/:try/;
85         use OpenSRF::DomainObject::oilsResponse;
86         use OpenSRF::Utils::Logger qw/:level/;
87         my $log = 'OpenSRF::Utils::Logger';
88
89         __PACKAGE__->set_sql( retrieve_limited => 'SELECT * FROM __TABLE__ ORDER BY id LIMIT ?' );
90
91         my $master_db;
92         my @slave_dbs;
93         my $_db_params;
94         sub child_init {
95                 my $self = shift;
96                 $_db_params = shift;
97
98                 $log->debug("Running child_init inside ".__PACKAGE__, INTERNAL);
99
100                 $_db_params = [ $_db_params ] unless (ref($_db_params) eq 'ARRAY');
101
102                 my %attrs = (   %{$self->_default_attributes},
103                                 RootClass => 'DBIx::ContextualFetch',
104                                 ShowErrorStatement => 1,
105                                 RaiseError => 1,
106                                 AutoCommit => 1,
107                                 PrintError => 1,
108                                 Taint => 1,
109                                 pg_enable_utf8 => 1,
110                                 FetchHashKeyName => 'NAME_lc',
111                                 ChopBlanks => 1,
112                 );
113
114                 my $master = shift @$_db_params;
115                 $master_db = DBI->connect("dbi:Pg:host=$$master{host};dbname=$$master{db}",$$master{user},$$master{pw}, \%attrs);
116                 $master_db->do("SET NAMES '$$master{client_encoding}';") if ($$master{client_encoding});
117
118                 $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
119                 
120                 for my $db (@$_db_params) {
121                         push @slave_dbs, DBI->connect("dbi:Pg:host=$$db{host};dbname=$$db{db}",$$db{user},$$db{pw}, \%attrs);
122                         $slave_dbs[-1]->do("SET NAMES '$$db{client_encoding}';") if ($$master{client_encoding});
123
124                         $log->debug("Connected to MASTER db '$$master{db} at $$master{host}", INFO);
125                 }
126
127                 $log->debug("All is well on the western front", INTERNAL);
128         }
129
130         sub db_Main {
131                 my $self = shift;
132                 return $master_db if ($self->current_xact_session);
133                 return $master_db unless (@slave_dbs);
134                 return ($master_db, @slave_dbs)[rand(scalar(@slave_dbs))];
135         }
136
137         sub quote {
138                 my $self = shift;
139                 return $self->db_Main->quote(@_)
140         }
141
142 #       sub tsearch2_trigger {
143 #               my $self = shift;
144 #               return unless ($self->value);
145 #               $self->index_vector(
146 #                       $self->db_Slaves->selectrow_array(
147 #                               "SELECT to_tsvector('default',?);",
148 #                               {},
149 #                               $self->value
150 #                       )
151 #               );
152 #       }
153
154         my $_xact_session;
155
156         sub current_xact_session {
157                 my $self = shift;
158                 if (defined($_xact_session)) {
159                         return $_xact_session;
160                 }
161                 return undef;
162         }
163
164         sub current_xact_is_auto {
165                 my $self = shift;
166                 return $_xact_session->session_data(autocommit => shift());
167         }
168
169         sub current_xact_id {
170                 my $self = shift;
171                 if (defined($_xact_session) and ref($_xact_session)) {
172                         return $_xact_session->session_id;
173                 }
174                 return undef;
175         }
176
177         sub set_xact_session {
178                 my $self = shift;
179                 my $ses = shift;
180                 if (!defined($ses)) {
181                         return undef;
182                 }
183                 $_xact_session = $ses;
184                 return $_xact_session;
185         }
186
187         sub unset_xact_session {
188                 my $self = shift;
189                 my $ses = $_xact_session;
190                 undef $_xact_session;
191                 return $ses;
192         }
193
194 }
195
196
197 {
198         package OpenILS::Application::Storage;
199         use OpenSRF::Utils::Logger;
200         my $log = 'OpenSRF::Utils::Logger';
201
202         my $pg = 'OpenILS::Application::Storage::Driver::Pg';
203
204         sub pg_begin_xaction {
205                 my $self = shift;
206                 my $client = shift;
207
208                 if (my $old_xact = $pg->current_xact_session) {
209                         if ($pg->current_xact_is_auto) {
210                                 $log->debug("Commiting old autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
211                                 $self->pg_commit_xaction($client);
212                         } else {
213                                 $log->debug("Rolling back old NON-autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
214                                 $self->pg_rollback_xaction($client);
215                                 return new OpenSRF::DomainObject::oilsException (
216                                                 statusCode => 500,
217                                                 status => "Previous transaction rolled back!",
218                                 );
219                         }
220                 }
221                 
222                 $pg->set_xact_session( $client->session );
223                 my $xact_id = $pg->current_xact_id;
224
225                 $log->debug("Beginning a new trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
226
227                 my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
228                 
229                 try {
230                         $dbh->begin_work;
231
232                 } catch Error with {
233                         my $e = shift;
234                         $log->debug("Failed to begin a new trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
235                         return $e;
236                 };
237
238
239                 my $death_cb = $client->session->register_callback(
240                         death => sub {
241                                 __PACKAGE__->pg_rollback_xaction;
242                         }
243                 );
244
245                 $log->debug("Registered 'death' callback [$death_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
246
247                 $client->session->session_data( death_cb => $death_cb );
248
249                 if ($self->api_name =~ /autocommit$/o) {
250                         $pg->current_xact_is_auto(1);
251                         my $dc_cb = $client->session->register_callback(
252                                 disconnect => sub {
253                                         my $ses = shift;
254                                         $ses->unregister_callback($death_cb);
255                                         __PACKAGE__->pg_commit_xaction;
256                                 }
257                         );
258                         $log->debug("Registered 'disconnect' callback [$dc_cb] for new trasaction with Open-ILS XACT-ID [$xact_id]", DEBUG);
259                         if ($client and $client->session) {
260                                 $client->session->session_data( disconnect_cb => $dc_cb );
261                         }
262                 }
263
264                 return 1;
265
266         }
267         __PACKAGE__->register_method(
268                 method          => 'pg_begin_xaction',
269                 api_name        => 'open-ils.storage.transaction.begin',
270                 api_level       => 1,
271                 argc            => 0,
272         );
273         __PACKAGE__->register_method(
274                 method          => 'pg_begin_xaction',
275                 api_name        => 'open-ils.storage.transaction.begin.autocommit',
276                 api_level       => 1,
277                 argc            => 0,
278         );
279
280         sub pg_commit_xaction {
281                 my $self = shift;
282
283
284                 try {
285                         $log->debug("Committing trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
286                         my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
287                         $dbh->commit;
288
289                 } catch Error with {
290                         my $e = shift;
291                         $log->debug("Failed to commit trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
292                         return 0;
293                 };
294                 
295                 $pg->current_xact_session->unregister_callback(
296                         $pg->current_xact_session->session_data( 'death_cb' )
297                 );
298
299                 if ($pg->current_xact_is_auto) {
300                         $pg->current_xact_session->unregister_callback(
301                                 $pg->current_xact_session->session_data( 'disconnect_cb' )
302                         );
303                 }
304
305                 $pg->unset_xact_session;
306
307                 return 1;
308                 
309         }
310         __PACKAGE__->register_method(
311                 method          => 'pg_commit_xaction',
312                 api_name        => 'open-ils.storage.transaction.commit',
313                 api_level       => 1,
314                 argc            => 0,
315         );
316
317         sub pg_rollback_xaction {
318                 my $self = shift;
319
320                 my $xact_id = $pg->current_xact_id;
321                 try {
322                         my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
323                         $log->debug("Rolling back a trasaction with Open-ILS XACT-ID [$xact_id]", INFO);
324                         $dbh->rollback;
325
326                 } catch Error with {
327                         my $e = shift;
328                         $log->debug("Failed to roll back trasaction with Open-ILS XACT-ID [$xact_id]: ".$e, INFO);
329                         return 0;
330                 };
331         
332                 $pg->current_xact_session->unregister_callback(
333                         $pg->current_xact_session->session_data( 'death_cb' )
334                 );
335
336                 if ($pg->current_xact_is_auto) {
337                         $pg->current_xact_session->unregister_callback(
338                                 $pg->current_xact_session->session_data( 'disconnect_cb' )
339                         );
340                 }
341
342                 $pg->unset_xact_session;
343
344                 return 1;
345         }
346         __PACKAGE__->register_method(
347                 method          => 'pg_rollback_xaction',
348                 api_name        => 'open-ils.storage.transaction.rollback',
349                 api_level       => 1,
350                 argc            => 0,
351         );
352
353 }
354
355 {
356         #---------------------------------------------------------------------
357         package asset::call_number;
358         
359         asset::call_number->table( 'asset.call_number' );
360         asset::call_number->sequence( 'asset.call_number_id_seq' );
361         
362         #---------------------------------------------------------------------
363         package asset::copy;
364         
365         asset::copy->table( 'asset.copy' );
366         asset::copy->sequence( 'asset.copy_id_seq' );
367         
368         #---------------------------------------------------------------------
369         package biblio::record_entry;
370         
371         biblio::record_entry->table( 'biblio.record_entry' );
372         biblio::record_entry->sequence( 'biblio.record_entry_id_seq' );
373
374         #---------------------------------------------------------------------
375         package biblio::record_node;
376         
377         biblio::record_node->table( 'biblio.record_data' );
378         biblio::record_node->sequence( 'biblio.record_data_id_seq' );
379         
380         #---------------------------------------------------------------------
381         package biblio::record_marc;
382         
383         biblio::record_marc->table( 'biblio.record_marc' );
384         biblio::record_marc->sequence( 'biblio.record_marc_id_seq' );
385
386         #---------------------------------------------------------------------
387         package biblio::record_mods;
388         
389         biblio::record_mods->table( 'biblio.record_mods' );
390         biblio::record_mods->sequence( 'biblio.record_mods_id_seq' );
391
392         #---------------------------------------------------------------------
393         package biblio::record_note;
394         
395         biblio::record_note->table( 'biblio.record_note' );
396         biblio::record_note->sequence( 'biblio.record_note_id_seq' );
397         
398         #---------------------------------------------------------------------
399         package actor::user;
400         
401         actor::user->table( 'actor.usr' );
402         actor::user->sequence( 'actor.usr_id_seq' );
403         
404         #---------------------------------------------------------------------
405         package actor::org_unit_type;
406         
407         actor::org_unit_type->table( 'actor.org_unit_type' );
408         actor::org_unit_type->sequence( 'actor.org_unit_type_id_seq' );
409
410         #---------------------------------------------------------------------
411         package actor::org_unit;
412         
413         actor::org_unit_type->table( 'actor.org_unit' );
414         actor::org_unit_type->sequence( 'actor.org_unit_id_seq' );
415
416         #---------------------------------------------------------------------
417
418         #-------------------------------------------------------------------------------
419         package metabib::metarecord;
420
421         metabib::metarecord->table( 'metabib.metarecord' );
422         metabib::metarecord->sequence( 'metabib.metarecord_id_seq' );
423
424         #-------------------------------------------------------------------------------
425
426         #-------------------------------------------------------------------------------
427         package metabib::title_field_entry;
428
429         metabib::title_field_entry->table( 'metabib.title_field_entry' );
430         metabib::title_field_entry->sequence( 'metabib.title_field_entry_id_seq' );
431         metabib::title_field_entry->columns( 'FTS' => 'index_vector' );
432
433 #       metabib::title_field_entry->add_trigger(
434 #               before_create => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
435 #       );
436 #       metabib::title_field_entry->add_trigger(
437 #               before_update => \&OpenILS::Application::Storage::Driver::Pg::tsearch2_trigger
438 #       );
439
440         #-------------------------------------------------------------------------------
441
442         #-------------------------------------------------------------------------------
443         package metabib::author_field_entry;
444
445         metabib::author_field_entry->table( 'metabib.author_field_entry' );
446         metabib::author_field_entry->sequence( 'metabib.author_field_entry_id_seq' );
447         metabib::author_field_entry->columns( 'FTS' => 'index_vector' );
448
449         #-------------------------------------------------------------------------------
450
451         #-------------------------------------------------------------------------------
452         package metabib::subject_field_entry;
453
454         metabib::subject_field_entry->table( 'metabib.subject_field_entry' );
455         metabib::subject_field_entry->sequence( 'metabib.subject_field_entry_id_seq' );
456         metabib::subject_field_entry->columns( 'FTS' => 'index_vector' );
457
458         #-------------------------------------------------------------------------------
459
460         #-------------------------------------------------------------------------------
461         package metabib::keyword_field_entry;
462
463         metabib::keyword_field_entry->table( 'metabib.keyword_field_entry' );
464         metabib::keyword_field_entry->sequence( 'metabib.keyword_field_entry_id_seq' );
465         metabib::keyword_field_entry->columns( 'FTS' => 'index_vector' );
466
467         #-------------------------------------------------------------------------------
468
469         #-------------------------------------------------------------------------------
470         package metabib::title_field_entry_source_map;
471
472         metabib::title_field_entry_source_map->table( 'metabib.title_field_entry_source_map' );
473
474         #-------------------------------------------------------------------------------
475
476         #-------------------------------------------------------------------------------
477         package metabib::author_field_entry_source_map;
478
479         metabib::author_field_entry_source_map->table( 'metabib.author_field_entry_source_map' );
480
481         #-------------------------------------------------------------------------------
482
483         #-------------------------------------------------------------------------------
484         package metabib::subject_field_entry_source_map;
485
486         metabib::subject_field_entry_source_map->table( 'metabib.subject_field_entry_source_map' );
487
488         #-------------------------------------------------------------------------------
489
490         #-------------------------------------------------------------------------------
491         package metabib::keyword_field_entry_source_map;
492
493         metabib::keyword_field_entry_source_map->table( 'metabib.keyword_field_entry_source_map' );
494
495         #-------------------------------------------------------------------------------
496
497         #-------------------------------------------------------------------------------
498         package metabib::metarecord_source_map;
499
500         metabib::metarecord_source_map->table( 'metabib.full_rec' );
501
502         #-------------------------------------------------------------------------------
503
504         #-------------------------------------------------------------------------------
505         package metabib::full_rec;
506
507         metabib::full_rec->table( 'metabib.full_rec' );
508         metabib::full_rec->sequence( 'metabib.full_rec_id_seq' );
509         metabib::full_rec->columns( 'FTS' => 'index_vector' );
510
511         #-------------------------------------------------------------------------------
512 }
513
514 1;