-
Notifications
You must be signed in to change notification settings - Fork 12
/
load-units.pl
executable file
·137 lines (116 loc) · 4.17 KB
/
load-units.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/perl
# load definitions.units.patched into the unit_prefixes and unit_units tables
# existing data is wiped!
use utf8;
use open ":std", ":encoding(UTF-8)";
use strict;
use warnings;
use DBD::Pg;
my $file = "definitions.units.patched";
open F, "< :encoding(utf-8)", $file or die "$file: $!";
my $dbh = DBI->connect("dbi:Pg:", '', '',
{AutoCommit => 1, PrintError => 0, RaiseError => 0}
) || die "PG connection failed";
$dbh->do("SET synchronous_commit = off");
$dbh->do("TRUNCATE unit_prefixes, unit_units");
$dbh->do("SET client_min_messages = 'error'");
$dbh->do("ALTER TABLE unit_prefixes ADD COLUMN IF NOT EXISTS ordering serial"); # add temp column to preserve load ordering for dumping
$dbh->do("ALTER TABLE unit_units ADD COLUMN IF NOT EXISTS ordering serial");
$dbh->do("RESET client_min_messages");
my $skip_british = 0;
my @todo;
my $continued = '';
<F>; # skip over initial line with BOM
while (<F>) {
# skip over locale specific parts
$skip_british = 1 if /^!var UNITS_ENGLISH GB/;
$skip_british = 0 if /^!endvar/;
next if ($skip_british);
if (/\s*(.*)\\$/) {
$continued .= $1;
next;
} elsif ($continued) {
s/^\s*//;
$_ = "$continued$_";
$continued = '';
}
s/#.*//;
s/\s+$//;
next if /^\s*$/; # skip emtpy lines
next if /^!/; # skip pragmas
next if /^\+/; # skip units from non-SI systems
#next if /^[0-9]/; # skip over table contents
#next if /^\s/; # skip over table contents/continued lines
unless (/^(\S+)\s+(.*)/) {
print "unknown line $_\n";
exit 1;
}
my ($unit, $def) = ($1, $2);
next if ($unit =~ /[(\[]/); # skip functions and table definitions
my $is_prefix = ($unit =~ s/-$//); # it's a prefix if it ends with '-'
$def = $unit if ($def eq '!'); # base unit
$def = 1 if ($def eq '!dimensionless');
my $u = { unit => $unit, def => $def, is_prefix => $is_prefix };
$u->{is_base} = ($u->{unit} eq $u->{def});
# shifted units
if ($unit =~ /^(℃|°C|degC|degcelsius)$/) {
$u->{shift} = '273.15'; # 0 °C in K
} elsif ($unit =~ /^(℉|°F|degF|degfahrenheit)$/) {
$u->{shift} = '255.3722222222222222'; # 0 °F in K
} elsif ($unit =~ /^(degreaumur)$/) {
$u->{shift} = '273.15'; # 0 °Ré in K
}
push @todo, $u;
}
# try repeatedly to insert units, unfortunately the input data contains some
# forward references
my ($n_todo, $new_n_todo);
do {
$n_todo = @todo;
print "$n_todo units left to try ...\n";
my @new_todo;
foreach my $u (@todo) {
my ($unit, $def, $shift, $is_prefix) = ($u->{unit}, $u->{def}, $u->{shift}, $u->{is_prefix});
if ($is_prefix) {
my $ret = $dbh->do("INSERT INTO unit_prefixes (prefix, factor, definition, dump) VALUES (?, value(?::unit), ?, NULL)",
undef,
$unit, $def, $def);
next if defined $ret;
# see if the prefix is defined in terms of another prefix
# (we can't simply inject all prefixes as units because conflicts exist, e.g. on 'T')
$ret = $dbh->do("INSERT INTO unit_prefixes (prefix, factor, definition, dump) SELECT ?, factor, ?, NULL FROM unit_prefixes WHERE prefix = ?",
undef,
$unit, $def, $def);
next if defined $ret and $ret > 0;
} else {
my ($is_hashed) = $dbh->selectrow_array("SELECT unit_is_hashed(?)", undef, $unit);
if ($is_hashed and not $u->{is_base}) {
# if the unit we are defining now was successfully used before,
# something went wrong. It indicates that the new unit could
# also be parsed as prefix-otherknownunit, e.g. "ft" vs "f-t"
print "Unit $unit has already been used before being defined. Bad.\n";
exit 1;
}
my $ret = $dbh->do("INSERT INTO unit_units (name, unit, shift, definition, dump) VALUES (?, ?, ?, ?, NULL)",
undef,
$unit, $def, $shift, $def);
next if defined $ret;
$u->{error} = $dbh->errstr;
}
push @new_todo, $u;
}
$new_n_todo = @new_todo;
@todo = @new_todo;
} while ($n_todo != $new_n_todo);
print "$new_n_todo units not inserted:\n";
open my $unres, '>', "definitions.unresolved";
foreach my $u (@todo) {
if ($u->{is_prefix}) {
print "Prefix $u->{unit}: $u->{def}\n";
} else {
print $unres "$u->{unit}\t$u->{def}\n";
next if ($u->{error} =~ /dollar|euro|pence|quid|shilling/); # skip currencies so we can see the rest better
print "$u->{unit}: $u->{def} ($u->{error})\n";
}
}
close $unres;