File Coverage

blib/lib/DBD/SQLeet.pm
Criterion Covered Total %
statement 303 317 95.5
branch 150 208 72.1
condition 85 137 62.0
subroutine 25 28 89.2
pod 0 1 0.0
total 563 691 81.4


line stmt bran cond sub pod time code
1             package DBD::SQLeet;
2              
3 101     101   8844793 use 5.006;
  101         1357  
4 101     101   561 use strict;
  101         186  
  101         2051  
5 101     101   472 use warnings;
  101         172  
  101         3897  
6              
7 101     101   1825 use DBI 1.57 ();
  101         19011  
  101         1871  
8 101     101   536 use DynaLoader ();
  101         211  
  101         10747  
9              
10             our $VERSION = '0.24.2';
11             our @ISA = 'DynaLoader';
12              
13             # sqlite_version cache (set in the XS bootstrap)
14             our ($sqlite_version, $sqlite_version_number);
15              
16             # not sure if we still need these...
17             our ($err, $errstr);
18              
19             __PACKAGE__->bootstrap($VERSION);
20              
21             # New or old API?
22 101     101   707 use constant NEWAPI => ($DBI::VERSION >= 1.608);
  101         194  
  101         18501  
23              
24             # global registry of collation functions, initialized with 2 builtins
25             our %COLLATION;
26             tie %COLLATION, 'DBD::SQLeet::_WriteOnceHash';
27             $COLLATION{perl} = sub { $_[0] cmp $_[1] };
28 101     101   64987 $COLLATION{perllocale} = sub { use locale; $_[0] cmp $_[1] };
  101         59089  
  101         554  
