File Coverage

blib/lib/IMDB/Local/DB/Base.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package IMDB::Local::DB::Base;
2              
3 1     1   11 use 5.006;
  1         1  
  1         26  
4 1     1   6 use strict;
  1         1  
  1         19  
5 1     1   3 use warnings;
  1         1  
  1         31  
6              
7             =head1 NAME
8              
9             IMDB::Local::DB::Base - The great new IMDB::Local::DB::Base!
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18              
19              
20             =head1 SYNOPSIS
21              
22             Quick summary of what the module does.
23              
24             Perhaps a little code snippet.
25              
26             use IMDB::Local::DB::Base;
27              
28             my $foo = IMDB::Local::DB::Base->new();
29             ...
30              
31             =head1 EXPORT
32              
33             A list of functions that can be exported. You can delete this section
34             if you don't export anything, such as for a purely object-oriented module.
35              
36             =head1 SUBROUTINES/METHODS
37              
38             =head2 new
39              
40             =cut
41              
42 1     1   221 use DBI;
  0            
  0            
43             use Carp;
44             use Time::HiRes;
45              
46             use Class::MethodMaker
47             [
48             scalar => ['dbh', 'modCount'],
49             scalar => ['driver'],
50             scalar => ['driverDetail'],
51             scalar => ['server'],
52             scalar => ['database'],
53             scalar => ['user'],
54             scalar => ['passwd'],
55             scalar => [{-default => 1}, 'db_AutoCommit'],
56             scalar => [{-default => 0}, 'db_RaiseError'],
57             scalar => [{-default => 102400}, 'maxReadLen'],
58             hash => ['connectAttrs'],
59             array => ['tables'],
60             hash => ['table_infos'],
61             scalar => ['mutexName', 'mutex'],
62             scalar => [{-default => sub { my ($self, $attempts, $gotit)=@_;
63             if ( !$gotit ) {
64              
65             if ( $attempts % 100 == 0 ) {
66             warn("waiting on db lock.. ($attempts attempts)");
67             Time::HiRes::usleep(5*1000*1000);
68             }
69             elsif ( $attempts % 10 == 0 ) {
70             warn("waiting on db lock.. ($attempts attempts)");
71             Time::HiRes::usleep(1*1000*1000);
72             }
73             else {
74             Time::HiRes::usleep(100*1000);
75             }
76             }
77             return(0);
78             }}, 'mutexWaitCallback'],
79             new => [qw/ -hash new /] ,
80             ];
81              
82              
83             sub DESTROY($)
84             {
85             my ($self)=@_;
86              
87             if ( $self->isConnected() ) {
88             $self->disconnect();
89             }
90             }
91              
92             =head2 connect
93              
94             =cut
95              
96             sub connect($)
97             {
98             my ($self)=@_;
99              
100             $self->disconnect();
101             return $self if ( !defined($self->driver()) );
102              
103             my $dsn='dbi:'.$self->driver;
104              
105             if ( defined($self->driverDetail()) ) {
106             $dsn.=":".$self->driverDetail();
107             }
108             if ( defined($self->server()) && length($self->server()) ) {
109             if ( $self->driver eq 'ODBC' ) {
110             $dsn.=";Server=".$self->server();
111             }
112             elsif ( $self->driver eq 'SQLite' ) {
113             # remote server not supported
114             }
115             elsif ( $self->driver eq 'mysql' ) {
116             $dsn.=":host=".$self->server();
117             }
118             }
119             if ( defined($self->database()) && length($self->database()) ) {
120             if ( $self->driver eq 'ODBC' ) {
121             $dsn.=";Database=".$self->database();
122             }
123             elsif ( $self->driver eq 'SQLite' ) {
124             $dsn.=":dbname=".$self->database();
125             }
126             else {
127             $dsn.=":database=".$self->database();
128             }
129             }
130              
131             if ( defined($self->mutexName) && length($self->mutexName) ) {
132             if ( $^O eq 'MSWin32' ) {
133             require Win32::Mutex;
134              
135             my $attempts=0;
136             while ( 1 ) {
137             my $name="Global\\".$self->mutexName;
138             if ( defined($self->database()) && length($self->database()) ) {
139             $name="Global\\".$self->database()."-".$self->mutexName();
140             }
141             my $mutex=Win32::Mutex->new(1, $name);
142             if ( $mutex ) {
143             if ( $^E == 183 ) {
144             undef($mutex);
145             my $sub=$self->mutexWaitCallback();
146             my $stop=&$sub($self, ++$attempts, 0);
147             if ( $stop ) {
148             return(undef);
149             }
150             }
151             else {
152             $self->mutex($mutex);
153             if ( $attempts != 0 ) {
154             my $sub=$self->mutexWaitCallback();
155             &$sub($self, $attempts, 1);
156             }
157             last;
158             }
159             }
160             }
161             #warn("mutex is MINE");
162             }
163             elsif ( $^O eq 'linux' ) {
164             use Fcntl ':flock';
165             my $attempts=0;
166             while ( 1 ) {
167             my $name="/tmp/.".$self->mutexName();
168             if ( defined($self->database()) && length($self->database()) ) {
169             $name=$self->database()."-".$self->mutexName().".lck";
170             }
171              
172             my $fd;
173              
174             if ( !open($fd, "> $name") ) {
175             warn("$name:$!");
176              
177             # die "Can't open $name for locking!\nError: $!\n";
178             my $sub=$self->mutexWaitCallback();
179             my $stop=&$sub($self, ++$attempts, 0);
180             if ( $stop ) {
181             return(undef);
182             }
183             }
184             elsif ( !flock($fd, LOCK_EX|LOCK_NB) ) {
185             #warn("$name:$!");
186             my $sub=$self->mutexWaitCallback();
187             my $stop=&$sub($self, ++$attempts, 0);
188             close($fd);
189             if ( $stop ) {
190             return(undef);
191             }
192             # spin and retry
193             }
194             else {
195             $self->mutex($fd);
196             if ( $attempts != 0 ) {
197             my $sub=$self->mutexWaitCallback();
198             &$sub($self, $attempts, 1);
199             }
200             last;
201             }
202             }
203             }
204             }
205              
206             #warn("DB: $dsn\n");
207             my %attrs=(RaiseError=>$self->db_RaiseError,
208             AutoCommit=>$self->db_AutoCommit);
209              
210             for my $k ($self->connectAttrs_keys()) {
211             $attrs{$k}=$self->connectAttrs_index($k);
212             }
213            
214             my $dbh=DBI->connect($dsn, $self->user, $self->passwd, \%attrs);
215            
216             if ( !$dbh ) {
217             print STDERR "connection failed:$DBI::errstr\n";
218             if ( defined($self->mutex) ) {
219             if ( $^O eq 'MSWin32' ) {
220             $self->mutex()->release();
221             }
222             elsif ( $^O eq 'linux' ) {
223             close $self->mutex();
224             }
225             }
226             $self->mutex(undef);
227             return(undef);
228             }
229             #print STDERR "autocommit: ".$dbh->{AutoCommit}."\n";
230              
231             # default for ODC is 80 bytes for LongReadLen, which causes
232             # ODBC SQL Server Driver String data, right truncation (SQL-01004)
233             # failures for things like xml data types such as ScheduledJobs.Config
234             #
235             # note: setting arbitrarily high # causes memory allocation problems.
236             $dbh->{LongReadLen} = $self->maxReadLen();
237             $dbh->{LongTruncOk} = 0;
238              
239             if ($dbh->err()) {
240             warn($dbh->errstr()."\n");
241             $dbh->disconnect();
242             if ( $^O eq 'MSWin32' ) {
243             $self->mutex()->release();
244             }
245             elsif ( $^O eq 'linux' ) {
246             close $self->mutex();
247             }
248             $self->mutex(undef);
249             return(undef);
250             }
251             $self->dbh($dbh);
252              
253             if ( $self->driver eq 'SQLite' ) {
254             #$self->runSQL("PRAGMA cache_size=200000");
255             }
256             $self->modCount(0);
257             return($self);
258             }
259              
260             =head2 disconnect
261              
262             =cut
263              
264             sub disconnect()
265             {
266             my ($self)=@_;
267              
268             if ( $self->dbh() ) {
269             $self->dbh->disconnect();
270             $self->dbh(undef);
271             }
272              
273             if ( defined($self->mutex) ) {
274             if ( $^O eq 'MSWin32' ) {
275             $self->mutex()->release();
276             }
277             elsif ( $^O eq 'linux' ) {
278             close $self->mutex();
279             }
280             #warn("mutex is NOT-MINE");
281             $self->mutex(undef);
282             }
283             $self->tables_reset();
284             }
285              
286             =head2 commit
287              
288             =cut
289              
290             sub commit()
291             {
292             my ($self)=@_;
293              
294             $self->dbh->commit();
295             $self->modCount(0);
296             }
297              
298             =head2 isConnected
299              
300             =cut
301              
302             sub isConnected($)
303             {
304             return(defined(shift->dbh));
305             }
306              
307             =head2 quote
308              
309             =cut
310              
311             sub quote($)
312             {
313             my ($self, @rest)=@_;
314             return($self->dbh->quote(@rest));
315             }
316              
317             =head2 last_inserted_key
318              
319             =cut
320              
321             sub last_inserted_key($$$)
322             {
323             my ($self, $table, $primaryKey)=@_;
324             #return $self->dbh->last_insert_id(undef, undef, $table, $primaryKey);
325             if ( $self->driver eq 'ODBC' ) {
326             return $self->select2Scalar("select \@\@identity");
327             }
328             return $self->dbh->last_insert_id($self->database, undef, $table, $primaryKey);
329             }
330              
331             =head2 runSQL
332              
333             =cut
334              
335             sub runSQL($)
336             {
337             my ($self, $stmt)=@_;
338              
339             my $dbh=$self->dbh();
340             #warn("$stmt");
341             $dbh->do($stmt);
342             if ( $dbh->err() ) {
343             carp($stmt);
344             return(0);
345             }
346             return(1);
347             }
348              
349             =head2 runSQL_err
350              
351             =cut
352              
353             sub runSQL_err($)
354             {
355             my ($self)=@_;
356             return $self->dbh()->err();
357             }
358              
359             =head2 runSQL_srrstr
360              
361             =cut
362              
363             sub runSQL_errstr($)
364             {
365             my ($self)=@_;
366             return $self->dbh()->errstr();
367             }
368              
369             =head2 prepare
370              
371             =cut
372              
373             sub prepare($$)
374             {
375             my ($self, $query)=@_;
376             if ( !defined($self->dbh) ) {
377             warn("attempt to prepare statement without db connection");
378             }
379             #warn("STMT:$query");
380             return $self->dbh->prepare($query);
381             }
382              
383             =head2 execute
384              
385             =cut
386              
387             sub execute($)
388             {
389             my ($self, $query)=@_;
390              
391             if ( !defined($self->dbh) ) {
392             warn("attempt to prepare statement without db connection");
393             return(undef);
394             }
395              
396             #warn("STMT:$query");
397             my $sth = $self->prepare($query);
398             if ( !$sth ) {
399             warn("STMT INVALID:$query");
400             return(undef);
401             }
402             if ( !$sth->execute() ) {
403             warn("FAILED STMT:$query");
404             return(undef);
405             }
406             return($sth);
407             }
408              
409             =head2 sth_columns
410              
411             =cut
412              
413             sub sth_columns($$)
414             {
415             my ($self, $sth)=@_;
416             my @columns;
417              
418             for (my $i=0; $i<$sth->{NUM_OF_FIELDS}; $i++) {
419             push(@columns, $sth->{NAME}->[$i]);
420             }
421             return(@columns);
422             }
423              
424             =head2 insert_db
425              
426             =cut
427              
428             sub insert_db($$$%)
429             {
430             my ($self, $table, $primaryKey, %args) = @_;
431              
432             my ($k,$v)=('','');
433             my @values;
434             for my $key (sort keys %args) {
435             if ( defined($args{$key}) ) {
436             $k.=$key.",";
437             #$k.=$self->dbh->quote($key).",";
438             push(@values, $args{$key});
439             #$v.=$self->dbh->quote($args{$key}).",";
440             $v.="?,";
441             }
442             }
443              
444             if ( !($k=~s/,$//) || !($v=~s/,$//) ) {
445             warn "attempt to insert nothing into table $table";
446             return(undef);
447             }
448             my $stmt="INSERT INTO $table ($k) VALUES ($v)";
449             #print STDERR "STMT:$stmt\n";
450             #print STDERR "VALUES:(".join(',', map($self->dbh->quote($_), @values)).")\n";
451            
452             my $dbh=$self->dbh();
453             my $sth=$dbh->prepare($stmt);
454             $sth->execute(@values);
455              
456             if ($dbh->err()) {
457             print STDERR "Error inserting into table: $table: ", $dbh->errstr()."\n";
458             print STDERR "STMT:$stmt\n";
459             #print STDERR "VALUES:$stmt\n";
460             return undef;
461             }
462             $self->modCount($self->modCount()+1);
463              
464             # return primary key if its specified
465             if ( !defined($primaryKey) ) {
466             return(1);
467             }
468             return $self->last_inserted_key($table, $primaryKey);
469             }
470              
471             sub _quoteField($$)
472             {
473             my ($self, $word)=@_;
474              
475             if ( $self->driver eq 'ODBC' ) {
476             # check for keywords used in column names we need to quote
477             if ( $word eq 'File' ) {
478             return('['.$word.']');
479             }
480              
481             # catch table.column in select
482             if ( $word=~m/^([^\.]+)\.(File)$/o ) {
483             return($1.'['.$2.']');
484             }
485             }
486             # no quote needed
487             return($word);
488             }
489              
490             =head2 query2SQLStatement
491              
492             =cut
493              
494             sub query2SQLStatement($%)
495             {
496             my ($self, %args)=@_;
497            
498             #print Dumper(\%args);
499             my @columnHeaders=$args{fields};
500            
501             my $st="SELECT ";
502             if ( $args{limit} && $self->driver eq 'ODBC' ) {
503             # mssql
504             $st.="TOP $args{limit} ";
505             }
506             my $cnt=0;
507             for (@{$args{fields}}) {
508             $st.=$self->_quoteField($_).",";
509             $cnt++;
510             }
511             $st=~s/,$// || die "no fields in select";
512              
513             $st.=" FROM ";
514             for (@{$args{tables}}) {
515             $st.=$_.",";
516             }
517             $st=~s/,$// || die "no tables specified";
518              
519             if ( $args{wheres} ) {
520             my $w=" WHERE ";
521             for (@{$args{wheres}}) {
522             $w.="$_ AND ";
523             }
524             $st.=$w if ( $w=~s/ AND $// );
525             }
526              
527             if ( $args{groupbys} ) {
528             my $w=" GROUP BY ";
529             for (@{$args{groupbys}}) {
530             $w.=$self->_quoteField($_).",";
531             }
532             $st.=$w if ( $w=~s/,$// );
533             }
534              
535             my $sortedField='';
536             my $sortedDescending=0;
537             if ( defined($args{sortByField}) && length($args{sortByField})) {
538             $sortedField=$args{sortByField};
539             if ( $sortedField=~s/^\-// ) {
540             $st.=" ORDER BY ".$self->_quoteField($sortedField)." DESC";
541             $sortedDescending=1;
542             }
543             else {
544             $st.=" ORDER BY ".$self->_quoteField($sortedField)." ASC";
545             $sortedDescending=0;
546             }
547             }
548             elsif ( defined($args{orderbys}) && @{$args{orderbys}} ) {
549             $st.=" ORDER BY ";
550             for (@{$args{orderbys}}) {
551             $st.=$self->_quoteField($_).",";
552             }
553             $st=~s/,$// || die "no orderbys specified";
554             }
555              
556             if ( $args{limit} ) {
557             if ( $self->driver eq 'ODBC' ) {
558             # covered at top of this sub
559             }
560             elsif ( $self->driver eq 'mysql' ) {
561             $st.=" LIMIT ".$args{limit};
562             }
563             elsif ( $self->driver eq 'SQLite' ) {
564             $st.=" LIMIT ".$args{limit};
565             }
566              
567             if ( $args{offset} ) {
568             if ( $args{offset} < 0 ) {
569             $args{offset}=0;
570             }
571             $st.=" OFFSET ".$args{offset};
572             }
573             else {
574             $args{offset}=0;
575             }
576             }
577             return($st);
578             }
579              
580             =head2 findRecords
581              
582             =cut
583              
584             sub findRecords($%)
585             {
586             my ($self, %args)=@_;
587              
588             my $cacheBy=1000;
589             if ( $args{cacheBy} ) {
590             $cacheBy=delete($args{cacheBy});
591             }
592             if ( $args{limit} && $cacheBy > $args{limit} ) {
593             $cacheBy=$args{limit};
594             }
595             my $st=$self->query2SQLStatement(%args);
596              
597             #print STDERR "running '$st'\n";
598             my $sth=$self->execute($st);
599             if ( !defined($sth) ) {
600             warn("sql '$st' failed:".$self->dbh->errstr()."\n");
601             return(undef);
602             }
603              
604             if ($self->dbh->err()) {
605             print STDERR "Query error: ", $self->dbh->errstr()."\n";
606             print STDERR "STMT:$st\n";
607             #print STDERR "VALUES:$stmt\n";
608             return undef;
609             }
610              
611             my $int=new EasyDBI::RecordIterator($sth);
612             $int->{cacheBy}=$cacheBy;
613             return($int);
614             }
615              
616             =head2 rowExists
617              
618             =cut
619              
620             sub rowExists($$$)
621             {
622             my ($self, $table, $column, $value)=@_;
623             my $sql="SELECT $column from $table where $column='$value'";
624              
625             my $v=$self->select2Scalar($sql);
626             return(defined($v) && $v eq $value);
627             }
628              
629             =head2 select2Scalar
630              
631             =cut
632              
633             sub select2Scalar($$)
634             {
635             my ($self, $sql)=@_;
636              
637             my $sth=$self->execute($sql);
638             if ( !$sth ) {
639             return(undef);
640             }
641             my @arr=$sth->fetchrow_array;
642             return $arr[0];
643             }
644              
645             =head2 select2Int
646              
647             =cut
648              
649             sub select2Int($$)
650             {
651             my ($self, $sql)=@_;
652              
653             my $r=$self->select2Scalar($sql);
654             if ( defined($r) ) {
655             $r=int($r);
656             }
657             return $r;
658             }
659              
660             =head2 select2Array
661              
662             =cut
663              
664             sub select2Array($$)
665             {
666             my ($self, $sql)=@_;
667              
668             my $sth=$self->execute($sql);
669             if ( !$sth ) {
670             return(undef);
671             }
672             my @arr;
673             my $all=$sth->fetchall_arrayref();
674             for my $refer (@$all) {
675             push(@arr, @$refer);
676             }
677             return \@arr;
678             }
679              
680             =head2 select2Matrix
681              
682             =cut
683              
684             sub select2Matrix($$)
685             {
686             my ($self, $sql)=@_;
687              
688             my $sth=$self->execute($sql);
689             if ( !$sth ) {
690             return(undef);
691             }
692             my $int=new EasyDBI::RecordIterator($sth);
693             my @arr;
694             while (my $refer=$int->nextRow()) {
695             push(@arr, $refer);
696             }
697             return(\@arr);
698             }
699              
700             =head2 select2HashRef
701              
702             =cut
703              
704             sub select2HashRef($$)
705             {
706             my ($self, $sql)=@_;
707              
708             my $sth=$self->execute($sql);
709             if ( !$sth ) {
710             return(undef);
711             }
712             return($sth->fetchrow_hashref());
713             }
714              
715             =head2 select2Hash
716              
717             =cut
718              
719             sub select2Hash($$)
720             {
721             my ($self, $sql)=@_;
722              
723             my $sth=$self->execute($sql);
724             if ( !$sth ) {
725             return(undef);
726             }
727             my $int=new EasyDBI::RecordIterator($sth);
728             my %arr;
729             while (my $refer=$int->nextRow()) {
730             my $key=$refer->[0];
731             my @r=splice(@$refer, 1);
732             if ( scalar(@r) == 1 ) {
733             $arr{$key}=$r[0];
734             }
735             else {
736             $arr{$key}=\@r;
737             }
738             }
739             return(\%arr);
740             }
741              
742             =head2 database_list
743              
744             =cut
745              
746             sub database_list($)
747             {
748             my $self=shift;
749              
750             my $sth;
751             if ( $self->driver eq 'ODBC' ) {
752             my @list;
753             for my $t (@{$self->select2Array("select Name from master..sysdatabases")} ) {
754             push(@list, $t);
755             }
756             return(@list);
757             }
758             elsif ( $self->driver eq 'SQLite' ) {
759             warn "database_list of sqlite db: unsupported";
760             return(undef);
761             }
762             else {
763             # mysql
764             my @list;
765             for my $t (@{$self->select2Array("show databases")} ) {
766             push(@list, $t);
767             }
768             return(@list);
769             }
770             }
771              
772             =head2 table_list
773              
774             =cut
775              
776             sub table_list($)
777             {
778             my $self=shift;
779              
780             if ( $self->tables_index(0) ) {
781             return($self->tables);
782             }
783              
784             my $sth;
785             if ( $self->driver eq 'ODBC' ) {
786             $sth=$self->dbh()->table_info($self->database, '', '', 'TABLE');
787             if ( !defined($sth) ) {
788             warn("lookup of table_info failed:".$self->dbh->errstr()."\n");
789             return($self->tables);
790             }
791             }
792             elsif ( $self->driver eq 'SQLite' ) {
793             #$sth=$self->dbh()->table_info('%', '%', '%', 'TABLE');
794             for my $t (@{$self->select2Array("select Name from sqlite_master where type='table'")} ) {
795             $self->tables_push($t);
796             }
797             return($self->tables);
798             }
799             else {
800             # mysql
801             for my $t (@{$self->select2Array("show tables")} ) {
802             $self->tables_push($t);
803             }
804             return($self->tables);
805             }
806            
807             my $int=new EasyDBI::RecordIterator($sth);
808             while (my $refer=$int->nextRow()) {
809             my ($table_cat, $table_schem, $table_name, $table_type, $remarks)=@$refer;
810             $self->tables_push($table_name);
811             }
812             #warn "tables:".join(',', $self->tables)."\n";
813             return($self->tables);
814             }
815              
816             =head2 table_exists
817              
818             =cut
819              
820             sub table_exists($$)
821             {
822             my ($dbh, $table)=@_;
823              
824             if ( !grep(/^$table$/, $dbh->table_list()) ) {
825             return(0);
826             }
827             return(1);
828             }
829              
830             =head2 column_info
831              
832             =cut
833              
834             sub column_info($$)
835             {
836             my ($self, $table)=@_;
837              
838             if ( $self->table_infos_exists($table) ) {
839             return $self->table_infos_index($table);
840             }
841              
842             my @key_column_names;
843             if ( $self->driver eq 'ODBC' ) {
844             @key_column_names = $self->dbh()->primary_key($self->database, '', $table);
845             }
846             elsif ( $self->driver eq 'SQLite' ) {
847             # untested
848             @key_column_names = $self->dbh()->primary_key(undef, undef, $table);
849             }
850             elsif ( $self->driver eq 'mysql' ) {
851             my @list;
852             my $res=$self->select2Matrix("describe $table");
853             if ( $res ) {
854             for my $h (@$res) {
855             my ($field, $type,$null, $key,$default,$extra)=@$h;
856             $default='' if (!defined($default));
857             my $t;
858             $t->{COLUMN_NAME}=$field;
859             $t->{TYPE_NAME}=$type;
860             $t->{COLUMN_SIZE}=0;
861             if ( $t->{TYPE_NAME}=~s/\((\d+)\)$// ) {
862             $t->{COLUMN_SIZE}=$1;
863             }
864             $t->{IS_NULLABLE}=uc($null);
865             $t->{IS_PRIMARY_KEY}=(uc($key) eq 'PRI')?1:0;
866             push(@list, $t);
867             }
868             }
869             if ( !@list ) {
870             warn("no table information for table '$table'");
871             return(undef);
872             }
873             @list=sort {$a->{COLUMN_NAME} cmp $b->{COLUMN_NAME}} @list;
874             $self->table_infos($table, \@list);
875             return $self->table_infos_index($table);
876             }
877             else {
878             die "unsupported";
879             }
880            
881             my $sth;
882             if ( $self->driver eq 'ODBC' ) {
883             $sth=$self->dbh()->column_info($self->database, '', $table, '%');
884             }
885             elsif ( $self->driver eq 'SQLite' ) {
886             # untested
887             $sth=$self->dbh()->column_info(undef, '%', $table, '%');
888             }
889             else {
890             $sth=$self->dbh()->column_info(undef, '%', $table, '%');
891             }
892             if ( !defined($sth) ) {
893             warn("no table information for table '$table'");
894             return(undef);
895             }
896            
897             my @list;
898             while (my $hash=$sth->fetchrow_hashref()) {
899             if ( @key_column_names ) {
900             my $col=$hash->{COLUMN_NAME};
901             if ( grep/^$col$/, @key_column_names ) {
902             $hash->{IS_PRIMARY_KEY}=1;
903             }
904             else {
905             $hash->{IS_PRIMARY_KEY}=0;
906             }
907             }
908             push(@list, $hash);
909             }
910              
911             if ( !@list ) {
912             warn("no table information for table '$table'");
913             return(undef);
914             }
915              
916             $self->table_infos($table, \@list);
917             return $self->table_infos_index($table);
918             }
919              
920             =head2 column_list
921              
922             =cut
923              
924             sub column_list($$)
925             {
926             my ($self, $table)=@_;
927              
928             my @columns;
929             for my $t (@{$self->column_info($table)}) {
930             push(@columns, $t->{COLUMN_NAME});
931             }
932             return(@columns);
933             }
934              
935             =head2 writeQuery2CSV
936              
937             =cut
938              
939             sub writeQuery2CSV($$$)
940             {
941             my ($self, $file, $hash)=@_;
942             require Text::CSV;
943              
944             my $csv=new Text::CSV({binary=>1, always_quote=>1});
945              
946             my $int=$self->findRecords(%$hash);
947             if ($int) {
948             if ( open(my $fd, "> $file") ) {
949             $csv->combine(@{$hash->{fields}});
950             print $fd $csv->string()."\n";
951            
952             while (my $refer=$int->nextRow()) {
953             $csv->combine(@$refer);
954             print $fd $csv->string()."\n";
955             }
956             close($fd);
957             return(1);
958             }
959             }
960             return(0);
961             }
962              
963             =head2 appendCSV2Table
964              
965             =cut
966              
967             sub appendCSV2Table($$$)
968             {
969             my ($self, $file, $table)=@_;
970             require Text::CSV;
971              
972             my $csv=new Text::CSV({binary=>1, always_quote=>1});
973              
974             if ( open(my $fd, "<:encoding(utf8)", $file) ) {
975             my $lineNum=1;
976             my @titleRow=@{$csv->getline($fd)};
977            
978             while ( my $row = $csv->getline($fd) ) {
979             $lineNum++;
980             if ( scalar(@titleRow) != scalar(@$row) ) {
981             warn("$file: invalid row: $lineNum\n");
982             }
983             else {
984             my %args;
985             my $c=0;
986             for my $t (@titleRow) {
987             $args{$t}=$row->[$c];
988             $c++;
989             }
990             $self->insert_db($table, undef, %args);
991             }
992             }
993            
994             $csv->eof or $csv->error_diag();
995             close $fd;
996             $self->commit();
997             return(1);
998             }
999             return(0);
1000             }
1001              
1002             =head2 table_row_count
1003              
1004             =cut
1005              
1006             sub table_row_count($$)
1007             {
1008             my ($self, $table)=@_;
1009              
1010             if ( $self->driver eq 'mysql' ) {
1011             return $self->select2Int("select count(*) from $table");
1012             }
1013             elsif ( $self->driver eq 'SQLite' ) {
1014             return $self->select2Int("select count(1) from $table");
1015             }
1016             else {
1017             # mssql magic
1018             my $sql="sp_Msforeachtable 'sp_spaceused ''$table'''";
1019             my $sth=$self->execute($sql);
1020             if ( !defined($sth) ) {
1021             carp("table_row_count: called with non-existing table $table");
1022             return(0);
1023             }
1024            
1025             my $int=new EasyDBI::RecordIterator($sth);
1026             my @infos;
1027             while (my $r=$int->nextRow()) {
1028             my ($table, $rows, $reserved_kb, $data_kb, $index_kb, $unused_kb)=@$r;
1029             return (int($rows));
1030             }
1031             #return $self->select2Scalar("select count(1) from $table");
1032             }
1033             }
1034              
1035             =head2 table_report
1036              
1037             =cut
1038              
1039             sub table_report($@)
1040             {
1041             my ($self, @tables)=@_;
1042              
1043             if ( $self->driver eq 'mysql' ) {
1044             if ( !@tables ) {
1045             @tables=$self->table_list();
1046             }
1047             my @infos;
1048             for my $table (@tables) {
1049             my $rows=$self->select2Array("select count(*) from $table");
1050             push(@infos, [$table, $rows->[0], 0, 0]);
1051             }
1052             return(\@infos);
1053             }
1054             elsif ( $self->driver eq 'SQLite' ) {
1055             if ( !@tables ) {
1056             @tables=$self->table_list();
1057             }
1058             my @infos;
1059             for my $table (@tables) {
1060             my $rows=$self->select2Array("select count(*) from $table");
1061             push(@infos, [$table, $rows->[0], 0, 0]);
1062             }
1063             return(\@infos);
1064             }
1065             else {
1066             # fall-through
1067             }
1068              
1069             # mssql magic
1070             my $sql="sp_Msforeachtable 'sp_spaceused ''?'''";
1071             my $sth=$self->execute($sql);
1072            
1073             my $int=new EasyDBI::RecordIterator($sth);
1074             my @infos;
1075             while (my $r=$int->nextRow()) {
1076             my ($table, $rows, $reserved_kb, $data_kb, $index_kb, $unused_kb)=@$r;
1077             if ( !@tables || grep(/^$table$/, @tables) ) {
1078              
1079             # knock off units
1080             $data_kb=~s/\s*KB$//o;
1081             $index_kb=~s/\s*KB$//o;
1082             $unused_kb=~s/\s*KB$//o;
1083              
1084             $rows=~s/^\s*//o;
1085             $rows=~s/\s*$//o;
1086             push(@infos, [$table, $rows, $data_kb, $index_kb]);
1087             }
1088             }
1089             return(\@infos);
1090             }
1091            
1092             =head1 AUTHOR
1093              
1094             jerryv, C<< >>
1095              
1096             =head1 BUGS
1097              
1098             Please report any bugs or feature requests to C, or through
1099             the web interface at L. I will be notified, and then you'll
1100             automatically be notified of progress on your bug as I make changes.
1101              
1102              
1103              
1104              
1105             =head1 SUPPORT
1106              
1107             You can find documentation for this module with the perldoc command.
1108              
1109             perldoc IMDB::Local::DB::Base
1110              
1111              
1112             You can also look for information at:
1113              
1114             =over 4
1115              
1116             =item * RT: CPAN's request tracker (report bugs here)
1117              
1118             L
1119              
1120             =item * AnnoCPAN: Annotated CPAN documentation
1121              
1122             L
1123              
1124             =item * CPAN Ratings
1125              
1126             L
1127              
1128             =item * Search CPAN
1129              
1130             L
1131              
1132             =back
1133              
1134              
1135             =head1 ACKNOWLEDGEMENTS
1136              
1137              
1138             =head1 LICENSE AND COPYRIGHT
1139              
1140             Copyright 2015 jerryv.
1141              
1142             This program is free software; you can redistribute it and/or modify it
1143             under the terms of the the Artistic License (2.0). You may obtain a
1144             copy of the full license at:
1145              
1146             L
1147              
1148             Any use, modification, and distribution of the Standard or Modified
1149             Versions is governed by this Artistic License. By using, modifying or
1150             distributing the Package, you accept this license. Do not use, modify,
1151             or distribute the Package, if you do not accept this license.
1152              
1153             If your Modified Version has been derived from a Modified Version made
1154             by someone other than you, you are nevertheless required to ensure that
1155             your Modified Version complies with the requirements of this license.
1156              
1157             This license does not grant you the right to use any trademark, service
1158             mark, tradename, or logo of the Copyright Holder.
1159              
1160             This license includes the non-exclusive, worldwide, free-of-charge
1161             patent license to make, have made, use, offer to sell, sell, import and
1162             otherwise transfer the Package with respect to any patent claims
1163             licensable by the Copyright Holder that are necessarily infringed by the
1164             Package. If you institute patent litigation (including a cross-claim or
1165             counterclaim) against any party alleging that the Package constitutes
1166             direct or contributory patent infringement, then this Artistic License
1167             to you shall terminate on the date that such litigation is filed.
1168              
1169             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1170             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1171             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1172             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1173             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1174             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1175             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1176             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1177              
1178              
1179             =cut
1180              
1181             1; # End of IMDB::Local::DB::Base