File Coverage

blib/lib/Hailo/Storage/Schema.pm
Criterion Covered Total %
statement 59 59 100.0
branch 7 14 50.0
condition n/a
subroutine 4 4 100.0
pod 0 2 0.0
total 70 79 88.6


line stmt bran cond sub pod time code
1             package Hailo::Storage::Schema;
2             our $AUTHORITY = 'cpan:AVAR';
3             $Hailo::Storage::Schema::VERSION = '0.75';
4 29     29   373 use v5.10.0;
  29         110  
5 29     29   156 use strict;
  29         72  
  29         24870  
6              
7             ## Soup to spawn the database itself / create statement handles
8             sub deploy {
9 42     42 0 155 my (undef, $dbd, $dbh, $order) = @_;
10 42         196 my @orders = (0 .. $order-1);
11              
12 42         151 my $int_primary_key = "INTEGER PRIMARY KEY AUTOINCREMENT";
13 42 50       164 $int_primary_key = "INTEGER PRIMARY KEY AUTO_INCREMENT" if $dbd eq "mysql";
14 42 50       123 $int_primary_key = "SERIAL UNIQUE" if $dbd eq "Pg";
15              
16 42         90 my $text = 'TEXT';
17 42 50       157 $text = 'VARCHAR(255)' if $dbd eq 'mysql';
18              
19 42         96 my $text_primary = 'TEXT NOT NULL PRIMARY KEY';
20 42 50       126 $text_primary = 'TEXT NOT NULL' if $dbd eq 'mysql';
21              
22 42         91 my @tables;
23              
24 42         171 push @tables => <<"TABLE";
25             CREATE TABLE info (
26             attribute $text_primary,
27             text TEXT NOT NULL
28             );
29             TABLE
30              
31 42         190 push @tables => <<"TABLE";
32             CREATE TABLE token (
33             id $int_primary_key,
34             spacing INTEGER NOT NULL,
35             text $text NOT NULL,
36             count INTEGER NOT NULL
37             );
38             TABLE
39              
40 42         140 my $token_n = join ",\n ", map { "token${_}_id INTEGER NOT NULL REFERENCES token (id)" } @orders;
  85         408  
41 42         184 push @tables => <<"TABLE";
42             CREATE TABLE expr (
43             id $int_primary_key,
44             $token_n
45             );
46             TABLE
47              
48 42         137 push @tables => <<"TABLE";
49             CREATE TABLE next_token (
50             id $int_primary_key,
51             expr_id INTEGER NOT NULL REFERENCES expr (id),
52             token_id INTEGER NOT NULL REFERENCES token (id),
53             count INTEGER NOT NULL
54             );
55             TABLE
56              
57 42         148 push @tables => <<"TABLE";
58             CREATE TABLE prev_token (
59             id $int_primary_key,
60             expr_id INTEGER NOT NULL REFERENCES expr (id),
61             token_id INTEGER NOT NULL REFERENCES token (id),
62             count INTEGER NOT NULL
63             );
64             TABLE
65              
66 42         160 for my $i (@orders) {
67 85         282 push @tables => "CREATE INDEX expr_token${i}_id on expr (token${i}_id);"
68             }
69              
70 42         130 my $columns = join(', ', map { "token${_}_id" } @orders);
  85         262  
71 42         146 push @tables => "CREATE INDEX expr_token_ids on expr ($columns);";
72              
73 42         98 push @tables => 'CREATE INDEX token_text on token (text);';
74 42         128 push @tables => 'CREATE INDEX next_token_expr_id ON next_token (expr_id);';
75 42         90 push @tables => 'CREATE INDEX prev_token_expr_id ON prev_token (expr_id);';
76              
77              
78 42         118 for (@tables) {
79 463         85302 $dbh->do($_);
80             }
81              
82 42         7441 return;
83             }
84              
85             # create statement handle objects
86             sub sth {
87 50     50 0 262 my (undef, $dbd, $dbh, $order) = @_;
88 50         211 my @orders = (0 .. $order-1);
89 50         174 my @columns = map { "token${_}_id" } 0 .. $order-1;
  103         379  
90 50         204 my $columns = join(', ', @columns);
91 50         224 my @ids = join(', ', ('?') x @columns);
92 50         181 my $ids = join(', ', @ids);
93              
94 50         106 my $q_rand = 'RANDOM()';
95 50 50       172 $q_rand = 'RAND()' if $dbd eq 'mysql';
96              
97 50         150 my $q_rand_id = "(abs($q_rand) % (SELECT max(id) FROM expr))";
98 50 50       171 $q_rand_id = "(random()*id+1)::int" if $dbd eq 'Pg';
99              
100             my %state = (
101             set_info => qq[INSERT INTO info (attribute, text) VALUES (?, ?);],
102              
103             random_expr => qq[SELECT * FROM expr WHERE id >= $q_rand_id LIMIT 1;],
104             token_resolve => qq[SELECT id, count FROM token WHERE spacing = ? AND text = ?;],
105             token_id => qq[SELECT id FROM token WHERE spacing = ? AND text = ?;],
106             token_info => qq[SELECT spacing, text FROM token WHERE id = ?;],
107             token_similar => qq[SELECT id, spacing, count FROM token WHERE text = ? ORDER BY $q_rand LIMIT 1;] ,
108             add_token => qq[INSERT INTO token (spacing, text, count) VALUES (?, ?, 0)],
109             inc_token_count => qq[UPDATE token SET count = count + ? WHERE id = ?],
110              
111             # ->stats()
112             expr_total => qq[SELECT COUNT(*) FROM expr;],
113             token_total => qq[SELECT COUNT(*) FROM token;],
114             prev_total => qq[SELECT COUNT(*) FROM prev_token;],
115             next_total => qq[SELECT COUNT(*) FROM next_token;],
116              
117             # Defaults, overriden in SQLite
118             last_expr_rowid => qq[SELECT id FROM expr ORDER BY id DESC LIMIT 1;],
119             last_token_rowid => qq[SELECT id FROM token ORDER BY id DESC LIMIT 1;],
120              
121             token_count => qq[SELECT count FROM token WHERE id = ?;],
122              
123             add_expr => qq[INSERT INTO expr ($columns) VALUES ($ids)],
124 50         358 expr_id => qq[SELECT id FROM expr WHERE ] . join(' AND ', map { "token${_}_id = ?" } @orders),
  103         1114  
125             );
126              
127 50         201 for my $table (qw(next_token prev_token)) {
128             $state{"${table}_links"} = qq[SELECT SUM(count) FROM $table WHERE expr_id = ?;],
129             $state{"${table}_count"} = qq[SELECT count FROM $table WHERE expr_id = ? AND token_id = ?;],
130             $state{"${table}_inc"} = qq[UPDATE $table SET count = count + ? WHERE expr_id = ? AND token_id = ?],
131             $state{"${table}_add"} = qq[INSERT INTO $table (expr_id, token_id, count) VALUES (?, ?, ?);],
132 100         1077 $state{"${table}_get"} = qq[SELECT token_id, count FROM $table WHERE expr_id = ?;],
133             }
134              
135 50         162 for (@orders) {
136 103         449 $state{"expr_by_token${_}_id"} = qq[SELECT * FROM expr WHERE token${_}_id = ? ORDER BY $q_rand LIMIT 1;];
137             }
138              
139             # DBD specific queries / optimizations / munging
140 50 50       230 if ($dbd eq 'SQLite') {
141             # Optimize these for SQLite
142 50         143 $state{expr_total} = qq[SELECT seq FROM sqlite_sequence WHERE name = 'expr';];
143 50         133 $state{token_total} = qq[SELECT seq FROM sqlite_sequence WHERE name = 'token';];
144 50         105 $state{prev_total} = qq[SELECT seq FROM sqlite_sequence WHERE name = 'prev_token';];
145 50         110 $state{next_total} = qq[SELECT seq FROM sqlite_sequence WHERE name = 'next_token';];
146             }
147              
148             # Sort to make error output easier to read if this fails. The
149             # order doesn't matter.
150 50         982 my @queries = sort keys %state;
151 50         207 my %sth = map { $_ => $dbh->prepare($state{$_}) } @queries;
  1453         91057  
152              
153 50         5650 return \%sth;
154             }
155              
156             1;
157              
158             =head1 NAME
159              
160             Hailo::Storage::Schema - Deploy the database schema Hailo uses
161              
162             =head1 DESCRIPTION
163              
164             Implements functions to create the database schema and prepared
165             database queries L<Hailo::Storage> needs.
166              
167             This class is internal to Hailo and has no public interface.
168              
169             =head1 AUTHOR
170              
171             E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
172              
173             =head1 LICENSE AND COPYRIGHT
174              
175             Copyright 2010 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason
176              
177             This program is free software, you can redistribute it and/or modify
178             it under the same terms as Perl itself.
179              
180             =cut