29              
30             our $drh;
31             my $methods_are_installed = 0;
32              
33             sub driver {
34 95 50   95 0 97085 return $drh if $drh;
35              
36 95 50 50     886 if (!$methods_are_installed && DBD::SQLeet::NEWAPI ) {
37 95         477 DBI->setup_driver('DBD::SQLeet');
38              
39 95         3632 DBD::SQLeet::db->install_method('sqlite_last_insert_rowid');
40 95         6076 DBD::SQLeet::db->install_method('sqlite_busy_timeout');
41 95         3360 DBD::SQLeet::db->install_method('sqlite_create_function');
42 95         3065 DBD::SQLeet::db->install_method('sqlite_create_aggregate');
43 95         3034 DBD::SQLeet::db->install_method('sqlite_create_collation');
44 95         3206 DBD::SQLeet::db->install_method('sqlite_collation_needed');
45 95         3063 DBD::SQLeet::db->install_method('sqlite_progress_handler');
46 95         3170 DBD::SQLeet::db->install_method('sqlite_commit_hook');
47 95         3174 DBD::SQLeet::db->install_method('sqlite_rollback_hook');
48 95         3110 DBD::SQLeet::db->install_method('sqlite_update_hook');
49 95         3124 DBD::SQLeet::db->install_method('sqlite_set_authorizer');
50 95         3138 DBD::SQLeet::db->install_method('sqlite_backup_from_file');
51 95         3203 DBD::SQLeet::db->install_method('sqlite_backup_to_file');
52 95         3478 DBD::SQLeet::db->install_method('sqlite_enable_load_extension');
53 95         3338 DBD::SQLeet::db->install_method('sqlite_load_extension');
54 95         3205 DBD::SQLeet::db->install_method('sqlite_register_fts3_perl_tokenizer');
55 95         3677 DBD::SQLeet::db->install_method('sqlite_trace', { O => 0x0004 });
56 95         3715 DBD::SQLeet::db->install_method('sqlite_profile', { O => 0x0004 });
57 95         3609 DBD::SQLeet::db->install_method('sqlite_table_column_metadata', { O => 0x0004 });
58 95         3552 DBD::SQLeet::db->install_method('sqlite_db_filename', { O => 0x0004 });
59 95         3534 DBD::SQLeet::db->install_method('sqlite_db_status', { O => 0x0004 });
60 95         3748 DBD::SQLeet::st->install_method('sqlite_st_status', { O => 0x0004 });
61 95         3599 DBD::SQLeet::db->install_method('sqlite_create_module');
62              
63 95         4213 $methods_are_installed++;
64             }
65              
66 95         1108 $drh = DBI::_new_drh( "$_[0]::dr", {
67             Name => 'SQLite',
68             Version => $VERSION,
69             Attribution => 'DBD::SQLeet by Dimitar D. Mitov et al',
70             } );
71              
72 95         4399 return $drh;
73             }
74              
75             sub CLONE {
76 0     0   0 undef $drh;
77             }
78              
79              
80             package # hide from PAUSE
81             DBD::SQLeet::dr;
82              
83             sub connect {
84 254     254   3467909 my ($drh, $dbname, $user, $auth, $attr) = @_;
85              
86             # Default PrintWarn to the value of $^W
87             # unless ( defined $attr->{PrintWarn} ) {
88             # $attr->{PrintWarn} = $^W ? 1 : 0;
89             # }
90              
91 254         1876 my $dbh = DBI::_new_dbh( $drh, {
92             Name => $dbname,
93             } );
94              
95 254         10057 my $real = $dbname;
96 254 100       1466 if ( $dbname =~ /=/ ) {
97 238         1104 foreach my $attrib ( split(/;/, $dbname) ) {
98 240         915 my ($key, $value) = split(/=/, $attrib, 2);
99 240 100       1743 if ( $key =~ /^(?:db(?:name)?|database)$/ ) {
    100          
100 227         761 $real = $value;
101             } elsif ( $key eq 'uri' ) {
102 6         8 $real = $value;
103 6         26 $attr->{sqlite_open_flags} |= DBD::SQLeet::OPEN_URI();
104             } else {
105 7         24 $attr->{$key} = $value;
106             }
107             }
108             }
109              
110 254 100       1102 if (my $flags = $attr->{sqlite_open_flags}) {
111 19 100       70 unless ($flags & (DBD::SQLeet::OPEN_READONLY() | DBD::SQLeet::OPEN_READWRITE())) {
112 13         29 $attr->{sqlite_open_flags} |= DBD::SQLeet::OPEN_READWRITE() | DBD::SQLeet::OPEN_CREATE();
113             }
114             }
115              
116             # To avoid unicode and long file name problems on Windows,
117             # convert to the shortname if the file (or parent directory) exists.
118 254 0 33     1571 if ( $^O =~ /MSWin32/ and $real ne ':memory:' and $real ne '' and $real !~ /^file:/ and !-f $real ) {
      33        
      0        
      0        
119 0         0 require File::Basename;
120 0         0 my ($file, $dir, $suffix) = File::Basename::fileparse($real);
121             # We are creating a new file.
122             # Does the directory it's in at least exist?
123 0 0       0 if ( -d $dir ) {
124 0         0 require Win32;
125 0         0 $real = join '', grep { defined } Win32::GetShortPathName($dir), $file, $suffix;
  0         0  
126             } else {
127             # SQLite can't do mkpath anyway.
128             # So let it go through as it and fail.
129             }
130             }
131              
132             # Hand off to the actual login function
133 254 100       263285 DBD::SQLeet::db::_login($dbh, $real, $user, $auth, $attr) or return undef;
134              
135             # Register the on-demand collation installer, REGEXP function and
136             # perl tokenizer
137 248         847 if ( DBD::SQLeet::NEWAPI ) {
138 248         3696 $dbh->sqlite_collation_needed( \&install_collation );
139 248         3342 $dbh->sqlite_create_function( "REGEXP", 2, \®exp );
140 248         37668 $dbh->sqlite_register_fts3_perl_tokenizer();
141             } else {
142             $dbh->func( \&install_collation, "collation_needed" );
143             $dbh->func( "REGEXP", 2, \®exp, "create_function" );
144             $dbh->func( "register_fts3_perl_tokenizer" );
145             }
146              
147             # HACK: Since PrintWarn = 0 doesn't seem to actually prevent warnings
148             # in DBD::SQLeet we set Warn to false if PrintWarn is false.
149              
150             # NOTE: According to the explanation by timbunce,
151             # "Warn is meant to report on bad practices or problems with
152             # the DBI itself (hence always on by default), while PrintWarn
153             # is meant to report warnings coming from the database."
154             # That is, if you want to disable an ineffective rollback warning
155             # etc (due to bad practices), you should turn off Warn,
156             # and to silence other warnings, turn off PrintWarn.
157             # Warn and PrintWarn are independent, and turning off PrintWarn
158             # does not silence those warnings that should be controlled by
159             # Warn.
160              
161             # unless ( $attr->{PrintWarn} ) {
162             # $attr->{Warn} = 0;
163             # }
164              
165 248         1938 return $dbh;
166             }
167              
168             sub install_collation {
169 12     12   31 my $dbh = shift;
170 12         22 my $name = shift;
171 12         71 my $collation = $DBD::SQLeet::COLLATION{$name};
172 12 50       88 unless ($collation) {
173 0 0       0 warn "Can't install unknown collation: $name" if $dbh->{PrintWarn};
174 0         0 return;
175             }
176 12         17 if ( DBD::SQLeet::NEWAPI ) {
177 12         72 $dbh->sqlite_create_collation( $name => $collation );
178             } else {
179             $dbh->func( $name => $collation, "create_collation" );
180             }
181             }
182              
183             # default implementation for sqlite 'REGEXP' infix operator.
184             # Note : args are reversed, i.e. "a REGEXP b" calls REGEXP(b, a)
185             # (see http://www.sqlite.org/vtab.html#xfindfunction)
186             sub regexp {
187 101     101   97170 use locale;
  101         260  
  101         461  
188 392 100 66 392   2827 return if !defined $_[0] || !defined $_[1];
189 264         1726 return scalar($_[1] =~ $_[0]);
190             }
191              
192             package # hide from PAUSE
193             DBD::SQLeet::db;
194              
195             sub prepare {
196 1137     1137   934843 my $dbh = shift;
197 1137         1912 my $sql = shift;
198 1137 50       4182 $sql = '' unless defined $sql;
199              
200 1137         4338 my $sth = DBI::_new_sth( $dbh, {
201             Statement => $sql,
202             } );
203              
204 1137 100       88713 DBD::SQLeet::st::_prepare($sth, $sql, @_) or return undef;
205              
206 1127         16076 return $sth;
207             }
208              
209             sub do {
210 2877     2877   2833687 my ($dbh, $statement, $attr, @bind_values) = @_;
211              
212             # shortcut
213 2877         10700 my $allow_multiple_statements = $dbh->FETCH('sqlite_allow_multiple_statements');
214 2877 100 100     15474 if (defined $statement && !defined $attr && !@bind_values) {
      100        
215             # _do() (i.e. sqlite3_exec()) runs semicolon-separate SQL
216             # statements, which is handy but insecure sometimes.
217             # Use this only when it's safe or explicitly allowed.
218 2778 100 66     8088 if (index($statement, ';') == -1 or $allow_multiple_statements) {
219 2755         8239456 return DBD::SQLeet::db::_do($dbh, $statement);
220             }
221             }
222              
223 122         224 my @copy = @{[@bind_values]};
  122         355  
224 122         276 my $rows = 0;
225              
226 122         269 while ($statement) {
227 127 100       485 my $sth = $dbh->prepare($statement, $attr) or return undef;
228 119 100       9066 $sth->execute(splice @copy, 0, $sth->{NUM_OF_PARAMS}) or return undef;
229 115         1126 $rows += $sth->rows;
230             # XXX: not sure why but $dbh->{sqlite...} wouldn't work here
231 115 100       1631 last unless $allow_multiple_statements;
232 8         173 $statement = $sth->{sqlite_unprepared_statements};
233             }
234              
235             # always return true if no error
236 110 100       912 return ($rows == 0) ? "0E0" : $rows;
237             }
238              
239             sub ping {
240 1     1   935 my $dbh = shift;
241              
242             # $file may be undef (ie. in-memory/temporary database)
243 1         24 my $file = DBD::SQLeet::NEWAPI ? $dbh->sqlite_db_filename
244             : $dbh->func("db_filename");
245              
246 1 50 33     14 return 0 if $file && !-f $file;
247 1 50       16 return $dbh->FETCH('Active') ? 1 : 0;
248             }
249              
250             sub _get_version {
251 0     0   0 return ( DBD::SQLeet::db::FETCH($_[0], 'sqlite_version') );
252             }
253              
254             my %info = (
255             17 => 'SQLite', # SQL_DBMS_NAME
256             18 => \&_get_version, # SQL_DBMS_VER
257             29 => '"', # SQL_IDENTIFIER_QUOTE_CHAR
258             );
259              
260             sub get_info {
261 63     63   452 my($dbh, $info_type) = @_;
262 63         192 my $v = $info{int($info_type)};
263 63 50       262 $v = $v->($dbh) if ref $v eq 'CODE';
264 63         176 return $v;
265             }
266              
267             sub _attached_database_list {
268 21     21   39 my $dbh = shift;
269 21         42 my @attached;
270              
271 21 50       71 my $sth_databases = $dbh->prepare( 'PRAGMA database_list' ) or return;
272 21 50       191 $sth_databases->execute or return;
273 21         505 while ( my $db_info = $sth_databases->fetchrow_hashref ) {
274 47 100       627 push @attached, $db_info->{name} if $db_info->{seq} >= 2;
275             }
276 21         283 return @attached;
277             }
278              
279             # SQL/CLI (ISO/IEC JTC 1/SC 32 N 0595), 6.63 Tables
280             # Based on DBD::Oracle's
281             # See also http://www.ch-werner.de/sqliteodbc/html/sqlite3odbc_8c.html#a213
282             sub table_info {
283 16     16   11328 my ($dbh, $cat_val, $sch_val, $tbl_val, $typ_val, $attr) = @_;
284              
285 16         34 my @where = ();
286 16         25 my $sql;
287 16 100 100     155 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        
288             && defined($sch_val) && $sch_val eq ''
289             && defined($tbl_val) && $tbl_val eq '') { # Rule 19a
290 1         2 $sql = <<'END_SQL';
291             SELECT NULL TABLE_CAT
292             , NULL TABLE_SCHEM
293             , NULL TABLE_NAME
294             , NULL TABLE_TYPE
295             , NULL REMARKS
296             END_SQL
297             } elsif ( defined($cat_val) && $cat_val eq ''
298             && defined($sch_val) && $sch_val eq '%'
299             && defined($tbl_val) && $tbl_val eq '') { # Rule 19b
300 1         5 $sql = <<'END_SQL';
301             SELECT NULL TABLE_CAT
302             , t.tn TABLE_SCHEM
303             , NULL TABLE_NAME
304             , NULL TABLE_TYPE
305             , NULL REMARKS
306             FROM (
307             SELECT 'main' tn
308             UNION SELECT 'temp' tn
309             END_SQL
310 1         5 for my $db_name (_attached_database_list($dbh)) {
311 0         0 $sql .= " UNION SELECT '$db_name' tn\n";
312             }
313 1         4 $sql .= ") t\n";
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             } else {
333 13         22 $sql = <<'END_SQL';
334             SELECT *
335             FROM
336             (
337             SELECT NULL TABLE_CAT
338             , TABLE_SCHEM
339             , tbl_name TABLE_NAME
340             , TABLE_TYPE
341             , NULL REMARKS
342             , sql sqlite_sql
343             FROM (
344             SELECT 'main' TABLE_SCHEM, tbl_name, upper(type) TABLE_TYPE, sql
345             FROM sqlite_master
346             UNION ALL
347             SELECT 'temp' TABLE_SCHEM, tbl_name, 'LOCAL TEMPORARY' TABLE_TYPE, sql
348             FROM sqlite_temp_master
349             END_SQL
350              
351 13         26 for my $db_name (_attached_database_list($dbh)) {
352 9         37 $sql .= <<"END_SQL";
353             UNION ALL
354             SELECT '$db_name' TABLE_SCHEM, tbl_name, upper(type) TABLE_TYPE, sql
355             FROM "$db_name".sqlite_master
356             END_SQL
357             }
358              
359 13         33 $sql .= <<'END_SQL';
360             UNION ALL
361             SELECT 'main' TABLE_SCHEM, 'sqlite_master' tbl_name, 'SYSTEM TABLE' TABLE_TYPE, NULL sql
362             UNION ALL
363             SELECT 'temp' TABLE_SCHEM, 'sqlite_temp_master' tbl_name, 'SYSTEM TABLE' TABLE_TYPE, NULL sql
364             )
365             )
366             END_SQL
367 13 50       43 $attr = {} unless ref $attr eq 'HASH';
368 13 50       38 my $escape = defined $attr->{Escape} ? " ESCAPE '$attr->{Escape}'" : '';
369 13 100       30 if (defined $sch_val) {
370 3         8 push @where, "TABLE_SCHEM LIKE '$sch_val'$escape";
371             }
372              
373 13 100       30 if (defined $tbl_val) {
374 11         33 push @where, "TABLE_NAME LIKE '$tbl_val'$escape";
375             }
376              
377 13 100       29 if (defined $typ_val) {
378 1         2 my $table_type_list;
379 1         4 $typ_val =~ s/^\s+//;
380 1         7 $typ_val =~ s/\s+$//;
381 1         4 my @ttype_list = split (/\s*,\s*/, $typ_val);
382 1         2 foreach my $table_type (@ttype_list) {
383 1 50       4 if ($table_type !~ /^'.*'$/) {
384 1         4 $table_type = "'" . $table_type . "'";
385             }
386             }
387 1         4 $table_type_list = join(', ', @ttype_list);
388 1 50       7 push @where, "TABLE_TYPE IN (\U$table_type_list)" if $table_type_list;
389             }
390 13 100       50 $sql .= ' WHERE ' . join("\n AND ", @where ) . "\n" if @where;
391 13         24 $sql .= " ORDER BY TABLE_TYPE, TABLE_SCHEM, TABLE_NAME\n";
392             }
393 16 50       56 my $sth = $dbh->prepare($sql) or return undef;
394 16 50       638 $sth->execute or return undef;
395 16         339 $sth;
396             }
397              
398             sub primary_key_info {
399 33     33   18595 my ($dbh, $catalog, $schema, $table, $attr) = @_;
400              
401 33         332 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}});
402              
403 33         3808 my @pk_info;
404 33         110 for my $database (@$databases) {
405 49         110 my $dbname = $database->{name};
406 49 100 66     168 next if defined $schema && $schema ne '%' && $schema ne $dbname;
      100        
407              
408 43         185 my $quoted_dbname = $dbh->quote_identifier($dbname);
409              
410 43 100       1288 my $master_table =
    100          
411             ($dbname eq 'main') ? 'sqlite_master' :
412             ($dbname eq 'temp') ? 'sqlite_temp_master' :
413             $quoted_dbname.'.sqlite_master';
414              
415 43 50       185 my $sth = $dbh->prepare("SELECT name, sql FROM $master_table WHERE type = ?") or return;
416 43 50       1214 $sth->execute("table") or return;
417 43         1101 while(my $row = $sth->fetchrow_hashref) {
418 55         156 my $tbname = $row->{name};
419 55 100 66     741 next if defined $table && $table ne '%' && $table ne $tbname;
      100        
420              
421 33         153 my $quoted_tbname = $dbh->quote_identifier($tbname);
422 33 50       1051 my $t_sth = $dbh->prepare("PRAGMA $quoted_dbname.table_info($quoted_tbname)") or return;
423 33 50       532 $t_sth->execute or return;
424 33         78 my @pk;
425 33         749 while(my $col = $t_sth->fetchrow_hashref) {
426 52 100       918 push @pk, $col->{name} if $col->{pk};
427             }
428              
429             # If there're multiple primary key columns, we need to
430             # find their order from one of the auto-generated unique
431             # indices (note that single column integer primary key
432             # doesn't create an index).
433 33 100 66     202 if (@pk > 1 and $row->{sql} =~ /\bPRIMARY\s+KEY\s*\(\s*
434             (
435             (?:
436             (
437             [a-z_][a-z0-9_]*
438             | (["'`])(?:\3\3|(?!\3).)+?\3(?!\3)
439             | \[[^\]]+\]
440             )
441             \s*,\s*
442             )+
443             (
444             [a-z_][a-z0-9_]*
445             | (["'`])(?:\5\5|(?!\5).)+?\5(?!\5)
446             | \[[^\]]+\]
447             )
448             )
449             \s*\)/six) {
450 4         16 my $pk_sql = $1;
451 4         11 @pk = ();
452 4         28 while($pk_sql =~ /
453             (
454             [a-z_][a-z0-9_]*
455             | (["'`])(?:\2\2|(?!\2).)+?\2(?!\2)
456             | \[([^\]]+)\]
457             )
458             (?:\s*,\s*|$)
459             /sixg) {
460 9         28 my($col, $quote, $brack) = ($1, $2, $3);
461 9 100       26 if ( defined $quote ) {
    100          
462             # Dequote "'`
463 2         5 $col = substr $col, 1, -1;
464 2         26 $col =~ s/$quote$quote/$quote/g;
465             } elsif ( defined $brack ) {
466             # Dequote []
467 1         2 $col = $brack;
468             }
469 9         43 push @pk, $col;
470             }
471             }
472              
473 33 100       192 my $key_name = $row->{sql} =~ /\bCONSTRAINT\s+(\S+|"[^"]+")\s+PRIMARY\s+KEY\s*\(/i ? $1 : 'PRIMARY KEY';
474 33         52 my $key_seq = 0;
475 33         85 foreach my $pk_field (@pk) {
476 38         1080 push @pk_info, {
477             TABLE_SCHEM => $dbname,
478             TABLE_NAME => $tbname,
479             COLUMN_NAME => $pk_field,
480             KEY_SEQ => ++$key_seq,
481             PK_NAME => $key_name,
482             };
483             }
484             }
485             }
486              
487 33 50       166 my $sponge = DBI->connect("DBI:Sponge:", '','')
488             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
489 33         18424 my @names = qw(TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME);
490             my $sth = $sponge->prepare( "primary_key_info", {
491 33 50       96 rows => [ map { [ @{$_}{@names} ] } @pk_info ],
  38         58  
  38         339  
492             NUM_OF_FIELDS => scalar @names,
493             NAME => \@names,
494             }) or return $dbh->DBI::set_err(
495             $sponge->err,
496             $sponge->errstr,
497             );
498 33         2909 return $sth;
499             }
500              
501             our %DBI_code_for_rule = (
502             # from DBI doc; curiously, they are not exported
503             # by the DBI module.
504             # codes for update/delete constraints
505             'CASCADE' => 0,
506             'RESTRICT' => 1,
507             'SET NULL' => 2,
508             'NO ACTION' => 3,
509             'SET DEFAULT' => 4,
510              
511             # codes for deferrability
512             'INITIALLY DEFERRED' => 5,
513             'INITIALLY IMMEDIATE' => 6,
514             'NOT DEFERRABLE' => 7,
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   10823 my ($dbh, $pk_catalog, $pk_schema, $pk_table, $fk_catalog, $fk_schema, $fk_table) = @_;
558              
559 10 50       135 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}}) or return;
560              
561 10         1048 my @fk_info;
562             my %table_info;
563 10         23 for my $database (@$databases) {
564 20         43 my $dbname = $database->{name};
565 20 100 66     67 next if defined $fk_schema && $fk_schema ne '%' && $fk_schema ne $dbname;
      100        
566              
567 19         73 my $quoted_dbname = $dbh->quote_identifier($dbname);
568 19 100       523 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       115 my $tables = $dbh->selectall_arrayref("SELECT name FROM $master_table WHERE type = ?", undef, "table") or return;
574 19         237 for my $table (@$tables) {
575 34         59 my $tbname = $table->[0];
576 34 100 66     147 next if defined $fk_table && $fk_table ne '%' && $fk_table ne $tbname;
      100        
577              
578 14         54 my $quoted_tbname = $dbh->quote_identifier($tbname);
579 14 50       365 my $sth = $dbh->prepare("PRAGMA $quoted_dbname.foreign_key_list($quoted_tbname)") or return;
580 14 50       132 $sth->execute or return;
581 14         310 while(my $row = $sth->fetchrow_hashref) {
582 18 100 66     133 next if defined $pk_table && $pk_table ne '%' && $pk_table ne $row->{table};
      100        
583              
584 15 100       39 unless ($table_info{$row->{table}}) {
585 13         50 my $quoted_tb = $dbh->quote_identifier($row->{table});
586 13         320 for my $db (@$databases) {
587 15         49 my $quoted_db = $dbh->quote_identifier($db->{name});
588 15 50       322 my $t_sth = $dbh->prepare("PRAGMA $quoted_db.table_info($quoted_tb)") or return;
589 15 50       156 $t_sth->execute or return;
590 15         86 my $cols = {};
591 15         339 while(my $r = $t_sth->fetchrow_hashref) {
592 30         440 $cols->{$r->{name}} = $r->{pk};
593             }
594 15 100       82 if (keys %$cols) {
595             $table_info{$row->{table}} = {
596             schema => $db->{name},
597 13         52 columns => $cols,
598             };
599 13         130 last;
600             }
601             }
602             }
603              
604 15 100 66     87 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       399 UNIQUE_OR_PRIMARY => $table_info{$row->{table}}{columns}{$row->{to}} ? 'PRIMARY' : 'UNIQUE',
622             };
623             }
624             }
625             }
626              
627 10 50       47 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       3920 rows => [ map { [@{$_}{@FOREIGN_KEY_INFO_ODBC} ] } @fk_info ],
  13         20  
  13         116  
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         1157 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   32264 my ($dbh, $catalog, $schema, $table, $unique_only, $quick) = @_;
658              
659 4 50       140 my $databases = $dbh->selectall_arrayref("PRAGMA database_list", {Slice => {}}) or return;
660              
661 4         486 my @statistics_info;
662 4         11 for my $database (@$databases) {
663 8         17 my $dbname = $database->{name};
664 8 0 33     25 next if defined $schema && $schema ne '%' && $schema ne $dbname;
      33        
665              
666 8         35 my $quoted_dbname = $dbh->quote_identifier($dbname);
667 8 50       214 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       49 my $tables = $dbh->selectall_arrayref("SELECT name FROM $master_table WHERE type = ?", undef, "table") or return;
673 8         92 for my $table_ref (@$tables) {
674 8         18 my $tbname = $table_ref->[0];
675 8 100 33     60 next if defined $table && $table ne '%' && uc($table) ne uc($tbname);
      66        
676              
677 4         18 my $quoted_tbname = $dbh->quote_identifier($tbname);
678 4 50       103 my $sth = $dbh->prepare("PRAGMA $quoted_dbname.index_list($quoted_tbname)") or return;
679 4 50       40 $sth->execute or return;
680 4         81 while(my $row = $sth->fetchrow_hashref) {
681              
682 16 100 100     98 next if $unique_only && !$row->{unique};
683 12         37 my $quoted_idx = $dbh->quote_identifier($row->{name});
684 12         260 for my $db (@$databases) {
685 24         70 my $quoted_db = $dbh->quote_identifier($db->{name});
686 24 50       513 my $i_sth = $dbh->prepare("PRAGMA $quoted_db.index_info($quoted_idx)") or return;
687 24 50       151 $i_sth->execute or return;
688 24         41 my $cols = {};
689 24         505 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       355 ASC_OR_DESC => undef,
701             CARDINALITY => undef,
702             PAGES => undef,
703             FILTER_CONDITION => undef,
704             };
705             }
706             }
707             }
708             }
709             }
710              
711 4 50       25 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       2869 rows => [ map { [@{$_}{@STATISTICS_INFO_ODBC} ] } @statistics_info ],
  16         20  
  16         115  
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         492 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             }
727              
728             my @COLUMN_INFO = qw(
729             TABLE_CAT
730             TABLE_SCHEM
731             TABLE_NAME
732             COLUMN_NAME
733             DATA_TYPE
734             TYPE_NAME
735             COLUMN_SIZE
736             BUFFER_LENGTH
737             DECIMAL_DIGITS
738             NUM_PREC_RADIX
739             NULLABLE
740             REMARKS
741             COLUMN_DEF
742             SQL_DATA_TYPE
743             SQL_DATETIME_SUB
744             CHAR_OCTET_LENGTH
745             ORDINAL_POSITION
746             IS_NULLABLE
747             );
748              
749             sub column_info {
750 7     7   5414 my ($dbh, $cat_val, $sch_val, $tbl_val, $col_val) = @_;
751              
752 7 100 100     49 if ( defined $col_val and $col_val eq '%' ) {
753 1         2 $col_val = undef;
754             }
755              
756             # Get a list of all tables ordered by TABLE_SCHEM, TABLE_NAME
757 7         14 my $sql = <<'END_SQL';
758             SELECT TABLE_SCHEM, tbl_name TABLE_NAME
759             FROM (
760             SELECT 'main' TABLE_SCHEM, tbl_name
761             FROM sqlite_master
762             WHERE type IN ('table','view')
763             UNION ALL
764             SELECT 'temp' TABLE_SCHEM, tbl_name
765             FROM sqlite_temp_master
766             WHERE type IN ('table','view')
767             END_SQL
768              
769 7         20 for my $db_name (_attached_database_list($dbh)) {
770 2         13 $sql .= <<"END_SQL";
771             UNION ALL
772             SELECT '$db_name' TABLE_SCHEM, tbl_name
773             FROM "$db_name".sqlite_master
774             WHERE type IN ('table','view')
775             END_SQL
776             }
777              
778 7         20 $sql .= <<'END_SQL';
779             UNION ALL
780             SELECT 'main' TABLE_SCHEM, 'sqlite_master' tbl_name
781             UNION ALL
782             SELECT 'temp' TABLE_SCHEM, 'sqlite_temp_master' tbl_name
783             )
784             END_SQL
785              
786 7         12 my @where;
787 7 50       19 if ( defined $sch_val ) {
788 0         0 push @where, "TABLE_SCHEM LIKE '$sch_val'";
789             }
790 7 50       19 if ( defined $tbl_val ) {
791 7         23 push @where, "TABLE_NAME LIKE '$tbl_val'";
792             }
793 7 50       39 $sql .= ' WHERE ' . join("\n AND ", @where ) . "\n" if @where;
794 7         16 $sql .= " ORDER BY TABLE_SCHEM, TABLE_NAME\n";
795 7 50       26 my $sth_tables = $dbh->prepare($sql) or return undef;
796 7 50       300 $sth_tables->execute or return undef;
797              
798             # Taken from Fey::Loader::SQLite
799 7         22 my @cols;
800 7         116 while ( my ($schema, $table) = $sth_tables->fetchrow_array ) {
801 9 50       125 my $sth_columns = $dbh->prepare(qq{PRAGMA "$schema".table_info("$table")}) or return;
802 9 50       115 $sth_columns->execute or return;
803              
804 9         207 for ( my $position = 1; my $col_info = $sth_columns->fetchrow_hashref; $position++ ) {
805 20 100       102 if ( defined $col_val ) {
806             # This must do a LIKE comparison
807 14 50       83 my $sth = $dbh->prepare("SELECT '$col_info->{name}' LIKE '$col_val'") or return undef;
808 14 50       137 $sth->execute or return undef;
809             # Skip columns that don't match $col_val
810 14 100       392 next unless ($sth->fetchrow_array)[0];
811             }
812              
813             my %col = (
814             TABLE_SCHEM => $schema,
815             TABLE_NAME => $table,
816             COLUMN_NAME => $col_info->{name},
817 13         88 ORDINAL_POSITION => $position,
818             );
819              
820 13         28 my $type = $col_info->{type};
821 13 100       93 if ( $type =~ s/(\w+)\s*\(\s*(\d+)(?:\s*,\s*(\d+))?\s*\)/$1/ ) {
822 7         20 $col{COLUMN_SIZE} = $2;
823 7         22 $col{DECIMAL_DIGITS} = $3;
824             }
825              
826 13         29 $col{TYPE_NAME} = $type;
827              
828 13 50       33 if ( defined $col_info->{dflt_value} ) {
829             $col{COLUMN_DEF} = $col_info->{dflt_value}
830 0         0 }
831              
832 13 100       30 if ( $col_info->{notnull} ) {
833 2         6 $col{NULLABLE} = 0;
834 2         4 $col{IS_NULLABLE} = 'NO';
835             } else {
836 11         25 $col{NULLABLE} = 1;
837 11         21 $col{IS_NULLABLE} = 'YES';
838             }
839              
840 13         208 push @cols, \%col;
841             }
842 9         159 $sth_columns->finish;
843             }
844 7         29 $sth_tables->finish;
845              
846 7 50       37 my $sponge = DBI->connect("DBI:Sponge:", '','')
847             or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
848             $sponge->prepare( "column_info", {
849 7 50       8237 rows => [ map { [ @{$_}{@COLUMN_INFO} ] } @cols ],
  13         24  
  13         164  
850             NUM_OF_FIELDS => scalar @COLUMN_INFO,
851             NAME => [ @COLUMN_INFO ],
852             } ) or return $dbh->DBI::set_err(
853             $sponge->err,
854             $sponge->errstr,
855             );
856             }
857              
858             #======================================================================
859             # An internal tied hash package used for %DBD::SQLeet::COLLATION, to
860             # prevent people from unintentionally overriding globally registered collations.
861              
862             package # hide from PAUSE
863             DBD::SQLeet::_WriteOnceHash;
864              
865             require Tie::Hash;
866              
867             our @ISA = qw(Tie::StdHash);
868              
869             sub TIEHASH {
870 101     101   461 bless {}, $_[0];
871             }
872              
873             sub STORE {
874 207 100   207   2568 ! exists $_[0]->{$_[1]} or die "entry $_[1] already registered";
875 205         734 $_[0]->{$_[1]} = $_[2];
876             }
877              
878             sub DELETE {
879 1     1   1022 die "deletion of entry $_[1] is forbidden";
880             }
881              
882             1;
883              
884             __END__