File Coverage

blib/lib/DBD/SQLeet.pm
Criterion Covered Total %
statement 300 314 95.5
branch 150 208 72.1
condition 85 137 62.0
subroutine 24 27 88.8
pod 0 1 0.0
total 559 687 81.3


line stmt bran cond sub pod time code
1             package DBD::SQLeet;
2              
3 109     109   8594948 use 5.006;
  109         1507  
4 109     109   486 use strict;
  109         170  
  109         2512  
5 109     109   1736 use DBI 1.57 ();
  109         17833  
  109         1642  
6 109     109   443 use DynaLoader ();
  109         172  
  109         9425  
7              
8             our $VERSION = '1.58';
9             our @ISA = 'DynaLoader';
10              
11             # sqlite_version cache (set in the XS bootstrap)
12             our ($sqlite_version, $sqlite_version_number);
13              
14             # not sure if we still need these...
15             our ($err, $errstr);
16              
17             __PACKAGE__->bootstrap($VERSION);
18              
19             # New or old API?
20 109     109   632 use constant NEWAPI => ($DBI::VERSION >= 1.608);
  109         188  
  109         17056  
21              
22             # global registry of collation functions, initialized with 2 builtins
23             our %COLLATION;
24             tie %COLLATION, 'DBD::SQLeet::_WriteOnceHash';
25             $COLLATION{perl} = sub { $_[0] cmp $_[1] };
26 109     109   45579 $COLLATION{perllocale} = sub { use locale; $_[0] cmp $_[1] };
  109         56567  
  109         506  
