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