1 # Vend::Table::Common - Common access methods for Interchange databases
3 # $Id: Common.pm,v 2.51 2008-05-26 02:30:04 markj Exp $
5 # Copyright (C) 2002-2008 Interchange Development Group
6 # Copyright (C) 1996-2002 Red Hat, Inc.
8 # This program was originally based on Vend 0.2 and 0.3
9 # Copyright 1995 by Andrew M. Wilcox <amw@wilcoxsolutions.com>
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public
22 # License along with this program; if not, write to the Free
23 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26 $VERSION = substr(q$Revision: 2.51 $, 10);
29 package Vend::Table::Common;
30 require Vend::DbSearch;
31 require Vend::TextSearch;
32 require Vend::CounterFile;
33 no warnings qw(uninitialized numeric);
38 use vars qw($Storable $VERSION @EXPORT @EXPORT_OK);
39 @EXPORT = qw(create_columns import_ascii_delimited import_csv config columns);
40 @EXPORT_OK = qw(import_quoted read_quoted_fields);
61 # See if we can do Storable
64 die unless $ENV{MINIVEND_STORABLE_DB};
74 $Hex_string[$i] = sprintf("%%%02X", $i);
79 my ($columns, $config) = @_;
80 $config = {} unless $config;
81 my $column_index = {};
83 #::logDebug("create_columns: " . ::uneval($config));
86 $key = $config->{KEY};
88 elsif (! defined $config->{KEY_INDEX}) {
89 $config->{KEY_INDEX} = 0;
90 $config->{KEY} = $columns->[0];
93 my $alias = $config->{FIELD_ALIAS} || {};
94 #::logDebug("field_alias: " . ::uneval($alias)) if $config->{FIELD_ALIAS};
95 for ($i = 0; $i < @$columns; ++$i) {
96 $column_index->{$columns->[$i]} = $i;
97 defined $alias->{$columns->[$i]}
98 and $column_index->{ $alias->{ $columns->[$i] } } = $i;
99 next unless defined $key and $key eq $columns->[$i];
100 $config->{KEY_INDEX} = $i;
102 #::logDebug("set KEY_INDEX to $i: " . ::uneval($config));
106 "Cannot find key column %s in %s (%s): %s",
111 ) unless defined $config->{KEY_INDEX};
113 return $column_index;
116 sub separate_definitions {
117 my ($options, $fields) = @_;
119 #::logDebug("separating '$_'");
120 next unless s/\s+(.*)//;
121 #::logDebug("needed separation: '$_'");
124 unless(defined $options->{COLUMN_DEF}{$fn}) {
125 $options->{COLUMN_DEF}{$fn} = $def;
133 return unless $s->[$CONFIG]{IC_LOCKING};
134 if($s->[$CONFIG]{_lock_handle}) {
135 close $s->[$CONFIG]{_lock_handle};
136 delete $s->[$CONFIG]{_lock_handle};
142 return unless $s->[$CONFIG]{IC_LOCKING};
144 if(not $lockhandle = $s->[$CONFIG]{_lock_handle}) {
145 my $lf = $s->[$CONFIG]{file} . '.lock';
146 unless($lf =~ m{/}) {
147 $lf = ($s->[$CONFIG]{dir} || $Vend::Cfg->{ProductDir}) . "/$lf";
149 $lockhandle = gensym;
150 $s->[$CONFIG]{_lock_file} = $lf;
151 $s->[$CONFIG]{_lock_handle} = $lockhandle;
152 open $lockhandle, ">> $lf"
153 or die errmsg("Cannot lock table %s (%s): %s", $s->[$CONFIG]{name}, $lf, $!);
155 #::logDebug("lock handle=$lockhandle");
156 Vend::Util::lockfile($lockhandle);
161 return unless $s->[$CONFIG]{IC_LOCKING};
162 Vend::Util::unlockfile($s->[$CONFIG]{_lock_handle});
167 $val =~ s,([\t\%]),$Hex_string[ord($1)],eg;
173 $val =~ s,%(..),chr(hex($1)),eg;
180 my $cfg = $s->[$CONFIG];
182 return $s->autosequence() if $cfg->{AUTO_SEQUENCE};
184 return '' if not $start = $cfg->{AUTO_NUMBER};
186 my $c = $s->[$CONFIG];
187 if(! defined $c->{AutoNumberCounter}) {
188 $c->{AutoNumberCounter} = new Vend::CounterFile
189 $cfg->{AUTO_NUMBER_FILE},
191 $c->{AUTO_NUMBER_DATE},
196 $num = $c->{AutoNumberCounter}->inc();
197 } while $s->record_exists($num);
201 # These don't work in non-DBI databases
206 return exists $_[0]->[$CONFIG]->{NUMERIC}->{$_[1]};
210 my($s, $value, $field) = @_;
211 return $value if $s->numeric($field);
217 my ($s, $key, $value) = @_;
218 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
219 return $s->[$CONFIG]{$key} unless defined $value;
220 $s->[$CONFIG]{$key} = $value;
225 my $db = Vend::Data::import_database($s->[0], 1);
226 return undef if ! $db;
227 $Vend::Database{$s->[0]{name}} = $db;
228 Vend::Data::update_productbase($s->[0]{name});
229 if($db->[$CONFIG]{export_now}) {
230 Vend::Data::export_database($db);
231 delete $db->[$CONFIG]{export_now};
238 return 1 if ! defined $s->[$TIE_HASH];
239 #::logDebug("closing table $s->[$FILENAME]");
242 untie %{$s->[$TIE_HASH]}
243 or $s->log_error("%s %s: %s", errmsg("untie"), $s->[$FILENAME], $!);
244 undef $s->[$TIE_HASH];
245 #::logDebug("closed table $s->[$FILENAME], self=" . ::uneval($s));
249 my ($s, $ary, $col, $filter) = @_;
252 next unless defined ($column = $col->{$_});
253 $ary->[$column] = Vend::Interpolate::filter_value(
263 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
264 return @{$s->[$COLUMN_NAMES]};
268 return defined test_column(@_);
272 my ($s, $column) = @_;
273 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
274 return $s->[$COLUMN_INDEX]{$column};
278 my ($s, $column) = @_;
279 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
280 my $i = $s->[$COLUMN_INDEX]{$column};
282 "There is no column named '%s' in %s",
289 *test_record = \&record_exists;
293 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
294 my $r = $s->[$DBM]->EXISTS("k$key");
300 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
301 return $s->[$CONFIG]{name};
306 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
307 return undef unless $s->record_exists($key);
309 @row{ @{$s->[$COLUMN_NAMES]} } = $s->row($key);
315 $s->lock_table() if $s->[$CONFIG]{IC_LOCKING};
316 my $line = $s->[$TIE_HASH]{"k$key"};
317 $s->unlock_table() if $s->[$CONFIG]{IC_LOCKING};
319 "There is no row with index '%s' in database %s",
322 ) unless defined $line;
323 return map(unstuff($_), split(/\t/, $line, 9999))
324 unless $s->[$CONFIG]{FILTER_FROM};
325 my @f = map(unstuff($_), split(/\t/, $line, 9999));
326 $s->filter(\@f, $s->[$COLUMN_INDEX], $s->[$CONFIG]{FILTER_FROM});
332 $s->lock_table() if $s->[$CONFIG]{IC_LOCKING};
333 my $line = $s->[$TIE_HASH]{"k$key"};
334 $s->unlock_table() if $s->[$CONFIG]{IC_LOCKING};
335 die $s->log_error( "There is no row with index '%s'", $key,)
336 unless defined $line;
337 return (@{ Storable::thaw($line) })
338 unless $s->[$CONFIG]{FILTER_FROM};
339 #::logDebug("filtering.");
340 my $f = Storable::thaw($line);
341 $s->filter($f, $s->[$COLUMN_INDEX], $s->[$CONFIG]{FILTER_FROM});
346 my ($s, $column) = @_;
347 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
348 my $index = $s->column_index($column);
351 return ($s->row($key))[$index];
357 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
359 my $key_idx = $s->[$KEY_INDEX] || 0;
362 push @index, $s->column_index($_);
364 #::logDebug("settor index=@index");
368 my $key = $vals[$key_idx];
370 @row = $s->row($key);
372 @row[@index] = @vals;
373 #::logDebug("setting $key indices '@index' to '@vals'");
379 my ($s, $key, $fary) = @_;
380 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
382 return undef unless $s->record_exists($key);
384 if(ref $fary ne 'ARRAY') {
389 my @result = ($s->row($key))[ map { $s->column_index($_) } @$fary ];
390 return wantarray ? @result : \@result;
394 my ($s, $key, $fary, $vary) = @_;
395 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
397 if($s->[$CONFIG]{Read_only}) {
399 "Attempt to set slice of %s in read-only table %s",
407 if (ref ($key) eq 'ARRAY') {
412 unless ref ($opt) eq 'HASH';
414 $opt->{dml} = 'upsert'
415 unless defined $opt->{dml};
417 if(ref $fary ne 'ARRAY') {
419 if(ref $href ne 'HASH') {
420 $href = { splice (@_, 2) };
422 $vary = [ values %$href ];
423 $fary = [ keys %$href ];
426 my $keyname = $s->[$CONFIG]{KEY};
428 my ($found_key) = grep $_ eq $keyname, @$fary;
431 unshift @$fary, $keyname;
432 unshift @$vary, $key;
437 if ($s->record_exists($key)) {
438 if ($opt->{dml} eq 'insert') {
440 "Duplicate key on set_slice insert for key '$key' on table %s",
445 @current = $s->row($key);
447 elsif ($opt->{dml} eq 'update') {
449 "No record to update set_slice for key '$key' on table %s",
455 @current[ map { $s->column_index($_) } @$fary ] = @$vary;
457 $key = $s->set_row(@current);
460 "Did set_slice with empty key on table %s",
468 my ($s, $column) = @_;
469 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
470 my $index = $s->column_index($column);
472 my ($key, $value) = @_;
473 my @row = $s->row($key);
474 $row[$index] = $value;
480 my ($s, $old, $new) = @_;
481 return undef unless $s->record_exists($old);
482 my @ary = $s->row($old);
483 $ary[$s->[$KEY_INDEX]] = $new;
489 my ($s, $col, $old, $new) = @_;
490 return unless $s->column_exists($col);
491 my $sel = $s->quote($old, $col);
492 my $name = $s->[$CONFIG]{name};
493 my ($ary, $nh, $na) = $s->query("select * from $name where $col = $sel");
494 my $fpos = $nh->{$col} || return undef;
495 $s->config('AUTO_NUMBER', '000001') unless $s->config('AUTO_NUMBER');
498 $line->[$s->[$KEY_INDEX]] = '';
499 $line->[$fpos] = $new;
506 my ($s, @fields) = @_;
507 my $key = $fields[$s->[$KEY_INDEX]];
509 #::logDebug("stuff key=$key");
510 $fields[$s->[$KEY_INDEX]] = $key = $s->autonumber()
512 $s->filter(\@fields, $s->[$COLUMN_INDEX], $s->[$CONFIG]{FILTER_TO})
513 if $s->[$CONFIG]{FILTER_TO};
516 $s->[$TIE_HASH]{"k$key"} = join("\t", map(stuff($_), @fields));
523 my ($s, @fields) = @_;
524 my $key = $fields[$s->[$KEY_INDEX]];
525 #::logDebug("freeze key=$key");
526 $fields[$s->[$KEY_INDEX]] = $key = $s->autonumber()
528 $s->filter(\@fields, $s->[$COLUMN_INDEX], $s->[$CONFIG]{FILTER_TO})
529 if $s->[$CONFIG]{FILTER_TO};
531 $s->[$TIE_HASH]{"k$key"} = Storable::freeze(\@fields);
537 *set_row = \&freeze_row;
541 *set_row = \&stuff_row;
542 *row = \&unstuff_row;
546 my ($s, $key, $foreign) = @_;
547 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
548 $key = $s->quote($key, $foreign);
549 my $q = "select $s->[$CONFIG]{KEY} from $s->[$CONFIG]{name} where $foreign = $key";
550 #::logDebug("foreign key query = $q");
551 my $ary = $s->query({ sql => $q });
552 #::logDebug("foreign key query returned" . ::uneval($ary));
553 return undef unless $ary and $ary->[0];
558 my ($s, $key, $column) = @_;
559 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
560 return ($s->row($key))[$s->column_index($column)];
564 my ($s, $key, $column, $value) = @_;
565 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
566 if($s->[$CONFIG]{Read_only}) {
568 "Attempt to write %s in read-only table",
569 "$s->[$CONFIG]{name}::${column}::$key",
574 if($s->record_exists($key)) {
575 @row = $s->row($key);
578 $row[$s->[$KEY_INDEX]] = $key;
580 $row[$s->column_index($column)] = $value
587 my ($s, $key, $column, $adder) = @_;
588 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
590 if($s->[$CONFIG]{Read_only}) {
592 "Attempt to write %s in read-only table",
593 "$s->[$CONFIG]{name}::${column}::$key",
597 my @row = $s->row($key);
598 my $idx = $s->column_index($column);
599 #::logDebug("ready to increment key=$key column=$column adder=$adder idx=$idx row=" . ::uneval(\@row));
600 $value = $row[$s->column_index($column)] += $adder;
611 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
613 utime $now, $now, $s->[$FILENAME];
618 return $s if defined $s->[$TIE_HASH];
619 return $s->import_db();
623 my($s, $sort_field, $sort_option) = @_;
624 if(length $sort_field) {
626 $opt->{to} = $sort_option
630 $opt->{tf} = $sort_field;
631 $opt->{query} = "select * from $s->[$CONFIG]{name}";
632 $s->[$EACH] = $s->query($opt);
639 if(! defined $s->[$EACH][0]) {
643 my $k = $s->[$EACH][0][$s->[$KEY_INDEX]];
644 return ($k, @{shift @{ $s->[$EACH] } });
649 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
652 return $s->each_sorted() if defined $s->[$EACH];
654 $key = each %{$s->[$TIE_HASH]};
656 if ($key =~ s/^k//) {
657 return ($key, $s->row($key));
674 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
677 if (! defined $restrict) {
680 #::logDebug("Found qual=$qual");
682 if($hfield =~ s/^\s+WHERE\s+(\w+)\s*!=\s*1($|\s+)//) {
684 #::logDebug("Found hf=$hf");
685 $s->test_column($hf) and $hfield = $s->column_index($hf);
691 #::logDebug("hf index=$hfield");
693 if($restrict = ($Vend::Cfg->{TableRestrict}{$s->config('name')} || 0)) {
694 #::logDebug("restricted?");
695 $sup = ! defined $Global::SuperUserFunction
697 $Global::SuperUserFunction->();
702 ($rfield, $rsession) = split /\s*=\s*/, $restrict;
703 $s->test_column($rfield) and $rfield = $s->column_index($rfield)
705 $rsession = $Vend::Session->{$rsession};
709 $restrict = 1 if $hfield and $s->[$CONFIG]{HIDE_FIELD} eq $hf;
714 $key = each %{$s->[$TIE_HASH]};
715 #::logDebug("each_nokey: $key field=$rfield sup=$sup");
720 $key =~ s/^k// or next;
722 my (@row) = $s->row($key);
723 #::logDebug("each_nokey: rfield='$row[$rfield]' eq '$rsession' ??") if defined $rfield;
724 #::logDebug("each_nokey: hfield='$row[$hfield]'") if defined $hfield;
725 next if defined $hfield and $row[$hfield];
726 next if defined $rfield and $row[$rfield] ne $rsession;
729 return [ $s->row($key) ];
736 return defined $_[0]->[$TIE_HASH];
741 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
742 if($s->[$CONFIG]{Read_only}) {
744 "Attempt to delete row '$key' in read-only table %s",
751 #::logDebug("delete row $key from $s->[$FILENAME]");
752 delete $s->[$TIE_HASH]{"k$key"};
756 sub sprintf_substitute {
757 my ($s, $query, $fields, $cols) = @_;
758 return sprintf $query, @$fields;
762 my ($s, $query, $opt) = @_;
764 $opt->{query} = $query;
766 return scalar $s->query($opt);
770 my($s, $opt, $text, @arg) = @_;
772 if(! CORE::ref($opt)) {
773 unshift @arg, $text if defined $text;
778 $s = $s->import_db() if ! defined $s->[$TIE_HASH];
779 $opt->{query} = $opt->{sql} || $text if ! $opt->{query};
781 #::logDebug("receieved query. object=" . ::uneval_it($opt));
783 if(defined $opt->{values}) {
784 @arg = $opt->{values} =~ /['"]/
785 ? ( Text::ParseWords::shellwords($opt->{values}) )
786 : (grep /\S/, split /\s+/, $opt->{values});
787 @arg = @{$::Values}{@arg};
791 $opt->{$opt->{type}} = 1 unless defined $opt->{$opt->{type}};
795 $query = ! scalar @arg
797 : sprintf_substitute ($s, $opt->{query}, \@arg);
799 my $codename = defined $s->[$CONFIG]{KEY} ? $s->[$CONFIG]{KEY} : 'code';
811 if($opt->{STATEMENT}) {
812 $stmt = $opt->{STATEMENT};
813 $spec = $opt->{SPEC};
814 #::logDebug('rerouted. Command is ' . $stmt->command());
818 ($spec, $stmt) = Vend::Scan::sql_statement($query, $opt);
821 my $msg = errmsg("SQL query failed: %s\nquery was: %s", $@, $query);
823 Carp::croak($msg) if $Vend::Try;
824 return ($opt->{failure} || undef);
826 my @additions = grep length($_) == 2, keys %$opt;
828 next unless length $opt->{$_};
829 $spec->{$_} = $opt->{$_};
832 my @tabs = @{$spec->{rt} || $spec->{fi}};
835 my $tname = $s->[$CONFIG]{name};
836 if ($tabs[0] ne $tname) {
837 if("$tabs[0]_txt" eq $tname or "$tabs[0]_asc" eq $tname) {
838 $tabs[0] = $spec->{fi}[0] = $tname;
846 unless ($reroute = $Vend::Database{$tabs[0]}) {
847 $s->log_error("Table %s not found in databases", $tabs[0]);
848 return $opt->{failure} || undef;
851 #::logDebug("rerouting to $tabs[0]");
852 $opt->{STATEMENT} = $stmt;
853 $opt->{SPEC} = $spec;
854 return $s->query($opt, $text);
860 if($stmt->command() ne 'SELECT') {
861 if(defined $s and $s->[$CONFIG]{Read_only}) {
863 "Attempt to write read-only table %s",
868 $update = $stmt->command();
869 @vals = $stmt->row_values();
870 #::logDebug("row_values returned=" . ::uneval(\@vals));
874 @na = @{$spec->{rf}} if $spec->{rf};
876 #::logDebug("spec->{ml}=$spec->{ml} opt->{ml}=$opt->{ml}");
877 $spec->{ml} = $opt->{ml} if $opt->{ml};
878 $spec->{ml} ||= '1000';
879 $spec->{fn} = [$s->columns];
883 if($update eq 'INSERT') {
884 if(! $spec->{rf} or $spec->{rf}[0] eq '*') {
885 @update_fields = @{$spec->{fn}};
888 @update_fields = @{$spec->{rf}};
890 #::logDebug("update fields: " . uneval(\@update_fields));
892 $sub = $s->row_settor(@update_fields);
894 elsif($update eq 'UPDATE') {
895 @update_fields = @{$spec->{rf}};
896 #::logDebug("update fields: " . uneval(\@update_fields));
897 my $key = $s->config('KEY');
901 $s->set_slice($key, [@update_fields], \@_);
904 elsif($update eq 'DELETE') {
906 $sub = sub { delete_record($s, @_) };
909 @na = @{$spec->{fn}} if ! scalar(@na) || $na[0] eq '*';
914 #::logDebug("tabs='@tabs' columns='@na' vals='@vals' uf=@update_fields update=$update");
917 if (! defined $opt->{st} or "\L$opt->{st}" eq 'db' ) {
921 $search = new Vend::DbSearch;
922 #::logDebug("created DbSearch object: " . ::uneval_it($search));
925 $search = new Vend::TextSearch;
926 #::logDebug("created TextSearch object: " . ::uneval_it($search));
931 %nh = map { (lc $_, $i++) } @na;
933 %fh = map { ($_, $i++) } @{$spec->{fn}};
935 #::logDebug("field hash: " . Vend::Util::uneval_it(\%fh));
937 next unless defined $spec->{$_};
938 map { $_ = $fh{$_} } @{$spec->{$_}};
942 $opt->{row_count} = 1;
943 die "Reached update query without object"
945 #::logDebug("Update operation is $update, sub=$sub");
946 die "Bad row settor for columns @na"
948 if($update eq 'INSERT') {
950 $ref = [[ $vals[0] ]];
953 $ref = $search->array($spec);
955 #::logDebug("returned =" . uneval($_) . ", update values: " . uneval(\@vals));
956 $sub->($_->[0], @vals);
960 elsif ($opt->{hashref}) {
961 $ref = $Vend::Interpolate::Tmp->{$opt->{hashref}} = $search->hash($spec);
964 #::logDebug( " \$Vend::Interpolate::Tmp->{$opt->{arrayref}}");
965 $ref = $Vend::Interpolate::Tmp->{$opt->{arrayref} || ''}
966 = $search->array($spec);
967 $opt->{object} = $search;
968 $opt->{prefix} = 'sql' unless defined $opt->{prefix};
971 #::logDebug("search spec: " . Vend::Util::uneval($spec));
972 #::logDebug("name hash: " . Vend::Util::uneval(\%nh));
973 #::logDebug("ref returned: " . substr(Vend::Util::uneval($ref), 0, 100));
974 #::logDebug("opt is: " . Vend::Util::uneval($opt));
977 "MVSQL query failed for %s: %s\nquery was: %s",
982 $return = $opt->{failure} || undef;
985 if($opt->{search_label}) {
986 $::Instance->{SearchObject}{$opt->{search_label}} = {
988 mv_field_names => \@na,
992 if ($opt->{row_count}) {
993 my $rc = $ref ? scalar @$ref : 0;
994 return $rc unless $opt->{list};
996 @na = [ 'row_count' ];
997 %nh = ( 'rc' => 0, 'count' => 0, 'row_count' => 0 );
1000 return Vend::Interpolate::tag_sql_list($text, $ref, \%nh, $opt, \@na)
1002 return Vend::Interpolate::html_table($opt, $ref, \@na)
1004 return Vend::Util::uneval($ref)
1006 return wantarray ? ($ref, \%nh, \@na) : $ref;
1009 *import_quoted = *import_csv = \&import_ascii_delimited;
1013 '' => sub { $a cmp $b },
1014 none => sub { $a cmp $b },
1015 f => sub { (lc $a) cmp (lc $b) },
1016 fr => sub { (lc $b) cmp (lc $a) },
1017 n => sub { $a <=> $b },
1018 nr => sub { $b <=> $a },
1019 r => sub { $b cmp $a },
1020 rf => sub { (lc $b) cmp (lc $a) },
1021 rn => sub { $b <=> $a },
1026 my $function = shift;
1030 sub import_ascii_delimited {
1031 my ($infile, $options, $table_name) = @_;
1034 my $delimiter = quotemeta($options->{'delimiter'});
1036 if ($delimiter eq 'CSV') {
1040 elsif ($options->{CONTINUE}) {
1041 $format = uc $options->{CONTINUE};
1048 if($options->{PRELOAD}) {
1049 # do not preload if $infile is a scalar reference
1050 if ($options->{scalar_ref} or
1051 (-f $infile and $options->{PRELOAD_EMPTY_ONLY})) {
1052 # Do nothing, no preload
1055 $realfile = -f $infile ? $infile : '';
1056 $infile = $options->{PRELOAD};
1057 $infile = "$Global::VendRoot/$infile" if ! -f $infile;
1058 ($infile = $realfile, undef $realfile) if ! -f $infile;
1062 if(! defined $realfile) {
1063 if($options->{scalar_ref}){
1064 open(IN, '+<', $infile)
1065 or die errmsg("%s %s: %s\n", errmsg("open scalar reference"), *$infile, $!);
1066 # locking of scalar reference filehandles in unsupported
1069 open(IN, "+<$infile")
1070 or die errmsg("%s %s: %s\n", errmsg("open read/write"), $infile, $!);
1071 lockfile(\*IN, 1, 1)
1072 or die errmsg("%s %s: %s\n", errmsg("lock"), $infile, $!);
1077 open(IN, "<$infile")
1078 or die errmsg("%s %s: %s\n", errmsg("open"), $infile, $!);
1081 new_filehandle(\*IN);
1085 my $codere = '[\w-_#/.]+';
1088 my($field_count, @field_names);
1090 if($options->{hs}) {
1092 <IN> while $i++ < $options->{hs};
1094 if($options->{field_names}) {
1095 @field_names = @{$options->{field_names}};
1097 # This pulls COLUMN_DEF out of a field name
1098 # remains in ASCII file, though
1099 separate_definitions($options,\@field_names);
1101 if($options->{CONTINUE} eq 'NOTES') {
1102 $para_sep = $options->{NOTES_SEPARATOR} ||$options->{SEPARATOR} || "\f";
1105 $field_hash->{$_} = $idx++;
1107 $idx = $#field_names;
1113 @field_names = read_quoted_fields(\*IN);
1116 $field_names = <IN>;
1118 $field_names =~ s/\s+$// unless $format eq 'NOTES';
1119 @field_names = split(/$delimiter/, $field_names);
1122 # This pulls COLUMN_DEF out of a field name
1123 # remains in ASCII file, though
1124 separate_definitions($options,\@field_names);
1126 #::logDebug("field names: @field_names");
1127 if($format eq 'NOTES') {
1132 die "Only one notes field allowed in NOTES format.\n"
1138 $field_hash->{$_} = $idx++;
1142 @field_names = grep $_, @field_names;
1143 $para_sep =~ s/($codere)[\t ]*(.)/$2/;
1144 push(@field_names, ($1 || 'notes_field'));
1145 $idx = $#field_names;
1146 $para_sep = $options->{NOTES_SEPARATOR} || "\f";
1150 local($/) = "\n" . $para_sep ."\n"
1153 $field_count = scalar @field_names;
1157 if($options->{ObjectType}) {
1158 $out = &{"$options->{ObjectType}::create"}(
1159 $options->{ObjectType},
1166 $out = $options->{Object};
1170 die errmsg(q{No database object for table: %s
1172 Probable mismatch of Database directive to database type,
1173 for example calling DBI without proper modules or database
1183 my $excel_addl = '';
1185 if($options->{EXCEL}) {
1186 #Fix for quoted includes supplied by Larry Lesczynski
1187 $excel = <<'EndOfExcel';
1188 if(/"[^\t]*(?:,|"")/) {
1197 $excel_addl = <<'EndOfExcel';
1198 if(/"[^\t]*(?:,|"")/) {
1209 my @fh; # Array of file handles for sort
1210 my @fc; # Array of file handles for copy when symlink fails
1211 my @i; # Array of field names for sort
1212 my @o; # Array of sort options
1214 if($options->{INDEX} and ! $options->{NO_ASCII_INDEX}) {
1218 @f = @{$options->{INDEX}};
1222 if( $f =~ s/:(.*)//) {
1226 elsif (exists $options->{INDEX_OPTIONS}{$f}) {
1228 push @o, $options->{INDEX_OPTIONS}{$f};
1242 (pop(@o), next) unless $found;
1247 my $f_string = join ",", @i;
1249 for($i = 0; $i < @i; $i++) {
1251 $fh = new IO::File "> $infile.$i[$i]";
1252 die errmsg("%s %s: %s\n", errmsg("create"), "$infile.$i[$i]",
1253 $!) unless defined $fh;
1255 new_filehandle($fh);
1258 unlink "$infile.$n[$i]" if -l "$infile.$n[$i]";
1259 symlink "$infile.$i[$i]", "$infile.$n[$i]";
1261 push @fc, ["$infile.$i[$i]", "$infile.$n[$i]"]
1264 if($o[$i] =~ s/c//) {
1265 $index .= <<EndOfIndex;
1266 map { print { \$fh[$i] } "\$_\\t\$fields[0]\\n" } split /\\s*,\\s*/, \$fields[$fnum];
1269 elsif($o[$i] =~ s/s//) {
1270 $index .= <<EndOfIndex;
1271 map { print { \$fh[$i] } "\$_\\t\$fields[0]\\n" } split /\\s*;\\s*/, \$fields[$fnum];
1275 $index .= <<EndOfIndex;
1276 print { \$fh[$i] } "\$fields[$fnum]\\t\$fields[0]\\n";
1283 my $numeric_guess = '';
1284 my $numeric_clean = '';
1290 if($options->{GUESS_NUMERIC} and $options->{type} ne '8') {
1291 @possible = (0 .. $#field_names);
1292 @empty = map { 1 } (0 .. $#field_names);
1294 $numeric_guess = <<'EOF';
1296 ($empty[$_] = 0, next) if $fields[$_] =~ /^-?\d+\.?\d*$/;
1297 next if $empty[$_] && ! length($fields[$_]);
1300 $non_numeric{$_} = 1;
1303 $numeric_clean = <<'EOF';
1306 @possible = grep ! $non_numeric{$_}, @possible;
1313 NOTES => <<EndOfRoutine,
1317 s/\\r?\\n\\r?\\n((?s:.)*)//
1318 and \$fields[$idx] = \$1;
1320 while(s!($codere):[ \\t]*(.*)\\n?!!) {
1321 next unless defined \$field_hash->{\$1};
1322 \$fields[\$field_hash->{\$1}] = \$2;
1326 \$out->set_row(\@fields);
1331 LINE => <<EndOfRoutine,
1334 \$fields = \@fields = split(/$delimiter/, \$_, $field_count);
1336 push (\@fields, '') until \$fields++ >= $field_count;
1338 \$out->set_row(\@fields);
1343 CSV => <<EndOfRoutine,
1344 while (\@fields = read_quoted_fields(\\*IN)) {
1345 \$fields = scalar \@fields;
1347 push (\@fields, '') until \$fields++ >= $field_count;
1349 \$out->set_row(\@fields);
1354 NONE => <<EndOfRoutine,
1357 \$fields = \@fields = split(/$delimiter/, \$_, 99999);
1360 push (\@fields, '') until \$fields++ >= $field_count;
1362 \$out->set_row(\@fields);
1367 UNIX => <<EndOfRoutine,
1374 elsif (s/<<(\\w+)\$//) {
1377 \$line .= Vend::Config::read_here(\\*IN, \$mark);
1382 \$fields = \@fields = split(/$delimiter/, \$_, 99999);
1385 push (\@fields, '') until \$fields++ >= $field_count;
1387 \$out->set_row(\@fields);
1392 DITTO => <<EndOfRoutine,
1396 \$fields = \@addl = split /$delimiter/, \$_, 99999;
1400 for(\$i = 0; \$i < \@addl; \$i++) {
1401 \$fields[\$i] .= "\n\$addl[\$i]"
1402 if \$addl[\$i] ne '';
1406 \$fields = \@fields = split(/$delimiter/, \$_, 99999);
1409 push (\@fields, '') until \$fields++ >= $field_count;
1412 \$out->set_row(\@fields);
1419 eval $format{$format};
1420 die errmsg("%s import into %s failed: %s", $options->{name}, $options->{table}, $@) if $@;
1423 or die errmsg("%s %s: %s\n", errmsg("close"), $infile, $!);
1425 open(IN, "+<$realfile")
1427 errmsg("%s %s: %s\n", errmsg("open read/write"), $realfile, $!);
1428 lockfile(\*IN, 1, 1)
1429 or die errmsg("%s %s: %s\n", errmsg("lock"), $realfile, $!);
1430 new_filehandle(\*IN);
1432 eval $format{$format};
1433 die errmsg("%s %s: %s\n", errmsg("import"), $options->{name}, $!) if $@;
1435 elsif (! open(IN, ">$realfile") && new_filehandle(\*IN) ) {
1436 die errmsg("%s %s: %s\n", errmsg("create"), $realfile, $!);
1439 print IN join($options->{DELIMITER}, @field_names);
1447 my $ftest = Vend::Util::catfile($Vend::Cfg->{ScratchDir}, 'sort.test');
1448 my $cmd = "echo you_have_no_sort_but_we_will_cope | sort -f -n -o $ftest";
1450 $no_sort = 1 if ! -f $ftest;
1454 for ($i = 0; $i < @fh; $i++) {
1455 close $fh[$i] or die "close: $!";
1457 $o[$i] = "-$o[$i]" if $o[$i];
1458 $cmd = "sort $o[$i] -o $infile.$i[$i] $infile.$i[$i]";
1462 $fh = new IO::File "$infile.$i[$i]";
1463 new_filehandle($fh);
1464 my (@lines) = <$fh>;
1465 close $fh or die "close: $!";
1466 my $option = $o[$i] || 'none';
1467 @lines = sort { &{$Sort{$option}} } @lines;
1468 $fh = new IO::File ">$infile.$i[$i]";
1469 new_filehandle($fh);
1471 close $fh or die "close: $!";
1478 File::Copy::copy(@{$_});
1482 unless($options->{no_commit}) {
1483 $out->commit() if $out->config('HAS_TRANSACTIONS');
1485 delete $out->[$CONFIG]{Clean_start};
1486 delete $out->[$CONFIG]{_Dirty};
1487 unless($options->{scalar_ref}){
1488 unlockfile(\*IN) or die "unlock\n";
1491 my $dot = $out->[$CONFIG]{HIDE_AUTO_FILES} ? '.' : '';
1492 if($numeric_guess) {
1493 my $fn = Vend::Util::catfile($out->[$CONFIG]{DIR}, "$dot$out->[$CONFIG]{file}");
1494 Vend::Util::writefile(
1496 join " ", map { $field_names[$_] } @possible,
1502 sub import_from_ic_db {
1503 my ($infile, $options, $table_name) = @_;
1505 my $tname = $options->{MIRROR}
1507 "Memory mirror table not specified for table %s.",
1510 #::logDebug("Importing mirrored $table_name from $tname");
1512 $Vend::Database{$tname} =
1513 Vend::Data::import_database($Vend::Cfg->{Database}{$tname})
1514 unless $Vend::Database{$tname};
1516 my $idb = Vend::Data::database_exists_ref($tname)
1518 "Memory mirror table %s does not exist (yet) to create mirror %s.\n",
1523 my @field_names = $idb->columns;
1527 if($options->{ObjectType}) {
1529 $odb = &{"$options->{ObjectType}::create"}(
1530 $options->{ObjectType},
1537 $odb = $options->{Object};
1540 #::logDebug("idb=$idb odb=$odb");
1543 while($f = $idb->each_nokey($options->{MIRROR_QUAL})) {
1544 #::logDebug("importing key=$f->[0]");
1551 "Problem with mirror import from source %s to target %s\n",
1557 $odb->[$CONFIG]{Mirror_complete} = 1;
1558 delete $odb->[$CONFIG]{Clean_start};
1564 sub read_quoted_fields {
1565 my ($filehandle) = @_;
1567 while(<$filehandle>) {
1568 s/[\r\n\cZ]+$//g; # ms-dos cruft
1569 next if m/^[$white]*$/o; # skip blank lines
1570 my @f = parse($_, $.);
1571 #::logDebug("read: '" . join("','", @f) . "'");
1572 return parse($_, $.);
1579 my $linenum = $_[1];
1585 if (m# \A ([$white]+) (.*) #ox) { }
1586 elsif (m# \A (,[$white]*) (.*) #ox) {
1587 push @a, '' if $expect;
1590 elsif (m# \A ([^",$white] (?:[$white]* [^,$white]+)*) (.*) #ox) {
1594 elsif (m# \A " ((?:[^"] | (?:""))*) " (?!") (.*) #x) {
1595 ($x = $1) =~ s/""/"/g;
1599 elsif (m# \A " #x) {
1600 die "Unterminated quote at line $linenum\n";
1602 else { die "Can't happen: '$_'" }
1605 $expect and push @a, '';
1614 return shift(@_)->[$CONFIG]{last_error};
1618 my ($s, $tpl, @args) = @_;
1619 if($tpl =~ /^(prepare|execute)$/) {
1621 $tpl = "Statement $tpl failed: %s";
1623 elsif (@args == 1) {
1624 $tpl = "Statement $tpl failed: %s\nQuery was: %s";
1627 $tpl = "Statement $tpl failed: %s\nQuery was: %s";
1628 $tpl .= "\nAdditional: %s" for (2 .. scalar(@args));
1630 unshift @args, $DBI::errstr;
1632 my $msg = errmsg($tpl, @args);
1633 my $ekey = 'table ' . $s->[$CONFIG]{name};
1634 my $cfg = $s->[$CONFIG];
1635 unless(defined $cfg->{LOG_ERROR_CATALOG} and ! $cfg->{LOG_ERROR_CATALOG}) {
1638 if($cfg->{LOG_ERROR_GLOBAL}) {
1641 if($Vend::admin or ! defined($cfg->{LOG_ERROR_SESSION}) or $cfg->{LOG_ERROR_SESSION}) {
1642 $Vend::Session->{errors} = {} unless CORE::ref($Vend::Session->{errors}) eq 'HASH';
1643 $Vend::Session->{errors}{$ekey} = $msg;
1645 die $msg if $cfg->{DIE_ERROR};
1646 return $cfg->{last_error} = $msg;
1649 sub new_filehandle {
1651 binmode($fh, ":utf8") if $::Variable->{MV_UTF8} || $Global::Variable->{MV_UTF8};