27              
28             our $drh;
29             my $methods_are_installed = 0;
30              
31             sub driver {
32 103 50   103 0 423190 return $drh if $drh;
33              
34 103 50 50     767 if (!$methods_are_installed && DBD::SQLeet::NEWAPI ) {
35 103         408 DBI->setup_driver('DBD::SQLeet');
36              
37 103         3438 DBD::SQLeet::db->install_method('sqlite_last_insert_rowid');
38 103         5420 DBD::SQLeet::db->install_method('sqlite_busy_timeout');
39 103         3012 DBD::SQLeet::db->install_method('sqlite_create_function');
40 103         2792 DBD::SQLeet::db->install_method('sqlite_create_aggregate');
41 103         2679 DBD::SQLeet::db->install_method('sqlite_create_collation');
42 103         2883 DBD::SQLeet::db->install_method('sqlite_collation_needed');
43 103         2720 DBD::SQLeet::db->install_method('sqlite_progress_handler');
44 103         2806 DBD::SQLeet::db->install_method('sqlite_commit_hook');
45 103         2854 DBD::SQLeet::db->install_method('sqlite_rollback_hook');
46 103         2795 DBD::SQLeet::db->install_method('sqlite_update_hook');
47 103         2722 DBD::SQLeet::db->install_method('sqlite_set_authorizer');
48 103         2877 DBD::SQLeet::db->install_method('sqlite_backup_from_file');
49 103         2915 DBD::SQLeet::db->install_method('sqlite_backup_to_file');
50 103         2964 DBD::SQLeet::db->install_method('sqlite_enable_load_extension');
51 103         2940 DBD::SQLeet::db->install_method('sqlite_load_extension');
52 103         2896 DBD::SQLeet::db->install_method('sqlite_register_fts3_perl_tokenizer');
53 103         3397 DBD::SQLeet::db->install_method('sqlite_trace', { O => 0x0004 });
54 103         3461 DBD::SQLeet::db->install_method('sqlite_profile', { O => 0x0004 });
55 103         3186 DBD::SQLeet::db->install_method('sqlite_table_column_metadata', { O => 0x0004 });
56 103         3501 DBD::SQLeet::db->install_method('sqlite_db_filename', { O => 0x0004 });
57 103         3083 DBD::SQLeet::db->install_method('sqlite_db_status', { O => 0x0004 });
58 103         3422 DBD::SQLeet::st->install_method('sqlite_st_status', { O => 0x0004 });
59 103         3157 DBD::SQLeet::db->install_method('sqlite_create_module');
60              
61 103         2725 $methods_are_installed++;
62             }
63              
64 103         987 $drh = DBI::_new_drh( "$_[0]::dr", {
65             Name => 'SQLite',
66             Version => $VERSION,
67             Attribution => 'DBD::SQLeet by Matt Sergeant et al',
68             } );
69              
70 103         4051 return $drh;
71             }
72              
73             sub CLONE {
74 0     0   0 undef $drh;
75             }
76              
77              
78             package # hide from PAUSE
79             DBD::SQLeet::dr;
80              
81             sub connect {
82 264     264   3476944 my ($drh, $dbname, $user, $auth, $attr) = @_;
83              
84             # Default PrintWarn to the value of $^W
85             # unless ( defined $attr->{PrintWarn} ) {
86             # $attr->{PrintWarn} = $^W ? 1 : 0;
87             # }
88              
89 264         1333 my $dbh = DBI::_new_dbh( $drh, {
90             Name => $dbname,
91             } );
92              
93 264         9282 my $real = $dbname;
94 264 100       1326 if ( $dbname =~ /=/ ) {
95 248         1056 foreach my $attrib ( split(/;/, $dbname) ) {
96 250         1107 my ($key, $value) = split(/=/, $attrib, 2);
97 250 100       1614 if ( $key =~ /^(?:db(?:name)?|database)$/ ) {
    100          
98 237         691 $real = $value;
99             } elsif ( $key eq 'uri' ) {
100 6         8 $real = $value;
101 6         22 $attr->{sqlite_open_flags} |= DBD::SQLeet::OPEN_URI();
102             } else {
103 7         19 $attr->{$key} = $value;
104             }
105             }
106             }
107              
108 264 100       963 if (my $flags = $attr->{sqlite_open_flags}) {
109 19 100       73 unless ($flags & (DBD::SQLeet::OPEN_READONLY() | DBD::SQLeet::OPEN_READWRITE())) {
110 13         29 $attr->{sqlite_open_flags} |= DBD::SQLeet::OPEN_READWRITE() | DBD::SQLeet::OPEN_CREATE();
111             }
112             }
113              
114             # To avoid unicode and long file name problems on Windows,
115             # convert to the shortname if the file (or parent directory) exists.
116 264 0 33     1528 if ( $^O =~ /MSWin32/ and $real ne ':memory:' and $real ne '' and $real !~ /^file:/ and !-f $real ) {
      33        
      0        
      0        
117 0         0 require File::Basename;
118 0         0 my ($file, $dir, $suffix) = File::Basename::fileparse($real);
119             # We are creating a new file.
120             # Does the directory it's in at least exist?
121 0 0       0 if ( -d $dir ) {
122 0         0 require Win32;
123 0         0 $real = join '', grep { defined } Win32::GetShortPathName($dir), $file, $suffix;
  0         0  
124             } else {
125             # SQLite can't do mkpath anyway.
126             # So let it go through as it and fail.
127             }
128             }
129              
130             # Hand off to the actual login function
131 264 100       87771 DBD::SQLeet::db::_login($dbh, $real, $user, $auth, $attr) or return undef;
132              
133             # Register the on-demand collation installer, REGEXP function and
134             # perl tokenizer
135 258         788 if ( DBD::SQLeet::NEWAPI ) {
136 258         3445 $dbh->sqlite_collation_needed( \&install_collation );
137 258         2944 $dbh->sqlite_create_function( "REGEXP", 2, \®exp );
138 258         35279 $dbh->sqlite_register_fts3_perl_tokenizer();
139             } else {
140             $dbh->func( \&install_collation, "collation_needed" );
141             $dbh->func( "REGEXP", 2, \®exp, "create_function" );
142             $dbh->func( "register_fts3_perl_tokenizer" );
143             }
144              
145             # HACK: Since PrintWarn = 0 doesn't seem to actually prevent warnings
146             # in DBD::SQLeet we set Warn to false if PrintWarn is false.
147              
148             # NOTE: According to the explanation by timbunce,
149             # "Warn is meant to report on bad practices or problems with
150             # the DBI itself (hence always on by default), while PrintWarn
151             # is meant to report warnings coming from the database."
152             # That is, if you want to disable an ineffective rollback warning
153             # etc (due to bad practices), you should turn off Warn,
154             # and to silence other warnings, turn off PrintWarn.
155             # Warn and PrintWarn are independent, and turning off PrintWarn
156             # does not silence those warnings that should be controlled by
157             # Warn.
158              
159             # unless ( $attr->{PrintWarn} ) {
160             # $attr->{Warn} = 0;
161             # }
162              
163 258         1637 return $dbh;
164             }
165              
166             sub install_collation {
167 12     12   30 my $dbh = shift;
168 12         15 my $name = shift;
169 12         54 my $collation = $DBD::SQLeet::COLLATION{$name};
170 12 50       75 unless ($collation) {
171 0 0       0 warn "Can't install unknown collation: $name" if $dbh->{PrintWarn};
172 0         0 return;
173             }
174 12         13 if ( DBD::SQLeet::NEWAPI ) {
175 12         58 $dbh->sqlite_create_collation( $name => $collation );
176             } else {
177             $dbh->func( $name => $collation, "create_collation" );
178             }
179             }
180              
181             # default implementation for sqlite 'REGEXP' infix operator.
182             # Note : args are reversed, i.e. "a REGEXP b" calls REGEXP(b, a)
183             # (see http://www.sqlite.org/vtab.html#xfindfunction)
184             sub regexp {
185 109     109   97765 use locale;
  109         231  
  109         409  
186 392 100 66 392   2315 return if !defined $_[0] || !defined $_[1];
187 264         1873 return scalar($_[1] =~ $_[0]);
188             }
189              
190             package # hide from PAUSE
191             DBD::SQLeet::db;
192              
193             sub prepare {
194 1285     1285   820771 my $dbh = shift;
195 1285         1813 my $sql = shift;
196 1285 50       2536 $sql = '' unless defined $sql;
197              
198 1285         4515 my $sth = DBI::_new_sth( $dbh, {
199             Statement => $sql,
200             } );
201              
202 1285 100       88261 DBD::SQLeet::st::_prepare($sth, $sql, @_) or return undef;
203              
204 1275         17144 return $sth;
205             }
206              
207             sub do {
208 2904     2904   3973191 my ($dbh, $statement, $attr, @bind_values) = @_;
209              
210             # shortcut
211 2904         10175 my $allow_multiple_statements = $dbh->FETCH('sqlite_allow_multiple_statements');
212 2904 100 100     13712 if (defined $statement && !defined $attr && !@bind_values) {
      100        
213             # _do() (i.e. sqlite3_exec()) runs semicolon-separate SQL
214             # statements, which is handy but insecure sometimes.
215             # Use this only when it's safe or explicitly allowed.
216 2805 100 66     7178 if (index($statement, ';') == -1 or $allow_multiple_statements) {
217 2782         9970578 return DBD::SQLeet::db::_do($dbh, $statement);
218             }
219             }
220              
221 122         229 my @copy = @{[@bind_values]};
  122         327  
222 122         213 my $rows = 0;
223              
224 122         283 while ($statement) {
225 127 100       466 my $sth = $dbh->prepare($statement, $attr) or return undef;
226 119 100       8268 $sth->execute(splice @copy, 0, $sth->{NUM_OF_PARAMS}) or return undef;
227 115         1080 $rows += $sth->rows;
228             # XXX: not sure why but $dbh->{sqlite...} wouldn't work here
229 115 100       1569 last unless $allow_multiple_statements;
230 8         111 $statement = $sth->{sqlite_unprepared_statements};
231             }
232              
233             # always return true if no error
234 110 100       757 return ($rows == 0) ? "0E0" : $rows;
235             }
236              
237             sub ping {
238 1     1   1052 my $dbh = shift;
239              
240             # $file may be undef (ie. in-memory/temporary database)
241 1         10 my $file = DBD::SQLeet::NEWAPI ? $dbh->sqlite_db_filename
242             : $dbh->func("db_filename");
243              
244 1 50 33     5 return 0 if $file && !-f $file;
245 1 50       10 return $dbh->FETCH('Active') ? 1 : 0;
246             }
247              
248             sub _get_version {
249 0     0   0 return ( DBD::SQLeet::db::FETCH($_[0], 'sqlite_version') );
250             }
251              
252             my %info = (
253             17 => 'SQLite', # SQL_DBMS_NAME
254             18 => \&_get_version, # SQL_DBMS_VER
255             29 => '"', # SQL_IDENTIFIER_QUOTE_CHAR
256             );
257              
258             sub get_info {
259 63     63   400 my($dbh, $info_type) = @_;
260 63         116 my $v = $info{int($info_type)};
261 63 50       112 $v = $v->($dbh) if ref $v eq 'CODE';
262 63         163 return $v;
263             }
264              
265             sub _attached_database_list {
266 21     21   27 my $dbh = shift;
267 21         30 my @attached;
268              
269 21 50       70 my $sth_databases = $dbh->prepare( 'PRAGMA database_list' ) or return;
270 21 50       167 $sth_databases->execute or return;
271 21         407 while ( my $db_info = $sth_databases->fetchrow_hashref ) {
272 47 100       495 push @attached, $db_info->{name} if $db_info->{seq} >= 2;
273             }
274 21         242 return @attached;
275             }
276              
277             # SQL/CLI (ISO/IEC JTC 1/SC 32 N 0595), 6.63 Tables
278             # Based on DBD::Oracle's
279             # See also http://www.ch-werner.de/sqliteodbc/html/sqlite3odbc_8c.html#a213
280             sub table_info {
281 16     16   9550 my ($dbh, $cat_val, $sch_val, $tbl_val, $typ_val, $attr) = @_;
282              
283 16         28 my @where = ();
284 16         18 my $sql;
285 16 100 100     142 if ( defined($cat_val) && $cat_val eq '%'
    100 66        
    100 66        
      33        
      33        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      33        
      33        
      33        
      33        
      33        
286             && defined($sch_val) && $sch_val eq ''
287             && defined($tbl_val) && $tbl_val eq '') { # Rule 19a
288 1         3 $sql = <<'END_SQL';
289             SELECT NULL TABLE_CAT
290             , NULL TABLE_SCHEM
291             , NULL TABLE_NAME
292             , NULL TABLE_TYPE
293             , NULL REMARKS
294             END_SQL
295             }
296             elsif ( defined($cat_val) && $cat_val eq ''
297             && defined($sch_val) && $sch_val eq '%'
298             && defined($tbl_val) && $tbl_val eq '') { # Rule 19b
299 1         2 $sql = <<'END_SQL';
300             SELECT NULL TABLE_CAT
301             , t.tn TABLE_SCHEM
302             , NULL TABLE_NAME
303             , NULL TABLE_TYPE
304             , NULL REMARKS
305             FROM (
306             SELECT 'main' tn
307             UNION SELECT 'temp' tn
308             END_SQL
309 1         5 for my $db_name (_attached_database_list($dbh)) {
310 0         0 $sql .= " UNION SELECT '$db_name' tn\n";
311             }
312 1         41 $sql .= ") t\n";
313             }
314             elsif ( defined($cat_val) && $cat_val eq ''
315             && defined($sch_val) && $sch_val eq ''
316             && defined($tbl_val) && $tbl_val eq ''
317             && defined($typ_val) && $typ_val eq '%') { # Rule 19c
318 1         3 $sql = <<'END_SQL';
319             SELECT NULL TABLE_CAT
320             , NULL TABLE_SCHEM
321             , NULL TABLE_NAME
322             , t.tt TABLE_TYPE
323             , NULL REMARKS
324             FROM (
325             SELECT 'TABLE' tt UNION
326             SELECT 'VIEW' tt UNION
327             SELECT 'LOCAL TEMPORARY' tt UNION
328             SELECT 'SYSTEM TABLE' tt
329             ) t
330             ORDER BY TABLE_TYPE
331             END_SQL
332             }
333             else {
334 13         22 $sql = <<'END_SQL';
335             SELECT *
336             FROM
337             (
338             SELECT NULL TABLE_CAT
339             , TABLE_SCHEM
340             , tbl_name TABLE_NAME
341             , TABLE_TYPE
342             , NULL REMARKS
343             , sql sqlite_sql
344             FROM (
345             SELECT 'main' TABLE_SCHEM, tbl_name, upper(type) TABLE_TYPE, sql
346             FROM sqlite_master
347             UNION ALL
348             SELECT 'temp' TABLE_SCHEM, tbl_name, 'LOCAL TEMPORARY' TABLE_TYPE, sql
349             FROM sqlite_temp_master
350             END_SQL
351              
352 13         24 for my $db_name (_attached_database_list($dbh)) {
353 9         39 $sql .= <<"END_SQL";
354             UNION ALL
355             SELECT '$db_name' TABLE_SCHEM, tbl_name, upper(type) TABLE_TYPE, sql
356             FROM "$db_name".sqlite_master
357             END_SQL
358             }
359              
360 13         24 $sql .= <<'END_SQL';
361             UNION ALL
362             SELECT 'main' TABLE_SCHEM, 'sqlite_master' tbl_name, 'SYSTEM TABLE' TABLE_TYPE, NULL sql
363             UNION ALL
364             SELECT 'temp' TABLE_SCHEM, 'sqlite_temp_master' tbl_name, 'SYSTEM TABLE' TABLE_TYPE, NULL sql
365             )
366             )
367             END_SQL
368 13 50       37 $attr = {} unless ref $attr eq 'HASH';
369 13 50       33 my $escape = defined $attr->{Escape} ? " ESCAPE '$attr->{Escape}'" : '';
370 13 100       26 if ( defined $sch_val ) {
371 3         8 push @where, "TABLE_SCHEM LIKE '$sch_val'$escape";
372             }
373 13 100       26 if ( defined $tbl_val ) {
374 11         30 push @where, "TABLE_NAME LIKE '$tbl_val'$escape";
375             }
376 13 100       24 if ( defined $typ_val ) {
377 1         1 my $table_type_list;
378 1         4 $typ_val =~ s/^\s+//;
379 1         4 $typ_val =~ s/\s+$//;
380 1         4 my @ttype_list = split (/\s*,\s*/, $typ_val);
381 1         2 foreach my $table_type (@ttype_list) {
382 1 50       4 if ($table_type !~ /^'.*'$/) {
383 1         4 $table_type = "'" . $table_type . "'";
384             }
385             }
386 1         4 $table_type_list = join(', ', @ttype_list);
387 1 50       5 push @where, "TABLE_TYPE IN (\U$table_type_list)" if $table_type_list;
388             }
389 13 100       48 $sql .= ' WHERE ' . join("\n AND ", @where ) . "\n" if @where;
390 13         20 $sql .= " ORDER BY TABLE_TYPE, TABLE_SCHEM, TABLE_NAME\n";
391             }
392 16 50       53 my $sth = $dbh->prepare($sql) or return undef;
393 16 50       526 $sth->execute or return undef;
394 16         265 $sth;
395             }
396              
397             sub primary_key_info {
398 33     33   19092 my ($dbh, $catalog, $schema, $table, $attr) = @_;
399              
400 33         322 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}});
401              
402 33         3359 my @pk_info;
403 33         75 for my $database (@$databases) {
404 49         95 my $dbname = $database->{name};
405 49 100 66     152 next if defined $schema && $schema ne '%' && $schema ne $dbname;
      100        
406              
407 43         167 my $quoted_dbname = $dbh->quote_identifier($dbname);
408              
409 43 100       1158 my $master_table =
    100          
410             ($dbname eq 'main') ? 'sqlite_master' :
411             ($dbname eq 'temp') ? 'sqlite_temp_master' :
412             $quoted_dbname.'.sqlite_master';
413              
414 43 50       280 my $sth = $dbh->prepare("SELECT name, sql FROM $master_table WHERE type = ?") or return;
415 43 50       1032 $sth->execute("table") or return;
416 43         927 while(my $row = $sth->fetchrow_hashref) {
417 55         126 my $tbname = $row->{name};
418 55 100 66     626 next if defined $table && $table ne '%' && $table ne $tbname;
      100        
419              
420 33         124 my $quoted_tbname = $dbh->quote_identifier($tbname);
421 33 50       860 my $t_sth = $dbh->prepare("PRAGMA $quoted_dbname.table_info($quoted_tbname)") or return;
422 33 50       416 $t_sth->execute or return;
423 33         85 my @pk;
424 33         670 while(my $col = $t_sth->fetchrow_hashref) {
425 52 100       716 push @pk, $col->{name} if $col->{pk};
426             }
427              
428             # If there're multiple primary key columns, we need to
429             # find their order from one of the auto-generated unique
430             # indices (note that single column integer primary key
431             # doesn't create an index).
432 33 100 66     226 if (@pk > 1 and $row->{sql} =~ /\bPRIMARY\s+KEY\s*\(\s*
433             (
434             (?:
435             (
436             [a-z_][a-z0-9_]*
437             | (["'`])(?:\3\3|(?!\3).)+?\3(?!\3)
438             | \[[^\]]+\]
439             )
440             \s*,\s*
441             )+
442             (
443             [a-z_][a-z0-9_]*
444             | (["'`])(?:\5\5|(?!\5).)+?\5(?!\5)
445             | \[[^\]]+\]
446             )
447             )
448             \s*\)/six) {
449 4         11 my $pk_sql = $1;
450 4         9 @pk = ();
451 4         20 while($pk_sql =~ /
452             (
453             [a-z_][a-z0-9_]*
454             | (["'`])(?:\2\2|(?!\2).)+?\2(?!\2)
455             | \[([^\]]+)\]
456             )
457             (?:\s*,\s*|$)
458             /sixg) {
459 9         27 my($col, $quote, $brack) = ($1, $2, $3);
460 9 100       19 if ( defined $quote ) {
    100          
461             # Dequote "'`
462 2         4 $col = substr $col, 1, -1;
463 2         19 $col =~ s/$quote$quote/$quote/g;
464             } elsif ( defined $brack ) {
465             # Dequote []
466 1         2 $col = $brack;
467             }
468 9         32 push @pk, $col;
469             }
470             }
471              
472 33 100       181 my $key_name = $row->{sql} =~ /\bCONSTRAINT\s+(\S+|"[^"]+")\s+PRIMARY\s+KEY\s*\(/i ? $1 : 'PRIMARY KEY';
473 33         46 my $key_seq = 0;
474 33         61 foreach my $pk_field (@pk) {
475 38         955 push @pk_info, {
476             TABLE_SCHEM => $dbname,
477             TABLE_NAME => $tbname,
478             COLUMN_NAME => $pk_field,
479             KEY_SEQ => ++$key_seq,
480             PK_NAME => $key_name,
481             };
482             }
483             }
484             }
485              
486 33 50       149 my $sponge = DBI->connect("DBI:Sponge:", '','')
487             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
488 33         17180 my @names = qw(TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME);
489             my $sth = $sponge->prepare( "primary_key_info", {
490 33 50       75 rows => [ map { [ @{$_}{@names} ] } @pk_info ],
  38         47  
  38         283  
491             NUM_OF_FIELDS => scalar @names,
492             NAME => \@names,
493             }) or return $dbh->DBI::set_err(
494             $sponge->err,
495             $sponge->errstr,
496             );
497 33         2768 return $sth;
498             }
499              
500              
501             our %DBI_code_for_rule = ( # from DBI doc; curiously, they are not exported
502             # by the DBI module.
503             # codes for update/delete constraints
504             'CASCADE' => 0,
505             'RESTRICT' => 1,
506             'SET NULL' => 2,
507             'NO ACTION' => 3,
508             'SET DEFAULT' => 4,
509              
510             # codes for deferrability
511             'INITIALLY DEFERRED' => 5,
512             'INITIALLY IMMEDIATE' => 6,
513             'NOT DEFERRABLE' => 7,
514             );
515              
516              
517             my @FOREIGN_KEY_INFO_ODBC = (
518             'PKTABLE_CAT', # The primary (unique) key table catalog identifier.
519             'PKTABLE_SCHEM', # The primary (unique) key table schema identifier.
520             'PKTABLE_NAME', # The primary (unique) key table identifier.
521             'PKCOLUMN_NAME', # The primary (unique) key column identifier.
522             'FKTABLE_CAT', # The foreign key table catalog identifier.
523             'FKTABLE_SCHEM', # The foreign key table schema identifier.
524             'FKTABLE_NAME', # The foreign key table identifier.
525             'FKCOLUMN_NAME', # The foreign key column identifier.
526             'KEY_SEQ', # The column sequence number (starting with 1).
527             'UPDATE_RULE', # The referential action for the UPDATE rule.
528             'DELETE_RULE', # The referential action for the DELETE rule.
529             'FK_NAME', # The foreign key name.
530             'PK_NAME', # The primary (unique) key name.
531             'DEFERRABILITY', # The deferrability of the foreign key constraint.
532             'UNIQUE_OR_PRIMARY', # qualifies the key referenced by the foreign key
533             );
534              
535             # Column names below are not used, but listed just for completeness's sake.
536             # Maybe we could add an option so that the user can choose which field
537             # names will be returned; the DBI spec is not very clear about ODBC vs. CLI.
538             my @FOREIGN_KEY_INFO_SQL_CLI = qw(
539             UK_TABLE_CAT
540             UK_TABLE_SCHEM
541             UK_TABLE_NAME
542             UK_COLUMN_NAME
543             FK_TABLE_CAT
544             FK_TABLE_SCHEM
545             FK_TABLE_NAME
546             FK_COLUMN_NAME
547             ORDINAL_POSITION
548             UPDATE_RULE
549             DELETE_RULE
550             FK_NAME
551             UK_NAME
552             DEFERABILITY
553             UNIQUE_OR_PRIMARY
554             );
555              
556             sub foreign_key_info {
557 10     10   10494 my ($dbh, $pk_catalog, $pk_schema, $pk_table, $fk_catalog, $fk_schema, $fk_table) = @_;
558              
559 10 50       84 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}}) or return;
560              
561 10         875 my @fk_info;
562             my %table_info;
563 10         24 for my $database (@$databases) {
564 20         59 my $dbname = $database->{name};
565 20 100 66     55 next if defined $fk_schema && $fk_schema ne '%' && $fk_schema ne $dbname;
      100        
566              
567 19         67 my $quoted_dbname = $dbh->quote_identifier($dbname);
568 19 100       478 my $master_table =
    100          
569             ($dbname eq 'main') ? 'sqlite_master' :
570             ($dbname eq 'temp') ? 'sqlite_temp_master' :
571             $quoted_dbname.'.sqlite_master';
572              
573 19 50       105 my $tables = $dbh->selectall_arrayref("SELECT name FROM $master_table WHERE type = ?", undef, "table") or return;
574 19         217 for my $table (@$tables) {
575 34         55 my $tbname = $table->[0];
576 34 100 66     148 next if defined $fk_table && $fk_table ne '%' && $fk_table ne $tbname;
      100        
577              
578 14         52 my $quoted_tbname = $dbh->quote_identifier($tbname);
579 14 50       341 my $sth = $dbh->prepare("PRAGMA $quoted_dbname.foreign_key_list($quoted_tbname)") or return;
580 14 50       119 $sth->execute or return;
581 14         288 while(my $row = $sth->fetchrow_hashref) {
582 18 100 66     131 next if defined $pk_table && $pk_table ne '%' && $pk_table ne $row->{table};
      100        
583              
584 15 100       48 unless ($table_info{$row->{table}}) {
585 13         45 my $quoted_tb = $dbh->quote_identifier($row->{table});
586 13         299 for my $db (@$databases) {
587 15         46 my $quoted_db = $dbh->quote_identifier($db->{name});
588 15 50       314 my $t_sth = $dbh->prepare("PRAGMA $quoted_db.table_info($quoted_tb)") or return;
589 15 50       128 $t_sth->execute or return;
590 15         33 my $cols = {};
591 15         239 while(my $r = $t_sth->fetchrow_hashref) {
592 30         382 $cols->{$r->{name}} = $r->{pk};
593             }
594 15 100       79 if (keys %$cols) {
595             $table_info{$row->{table}} = {
596             schema => $db->{name},
597 13         48 columns => $cols,
598             };
599 13         130 last;
600             }
601             }
602             }
603              
604 15 100 66     83 next if defined $pk_schema && $pk_schema ne '%' && $pk_schema ne $table_info{$row->{table}}{schema};
      100        
605              
606             push @fk_info, {
607             PKTABLE_CAT => undef,
608             PKTABLE_SCHEM => $table_info{$row->{table}}{schema},
609             PKTABLE_NAME => $row->{table},
610             PKCOLUMN_NAME => $row->{to},
611             FKTABLE_CAT => undef,
612             FKTABLE_SCHEM => $dbname,
613             FKTABLE_NAME => $tbname,
614             FKCOLUMN_NAME => $row->{from},
615             KEY_SEQ => $row->{seq} + 1,
616             UPDATE_RULE => $DBI_code_for_rule{$row->{on_update}},
617             DELETE_RULE => $DBI_code_for_rule{$row->{on_delete}},
618             FK_NAME => undef,
619             PK_NAME => undef,
620             DEFERRABILITY => undef,
621 13 100       382 UNIQUE_OR_PRIMARY => $table_info{$row->{table}}{columns}{$row->{to}} ? 'PRIMARY' : 'UNIQUE',
622             };
623             }
624             }
625             }
626              
627 10 50       43 my $sponge_dbh = DBI->connect("DBI:Sponge:", "", "")
628             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
629             my $sponge_sth = $sponge_dbh->prepare("foreign_key_info", {
630             NAME => \@FOREIGN_KEY_INFO_ODBC,
631 10 50       3471 rows => [ map { [@{$_}{@FOREIGN_KEY_INFO_ODBC} ] } @fk_info ],
  13         16  
  13         113  
632             NUM_OF_FIELDS => scalar(@FOREIGN_KEY_INFO_ODBC),
633             }) or return $dbh->DBI::set_err(
634             $sponge_dbh->err,
635             $sponge_dbh->errstr,
636             );
637 10         939 return $sponge_sth;
638             }
639              
640             my @STATISTICS_INFO_ODBC = (
641             'TABLE_CAT', # The catalog identifier.
642             'TABLE_SCHEM', # The schema identifier.
643             'TABLE_NAME', # The table identifier.
644             'NON_UNIQUE', # Unique index indicator.
645             'INDEX_QUALIFIER', # Index qualifier identifier.
646             'INDEX_NAME', # The index identifier.
647             'TYPE', # The type of information being returned.
648             'ORDINAL_POSITION', # Column sequence number (starting with 1).
649             'COLUMN_NAME', # The column identifier.
650             'ASC_OR_DESC', # Column sort sequence.
651             'CARDINALITY', # Cardinality of the table or index.
652             'PAGES', # Number of storage pages used by this table or index.
653             'FILTER_CONDITION', # The index filter condition as a string.
654             );
655              
656             sub statistics_info {
657 4     4   32662 my ($dbh, $catalog, $schema, $table, $unique_only, $quick) = @_;
658              
659 4 50       53 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}}) or return;
660              
661 4         466 my @statistics_info;
662 4         11 for my $database (@$databases) {
663 8         59 my $dbname = $database->{name};
664 8 0 33     23 next if defined $schema && $schema ne '%' && $schema ne $dbname;
      33        
665              
666 8         33 my $quoted_dbname = $dbh->quote_identifier($dbname);
667 8 50       205 my $master_table =
    100          
668             ($dbname eq 'main') ? 'sqlite_master' :
669             ($dbname eq 'temp') ? 'sqlite_temp_master' :
670             $quoted_dbname.'.sqlite_master';
671              
672 8 50       45 my $tables = $dbh->selectall_arrayref("SELECT name FROM $master_table WHERE type = ?", undef, "table") or return;
673 8         103 for my $table_ref (@$tables) {
674 8         19 my $tbname = $table_ref->[0];
675 8 100 33     58 next if defined $table && $table ne '%' && uc($table) ne uc($tbname);
      66        
676              
677 4         17 my $quoted_tbname = $dbh->quote_identifier($tbname);
678 4 50       101 my $sth = $dbh->prepare("PRAGMA $quoted_dbname.index_list($quoted_tbname)") or return;
679 4 50       39 $sth->execute or return;
680 4         80 while(my $row = $sth->fetchrow_hashref) {
681              
682 16 100 100     93 next if $unique_only && !$row->{unique};
683 12         39 my $quoted_idx = $dbh->quote_identifier($row->{name});
684 12         263 for my $db (@$databases) {
685 24         74 my $quoted_db = $dbh->quote_identifier($db->{name});
686 24 50       537 my $i_sth = $dbh->prepare("PRAGMA $quoted_db.index_info($quoted_idx)") or return;
687 24 50       163 $i_sth->execute or return;
688 24         41 my $cols = {};
689 24         499 while(my $info = $i_sth->fetchrow_hashref) {
690             push @statistics_info, {
691             TABLE_CAT => undef,
692             TABLE_SCHEM => $db->{name},
693             TABLE_NAME => $tbname,
694             NON_UNIQUE => $row->{unique} ? 0 : 1,
695             INDEX_QUALIFIER => undef,
696             INDEX_NAME => $row->{name},
697             TYPE => 'btree', # see http://www.sqlite.org/version3.html esp. "Traditional B-trees are still used for indices"
698             ORDINAL_POSITION => $info->{seqno} + 1,
699             COLUMN_NAME => $info->{name},
700 16 100       374 ASC_OR_DESC => undef,
701             CARDINALITY => undef,
702             PAGES => undef,
703             FILTER_CONDITION => undef,
704             };
705             }
706             }
707             }
708             }
709             }
710              
711 4 50       22 my $sponge_dbh = DBI->connect("DBI:Sponge:", "", "")
712             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
713             my $sponge_sth = $sponge_dbh->prepare("statistics_info", {
714             NAME => \@STATISTICS_INFO_ODBC,
715 4 50       2805 rows => [ map { [@{$_}{@STATISTICS_INFO_ODBC} ] } @statistics_info ],
  16         18  
  16         86  
716             NUM_OF_FIELDS => scalar(@STATISTICS_INFO_ODBC),
717             }) or return $dbh->DBI::set_err(
718             $sponge_dbh->err,
719             $sponge_dbh->errstr,
720             );
721 4         501 return $sponge_sth;
722             }
723              
724             sub type_info_all {
725 0     0   0 return; # XXX code just copied from DBD::Oracle, not yet thought about
726             # return [
727             # {
728             # TYPE_NAME => 0,
729             # DATA_TYPE => 1,
730             # COLUMN_SIZE => 2,
731             # LITERAL_PREFIX => 3,
732             # LITERAL_SUFFIX => 4,
733             # CREATE_PARAMS => 5,
734             # NULLABLE => 6,
735             # CASE_SENSITIVE => 7,
736             # SEARCHABLE => 8,
737             # UNSIGNED_ATTRIBUTE => 9,
738             # FIXED_PREC_SCALE => 10,
739             # AUTO_UNIQUE_VALUE => 11,
740             # LOCAL_TYPE_NAME => 12,
741             # MINIMUM_SCALE => 13,
742             # MAXIMUM_SCALE => 14,
743             # SQL_DATA_TYPE => 15,
744             # SQL_DATETIME_SUB => 16,
745             # NUM_PREC_RADIX => 17,
746             # },
747             # [ 'CHAR', 1, 255, '\'', '\'', 'max length', 1, 1, 3,
748             # undef, '0', '0', undef, undef, undef, 1, undef, undef
749             # ],
750             # [ 'NUMBER', 3, 38, undef, undef, 'precision,scale', 1, '0', 3,
751             # '0', '0', '0', undef, '0', 38, 3, undef, 10
752             # ],
753             # [ 'DOUBLE', 8, 15, undef, undef, undef, 1, '0', 3,
754             # '0', '0', '0', undef, undef, undef, 8, undef, 10
755             # ],
756             # [ 'DATE', 9, 19, '\'', '\'', undef, 1, '0', 3,
757             # undef, '0', '0', undef, '0', '0', 11, undef, undef
758             # ],
759             # [ 'VARCHAR', 12, 1024*1024, '\'', '\'', 'max length', 1, 1, 3,
760             # undef, '0', '0', undef, undef, undef, 12, undef, undef
761             # ]
762             # ];
763             }
764              
765             my @COLUMN_INFO = qw(
766             TABLE_CAT
767             TABLE_SCHEM
768             TABLE_NAME
769             COLUMN_NAME
770             DATA_TYPE
771             TYPE_NAME
772             COLUMN_SIZE
773             BUFFER_LENGTH
774             DECIMAL_DIGITS
775             NUM_PREC_RADIX
776             NULLABLE
777             REMARKS
778             COLUMN_DEF
779             SQL_DATA_TYPE
780             SQL_DATETIME_SUB
781             CHAR_OCTET_LENGTH
782             ORDINAL_POSITION
783             IS_NULLABLE
784             );
785              
786             sub column_info {
787 7     7   4375 my ($dbh, $cat_val, $sch_val, $tbl_val, $col_val) = @_;
788              
789 7 100 100     48 if ( defined $col_val and $col_val eq '%' ) {
790 1         2 $col_val = undef;
791             }
792              
793             # Get a list of all tables ordered by TABLE_SCHEM, TABLE_NAME
794 7         16 my $sql = <<'END_SQL';
795             SELECT TABLE_SCHEM, tbl_name TABLE_NAME
796             FROM (
797             SELECT 'main' TABLE_SCHEM, tbl_name
798             FROM sqlite_master
799             WHERE type IN ('table','view')
800             UNION ALL
801             SELECT 'temp' TABLE_SCHEM, tbl_name
802             FROM sqlite_temp_master
803             WHERE type IN ('table','view')
804             END_SQL
805              
806 7         22 for my $db_name (_attached_database_list($dbh)) {
807 2         10 $sql .= <<"END_SQL";
808             UNION ALL
809             SELECT '$db_name' TABLE_SCHEM, tbl_name
810             FROM "$db_name".sqlite_master
811             WHERE type IN ('table','view')
812             END_SQL
813             }
814              
815 7         58 $sql .= <<'END_SQL';
816             UNION ALL
817             SELECT 'main' TABLE_SCHEM, 'sqlite_master' tbl_name
818             UNION ALL
819             SELECT 'temp' TABLE_SCHEM, 'sqlite_temp_master' tbl_name
820             )
821             END_SQL
822              
823 7         14 my @where;
824 7 50       21 if ( defined $sch_val ) {
825 0         0 push @where, "TABLE_SCHEM LIKE '$sch_val'";
826             }
827 7 50       18 if ( defined $tbl_val ) {
828 7         26 push @where, "TABLE_NAME LIKE '$tbl_val'";
829             }
830 7 50       33 $sql .= ' WHERE ' . join("\n AND ", @where ) . "\n" if @where;
831 7         16 $sql .= " ORDER BY TABLE_SCHEM, TABLE_NAME\n";
832 7 50       25 my $sth_tables = $dbh->prepare($sql) or return undef;
833 7 50       278 $sth_tables->execute or return undef;
834              
835             # Taken from Fey::Loader::SQLite
836 7         19 my @cols;
837 7         101 while ( my ($schema, $table) = $sth_tables->fetchrow_array ) {
838 9 50       55 my $sth_columns = $dbh->prepare(qq{PRAGMA "$schema".table_info("$table")}) or return;
839 9 50       91 $sth_columns->execute or return;
840              
841 9         182 for ( my $position = 1; my $col_info = $sth_columns->fetchrow_hashref; $position++ ) {
842 20 100       50 if ( defined $col_val ) {
843             # This must do a LIKE comparison
844 14 50       70 my $sth = $dbh->prepare("SELECT '$col_info->{name}' LIKE '$col_val'") or return undef;
845 14 50       134 $sth->execute or return undef;
846             # Skip columns that don't match $col_val
847 14 100       323 next unless ($sth->fetchrow_array)[0];
848             }
849              
850             my %col = (
851             TABLE_SCHEM => $schema,
852             TABLE_NAME => $table,
853             COLUMN_NAME => $col_info->{name},
854 13         76 ORDINAL_POSITION => $position,
855             );
856              
857 13         24 my $type = $col_info->{type};
858 13 100       77 if ( $type =~ s/(\w+)\s*\(\s*(\d+)(?:\s*,\s*(\d+))?\s*\)/$1/ ) {
859 7         18 $col{COLUMN_SIZE} = $2;
860 7         14 $col{DECIMAL_DIGITS} = $3;
861             }
862              
863 13         25 $col{TYPE_NAME} = $type;
864              
865 13 50       39 if ( defined $col_info->{dflt_value} ) {
866             $col{COLUMN_DEF} = $col_info->{dflt_value}
867 0         0 }
868              
869 13 100       28 if ( $col_info->{notnull} ) {
870 2         5 $col{NULLABLE} = 0;
871 2         4 $col{IS_NULLABLE} = 'NO';
872             } else {
873 11         20 $col{NULLABLE} = 1;
874 11         19 $col{IS_NULLABLE} = 'YES';
875             }
876              
877 13         187 push @cols, \%col;
878             }
879 9         168 $sth_columns->finish;
880             }
881 7         22 $sth_tables->finish;
882              
883 7 50       37 my $sponge = DBI->connect("DBI:Sponge:", '','')
884             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
885             $sponge->prepare( "column_info", {
886 7 50       7326 rows => [ map { [ @{$_}{@COLUMN_INFO} ] } @cols ],
  13         25  
  13         151  
887             NUM_OF_FIELDS => scalar @COLUMN_INFO,
888             NAME => [ @COLUMN_INFO ],
889             } ) or return $dbh->DBI::set_err(
890             $sponge->err,
891             $sponge->errstr,
892             );
893             }
894              
895             #======================================================================
896             # An internal tied hash package used for %DBD::SQLeet::COLLATION, to
897             # prevent people from unintentionally overriding globally registered collations.
898              
899             package # hide from PAUSE
900             DBD::SQLeet::_WriteOnceHash;
901              
902             require Tie::Hash;
903              
904             our @ISA = qw(Tie::StdHash);
905              
906             sub TIEHASH {
907 109     109   435 bless {}, $_[0];
908             }
909              
910             sub STORE {
911 223 100   223   2408 ! exists $_[0]->{$_[1]} or die "entry $_[1] already registered";
912 221         678 $_[0]->{$_[1]} = $_[2];
913             }
914              
915             sub DELETE {
916 1     1   504 die "deletion of entry $_[1] is forbidden";
917             }
918              
919             1;
920              
921             __END__