1 package OpenILS::Application::Storage::CDBI;
2 use base qw/Class::DBI/;
4 use Class::DBI::AbstractSearch;
6 use OpenILS::Application::Storage::CDBI::actor;
7 use OpenILS::Application::Storage::CDBI::action;
8 use OpenILS::Application::Storage::CDBI::asset;
9 use OpenILS::Application::Storage::CDBI::authority;
10 use OpenILS::Application::Storage::CDBI::biblio;
11 use OpenILS::Application::Storage::CDBI::config;
12 use OpenILS::Application::Storage::CDBI::metabib;
13 use OpenILS::Application::Storage::CDBI::money;
14 use OpenILS::Application::Storage::CDBI::permission;
15 use OpenILS::Application::Storage::CDBI::container;
18 use OpenSRF::Utils::Logger qw(:level);
19 use OpenSRF::EX qw/:try/;
22 my $log = 'OpenSRF::Utils::Logger';
27 $log->debug("Creating ImaDBI Querys", DEBUG);
28 __PACKAGE__->set_sql( 'OILSFastSearch', <<" SQL", 'Main');
34 __PACKAGE__->set_sql( 'OILSFastOrderedSearchLike', <<" SQL", 'Main');
41 __PACKAGE__->set_sql( 'OILSFastOrderedSearch', <<" SQL", 'Main');
48 $log->debug("Calling Driver child_init", DEBUG);
49 $self->SUPER::child_init(@_);
55 $class = ref($class) || $class;
63 if (!(defined($order) and ref($order) and ref($order) eq 'HASH')) {
64 if (defined($value) and ref($value) and ref($value) eq 'HASH') {
68 $order = { order_by => $class->columns('Primary') }
72 unless (defined $value) {
74 ($field) = $class->columns('Primary');
77 unless (defined $field) {
78 ($field) = $class->columns('Primary');
81 unless ($order->{order_by}) {
82 $order = { order_by => $class->columns('Primary') }
85 my $fm_class = 'Fieldmapper::'.$class;
86 my $field_list = join ',', $class->columns('Essential');
90 $sth = $class->sql_OILSFastOrderedSearch( $field_list, $class->table, $field, $order->{order_by});
92 $sth = $class->sql_OILSFastOrderedSearchLike( $field_list, $class->table, $field, $order->{order_by});
94 $sth->execute($value);
100 return map $class->construct($_), $self->fast_flesh_sth(@_)->fetchall_hash;
103 sub fast_fieldmapper {
109 my $class = ref($self) || $self;
110 my $fm_class = 'Fieldmapper::'.$class;
112 $log->debug("fast_fieldmapper() ==> Retrieving $fm_class", INTERNAL);
114 for my $hash ($self->fast_flesh_sth( $col, "$id", { order_by => $col }, $like )->fetchall_hash) {
115 my $fm = $fm_class->new;
116 for my $field ( $fm_class->real_fields ) {
117 $fm->$field( $$hash{$field} );
122 my $search_type = 'search';
124 $search_type = 'search_fts'
125 } elsif ($like == 3) {
126 $search_type = 'search_regex'
129 for my $obj ($class->$search_type({ $col => $id}, $options)) {
130 push @fms, $obj->to_fieldmapper;
140 (UNIVERSAL::isa($arg => 'Fieldmapper') ||
141 UNIVERSAL::isa($arg => 'Class::DBI')) ) {
142 my ($col) = $self->primary_column;
143 $log->debug("Using field $col as the primary key", INTERNAL);
146 my ($col) = $self->primary_column;
147 $log->debug("Using field $col as the primary key", INTERNAL);
151 $log->debug("Retrieving $self with $arg", INTERNAL);
154 $rec = $self->SUPER::retrieve("$arg");
156 $log->debug("Could not retrieve $self with $arg! -- ".shift(), DEBUG);
164 my $class = ref($obj) || $obj;
166 my $fm_class = 'Fieldmapper::'.$class;
167 my $fm = $fm_class->new;
170 for my $field ( $fm->real_fields ) {
171 $fm->$field( $obj->$field );
183 delete $$arg{$_} for (keys %$search);
185 $log->debug("CDBI->merge: \$search is $search (".ref($search)." : ".join(',',map{"$_ => $$search{$_}"}keys(%$search)).")",DEBUG);
186 $log->debug("CDBI->merge: \$arg is $arg (".ref($arg)." : ".join(',',map{"$_ => $$arg{$_}"}keys(%$arg)).")",DEBUG);
189 @objs = $self->search_where($search) unless (ref $self);
192 $objs[0]->update($arg);
194 } elsif (@objs == 0) {
195 return $self->create({%$search,%$arg});
197 throw OpenSRF::EX::WARN ("Non-unique search key for merge. Perhaps you meant to use remote_update?");
206 delete $$arg{$_} for (keys %$search);
208 $log->debug("CDBI->remote_update: \$search is $search (".ref($search)." : ".join(',',map{"$_ => $$search{$_}"}keys(%$search)).")",DEBUG);
209 $log->debug("CDBI->remote_update: \$arg is $arg (".ref($arg)." : ".join(',',map{"$_ => $$arg{$_}"}keys(%$arg)).")",DEBUG);
211 # my @objs = $self->search_where($search);
212 # throw OpenSRF::EX::WARN ("No objects found for remote_update. Perhaps you meant to use merge?")
215 # $_->update($arg) for (@objs);
216 # return scalar(@objs);
218 my @finds = sort keys %$search;
219 my @sets = sort keys %$arg;
221 my @find_vals = @$search{@finds};
222 my @set_vals = @$arg{@sets};
224 my $sql = 'UPDATE %s SET %s WHERE %s';
226 my $table = $self->table;
227 my $set = join(', ', map { "$_=?" } @sets);
228 my $where = join(', ', map { "$_=?" } @finds);
230 my $sth = $self->db_Main->prepare(sprintf($sql, $table, $set, $where));
231 $sth->execute(@set_vals,@find_vals);
240 $log->debug("CDBI->create: \$arg is $arg (".ref($arg)." : ".JSON->perl2JSON($arg).")",DEBUG);
242 if (ref($arg) && UNIVERSAL::isa($arg => 'Fieldmapper')) {
243 return $self->create_from_fieldmapper($arg,@_);
246 return $self->SUPER::create($arg,@_);
249 sub create_from_fieldmapper {
254 $log->debug("Creating node of type ".ref($fm), DEBUG);
256 my $class = ref($obj) || $obj;
257 my ($primary) = $class->columns('Primary');
259 if (ref($fm) &&UNIVERSAL::isa($fm => 'Fieldmapper')) {
260 my %hash = map { defined $fm->$_ ?
263 } grep { $_ ne $primary } $class->columns('Essential');
265 if ($class->find_column( 'last_xact_id' )) {
266 if ($OpenILS::Application::Storage::IGNORE_XACT_ID_FAILURE) {
267 $hash{last_xact_id} = 'unknown.'.time.'.'.$$.'.'.rand($$);
269 my $xact_id = $class->current_xact_id;
270 throw Error unless ($xact_id);
271 $hash{last_xact_id} = $xact_id;
275 return $class->create( \%hash, @params );
286 my $class = ref($self) || $self;
288 $self = $self->retrieve($arg) if (!ref($self));
289 unless (defined $self) {
290 $log->debug("ARG! Couldn't retrieve record ".$arg->id, DEBUG);
291 throw OpenSRF::EX::WARN ("ARG! Couldn't retrieve record ");
294 if ($class->find_column( 'last_xact_id' )) {
295 my $xact_id = $self->current_xact_id;
297 throw Error ("Deleting from $class requires a transaction be established")
300 throw Error ("The row you are attempting to delete has been changed since you read it")
301 unless ( $orig->last_xact_id eq $self->last_xact_id);
303 $self->last_xact_id( $class->current_xact_id );
304 $self->SUPER::update;
307 $self->SUPER::delete;
316 $string .= "Object type:\t".ref($obj)."\n";
317 $string .= "Object string:\t$obj\n";
319 if (ref($obj) && UNIVERSAL::isa($obj => 'Fieldmapper')) {
320 $string .= "Object fields:\n";
321 for my $col ($obj->real_fields()) {
322 $string .= "\t$col\t=> ".$obj->$col."\n";
324 } elsif (ref($obj) && UNIVERSAL::isa($obj => 'Class::DBI')) {
325 $string .= "Object cols:\n";
326 for my $col ($obj->columns('All')) {
327 $string .= "\t$col\t=> ".$obj->$col."\n";
329 } elsif (ref($obj) && UNIVERSAL::isa($obj => 'HASH')) {
330 $string .= "Object keys and vals:\n";
331 for my $col (keys %$obj) {
332 $string .= "\t$col\t=> $$obj{$col}\n";
338 $log->debug($string,DEBUG);
346 $log->debug("Attempting to update using $arg", DEBUG) if ($arg);
349 $self = $self->modify_from_fieldmapper($arg);
350 unless (defined $self) {
351 $log->debug("Modification of $arg seems to have failed....", DEBUG);
356 $log->debug("Calling Class::DBI->update on modified object $self", DEBUG);
358 #debug_object($self);
360 return $self->SUPER::update if ($self->is_changed);
364 sub modify_from_fieldmapper {
372 $log->debug("Modifying object using fieldmapper", DEBUG);
374 my $class = ref($obj) || $obj;
375 my ($primary) = $class->columns('Primary');
379 $obj = $class->retrieve($fm);
382 $log->debug("Retrieve of $class using $fm (".$fm->id.") failed! -- ".shift(), ERROR);
383 throw OpenSRF::EX::WARN ("No $class with id of ".$fm->id."!!");
389 if (ref($fm) and UNIVERSAL::isa($fm => 'Fieldmapper')) {
390 %hash = map { ($_ => $fm->$_) } grep { $_ ne $primary } $class->columns('Essential');
395 my $au = $obj->autoupdate;
400 for my $field ( keys %hash ) {
401 $obj->$field( $hash{$field} ) if ($obj->$field ne $hash{$field});
402 $log->debug("Setting field $field on $obj to $hash{$field}",INTERNAL);
405 if ($class->find_column( 'last_xact_id' ) and $obj->is_changed) {
406 my ($xact_id) = OpenILS::Application::Storage->method_lookup('open-ils.storage.transaction.current')->run();
407 throw Error ("Updating $class requires a transaction be established")
409 throw Error ("The row you are attempting to delete has been changed since you read it")
410 unless ( $fm->last_xact_id eq $obj->last_xact_id);
411 $obj->last_xact_id( $xact_id );
413 $obj->autoupdate($au)
421 #-------------------------------------------------------------------------------
422 actor::user->has_a( home_ou => 'actor::org_unit' );
423 actor::user->has_a( card => 'actor::card' );
424 actor::user->has_a( standing => 'config::standing' );
425 actor::user->has_a( profile => 'actor::profile' );
426 actor::user->has_a( mailing_address => 'actor::user_address' );
427 actor::user->has_a( billing_address => 'actor::user_address' );
428 actor::user->has_a( ident_type => 'config::identification_type' );
429 actor::user->has_a( ident_type2 => 'config::identification_type' );
430 actor::user->has_a( net_access_level => 'config::net_access_level' );
432 actor::user_address->has_a( usr => 'actor::user' );
434 actor::card->has_a( usr => 'actor::user' );
436 actor::org_unit->has_a( parent_ou => 'actor::org_unit' );
437 actor::org_unit->has_a( ou_type => 'actor::org_unit_type' );
438 #actor::org_unit->has_a( address => 'actor::org_address' );
440 actor::stat_cat_entry->has_a( stat_cat => 'actor::stat_cat' );
441 actor::stat_cat->has_many( entries => 'actor::stat_cat_entry' );
442 actor::stat_cat_entry_user_map->has_a( stat_cat => 'actor::stat_cat' );
443 actor::stat_cat_entry_user_map->has_a( stat_cat_entry => 'actor::stat_cat_entry' );
444 actor::stat_cat_entry_user_map->has_a( target_usr => 'actor::user' );
446 asset::stat_cat_entry->has_a( stat_cat => 'asset::stat_cat' );
447 asset::stat_cat->has_many( entries => 'asset::stat_cat_entry' );
448 asset::stat_cat_entry_copy_map->has_a( stat_cat => 'asset::stat_cat' );
449 asset::stat_cat_entry_copy_map->has_a( stat_cat_entry => 'asset::stat_cat_entry' );
450 asset::stat_cat_entry_copy_map->has_a( owning_copy => 'asset::copy' );
452 action::survey_response->has_a( usr => 'actor::user' );
453 action::survey_response->has_a( survey => 'action::survey' );
454 action::survey_response->has_a( question => 'action::survey_question' );
455 action::survey_response->has_a( answer => 'action::survey_answer' );
457 action::survey_question->has_a( survey => 'action::survey' );
459 action::survey_answer->has_a( question => 'action::survey' );
461 asset::copy_note->has_a( owning_copy => 'asset::copy' );
463 actor::user->has_many( stat_cat_entries => [ 'actor::stat_cat_entry_user_map' => 'stat_cat_entry' ] );
464 actor::user->has_many( stat_cat_entry_user_maps => 'actor::stat_cat_entry_user_map' );
466 asset::copy->has_many( stat_cat_entries => [ 'asset::stat_cat_entry_copy_map' => 'stat_cat_entry' ] );
467 asset::copy->has_many( stat_cat_entry_copy_maps => 'asset::stat_cat_entry_copy_map' );
469 asset::copy->has_a( call_number => 'asset::call_number' );
470 asset::copy->has_a( creator => 'actor::user' );
471 asset::copy->has_a( editor => 'actor::user' );
472 asset::copy->has_a( status => 'config::copy_status' );
473 asset::copy->has_a( location => 'asset::copy_location' );
474 asset::copy->has_a( circ_lib => 'actor::org_unit' );
476 asset::call_number_note->has_a( call_number => 'asset::call_number' );
478 asset::call_number->has_a( record => 'biblio::record_entry' );
479 asset::call_number->has_a( creator => 'actor::user' );
480 asset::call_number->has_a( editor => 'actor::user' );
482 authority::record_note->has_a( record => 'authority::record_entry' );
483 biblio::record_note->has_a( record => 'biblio::record_entry' );
485 authority::record_entry->has_a( creator => 'actor::user' );
486 authority::record_entry->has_a( editor => 'actor::user' );
487 biblio::record_entry->has_a( creator => 'actor::user' );
488 biblio::record_entry->has_a( editor => 'actor::user' );
490 metabib::metarecord->has_a( master_record => 'biblio::record_entry' );
492 authority::record_descriptor->has_a( record => 'authority::record_entry' );
493 metabib::record_descriptor->has_a( record => 'biblio::record_entry' );
495 authority::full_rec->has_a( record => 'authority::record_entry' );
496 metabib::full_rec->has_a( record => 'biblio::record_entry' );
498 metabib::title_field_entry->has_a( source => 'biblio::record_entry' );
499 metabib::title_field_entry->has_a( field => 'config::metabib_field' );
501 metabib::author_field_entry->has_a( source => 'biblio::record_entry' );
502 metabib::author_field_entry->has_a( field => 'config::metabib_field' );
504 metabib::subject_field_entry->has_a( source => 'biblio::record_entry' );
505 metabib::subject_field_entry->has_a( field => 'config::metabib_field' );
507 metabib::keyword_field_entry->has_a( source => 'biblio::record_entry' );
508 metabib::keyword_field_entry->has_a( field => 'config::metabib_field' );
510 metabib::series_field_entry->has_a( source => 'biblio::record_entry' );
511 metabib::series_field_entry->has_a( field => 'config::metabib_field' );
513 metabib::metarecord_source_map->has_a( metarecord => 'metabib::metarecord' );
514 metabib::metarecord_source_map->has_a( source => 'biblio::record_entry' );
516 action::circulation->has_a( usr => 'actor::user' );
517 action::circulation->has_a( target_copy => 'asset::copy' );
518 action::circulation->has_a( circ_lib => 'actor::org_unit' );
520 money::billable_transaction->has_a( usr => 'actor::user' );
523 #-------------------------------------------------------------------------------
524 actor::user->has_many( survey_responses => 'action::survey_response' );
525 actor::user->has_many( addresses => 'actor::user_address' );
526 actor::user->has_many( cards => 'actor::card' );
528 actor::org_unit->has_many( users => 'actor::user' );
529 actor::profile->has_many( users => 'actor::user' );
531 action::survey->has_many( questions => 'action::survey_question' );
532 action::survey->has_many( responses => 'action::survey_response' );
534 action::survey_question->has_many( answers => 'action::survey_answer' );
535 action::survey_question->has_many( responses => 'action::survey_response' );
537 action::survey_answer->has_many( responses => 'action::survey_response' );
539 asset::copy->has_many( notes => 'asset::copy_note' );
540 asset::call_number->has_many( copies => 'asset::copy' );
541 asset::call_number->has_many( notes => 'asset::call_number_note' );
543 authority::record_entry->has_many( record_descriptor => 'authority::record_descriptor' );
544 authority::record_entry->has_many( notes => 'authority::record_note' );
546 biblio::record_entry->has_many( record_descriptor => 'metabib::record_descriptor' );
547 biblio::record_entry->has_many( notes => 'biblio::record_note' );
548 biblio::record_entry->has_many( call_numbers => 'asset::call_number' );
549 biblio::record_entry->has_many( full_record_entries => 'metabib::full_rec' );
550 biblio::record_entry->has_many( title_field_entries => 'metabib::title_field_entry' );
551 biblio::record_entry->has_many( author_field_entries => 'metabib::author_field_entry' );
552 biblio::record_entry->has_many( subject_field_entries => 'metabib::subject_field_entry' );
553 biblio::record_entry->has_many( keyword_field_entries => 'metabib::keyword_field_entry' );
554 biblio::record_entry->has_many( series_field_entries => 'metabib::series_field_entry' );
556 metabib::metarecord->has_many( source_records => [ 'metabib::metarecord_source_map' => 'source'] );
558 money::billable_transaction->has_many( billings => 'money::billing' );
559 money::billable_transaction->has_many( payments => 'money::payment' );
561 money::grocery->has_many( billings => 'money::billing' );
562 money::grocery->has_many( payments => 'money::payment' );
564 money::billing->has_a( xact => 'money::billable_transaction' );
565 money::payment->has_a( xact => 'money::billable_transaction' );
567 money::cash_payment->has_a( xact => 'money::billable_transaction' );
568 money::cash_payment->has_a( accepting_usr => 'actor::user' );
570 money::check_payment->has_a( xact => 'money::billable_transaction' );
571 money::check_payment->has_a( accepting_usr => 'actor::user' );
573 money::credit_card_payment->has_a( xact => 'money::billable_transaction' );
574 money::credit_card_payment->has_a( accepting_usr => 'actor::user' );
576 money::forgive_payment->has_a( xact => 'money::billable_transaction' );
577 money::forgive_payment->has_a( accepting_usr => 'actor::user' );
579 money::work_payment->has_a( xact => 'money::billable_transaction' );
580 money::work_payment->has_a( accepting_usr => 'actor::user' );
582 money::credit_payment->has_a( xact => 'money::billable_transaction' );
583 money::credit_payment->has_a( accepting_usr => 'actor::user' );
585 permission::grp_tree->has_a( parent => 'permission::grp_tree' );
587 permission::grp_perm_map->has_a( grp => 'permission::grp_tree' );
588 permission::grp_perm_map->has_a( perm => 'permission::perm_list' );
589 permission::grp_perm_map->has_a( depth => 'actor::org_unit_type' );
591 permission::usr_perm_map->has_a( usr => 'actor::user' );
592 permission::usr_perm_map->has_a( perm => 'permission::perm_list' );
593 permission::usr_perm_map->has_a( depth => 'actor::org_unit_type' );
595 permission::usr_grp_map->has_a( usr => 'actor::user' );
596 permission::usr_grp_map->has_a( grp => 'permission::grp_tree' );
598 action::hold_notification->has_a( hold => 'action::hold_request' );
600 action::hold_copy_map->has_a( hold => 'action::hold_request' );
601 action::hold_copy_map->has_a( target_copy => 'asset::copy' );
603 action::hold_request->has_a( current_copy => 'asset::copy' );
604 action::hold_request->has_a( requestor => 'actor::user' );
605 action::hold_request->has_a( usr => 'actor::user' );
606 action::hold_request->has_a( pickup_lib => 'actor::org_unit' );
607 action::hold_request->has_a( request_lib => 'actor::org_unit' );
609 action::hold_request->has_many( notifications => 'action::hold_notification' );
610 action::hold_request->has_many( eligible_copies => [ 'action::hold_copy_map' => 'target_copy' ] );
612 asset::copy->has_many( holds => [ 'action::hold_copy_map' => 'hold' ] );