File Coverage

blib/lib/DBIx/QuickORM/Dialect/SQLite.pm
Criterion Covered Total %
statement 182 193 94.3
branch 19 22 86.3
condition 29 49 59.1
subroutine 30 41 73.1
pod 0 28 0.0
total 260 333 78.0


line stmt bran cond sub pod time code
1             package DBIx::QuickORM::Dialect::SQLite;
2 24     24   207 use strict;
  24         57  
  24         6993  
3 24     24   141 use warnings;
  24         53  
  24         6753  
4              
5             our $VERSION = '0.000019';
6              
7 24     24   1098 use DBD::SQLite 1.0;
  24         16213  
  24         5148  
8              
9 24     24   158 use Carp qw/croak/;
  24         66  
  24         2066  
10 24     24   194 use DBIx::QuickORM::Affinity qw/affinity_from_type/;
  24         51  
  24         2248  
11 24     24   166 use DBIx::QuickORM::Util qw/column_key/;
  24         101  
  24         486  
12              
13 24     24   1193 use parent 'DBIx::QuickORM::Dialect';
  24         58  
  24         260  
14 24     24   1626 use DBIx::QuickORM::Util::HashBase;
  24         13108  
  24         219  
15              
16 24     24   31243 use DBIx::QuickORM::Schema;
  24         108  
  24         1034  
17 24     24   27597 use DBIx::QuickORM::Schema::Table;
  24         124  
  24         1195  
18 24     24   45429 use DBIx::QuickORM::Schema::Table::Column;
  24         132  
  24         949  
19 24     24   39387 use DBIx::QuickORM::Schema::View;
  24         98  
  24         100543  
20              
21 0     0 0 0 sub fallback_ver { 1 }
22 0     0 0 0 sub oldest_ver { 1 }
23 0     0 0 0 sub latest_ver { 1 }
24 2     2 0 10 sub dbi_driver { 'DBD::SQLite' }
25 2     2 0 76 sub dialect_name { 'SQLite' }
26              
27 0     0 0 0 sub supports_returning_update { 1 }
28 81     81 0 269 sub supports_returning_insert { 1 }
29 4     4 0 10 sub supports_returning_delete { 1 }
30              
31 2     2 0 18 sub async_supported { 0 }
32 0     0 0 0 sub async_cancel_supported { 0 }
33 0     0 0 0 sub async_prepare_args { croak "Dialect '" . $_[0]->dialect_name . "' does not support async queries" }
34 0     0 0 0 sub async_ready { croak "Dialect '" . $_[0]->dialect_name . "' does not support async queries" }
35 0     0 0 0 sub async_result { croak "Dialect '" . $_[0]->dialect_name . "' does not support async queries" }
36 0     0 0 0 sub async_cancel { croak "Dialect '" . $_[0]->dialect_name . "' does not support async queries" }
37              
38 23   33 23 0 55 sub start_txn { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->begin_work }
  23         93  
  23         226  
39 15   33 15 0 42 sub commit_txn { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->commit }
  15         115  
  15         175596  
40 8   33 8 0 21 sub rollback_txn { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->rollback }
  8         52  
  8         712  
41 4   33 4 0 46 sub create_savepoint { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->do("SAVEPOINT $p{savepoint}") }
  4         24  
  4         46  
42 3   33 3 0 8 sub commit_savepoint { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->do("RELEASE SAVEPOINT $p{savepoint}") }
  3         17  
  3         30  
