X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.db-test.git;a=blobdiff_plain;f=yarrg%2Fdb-idempotent-populate;h=aa8026272f3727f77ccd7c740eb7da43040fe110;hp=0ecde9e51ab82ee1bdffc4c71471fee19109c568;hb=ee1e57fa0fab5d840206d73a3968fd59d7fa7127;hpb=59316f0dcddd4e5d15e47dfde36f513e1685c4ae diff --git a/yarrg/db-idempotent-populate b/yarrg/db-idempotent-populate index 0ecde9e..aa80262 100755 --- a/yarrg/db-idempotent-populate +++ b/yarrg/db-idempotent-populate @@ -36,9 +36,17 @@ use DBI; use Commods; use CommodsDatabase; +my $trace; +if (@ARGV and $ARGV[0] eq '-D') { + $trace=1; + shift @ARGV; +} + @ARGV==1 or die; my ($oceanname) = @ARGV; +$|=1; + #---------- setup ---------- parse_info_serverside(); @@ -47,7 +55,133 @@ db_setocean($oceanname); db_writer(); db_connect(); -#---------- schema ---------- +$dbh->trace(1) if $trace; + + +#---------- schema update code ---------- + +our @need_compact; + +sub table ($$) { + my ($table, $fields) = @_; + table_maycompact($table,undef,undef,$fields); +} + +sub table_maycompact ($$$$) { + my ($table, $cpact_idfield, $cpact_needupdates, $fields) = @_; + + #----- parse $fields ----- + + my @want_fields; + my @want_field_specs; + my %want_field_specs; + + foreach my $fspec (split /\n/, $fields) { + next unless $fspec =~ m/\S/; + if ($fspec =~ m/^\s*\+/) { + push @want_field_specs, "\t".$'; + next; + } elsif ($fspec =~ m/^\s*(\w+)(\s+)(\w.*\S)\s*$/) { + my ($f,$spaces,$rhs) = ($1,$2,$3); + my $spec= "\t".$f.$spaces.$rhs; + push @want_fields, $f; + push @want_field_specs, $spec; + $want_field_specs{$f}= $spec; + } else { + die "$table $fspec ?"; + } + } + + my $want_field_specs= join ",\n", @want_field_specs; + + #----- ensure table exists ----- + + db_doall(<prepare(<execute($table,$table); + my ($sql)= $autoinc->fetchrow_array(); + die unless defined $sql; + push @need_recreate, 'remove autoinc' + if $sql =~ m/\bautoinc/i; + } + + #----- check whether we need to add fields ----- + + my $check= $dbh->prepare("SELECT * FROM $table LIMIT 1"); + $check->execute(); + my %have_fields; + $have_fields{$_}=1 foreach @{ $check->{NAME_lc} }; + $check->finish(); + + my @have_fields; + my @have_field_specs; + + foreach my $f (@want_fields) { + if ($have_fields{$f}) { + push @have_fields, $f; + push @have_field_specs, $want_field_specs{$f}; + } else { + push @need_recreate, "field $f"; + } + } + + #----- Do we need to recreate ? ----- + return unless @need_recreate; + # yes: + + print " Recreating $table: ", join('; ',@need_recreate); + + my $have_fields= join ',', @have_fields; + my $have_field_specs= join ",\n", @have_field_specs; + + db_doall(< $table, + Id => $cpact_idfield, + Updates => $cpact_needupdates, + Fields => [ @want_fields ], + FieldSpecs => $want_field_specs + }; +} + + +#---------- actual schema ---------- foreach my $bs (qw(buy sell)) { db_doall(<commit; #---------- commodity list ---------- +sub commodsortkey ($) { + my ($commod) = @_; + my $ordval= $commods{$commod}{Ordval}; + return sprintf "B %20d", $ordval if defined $ordval; + return sprintf "A %s", $commod; +} + { - my $insert= $dbh->prepare(<<'END') + my $insert= $dbh->prepare(<<'END'); INSERT OR IGNORE INTO commods (unitmass, unitvolume, commodname) VALUES (?,?,?); END - ; - my $update= $dbh->prepare(<<'END') + my $setsizes= $dbh->prepare(<<'END'); UPDATE commods SET unitmass = ?, unitvolume = ? WHERE commodname = ? END - ; - foreach my $commod (sort keys %commods) { + my $setordval= $dbh->prepare(<<'END'); + UPDATE commods + SET ordval = ? + WHERE commodname = ? +END + my $setclass= $dbh->prepare(<<'END'); + UPDATE commods + SET commodclass = ? + WHERE commodname = ? +END + my $setinclass= $dbh->prepare(<<'END'); + UPDATE commods + SET inclass = ? + WHERE commodname = ? +END + my %incl; + foreach my $commod (sort { + commodsortkey($a) cmp commodsortkey($b) + } keys %commods) { my $c= $commods{$commod}; die "no mass for $commod" unless defined $c->{Mass}; - die "no colume for $commod" unless defined $c->{Volume}; + die "no volume for $commod" unless defined $c->{Volume}; + my @qa= ($c->{Mass}, $c->{Volume}, $commod); $insert->execute(@qa); - $update->execute(@qa); + $setsizes->execute(@qa); + $setordval->execute($c->{Ordval} || 0, $commod); + my $cl= $c->{Class}; + $setclass->execute($cl, $commod); + + if (defined $c->{Ordval} and defined $cl) { + $incl{$cl}++; + $setinclass->execute($incl{$cl}, $commod); + } elsif (defined $cl) { + $incl{$cl} += 0; + } + } + db_doall(<prepare(<<'END'); + INSERT INTO commodclasses + (commodclass, size) + VALUES (?,?) +END + foreach my $cl (sort keys %incl) { + $addclass->execute($cl, $incl{$cl}); } - $dbh->commit; + + my $search= $dbh->prepare(<<'END'); + SELECT commodname,commodid FROM commods; +END + my %check; + foreach my $bs (qw(buy sell)) { + $check{$bs}= $dbh->prepare(<prepare(<<'END'); + DELETE FROM commods WHERE commodid = ? +END + $search->execute(); + my $any=0; + while (my $row= $search->fetchrow_hashref()) { + next if defined $commods{$row->{'commodname'}}; + print $any++ ? '; ' : " Dropping old commodities: ", + $row->{'commodname'}; + foreach my $bs (qw(buy sell)) { + $check{$bs}->execute($row->{'commodid'}); + my $problem= $check{$bs}->fetchrow_hashref(); + if ($problem) { + print "\n"; + die <{'commodid'} + $row->{'commodname'} + but + $bs + $problem->{'islandname'} + $problem->{'stallname'} + $problem->{'qty'} at $problem->{'price'} +END + } + } + $delete->execute($row->{'commodid'}); + } + print ".\n" if $any; + db_check_referential_integrity(); } + #---------- vessel types ---------- { my $idempotent= $dbh->prepare(<<'END') @@ -158,5 +398,72 @@ END my @qa= ($name, $shotdamage, map { $v->{$_} } qw(Mass Volume)); $idempotent->execute(@qa); } - $dbh->commit; +} + + +#---------- compact IDs ---------- + +sub getminmax ($$$) { + my ($tab,$minmax,$f) = @_; + my $sth= $dbh->prepare("SELECT $minmax($f) FROM $tab"); + $sth->execute(); + my ($val)= $sth->fetchrow_array(); + return defined($val) ? $val : '?'; +} + +foreach my $cp (@need_compact) { + print " Compacting $cp->{Table}"; + my $tab= $cp->{Table}; + my $id= $cp->{Id}; + my $tmp_field_specs= $cp->{FieldSpecs}; + my $fields= join ',', @{$cp->{Fields}}; + $tmp_field_specs =~ s/\bprimary key\b/UNIQUE/i or + die "$tab $tmp_field_specs ?"; + db_doall(<1..%d:", + $cp->{Id}, + getminmax($tab,'min',$id), + $oldmax, + getminmax("aside_$tab",'max',"new_$id")); + my @updates= @{ $cp->{Updates} }; + while (@updates) { + my $utabs= shift @updates; + my $ufields= shift @updates; + foreach my $utab (@$utabs) { + printf(" %s",$utab); + my $fh= '.'; + foreach my $ufield (@$ufields) { + printf("%s%s",$fh,$ufield); $fh=','; + db_doall(<{AutoCommit} = 1; + $dbh->do('VACUUM'); }