X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FRecord.pm;h=4e5e18a846ede4a9e26a80fff87a41f87ac6b100;hp=9d82d949b878e217d1cd8e8beb7fd5886eaa9b9d;hb=dc91892bd1c4567013bbaf11dcb0c6064899a357;hpb=58d44fbe5eb9ab32e6d87063a4a3b22ddba9a828 diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 9d82d949b..4e5e18a84 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -2,14 +2,14 @@ package FS::Record; use strict; use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG - $me %dbdef_cache ); + $me %dbdef_cache %virtual_fields_cache ); use subs qw(reload_dbdef); use Exporter; use Carp qw(carp cluck croak confess); use File::CounterFile; use Locale::Country; use DBI qw(:sql_types); -use DBIx::DBSchema 0.21; +use DBIx::DBSchema 0.23; use FS::UID qw(dbh getotaker datasrc driver_name); use FS::SearchCache; use FS::Msgcat qw(gettext); @@ -21,7 +21,7 @@ use Tie::IxHash; @ISA = qw(Exporter); @EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch); -$DEBUG = 2; +$DEBUG = 0; $me = '[FS::Record]'; #ask FS::UID to run this stuff for us later @@ -204,10 +204,19 @@ sub qsearch { my $dbh = dbh; my $table = $cache ? $cache->table : $stable; - my $pkey = $dbdef->table($table)->primary_key; + my $dbdef_table = $dbdef->table($table) + or die "No schema for table $table found - ". + "do you need to create it or run dbdef-create?"; + my $pkey = $dbdef_table->primary_key; my @real_fields = grep exists($record->{$_}), real_fields($table); - my @virtual_fields = grep exists($record->{$_}), "FS::$table"->virtual_fields; + my @virtual_fields; + if ( eval 'scalar(@FS::'. $table. '::ISA);' ) { + @virtual_fields = grep exists($record->{$_}), "FS::$table"->virtual_fields; + } else { + cluck "warning: FS::$table not loaded; virtual fields not searchable"; + @virtual_fields = (); + } my $statement = "SELECT $select FROM $stable"; if ( @real_fields or @virtual_fields ) { @@ -230,7 +239,8 @@ sub qsearch { if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) { if ( $op eq '=' ) { if ( driver_name eq 'Pg' ) { - if ( $dbdef->table($table)->column($column)->type =~ /(int)/i ) { + my $type = $dbdef->table($table)->column($column)->type; + if ( $type =~ /(int|serial)/i ) { qq-( $column IS NULL )-; } else { qq-( $column IS NULL OR $column = '' )-; @@ -240,7 +250,8 @@ sub qsearch { } } elsif ( $op eq '!=' ) { if ( driver_name eq 'Pg' ) { - if ( $dbdef->table($table)->column($column)->type =~ /(int)/i ) { + my $type = $dbdef->table($table)->column($column)->type; + if ( $type =~ /(int|serial)/i ) { qq-( $column IS NOT NULL )-; } else { qq-( $column IS NOT NULL AND $column != '' )-; @@ -309,7 +320,7 @@ sub qsearch { grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields ) { if ( $record->{$field} =~ /^\d+(\.\d+)?$/ - && $dbdef->table($table)->column($field)->type =~ /(int)/i + && $dbdef->table($table)->column($field)->type =~ /(int|serial)/i ) { $sth->bind_param($bind++, $record->{$field}, { TYPE => SQL_INTEGER } ); } else { @@ -323,10 +334,15 @@ sub qsearch { $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr; + if ( eval 'scalar(@FS::'. $table. '::ISA);' ) { + @virtual_fields = "FS::$table"->virtual_fields; + } else { + cluck "warning: FS::$table not loaded; virtual fields not returned either"; + @virtual_fields = (); + } + my %result; tie %result, "Tie::IxHash"; - @virtual_fields = "FS::$table"->virtual_fields; - my @stuff = @{ $sth->fetchall_arrayref( {} ) }; if($pkey) { %result = map { $_->{$pkey}, $_ } @stuff; @@ -335,6 +351,7 @@ sub qsearch { } $sth->finish; + if ( keys(%result) and @virtual_fields ) { $statement = "SELECT virtual_field.recnum, part_virtual_field.name, ". @@ -535,6 +552,8 @@ To make a distinct duplicate of an FS::Record object, you can do: sub hash { my($self) = @_; + confess $self. ' -> hash: Hash attribute is undefined' + unless defined($self->{'Hash'}); %{ $self->{'Hash'} }; } @@ -664,7 +683,7 @@ sub insert { if (@virtual_fields) { my %v_values = map { $_, $self->getfield($_) } @virtual_fields; - my $vfieldpart = vfieldpart_hashref($table); + my $vfieldpart = $self->vfieldpart_hashref; my $v_statement = "INSERT INTO virtual_field(recnum, vfieldpart, value) ". "VALUES (?, ?, ?)"; @@ -753,7 +772,7 @@ sub delete { my $primary_key = $self->dbdef_table->primary_key; my $v_sth; my @del_vfields; - my $vfp = vfieldpart_hashref($self->table); + my $vfp = $self->vfieldpart_hashref; foreach($self->virtual_fields) { next if $self->getfield($_) eq ''; unless(@del_vfields) { @@ -804,7 +823,24 @@ returns the error, otherwise returns false. =cut sub replace { - my ( $new, $old ) = ( shift, shift ); + my $new = shift; + + my $old; + if ( @_ ) { + $old = shift; + } else { + warn "[debug]$me replace called with no arguments; autoloading old record\n" + if $DEBUG; + my $primary_key = $new->dbdef_table->primary_key; + if ( $primary_key ) { + $old = qsearchs($new->table, { $primary_key => $new->$primary_key() } ) + or croak "can't find ". $new->table. ".$primary_key ". + $new->$primary_key(); + } else { + croak $new->table. " has no primary key; pass old record as argument"; + } + } + warn "[debug]$me $new ->replace $old\n" if $DEBUG; return "Records not in same table!" unless $new->table eq $old->table; @@ -836,7 +872,7 @@ sub replace { $old->getfield($_) eq '' #? "( $_ IS NULL OR $_ = \"\" )" ? ( driver_name eq 'Pg' - ? "$_ IS NULL" + ? "( $_ IS NULL OR $_ = '' )" : "( $_ IS NULL OR $_ = \"\" )" ) : "$_ = ". _quote($old->getfield($_),$old->table,$_) @@ -870,7 +906,7 @@ sub replace { my $v_rep_sth; my $v_del_sth; my (@add_vfields, @rep_vfields, @del_vfields); - my $vfp = vfieldpart_hashref($old->table); + my $vfp = $old->vfieldpart_hashref; foreach(grep { exists($diff{$_}) } $new->virtual_fields) { if($diff{$_} eq '') { # Delete @@ -967,7 +1003,14 @@ sub check { for ($self->getfield($field)) { # See notes on check_block in FS::part_virtual_field. eval $self->pvf($field)->check_block; - return $@ if $@; + if ( $@ ) { + #this is bad, probably want to follow the stack backtrace up and see + #wtf happened + my $err = "Fatal error checking $field for $self"; + cluck "$err: $@"; + return "$err (see log for backtrace): $@"; + + } $self->setfield($field, $_); } } @@ -1051,6 +1094,21 @@ sub ut_float { ''; } +=item ut_snumber COLUMN + +Check/untaint signed numeric data (whole numbers). May not be null. If there +is an error, returns the error, otherwise returns false. + +=cut + +sub ut_snumber { + my($self, $field) = @_; + $self->getfield($field) =~ /^(-?)\s*(\d+)$/ + or return "Illegal or empty (numeric) $field: ". $self->getfield($field); + $self->setfield($field, "$1$2"); + ''; +} + =item ut_number COLUMN Check/untaint simple numeric data (whole numbers). May not be null. If there @@ -1273,9 +1331,13 @@ sub ut_zip { $self->getfield($field); $self->setfield($field,$1); } else { - $self->getfield($field) =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/ - or return gettext('illegal_zip'). " $field: ". $self->getfield($field); - $self->setfield($field,$1); + if ( $self->getfield($field) =~ /^\s*$/ ) { + $self->setfield($field,''); + } else { + $self->getfield($field) =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/ + or return gettext('illegal_zip'). " $field: ". $self->getfield($field); + $self->setfield($field,$1); + } } ''; } @@ -1369,20 +1431,25 @@ be exported, and should only be called as an instance or class method. =cut sub virtual_fields { - my $something = shift; + my $self = shift; my $table; - $table = $something->table or confess "virtual_fields called on non-table"; + $table = $self->table or confess "virtual_fields called on non-table"; confess "Unknown table $table" unless $dbdef->table($table); - # This should be smart enough to cache results. + return () unless $self->dbdef->table('part_virtual_field'); + + unless ( $virtual_fields_cache{$table} ) { + my $query = 'SELECT name from part_virtual_field ' . + "WHERE dbtable = '$table'"; + my $dbh = dbh; + my $result = $dbh->selectcol_arrayref($query); + confess $dbh->errstr if $dbh->err; + $virtual_fields_cache{$table} = $result; + } + + @{$virtual_fields_cache{$table}}; - my $query = 'SELECT name from part_virtual_field ' . - "WHERE dbtable = '$table'"; - my $dbh = dbh; - my $result = $dbh->selectcol_arrayref($query); - confess $dbh->errstr if $dbh->err; - return @$result; } @@ -1509,9 +1576,11 @@ TABLE. =cut sub vfieldpart_hashref { - my ($table) = @_; + my $self = shift; + my $table = $self->table; + + return {} unless $self->dbdef->table('part_virtual_field'); - return () unless $table; my $dbh = dbh; my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ". "dbtable = '$table'";