43 1   33 1 0 4 sub rollback_savepoint { my ($s, %p) = @_; my $dbh = $p{dbh} // $s->dbh; $dbh->do("ROLLBACK TO SAVEPOINT $p{savepoint}") }
  1         7  
  1         12  
44              
45 0     0 0 0 sub version_search { 0 }
46              
47 0     0 0 0 sub db_version { DBD::SQLite->VERSION }
48              
49             sub dsn {
50 2     2 0 5 my $self_or_class = shift;
51 2         7 my ($db) = @_;
52              
53 2   33     17 my $driver = $db->dbi_driver // $self_or_class->dbi_driver;
54 2         12 $driver =~ s/^DBD:://;
55              
56 2         10 my $db_name = $db->db_name;
57              
58 2         42 return "dbi:${driver}:dbname=${db_name}";
59             }
60              
61             ###############################################################################
62             # {{{ Schema Builder Code
63             ###############################################################################
64              
65              
66             my %TABLE_TYPES = (
67             'table' => 'DBIx::QuickORM::Schema::Table',
68             'view' => 'DBIx::QuickORM::Schema::View',
69             );
70              
71             sub build_tables_from_db {
72 22     22 0 85 my $self = shift;
73 22         116 my %params = @_;
74              
75 22         73 my $dbh = $self->{+DBH};
76              
77 22         90 my @queries = (
78             "SELECT name, type, 0 FROM sqlite_schema WHERE type IN ('table', 'view')",
79             "SELECT name, type, 1 FROM sqlite_temp_schema WHERE type IN ('table', 'view')",
80             );
81              
82 22         60 my %tables;
83              
84 22         95 for my $q (@queries) {
85 44         639 my $sth = $dbh->prepare($q);
86 44         13716 $sth->execute();
87              
88 44         1178 while (my ($tname, $type, $temp) = $sth->fetchrow_array) {
89 51 100       664 next if $tname =~ m/^sqlite_/;
90              
91 30         254 my $table = {name => $tname, db_name => $tname, is_temp => $temp};
92 30   50     194 my $class = $TABLE_TYPES{lc($type)} // 'DBIx::QuickORM::Schema::Table';
93 30         555 $params{autofill}->hook(pre_table => {table => $table, class => \$class});
94              
95 30         738 $table->{columns} = $self->build_columns_from_db($tname, %params);
96 30         360 $params{autofill}->hook(columns => {columns => $table->{columns}, table => $table});
97              
98 30         173 $table->{indexes} = $self->build_indexes_from_db($tname, %params);
99 30         269 $params{autofill}->hook(indexes => {indexes => $table->{indexes}, table => $table});
100              
101 30         171 @{$table}{qw/primary_key unique _links/} = $self->build_table_keys_from_db($tname, %params);
  30         230  
102              
103 30         275 $params{autofill}->hook(post_table => {table => $table, class => \$class});
104              
105 30         361 $tables{$tname} = $class->new($table);
106 30         246 $params{autofill}->hook(table => {table => $tables{$tname}});
107             }
108             }
109              
110 22         190 return \%tables;
111             }
112              
113             sub build_table_keys_from_db {
114 30     30 0 97 my $self = shift;
115 30         115 my ($table, %params) = @_;
116              
117 30         82 my $dbh = $self->{+DBH};
118              
119 30         237 my $sth = $dbh->prepare(<<" EOT");
120             SELECT il.name AS grp,
121             origin AS type,
122             ii.name AS column
123             FROM pragma_index_list(?) AS il,
124             pragma_index_info(il.name) AS ii
125             ORDER BY seq, il.name, seqno, cid
126             EOT
127              
128 30         4562 $sth->execute($table);
129              
130 30         153 my ($pk, %unique, @links);
131              
132 30         0 my %index;
133 30         956 while (my $row = $sth->fetchrow_hashref()) {
134 29   100     296 my $idx = $index{$row->{grp}} //= {};
135 29         97 $idx->{type} = $row->{type};
136 29   100     81 push @{$idx->{cols} //= []} => $row->{column};
  29         716  
137             }
138              
139 30         197 for my $idx (sort values %index) {
140 25         71 $unique{column_key(@{$idx->{cols}})} = $idx->{cols};
  25         221  
141 25 100       162 $pk = $idx->{cols} if $idx->{type} eq 'pk';
142             }
143              
144 30 100 66     189 unless ($pk && @$pk) {
145 28         194 my @found = $self->_primary_key($table);
146              
147 28 100       131 if (@found) {
148 26         71 $pk = \@found;
149 26         149 $unique{column_key(@found)} = \@found;
150             }
151             else {
152 2         7 $pk = undef;
153             }
154             }
155              
156 30         118 %index = ();
157 30         228 $sth = $dbh->prepare("SELECT `id`, `table`, `from`, `to` FROM pragma_foreign_key_list(?) order by id, seq");
158 30         9327 $sth->execute($table);
159 30         726 while (my $row = $sth->fetchrow_hashref()) {
160 8   100     75 my $idx = $index{$row->{id}} //= {};
161              
162 8   100     20 push @{$idx->{columns} //= []} => $row->{from};
  8         57  
163              
164 8   66     50 $idx->{ftable} //= $row->{table};
165 8   100     15 push @{$idx->{fcolumns} //= []} => $row->{to};
  8         243  
166             }
167              
168 30         165 @links = map { [[$table, $_->{columns}], [$_->{ftable}, $_->{fcolumns}]] } values %index;
  7         48  
169              
170 30         282 $params{autofill}->hook(links => {links => \@links, table_name => $table});
171 30         216 $params{autofill}->hook(primary_key => {primary_key => $pk, table_name => $table});
172 30         184 $params{autofill}->hook(unique_keys => {unique_keys => \%unique, table_name => $table});
173              
174 30         614 return($pk, \%unique, \@links);
175             }
176              
177             sub table_has_autoinc {
178 30     30 0 79 my $self = shift;
179 30         113 my ($table) = @_;
180              
181 30 50       188 croak "A table name is required" unless $table;
182 30         97 my $dbh = $self->{+DBH};
183              
184 30         301 my $sth = $dbh->prepare(qq{SELECT 1 FROM sqlite_master WHERE tbl_name=? AND sql LIKE "\%AUTOINCREMENT\%"});
185 30         4787 $sth->execute($table);
186 30         409 my ($res) = $sth->fetchrow_array;
187              
188 30 100       1143 return $res ? 1 : 0;
189             }
190              
191             sub build_columns_from_db {
192 30     30 0 80 my $self = shift;
193 30         428 my ($table, %params) = @_;
194              
195 30 50       200 croak "A table name is required" unless $table;
196 30         111 my $dbh = $self->{+DBH};
197              
198 30         487 my $sth = $dbh->prepare("SELECT * FROM pragma_table_info(?)");
199 30         10251 $sth->execute($table);
200              
201 30         6528 my $has_autoinc = $self->table_has_autoinc($table);
202              
203 30         338 my (%columns, @links);
204 30         2528 while (my $res = $sth->fetchrow_hashref) {
205 87         478 my $col = {};
206              
207 87         713 $params{autofill}->hook(pre_column => {column => $col, table_name => $table, column_info => $res});
208              
209 87         334 $col->{name} = $res->{name};
210 87         238 $col->{db_name} = $res->{name};
211 87         1605 $col->{order} = $res->{cid} + 1;
212 87 100 100     1253 $col->{identity} = 1 if $has_autoinc && $res->{pk};
213              
214 87         234 my $type = $res->{type};
215 87         3757 $type =~ s/\(.*$//;
216 87         246 $col->{type} = \$type;
217              
218 87 100       567 $col->{nullable} = $res->{notnull} ? 0 : 1;
219 87   50     910 $col->{affinity} //= affinity_from_type($type) // 'string';
      33        
220              
221 87         722 $params{autofill}->process_column($col);
222 87         772 $params{autofill}->hook(post_column => {column => $col, table_name => $table, column_info => $res});
223              
224 87         876 $columns{$col->{name}} = DBIx::QuickORM::Schema::Table::Column->new($col);
225 87         685 $params{autofill}->hook(column => {column => $columns{$col->{name}}, table_name => $table, column_info => $res});
226             }
227              
228 30         777 return \%columns;
229             }
230              
231             sub build_indexes_from_db {
232 30     30 0 68 my $self = shift;
233 30         134 my ($table, %params) = @_;
234              
235 30         4787 my $dbh = $self->dbh;
236              
237 30         391 my $sth = $dbh->prepare(<<" EOT");
238             SELECT il.`name` AS name,
239             il.`unique` AS u,
240             ii.`name` AS column
241             FROM pragma_index_list(?) AS il,
242             pragma_index_info(il.name) AS ii
243             ORDER BY il.name, ii.seqno
244             EOT
245              
246 30         8536 $sth->execute($table);
247              
248 30         253 my %out;
249 30         564 while (my ($name, $u, $col) = $sth->fetchrow_array) {
250 29 50 100     389 my $idx = $out{$name} //= {name => $name, columns => [], unique => $u ? 1 : 0};
251 29         72 push @{$idx->{columns}} => $col;
  29         268  
252             }
253              
254 30 100       214 if (my @pk = $self->_primary_key($table)) {
255 28         241 $out{"${table}:pk"} = {name => "${table}:pk", unique => 1, columns => \@pk};
256             }
257              
258 30         235 return [map { $params{autofill}->hook(index => {index => $out{$_}, table_name => $table}); $out{$_} } sort keys %out];
  53         390  
  53         729  
259             }
260              
261             sub _primary_key {
262 58     58   132 my $self = shift;
263 58         157 my ($table) = @_;
264              
265 58         588 my $sth = $self->dbh->prepare("SELECT name FROM pragma_table_info(?) WHERE pk > 0 ORDER BY pk ASC");
266 58         8201 $sth->execute($table);
267              
268 58         191 my @out;
269 58         1372 while (my $row = $sth->fetchrow_hashref()) {
270 56         802 push @out => $row->{name};
271             }
272              
273 58         1201 return @out;
274             }
275              
276             ###############################################################################
277             # }}} Schema Builder Code
278             ###############################################################################
279              
280             1;