File Coverage

blib/lib/DBIx/Array.pm
Criterion Covered Total %
statement 145 382 37.9
branch 42 156 26.9
condition 4 12 33.3
subroutine 30 86 34.8
pod 64 64 100.0
total 285 700 40.7


line stmt bran cond sub pod time code
1             package DBIx::Array;
2 13     13   1478592 use strict;
  13         171  
  13         396  
3 13     13   67 use warnings;
  13         35  
  13         399  
4 13     13   101 use File::Basename qw{basename};
  13         37  
  13         1400  
5 13     13   6889 use Tie::Cache;
  13         41967  
  13         472  
6 13     13   8138 use Data::Dumper qw{Dumper};
  13         93223  
  13         1057  
7 13     13   115 use List::Util qw(sum);
  13         26  
  13         849  
8 13     13   23914 use DBI;
  13         250769  
  13         832  
9 13     13   6278 use DBIx::Array::Session::Action;
  13         36  
  13         69874  
10              
11             our $VERSION = '0.65';
12             our $PACKAGE = __PACKAGE__;
13              
14             =head1 NAME
15              
16             DBIx::Array - DBI Wrapper with Perl style data structure interfaces
17              
18             =head1 SYNOPSIS
19              
20             use DBIx::Array;
21             my $dbx = DBIx::Array->new;
22             $dbx->connect($connection, $user, $pass, \%opt); #passed to DBI
23             my @array = $dbx->sqlarray($sql, @params);
24              
25             With a connected database handle
26              
27             use DBIx::Array;
28             my $dbx = DBIx::Array->new(dbh=>$dbh);
29              
30             With stored connection information from a File
31              
32             use DBIx::Array::Connect;
33             my $dbx = DBIx::Array::Connect->new(file=>"my.ini")->connect("mydatabase");
34              
35             =head1 DESCRIPTION
36              
37             This module provides a Perl data structure interface for Structured Query Language (SQL). This module is for people who truly understand SQL and who understand Perl data structures. If you understand how to modify your SQL to meet your data requirements then this module is for you.
38              
39             This module is used to connect to Oracle 10g and 11g using L on both Linux and Win32, MySQL 4 and 5 using L on Linux, Microsoft SQL Server using L on Linux and using L on Win32 systems, and PostgreSQL using L in a 24x7 production environment. Tests are written against L and L.
40              
41             =head2 CONVENTIONS
42              
43             =over
44              
45             =item Methods are named "type + data structure".
46              
47             =over
48              
49             =item sql - Methods that are type "sql" use the passed SQL to hit the database.
50              
51             =item abs - Methods that are type "abs" use L to build the SQL to hit the database.
52              
53             =item sqlwhere - Methods that are type "sqlwhere" use the passed SQL appended with the passed where structure with L->where to build the SQL to hit the database.
54              
55             =back
56              
57             =item Methods data structures are:
58              
59             =over
60              
61             =item scalar - which is a single value the value from the first column of the first row.
62              
63             =item array - which is a flattened list of values from all columns from all rows.
64              
65             =item hash - which is the first two columns of values as a hash or hash reference
66              
67             =item arrayarray - which is an array of array references (i.e. data table)
68              
69             =item arrayhash - which is an array of hash references (works best when used with case sensitive column aliases)
70              
71             =item hashhash - which is a hash where the keys are the values of the first column and the values are a hash reference of all (including the key) column values.
72              
73             =item arrayarrayname - which is an array of array references (i.e. data table) with the first row being the column names passed from the database
74              
75             =item arrayhashname - which is an array of hash references with the first row being the column names passed from the database
76              
77             =item arrayobject - which is an array of hash references blessed into the passed class namespace
78              
79             =back
80              
81             =item Methods are context sensitive
82              
83             =over
84              
85             =item Methods in list context return a list e.g. (), ([],[],[],...), ({},{},{},...)
86              
87             =item Methods in scalar context return an array reference e.g. [], [[],[],[],...], [{},{},{},...]
88              
89             =back
90              
91             =back
92              
93             =head1 USAGE
94              
95             Loop through data
96              
97             foreach my $row ($dbx->sqlarrayhash($sql, @bind)) {
98             do_something($row->{"id"}, $row->{"column"});
99             }
100              
101             Easily generate an HTML table
102              
103             my $cgi = CGI->new("");
104             my $html = $cgi->table($cgi->Tr([map {$cgi->td($_)} $dbx->sqlarrayarrayname($sql, @param)]));
105              
106             Bless directly into a class
107              
108             my ($object) = $dbx->sqlarrayobject("My::Package", $sql, {id=>$id}); #bless({id=>1, name=>'foo'}, 'My::Package');
109             my @objects = $dbx->absarrayobject("My::Package", "myview", '*', {active=>1}, ["name"]); #($object, $object, ...)
110              
111             =head1 CONSTRUCTOR
112              
113             =head2 new
114              
115             my $dbx = DBIx::Array->new();
116             $dbx->connect(...); #connect to database, sets and returns dbh
117              
118             my $dbx = DBIx::Array->new(dbh=>$dbh); #already have a handle
119              
120             =cut
121              
122             sub new {
123 21     21 1 768630 my $this = shift;
124 21 100       97 my $class = ref($this) ? ref($this) : $this;
125 21         50 my $self = {};
126 21         77 bless $self, $class;
127 21         101 $self->initialize(@_);
128 21         60 return $self;
129             }
130              
131             =head2 initialize
132              
133             =cut
134              
135             sub initialize {
136 21     21 1 46 my $self = shift;
137 21         90 %$self = @_;
138             }
139              
140             =head1 METHODS (Properties)
141              
142             =head2 dbh
143              
144             Sets or returns the database handle object.
145              
146             my $dbh = $dbx->dbh;
147             $dbx->dbh($dbh); #if you already have a connection
148              
149             =cut
150              
151             sub dbh {
152 39     39 1 23818 my $self = shift;
153 39 100       113 if (@_) {
154 5         14 CORE::delete $self->{'_prepared'}; #clear cache if we switch handles
155 5         15 $self->{'dbh'} = shift;
156             }
157 39         334 return $self->{'dbh'};
158             }
159              
160             =head2 name
161              
162             Sets or returns a user friendly identification string for this database connection
163              
164             my $name = $dbx->name;
165             $dbx->name($string);
166              
167             =cut
168              
169             sub name {
170 4     4 1 1480 my $self = shift;
171 4 100       14 $self->{'name'} = shift if @_;
172 4         19 return $self->{'name'};
173             }
174              
175             =head1 METHODS (DBI Wrappers)
176              
177             =head2 connect
178              
179             Wrapper around DBI->connect; Connects to the database, sets dbh property, and returns the database handle.
180              
181             $dbx->connect($connection, $user, $pass, \%opt); #sets $dbx->dbh
182             my $dbh = $dbx->connect($connection, $user, $pass, \%opt);
183              
184             Examples:
185              
186             $dbx->connect("DBI:mysql:database=mydb;host=myhost", "user", "pass", {AutoCommit=>1, RaiseError=>1});
187             $dbx->connect("DBI:Sybase:server=myhost;datasbase=mydb", "user", "pass", {AutoCommit=>1, RaiseError=>1}); #Microsoft SQL Server API is same as Sybase API
188             $dbx->connect("DBI:Oracle:TNSNAME", "user", "pass", {AutoCommit=>1, RaiseError=>1});
189              
190             =cut
191              
192             sub connect {
193 5     5 1 1077262 my $self = shift;
194 5         531 local $0 = sprintf("perl:%s", basename($0)); #Force DBD::Oracle to show "perl:script@host" in v$session.program instead of "perl@host"
195 5         75 my $dbh = DBI->connect(@_);
196 5         13587 $self->dbh($dbh);
197 5 50       53 CORE::delete $self->{'action'} if exists $self->{'action'};
198 5         98 tie $self->{'action'}, "DBIx::Array::Session::Action", (parent=>$self);
199 5         28 return $self->dbh;
200             }
201              
202             =head2 disconnect
203              
204             Wrapper around dbh->disconnect
205              
206             $dbx->disconnect;
207              
208             =cut
209              
210             sub disconnect {
211 0     0 1 0 my $self = shift;
212 0         0 untie $self->{'action'};
213 0         0 CORE::delete $self->{'action'};
214 0         0 return $self->dbh->disconnect
215             }
216              
217             =head2 commit
218              
219             Wrapper around dbh->commit
220              
221             $dbx->commit;
222              
223             =cut
224              
225             sub commit {
226 0     0 1 0 my $self = shift;
227 0         0 local $self->dbh->{'AutoCommit'} = 0;
228 0         0 return $self->dbh->commit;
229             }
230              
231             =head2 rollback
232              
233             Wrapper around dbh->rollback
234              
235             $dbx->rollback;
236              
237             =cut
238              
239             sub rollback {
240 0     0 1 0 my $self = shift;
241 0         0 return $self->dbh->rollback;
242             }
243              
244             =head2 prepare
245              
246             Wrapper around dbh->prepare with a L cache.
247              
248             my $sth = $dbx->prepare($sql);
249              
250             =cut
251              
252             sub prepare {
253 49     49 1 75 my $self = shift;
254 49         71 my $sql = shift;
255 49         75 my $sth;
256 49 50       116 if ($self->prepare_max_count > 0) {
257 49   66     140 my $cache = $self->{'_prepared'} ||= $self->_prepare_tie; #orisahash
258 49   66     272 $sth = $cache->{$sql} ||= $self->dbh->prepare($sql); #orisacache
259             } else {
260 0         0 $sth = $self->dbh->prepare($sql);
261             }
262 49 50       40292 die($self->errstr) unless $sth;
263 49         128 return $sth;
264             }
265              
266             sub _prepare_tie {
267 6     6   13 my $self = shift;
268 6         16 my $hash = {};
269 6         21 tie %$hash, 'Tie::Cache', {MaxCount => $self->prepare_max_count};
270 6         300 return $hash;
271             }
272              
273             =head2 prepare_max_count
274              
275             Maximum number of prepared statements to keep in the cache.
276              
277             $dbx->prepare_max_count(128); #default
278             $dbx->prepare_max_count(0); #disabled
279              
280             =cut
281              
282             sub prepare_max_count {
283 58     58 1 93 my $self = shift;
284 58 100       146 if (@_) {
285 1         4 $self->{"prepare_max_count"} = shift;
286 1         2 CORE::delete $self->{'_prepared'}; #clear cache if we switch handles
287             }
288 58 100       174 $self->{"prepare_max_count"} = 128 unless defined $self->{"prepare_max_count"};
289 58         220 return $self->{"prepare_max_count"};
290             }
291              
292             =head2 AutoCommit
293              
294             Wrapper around dbh->{'AutoCommit'}
295              
296             $dbx->AutoCommit(1);
297             &doSomething if $dbx->AutoCommit;
298              
299             For transactions that must complete together, I recommend
300              
301             { #block to keep local... well... local.
302             local $dbx->dbh->{'AutoCommit'} = 0;
303             $dbx->sqlinsert($sql1, @bind1);
304             $dbx->sqlupdate($sql2, @bind2);
305             $dbx->sqlinsert($sql3, @bind3);
306             } #What is AutoCommit now? Do you care?
307              
308             If AutoCommit reverts to true at the end of the block then DBI commits. Else AutoCommit is still false and still not committed. This allows higher layers to determine commit functionality.
309              
310             =cut
311              
312             sub AutoCommit {
313 0     0 1 0 my $self = shift;
314 0 0       0 if (@_) {
315 0         0 $self->dbh->{'AutoCommit'} = shift;
316             }
317 0         0 return $self->dbh->{'AutoCommit'};
318             }
319              
320             =head2 RaiseError
321              
322             Wrapper around dbh->{'RaiseError'}
323              
324             $dbx->RaiseError(1);
325             &doSomething if $dbx->RaiseError;
326              
327             { #local block
328             local $dbx->dbh->{'RaiseError'} = 0;
329             $dbx->sqlinsert($sql, @bind); #do not die
330             }
331              
332             =cut
333              
334             sub RaiseError {
335 0     0 1 0 my $self = shift;
336 0 0       0 if (@_) {
337 0         0 $self->dbh->{'RaiseError'} = shift;
338             }
339 0         0 return $self->dbh->{'RaiseError'};
340             }
341              
342             =head2 errstr
343              
344             Wrapper around $DBI::errstr
345              
346             my $err = $dbx->errstr;
347              
348             =cut
349              
350 0     0 1 0 sub errstr {$DBI::errstr};
351              
352             =head1 METHODS (Read) - SQL
353              
354             =head2 sqlcursor
355              
356             Returns the prepared and executed SQL cursor so that you can use the cursor elsewhere. Every method in this package uses this single method to generate a sqlcursor.
357              
358             my $sth = $dbx->sqlcursor($sql, @param); #binds are ? values are positional
359             my $sth = $dbx->sqlcursor($sql, \@param); #binds are ? values are positional
360             my $sth = $dbx->sqlcursor($sql, \%param); #binds are :key
361              
362             Note: In true Perl fashion extra hash binds are ignored.
363              
364             my @foo = $dbx->sqlarray("select :foo, :bar from dual",
365             {foo=>"a", bar=>1, baz=>"buz"}); #returns ("a", 1)
366              
367             my $one = $dbx->sqlscalar("select ? from dual", ["one"]); #returns "one"
368              
369             my $two = $dbx->sqlscalar("select ? from dual", "two"); #returns "two"
370              
371             Scalar references are passed in and out with a hash bind.
372              
373             my $inout = 3;
374             $dbx->sqlexecute("BEGIN :inout := :inout * 2; END;", {inout=>\$inout});
375             print "$inout\n"; #$inout is 6
376              
377             Direct Plug-in for L but no column alias support.
378              
379             my $sabs = SQL::Abstract->new;
380             my $sth = $dbx->sqlcursor($sabs->select($table, \@columns, \%where, \@sort));
381              
382             =cut
383              
384 0         0 sub sqlcursor {
385 49     49 1 821 my $self = shift;
386 49         80 my $sql = shift;
387 49         149 my $sth = $self->prepare($sql);
388 49 100       164 if (ref($_[0]) eq "ARRAY") {
    50          
389 2         9 my $bind_aref = shift;
390 2 50       18 $sth->execute(@$bind_aref) or die(&_error_string($self->errstr, $sql, sprintf("[%s]", join(", ", @$bind_aref)), "Array Reference"));
391             } elsif (ref($_[0]) eq "HASH") {
392 0         0 my $bind_href = shift;
393 0         0 foreach my $key (keys %$bind_href) {
394 0 0       0 next unless $sql =~ m/:$key\b/; #TODO: comments are scanned so /* :foo */ is not supported here
395 0 0       0 if (ref($bind_href->{$key}) eq "SCALAR") {
396 0         0 $sth->bind_param_inout(":$key" => $bind_href->{$key}, 255);
397             } else {
398 0         0 $sth->bind_param(":$key" => $bind_href->{$key});
399             }
400             }
401 0 0       0 $sth->execute or die(&_error_string($self->errstr, $sql, sprintf("{%s}", join(", ", map {join("=>", $_ => $bind_href->{$_})} sort keys %$bind_href)), "Hash Reference"));
  0         0  
402             } else {
403 47         103 my @bind = @_;
404 47 50       292 $sth->execute(@bind) or die(&_error_string($self->errstr, $sql, sprintf("(%s)", join(", ", @bind)), "List"));
405             }
406 49         84187 return $sth;
407              
408             sub _error_string {
409 0     0   0 my $err = shift;
410 0         0 my $sql = shift;
411 0         0 my $bind_str = shift;
412 0         0 my $type = shift;
413 0 0       0 if ($bind_str) {
414 0         0 return sprintf("Database Execute Error: %s\nSQL: %s\nBind(%s): %s\n", $err, $sql, $type, $bind_str);
415             } else {
416 0         0 return sprintf("Database Prepare Error: %s\nSQL: %s\n", $err, $sql);
417             }
418             }
419             }
420              
421             =head2 sqlscalar
422              
423             Returns the first row first column value as a scalar.
424              
425             This works great for selecting one value.
426              
427             my $scalar = $dbx->sqlscalar($sql, @parameters); #returns $
428             my $scalar = $dbx->sqlscalar($sql, \@parameters); #returns $
429             my $scalar = $dbx->sqlscalar($sql, \%parameters); #returns $
430              
431             =cut
432              
433             sub sqlscalar {
434 8     8 1 885 my $self = shift;
435 8         49 my @data = $self->sqlarray(@_);
436 8         67 return $data[0];
437             }
438              
439             =head2 sqlarray
440              
441             Returns the SQL result as an array or array reference.
442              
443             This works great for selecting one column from a table or selecting one row from a table.
444              
445             my $array = $dbx->sqlarray($sql, @parameters); #returns [$,$,$,...]
446             my @array = $dbx->sqlarray($sql, @parameters); #returns ($,$,$,...)
447             my $array = $dbx->sqlarray($sql, \@parameters); #returns [$,$,$,...]
448             my @array = $dbx->sqlarray($sql, \@parameters); #returns ($,$,$,...)
449             my $array = $dbx->sqlarray($sql, \%parameters); #returns [$,$,$,...]
450             my @array = $dbx->sqlarray($sql, \%parameters); #returns ($,$,$,...)
451              
452             =cut
453              
454             sub sqlarray {
455 15     15 1 6859 my $self = shift;
456 15         52 my $rows = $self->sqlarrayarray(@_);
457 15         45 my @rows = map {@$_} @$rows;
  19         56  
458 15 100       70 return wantarray ? @rows : \@rows;
459             }
460              
461             =head2 sqlhash
462              
463             Returns the first two columns of the SQL result as a hash or hash reference {Key=>Value, Key=>Value, ...}
464              
465             my $hash = $dbx->sqlhash($sql, @parameters); #returns {$=>$, $=>$, ...}
466             my %hash = $dbx->sqlhash($sql, @parameters); #returns ($=>$, $=>$, ...)
467             my @hash = $dbx->sqlhash($sql, @parameters); #this is ordered
468             my @keys = grep {!($n++ % 2)} @hash; #ordered keys
469              
470             my $hash = $dbx->sqlhash($sql, \@parameters); #returns {$=>$, $=>$, ...}
471             my %hash = $dbx->sqlhash($sql, \@parameters); #returns ($=>$, $=>$, ...)
472             my $hash = $dbx->sqlhash($sql, \%parameters); #returns {$=>$, $=>$, ...}
473             my %hash = $dbx->sqlhash($sql, \%parameters); #returns ($=>$, $=>$, ...)
474              
475             =cut
476              
477             sub sqlhash {
478 2     2 1 10335 my $self = shift;
479 2         19 my @rows = map {$_->[0], $_->[1]} $self->sqlarrayarray(@_);
  6         18  
480 2 100       18 return wantarray ? @rows : {@rows};
481             }
482              
483             =head2 sqlhashhash
484              
485             Returns a hash where the keys are the values of the first column and the values are a hash reference of all (including the key) column values.
486              
487             my $hash = $dbx->sqlhashhash($sql, @parameters); #returns {$=>{}, $=>{}, ...}
488             my %hash = $dbx->sqlhashhash($sql, @parameters); #returns ($=>{}, $=>{}, ...)
489             my @hash = $dbx->sqlhashhash($sql, @parameters); #returns ($=>{}, $=>{}, ...) #ordered
490              
491             =cut
492              
493             sub sqlhashhash {
494 2     2 1 8195 my $self = shift;
495 2         10 my $rows = $self->sqlarrayhashname(@_);
496 2         10 my $header = shift @$rows;
497 2         5 my $column = shift @$header;
498 2         4 my @rows = map {$_->{$column} => $_} @$rows;
  6         15  
499 2 100       26 return wantarray ? @rows : {@rows};
500             }
501              
502             =head2 sqlarrayarray
503              
504             Returns the SQL result as an array or array ref of array references ([],[],...) or [[],[],...]
505              
506             my $array = $dbx->sqlarrayarray($sql, @parameters); #returns [[$,$,...],[],[],...]
507             my @array = $dbx->sqlarrayarray($sql, @parameters); #returns ([$,$,...],[],[],...)
508             my $array = $dbx->sqlarrayarray($sql, \@parameters); #returns [[$,$,...],[],[],...]
509             my @array = $dbx->sqlarrayarray($sql, \@parameters); #returns ([$,$,...],[],[],...)
510             my $array = $dbx->sqlarrayarray($sql, \%parameters); #returns [[$,$,...],[],[],...]
511             my @array = $dbx->sqlarrayarray($sql, \%parameters); #returns ([$,$,...],[],[],...)
512              
513             =cut
514              
515             sub sqlarrayarray {
516 19     19 1 9518 my $self = shift;
517 19         43 my $sql = shift;
518 19         80 return $self->_sqlarrayarray(sql=>$sql, param=>[@_], name=>0);
519             }
520              
521             =head2 sqlarrayarrayname
522              
523             Returns the SQL result as an array or array ref of array references ([],[],...) or [[],[],...] where the first row contains an array reference to the column names
524              
525             my $array = $dbx->sqlarrayarrayname($sql, @parameters); #returns [[$,$,...],[]...]
526             my @array = $dbx->sqlarrayarrayname($sql, @parameters); #returns ([$,$,...],[]...)
527             my $array = $dbx->sqlarrayarrayname($sql, \@parameters); #returns [[$,$,...],[]...]
528             my @array = $dbx->sqlarrayarrayname($sql, \@parameters); #returns ([$,$,...],[]...)
529             my $array = $dbx->sqlarrayarrayname($sql, \%parameters); #returns [[$,$,...],[]...]
530             my @array = $dbx->sqlarrayarrayname($sql, \%parameters); #returns ([$,$,...],[]...)
531              
532             Create an HTML table with L
533              
534             my $cgi = CGI->new;
535             my $html = $cgi->table($cgi->Tr([map {$cgi->td($_)} $dbx->sqlarrayarrayname($sql, @param)]));
536              
537             =cut
538              
539             sub sqlarrayarrayname {
540 2     2 1 17074 my $self = shift;
541 2         6 my $sql = shift;
542 2         8 return $self->_sqlarrayarray(sql=>$sql, param=>[@_], name=>1);
543             }
544              
545             # _sqlarrayarray
546             #
547             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[ @parameters], name=>1);
548             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[ @parameters], name=>1);
549             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[ @parameters], name=>0);
550             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[ @parameters], name=>0);
551             #
552             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\@parameters], name=>1);
553             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\@parameters], name=>1);
554             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\@parameters], name=>0);
555             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\@parameters], name=>0);
556             #
557             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\%parameters], name=>1);
558             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\%parameters], name=>1);
559             # my $array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\%parameters], name=>0);
560             # my @array = $dbx->_sqlarrayarray(sql=>$sql, param=>[\%parameters], name=>0);
561              
562             sub _sqlarrayarray {
563 21     21   34 my $self = shift;
564 21         78 my %data = @_;
565 21 50       50 my $sth = $self->sqlcursor($data{'sql'}, @{$data{'param'}}) or die($self->errstr);
  21         62  
566 21         187 my $name = $sth->{'NAME'}; #DBD::mysql must store this first
567 21         815 my @rows = ();
568             #TODO: replace with fetchall_arrayref
569 21         124 while (my $row = $sth->fetchrow_arrayref()) {
570 37         1404 push @rows, [@$row];
571             }
572 21 100       462 unshift @rows, $name if $data{'name'};
573 21         76 $sth->finish;
574 21 100       222 return wantarray ? @rows : \@rows;
575             }
576              
577             =head2 sqlarrayhash
578              
579             Returns the SQL result as an array or array ref of hash references ({},{},...) or [{},{},...]
580              
581             my $array = $dbx->sqlarrayhash($sql, @parameters); #returns [{},{},{},...]
582             my @array = $dbx->sqlarrayhash($sql, @parameters); #returns ({},{},{},...)
583             my $array = $dbx->sqlarrayhash($sql, \@parameters); #returns [{},{},{},...]
584             my @array = $dbx->sqlarrayhash($sql, \@parameters); #returns ({},{},{},...)
585             my $array = $dbx->sqlarrayhash($sql, \%parameters); #returns [{},{},{},...]
586             my @array = $dbx->sqlarrayhash($sql, \%parameters); #returns ({},{},{},...)
587              
588             This method is best used to select a list of hashes out of the database to bless directly into a package.
589              
590             my $sql = q{SELECT COL1 AS "id", COL2 AS "name" FROM TABLE1};
591             my @objects = map {bless $_, MyPackage} $dbx->sqlarrayhash($sql, @parameters);
592             my @objects = map {MyPackage->new(%$_)} $dbx->sqlarrayhash($sql, @parameters);
593              
594             The @objects array is now a list of blessed MyPackage objects.
595              
596             =cut
597              
598             sub sqlarrayhash {
599 4     4 1 17040 my $self = shift;
600 4         10 my $sql = shift;
601 4         18 return $self->_sqlarrayhash(sql=>$sql, param=>[@_], name=>0);
602             }
603              
604             =head2 sqlarrayhashname
605              
606             Returns the SQL result as an array or array ref of hash references ([],{},{},...) or [[],{},{},...] where the first row contains an array reference to the column names
607              
608             my $array = $dbx->sqlarrayhashname($sql, @parameters); #returns [[],{},{},...]
609             my @array = $dbx->sqlarrayhashname($sql, @parameters); #returns ([],{},{},...)
610             my $array = $dbx->sqlarrayhashname($sql, \@parameters); #returns [[],{},{},...]
611             my @array = $dbx->sqlarrayhashname($sql, \@parameters); #returns ([],{},{},...)
612             my $array = $dbx->sqlarrayhashname($sql, \%parameters); #returns [[],{},{},...]
613             my @array = $dbx->sqlarrayhashname($sql, \%parameters); #returns ([],{},{},...)
614              
615             =cut
616              
617             sub sqlarrayhashname {
618 4     4 1 16965 my $self = shift;
619 4         18 my $sql = shift;
620 4         27 return $self->_sqlarrayhash(sql=>$sql, param=>[@_], name=>1);
621             }
622              
623             # _sqlarrayhash
624             #
625             # Returns the SQL result as an array or array ref of hash references ({},{},...) or [{},{},...]
626             #
627             # my $array = $dbx->_sqlarrayhash(sql=>$sql, param=>\@parameters, name=>1);
628             # my @array = $dbx->_sqlarrayhash(sql=>$sql, param=>\@parameters, name=>1);
629             # my $array = $dbx->_sqlarrayhash(sql=>$sql, param=>\@parameters, name=>0);
630             # my @array = $dbx->_sqlarrayhash(sql=>$sql, param=>\@parameters, name=>0);
631              
632             sub _sqlarrayhash {
633 8     8   16 my $self = shift;
634 8         31 my %data = @_;
635 8 50       18 my $sth = $self->sqlcursor($data{'sql'}, @{$data{'param'}}) or die($self->errstr);
  8         24  
636 8         62 my $name = $sth->{'NAME'}; #DBD::mysql must store this first
637 8         326 my @rows = ();
638 8         72 while (my $row = $sth->fetchrow_hashref()) {
639 24         2160 push @rows, {%$row};
640             }
641 8 100       422 unshift @rows, $name if $data{'name'};
642 8         33 $sth->finish;
643 8 100       121 return wantarray ? @rows : \@rows;
644             }
645              
646             =head2 sqlarrayobject
647              
648             Returns the SQL result as an array of blessed hash objects in to the $class namespace.
649              
650             my $array = $dbx->sqlarrayobject($class, $sql, @parameters); #returns [bless({}, $class), ...]
651             my @array = $dbx->sqlarrayobject($class, $sql, @parameters); #returns (bless({}, $class), ...)
652             my ($object) = $dbx->sqlarrayobject($class, $sql, {id=>$id}); #$object is bless({}, $class)
653              
654             =cut
655              
656             sub sqlarrayobject {
657 3     3 1 39686 my $self = shift;
658 3 100       19 my $class = shift or die("Error: The sqlarrayobject method requires a class parameter");
659 2         9 my @objects = map {bless($_, $class)} $self->sqlarrayhash(@_);
  6         25  
660 2 100       10 wantarray ? @objects : \@objects;
661             }
662              
663             =head2 sqlsort (Oracle Specific?)
664              
665             Returns the SQL statement with the correct ORDER BY clause given a SQL statement (without an ORDER BY clause) and a signed integer on which column to sort.
666              
667             my $sql = $dbx->sqlsort(qq{SELECT 1,'Z' FROM DUAL UNION SELECT 2,'A' FROM DUAL}, -2);
668              
669             Returns
670              
671             SELECT 1,'Z' FROM DUAL UNION SELECT 2,'A' FROM DUAL ORDER BY 2 DESC
672              
673             Note: The sqlsort method is no longer preferred. It is recommended to use the newer sqlwhere capability.
674              
675             =cut
676              
677             sub sqlsort {
678 2     2 1 9577 my $self = shift;
679 2         4 my $sql = shift;
680 2         5 my $sort = int(shift); #not sure we need int here but I did not want to change behavior
681 2 50       5 if (defined($sort)) {
682 2         6 my $column = abs($sort);
683 2 100       7 my $direction = $sort < 0 ? "DESC" : "ASC";
684 2         25 return join " ", $sql, sprintf("ORDER BY %u %s", $column, $direction);
685             } else {
686 0         0 return $sql;
687             }
688             }
689              
690             =head2 sqlarrayarraynamesort
691              
692             Returns a sqlarrayarrayname for $sql sorted on column $n where n is an integer ascending for positive, descending for negative, and 0 for no sort.
693              
694             my $data = $dbx->sqlarrayarraynamesort($sql, $n, @parameters);
695             my $data = $dbx->sqlarrayarraynamesort($sql, $n, \@parameters);
696             my $data = $dbx->sqlarrayarraynamesort($sql, $n, \%parameters);
697              
698             Note: $sql must not have an "ORDER BY" clause in order for this function to work correctly.
699              
700             Note: The sqlarrayarraynamesort method is no longer preferred. It is recommended to use the newer sqlwherearrayarrayname capability.
701              
702             =cut
703              
704             sub sqlarrayarraynamesort {
705 0     0 1 0 my $self = shift;
706 0         0 my $sql = shift;
707 0         0 my $sort = shift;
708 0         0 return $self->sqlarrayarrayname($self->sqlsort($sql, $sort), @_);
709             }
710              
711             =head1 METHODS (Read) - SQL::Abstract
712              
713             Please note the "abs" API is a 100% pass through to L. Please reference the L documentation for syntax assistance with that API.
714              
715             =head2 abscursor
716              
717             Returns the prepared and executed SQL cursor.
718              
719             my $sth = $dbx->abscursor($table, \@columns, \%where, \@order);
720             my $sth = $dbx->abscursor($table, \@columns, \%where); #no order required defaults to storage
721             my $sth = $dbx->abscursor($table, \@columns); #no where required defaults to all
722             my $sth = $dbx->abscursor($table); #no columns required defaults to '*' (all)
723              
724             =cut
725              
726             sub abscursor {
727 0     0 1 0 my $self = shift;
728 0         0 return $self->sqlcursor($self->abs->select(@_));
729             }
730              
731             =head2 absscalar
732              
733             Returns the first row first column value as a scalar.
734              
735             This works great for selecting one value.
736              
737             my $scalar = $dbx->absscalar($table, \@columns, \%where, \@order); #returns $
738              
739             =cut
740              
741             sub absscalar {
742 0     0 1 0 my $self = shift;
743 0         0 return $self->sqlscalar($self->abs->select(@_));
744             }
745              
746             =head2 absarray
747              
748             Returns the SQL result as a array.
749              
750             This works great for selecting one column from a table or selecting one row from a table.
751              
752             my @array = $dbx->absarray($table, \@columns, \%where, \@order); #returns ()
753             my $array = $dbx->absarray($table, \@columns, \%where, \@order); #returns []
754              
755             =cut
756              
757             sub absarray {
758 0     0 1 0 my $self = shift;
759 0         0 return $self->sqlarray($self->abs->select(@_));
760             }
761              
762             =head2 abshash
763              
764             Returns the first two columns of the SQL result as a hash or hash reference {Key=>Value, Key=>Value, ...}
765              
766             my $hash = $dbx->abshash($table, \@columns, \%where, \@order); #returns {}
767             my %hash = $dbx->abshash($table, \@columns, \%where, \@order); #returns ()
768              
769             =cut
770              
771             sub abshash {
772 0     0 1 0 my $self = shift;
773 0         0 return $self->sqlhash($self->abs->select(@_));
774             }
775              
776             =head2 abshashhash
777              
778             Returns a hash where the keys are the values of the first column and the values are a hash reference of all (including the key) column values.
779              
780             my $hash = $dbx->abshashhash($table, \@columns, \%where, \@order); #returns {}
781             my %hash = $dbx->abshashhash($table, \@columns, \%where, \@order); #returns ()
782              
783             =cut
784              
785             sub abshashhash {
786 0     0 1 0 my $self = shift;
787 0         0 return $self->sqlhashhash($self->abs->select(@_));
788             }
789              
790              
791             =head2 absarrayarray
792              
793             Returns the SQL result as an array or array ref of array references ([],[],...) or [[],[],...]
794              
795             my $array = $dbx->absarrayarray($table, \@columns, \%where, \@order); #returns [[$,$,...],[],[],...]
796             my @array = $dbx->absarrayarray($table, \@columns, \%where, \@order); #returns ([$,$,...],[],[],...)
797              
798             =cut
799              
800             sub absarrayarray {
801 0     0 1 0 my $self = shift;
802 0         0 return $self->sqlarrayarray($self->abs->select(@_));
803             }
804              
805             =head2 absarrayarrayname
806              
807             Returns the SQL result as an array or array ref of array references ([],[],...) or [[],[],...] where the first row contains an array reference to the column names
808              
809             my $array = $dbx->absarrayarrayname($table, \@columns, \%where, \@order); #returns [[$,$,...],[],[],...]
810             my @array = $dbx->absarrayarrayname($table, \@columns, \%where, \@order); #returns ([$,$,...],[],[],...)
811              
812             =cut
813              
814             sub absarrayarrayname {
815 0     0 1 0 my $self = shift;
816 0         0 return $self->sqlarrayarrayname($self->abs->select(@_));
817             }
818              
819             =head2 absarrayhash
820              
821             Returns the SQL result as an array or array ref of hash references ({},{},...) or [{},{},...]
822              
823             my $array = $dbx->absarrayhash($table, \@columns, \%where, \@order); #returns [{},{},{},...]
824             my @array = $dbx->absarrayhash($table, \@columns, \%where, \@order); #returns ({},{},{},...)
825              
826             =cut
827              
828             sub absarrayhash {
829 0     0 1 0 my $self = shift;
830 0         0 return $self->sqlarrayhash($self->abs->select(@_));
831             }
832              
833             =head2 absarrayhashname
834              
835             Returns the SQL result as an array or array ref of hash references ({},{},...) or [{},{},...] where the first row contains an array reference to the column names.
836              
837             my $array = $dbx->absarrayhashname($table, \@columns, \%where, \@order); #returns [[],{},{},...]
838             my @array = $dbx->absarrayhashname($table, \@columns, \%where, \@order); #returns ([],{},{},...)
839              
840             =cut
841              
842             sub absarrayhashname {
843 0     0 1 0 my $self = shift;
844 0         0 return $self->sqlarrayhashname($self->abs->select(@_));
845             }
846              
847             =head2 absarrayobject
848              
849             Returns the SQL result as an array of blessed hash objects in to the $class namespace.
850              
851             my $array = $dbx->absarrayobject($class, $table, \@columns, \%where, \@order); #returns [bless({}, $class), ...]
852             my @array = $dbx->absarrayobject($class, $table, \@columns, \%where, \@order); #returns (bless({}, $class), ...)
853              
854             =cut
855              
856             sub absarrayobject {
857 0     0 1 0 my $self = shift;
858 0 0       0 my $class = shift or die("Error: The absarrayobject method requires a class parameter");
859 0         0 my @objects = map {bless($_, $class)} $self->absarrayhash(@_);
  0         0  
860 0 0       0 wantarray ? @objects : \@objects;
861             }
862              
863             =head1 METHODS (Read) - SQL + SQL::Abstract->where
864              
865             =head2 sqlwhere
866              
867             Returns SQL part appended with the WHERE and ORDER BY clauses
868              
869             my ($sql, @bind) = $sql->sqlwhere($sqlpart, \%where, \@order);
870              
871             Note: sqlwhere function should be ported into L RT125805
872              
873             =cut
874              
875             sub sqlwhere {
876 0     0 1 0 my $self = shift;
877 0         0 my $sqlpart = shift;
878 0         0 my ($where, @bind) = $self->abs->where(@_);
879 0 0       0 $sqlpart .= " $/ $where" if length($where);
880 0         0 return($sqlpart, @bind);
881             }
882              
883             =head2 sqlwherecursor
884              
885             my $return = $sql->sqlwherecursor($sqlpart, \%where, \@order);
886              
887             =cut
888              
889             sub sqlwherecursor {
890 0     0 1 0 my $self = shift;
891 0         0 return $self->sqlcursor($self->sqlwhere(@_));
892             }
893              
894             =head2 sqlwherescalar
895              
896             my $return = $sql->sqlwherescalar($sqlpart, \%where, \@order);
897              
898             =cut
899              
900             sub sqlwherescalar {
901 0     0 1 0 my $self = shift;
902 0         0 return $self->sqlscalar($self->sqlwhere(@_));
903             }
904              
905             =head2 sqlwherearray
906              
907             my $return = $sql->sqlwherearray($sqlpart, \%where, \@order);
908              
909             =cut
910              
911             sub sqlwherearray {
912 0     0 1 0 my $self = shift;
913 0         0 return $self->sqlarray($self->sqlwhere(@_));
914             }
915              
916             =head2 sqlwherehash
917              
918             my $return = $sql->sqlwherehash($sqlpart, \%where, \@order);
919              
920             =cut
921              
922             sub sqlwherehash {
923 0     0 1 0 my $self = shift;
924 0         0 return $self->sqlhash($self->sqlwhere(@_));
925             }
926              
927             =head2 sqlwherehashhash
928              
929             my $return = $sql->sqlwherehashhash($sqlpart, \%where, \@order);
930              
931             =cut
932              
933             sub sqlwherehashhash {
934 0     0 1 0 my $self = shift;
935 0         0 return $self->sqlhashhash($self->sqlwhere(@_));
936             }
937              
938             =head2 sqlwherearrayarray
939              
940             my $return = $sql->sqlwherearrayarray($sqlpart, \%where, \@order);
941              
942             =cut
943              
944             sub sqlwherearrayarray {
945 0     0 1 0 my $self = shift;
946 0         0 return $self->sqlarrayarray($self->sqlwhere(@_));
947             }
948              
949             =head2 sqlwherearrayarrayname
950              
951             my $return = $sql->sqlwherearrayarrayname($sqlpart, \%where, \@order);
952              
953             =cut
954              
955             sub sqlwherearrayarrayname {
956 0     0 1 0 my $self = shift;
957 0         0 return $self->sqlarrayarrayname($self->sqlwhere(@_));
958             }
959              
960             =head2 sqlwherearrayhash
961              
962             my $return = $sql->sqlwherearrayhash($sqlpart, \%where, \@order);
963              
964             =cut
965              
966             sub sqlwherearrayhash {
967 0     0 1 0 my $self = shift;
968 0         0 return $self->sqlarrayhash($self->sqlwhere(@_));
969             }
970              
971             =head2 sqlwherearrayhashname
972              
973             my $return = $sql->sqlwherearrayhashname($sqlpart, \%where, \@order);
974              
975             =cut
976              
977             sub sqlwherearrayhashname {
978 0     0 1 0 my $self = shift;
979 0         0 return $self->sqlarrayhashname($self->sqlwhere(@_));
980             }
981              
982             =head2 sqlwherearrayobject
983              
984             my $return = $sql->sqlwherearrayobject($class, $sqlpart, \%where, \@order);
985              
986             =cut
987              
988             sub sqlwherearrayobject {
989 0     0 1 0 my $self = shift;
990 0 0       0 my $class = shift or die("Error: sqlwherearrayobject parameter class missing");
991 0         0 return $self->sqlarrayobject($class, $self->sqlwhere(@_));
992             }
993              
994             =head1 METHODS (Write) - SQL
995              
996             Remember to commit or use AutoCommit
997              
998             Note: It appears that some drivers do not support the count of rows.
999              
1000             =head2 sqlinsert, insert
1001              
1002             Returns the number of rows inserted by the SQL statement.
1003              
1004             my $count = $dbx->sqlinsert( $sql, @parameters);
1005             my $count = $dbx->sqlinsert( $sql, \@parameters);
1006             my $count = $dbx->sqlinsert( $sql, \%parameters);
1007              
1008             =cut
1009              
1010             *sqlinsert = \&sqlupdate;
1011             *insert = \&sqlupdate;
1012              
1013             =head2 sqlupdate, update
1014              
1015             Returns the number of rows updated by the SQL statement.
1016              
1017             my $count = $dbx->sqlupdate( $sql, @parameters);
1018             my $count = $dbx->sqlupdate( $sql, \@parameters);
1019             my $count = $dbx->sqlupdate( $sql, \%parameters);
1020              
1021             =cut
1022              
1023             *update = \&sqlupdate;
1024              
1025             sub sqlupdate {
1026 19     19 1 10298 my $self = shift;
1027 19         39 my $sql = shift;
1028 19 50       68 my $sth = $self->sqlcursor($sql, @_) or die($self->errstr);
1029 19         107 my $rows = $sth->rows;
1030 19         129 $sth->finish;
1031 19         263 return $rows;
1032             }
1033              
1034             =head2 sqldelete, delete
1035              
1036             Returns the number of rows deleted by the SQL statement.
1037              
1038             my $count = $dbx->sqldelete($sql, @parameters);
1039             my $count = $dbx->sqldelete($sql, \@parameters);
1040             my $count = $dbx->sqldelete($sql, \%parameters);
1041              
1042             Note: Some Oracle clients do not support row counts on delete instead the value appears to be a success code.
1043              
1044             =cut
1045              
1046             *sqldelete = \&sqlupdate;
1047             *delete = \&sqlupdate;
1048              
1049             =head2 sqlexecute, execute, exec
1050              
1051             Executes stored procedures and generic SQL.
1052              
1053             my $out;
1054             my $return = $dbx->sqlexecute($sql, $in, \$out); #pass in/out vars as scalar reference
1055             my $return = $dbx->sqlexecute($sql, [$in, \$out]);
1056             my $return = $dbx->sqlexecute($sql, {in=>$in, out=>\$out});
1057              
1058             Note: Currently sqlupdate, sqlinsert, sqldelete, and sqlexecute all point to the same method. This may change in the future if we need to change the behavior of one method. So, please use the correct method name for your function.
1059              
1060             =cut
1061              
1062             *sqlexecute = \&sqlupdate;
1063             *execute = \&sqlupdate; #deprecated
1064             *exec = \&sqlupdate; #deprecated
1065              
1066             =head1 METHODS (Write) - SQL::Abstract
1067              
1068             =head2 absinsert
1069              
1070             Returns the number of rows inserted.
1071              
1072             my $count = $dbx->absinsert($table, \%column_values);
1073              
1074             =cut
1075              
1076             sub absinsert {
1077 0     0 1   my $self = shift;
1078 0           return $self->sqlinsert($self->abs->insert(@_));
1079             }
1080              
1081             =head2 absupdate
1082              
1083             Returns the number of rows updated.
1084              
1085             my $count = $dbx->absupdate($table, \%column_values, \%where);
1086              
1087             =cut
1088              
1089             sub absupdate {
1090 0     0 1   my $self = shift;
1091 0           return $self->sqlupdate($self->abs->update(@_));
1092             }
1093              
1094             =head2 absdelete
1095              
1096             Returns the number of rows deleted.
1097              
1098             my $count = $dbx->absdelete($table, \%where);
1099              
1100             =cut
1101              
1102             sub absdelete {
1103 0     0 1   my $self = shift;
1104 0           return $self->sqldelete($self->abs->delete(@_));
1105             }
1106              
1107             =head1 METHODS (Write) - Bulk - SQL
1108              
1109             =head2 bulksqlinsertarrayarray
1110              
1111             Insert records in bulk.
1112              
1113             my @arrayarray = (
1114             [$data1, $data2, $data3, $data4, ...],
1115             [@row_data_2],
1116             [@row_data_3], ...
1117             );
1118             my $count = $dbx->bulksqlinsertarrayarray($sql, \@arrayarray);
1119              
1120             =cut
1121              
1122             sub bulksqlinsertarrayarray {
1123 0     0 1   my $self = shift;
1124 0 0         my $sql = shift or die('Error: sql required.');
1125 0 0         my $arrayarray = shift or die('Error: array of array references required.');
1126 0           my $sth = $self->prepare($sql);
1127 0           my $rows = 0;
1128 0           my $size = @$arrayarray;
1129 0           my @tuple_status = ();
1130 0     0     my ($tupples, $count) = $sth->execute_for_fetch( sub {shift @$arrayarray}, \@tuple_status);
  0            
1131             #print Dumper \@tuple_status, $tupples, $count;
1132 0 0         if (not defined $count) { #driver does not support count yet
1133 0           foreach my $status (@tuple_status) {
1134 0 0         if (ref($status) eq "ARRAR") {
    0          
1135 0           warn($status->[1]);
1136             } elsif ($status == -1) {
1137 0           $rows++; #no error assume 1 row inserted.
1138             } else {
1139 0           warn(Dumper $status);
1140             }
1141             }
1142 0           $count = $rows;
1143             }
1144 0           return $count;
1145             }
1146              
1147             =head2 bulksqlinsertarrayhash
1148              
1149             Insert records in bulk.
1150              
1151             my @columns = ("Col1", "Col2", "Col3", "Col4", ...); #case sensitive with respect to @arrayhash
1152             my @arrayhash = (
1153             {C0l1=>data1, Col2=>$data2, Col3=>$data3, Col4=>$data4, ...}, #extra hash items ignored when sliced using @columns
1154             \%row_hash_data_2,
1155             \%row_hash_data_3, ...
1156             );
1157             my $count = $dbx->bulksqlinsertarrayhash($sql, \@columns, \@arrayhash);
1158              
1159             =cut
1160              
1161             sub bulksqlinsertarrayhash {
1162 0     0 1   my $self = shift;
1163 0 0         my $sql = shift or die("Error: SQL required.");
1164 0 0         my $columns = shift or die("Error: columns array reference required.");
1165 0 0         my $arrayhash = shift or die("Error: array of hash references required.");
1166 0           my @arrayarray = map {my %hash = %$_; my @slice = @hash{@$columns}; \@slice} @$arrayhash;
  0            
  0            
  0            
1167 0           return $self->bulksqlinsertarrayarray($sql, \@arrayarray);
1168             }
1169              
1170             =head2 bulksqlinsertcursor
1171              
1172             Insert records in bulk.
1173              
1174             Step 1 select data from table 1 in database 1
1175              
1176             my $sth1 = $dbx1->sqlcursor('Select Col1 AS "ColA", Col2 AS "ColB", Col3 AS "ColC" from table1');
1177              
1178             Step 2 insert in to table 2 in database 2
1179              
1180             my $count = $dbx2->bulksqlinsertcursor($sql, $sth1);
1181              
1182             Note: If you are inside a single database, it is much more efficient to use insert from select syntax as no data needs to be transferred to and from the client.
1183              
1184             =cut
1185              
1186             sub bulksqlinsertcursor {
1187 0     0 1   my $self = shift;
1188 0 0         my $sql = shift or die('Error: sql required.');
1189 0 0         my $cursor = shift or die('Error: cursor required.');
1190 0           my $sth = $self->prepare($sql);
1191 0           my @tuple_status = ();
1192 0           my $size = 0;
1193 0 0   0     my $count = $sth->execute_for_fetch( sub {my $row = $cursor->fetchrow_arrayref; $size++ if $row; return $row}, \@tuple_status);
  0            
  0            
  0            
1194 0 0         unless ($count == $size) {
1195 0           warn Dumper \@tuple_status; #TODO better error trapping...
1196             }
1197 0           return $count;
1198             }
1199              
1200             =head2 bulksqlupdatearrayarray
1201              
1202             Update records in bulk.
1203              
1204             my @arrayarray = (
1205             [$data1, $data2, $data3, $data4, $id],
1206             [@row_data_2],
1207             [@row_data_3], ...
1208             );
1209             my $count = $dbx->bulksqlupdatearrayarray($sql, \@arrayarray);
1210              
1211             =cut
1212              
1213             sub bulksqlupdatearrayarray {
1214 0     0 1   my $self = shift;
1215 0 0         my $sql = shift or die('Error: sql required.');
1216 0 0         my $arrayarray = shift or die('Error: array of array references required.');
1217 0           my $sth = $self->prepare($sql);
1218 0           my $size = @$arrayarray;
1219 0           my @tuple_status = (); #pass to set $tupples
1220 0     0     my ($tupples, $count) = $sth->execute_for_fetch( sub {shift @$arrayarray}, \@tuple_status);
  0            
1221 0 0         warn("Warning: Attempted $size transactions but only $tupples where successful.") unless $size == $tupples;
1222             #warn Dumper \@tuple_status;
1223 0 0 0       unless (defined($count) and $count >= 0) {
1224 0           $count = sum(0, grep {$_ > 0} grep {not ref($_)} @tuple_status);
  0            
  0            
1225             }
1226 0           return $count;
1227             }
1228              
1229             =head1 METHODS (Write) - Bulk - SQL::Abstract-like
1230              
1231             These bulk methods do not use L but our own similar SQL insert and update methods.
1232              
1233             =head2 bulkabsinsertarrayarray
1234              
1235             Insert records in bulk.
1236              
1237             my @columns = ("Col1", "Col2", "Col3", "Col4", ...);
1238             my @arrayarray = (
1239             [data1, $data2, $data3, $data4, ...],
1240             [@row_data_2],
1241             [@row_data_3], ...
1242             );
1243             my $count = $dbx->bulkabsinsertarrayarray($table, \@columns, \@arrayarray);
1244              
1245             =cut
1246              
1247             sub bulkabsinsertarrayarray {
1248 0     0 1   my $self = shift;
1249 0 0         my $table = shift or die('Error: table name required.');
1250 0 0         my $columns = shift or die('Error: columns array reference required.');
1251 0 0         my $arrayarray = shift or die('Error: array of array references required.');
1252 0           my $sql = $self->_bulkinsert_sql($table => $columns);
1253 0           return $self->bulksqlinsertarrayarray($sql, $arrayarray);
1254             }
1255              
1256             =head2 bulkabsinsertarrayhash
1257              
1258             Insert records in bulk.
1259              
1260             my @columns = ("Col1", "Col2", "Col3", "Col4", ...); #case sensitive with respect to @arrayhash
1261             my @arrayhash = (
1262             {C0l1=>data1, Col2=>$data2, Col3=>$data3, Col4=>$data4, ...}, #extra hash items ignored when sliced using @columns
1263             \%row_hash_data_2,
1264             \%row_hash_data_3, ...
1265             );
1266             my $count = $dbx->bulkabsinsertarrayhash($table, \@columns, \@arrayhash);
1267              
1268             =cut
1269              
1270             sub bulkabsinsertarrayhash {
1271 0     0 1   my $self = shift;
1272 0 0         my $table = shift or die("Error: table name required.");
1273 0 0         my $columns = shift or die("Error: columns array reference required.");
1274 0 0         my $arrayhash = shift or die("Error array of hash references required");
1275 0           my @arrayarray = map {my %hash = %$_; my @slice = @hash{@$columns}; \@slice} @$arrayhash;
  0            
  0            
  0            
1276 0           return $self->bulkabsinsertarrayarray($table, $columns, \@arrayarray);
1277             }
1278              
1279             =head2 bulkabsinsertcursor
1280              
1281             Insert records in bulk.
1282              
1283             Step 1 select data from table 1 in database 1
1284              
1285             my $sth1 = $dbx1->sqlcursor('Select Col1 AS "ColA", Col2 AS "ColB", Col3 AS "ColC" from table1');
1286              
1287             Step 2 insert in to table 2 in database 2
1288              
1289             my $count = $dbx2->bulkabsinsertcursor($table2, $sth1);
1290              
1291             my $count = $dbx2->bulkabsinsertcursor($table2, \@columns, $sth1); #if your DBD/API does not support column alias support
1292              
1293             Note: If you are inside a single database, it is much more efficient to use insert from select syntax as no data needs to be transferred to and from the client.
1294              
1295             =cut
1296              
1297             sub bulkabsinsertcursor {
1298 0     0 1   my $self = shift;
1299 0 0         my $table = shift or die('Error: table name required.');
1300 0 0         my $cursor = pop or die('Error: cursor required.');
1301 0   0       my $columns = shift || $cursor->{'NAME'};
1302 0           my $sql = $self->_bulkinsert_sql($table => $columns);
1303 0           return $self->bulksqlinsertcursor($sql, $cursor);
1304             }
1305              
1306             #head2 _bulkinsert_sql
1307             #
1308             #Our own method since SQL::Abstract does not support ordered column values
1309             #
1310             #cut
1311              
1312             sub _bulkinsert_sql {
1313 0     0     my $self = shift;
1314 0           my $table = shift;
1315 0           my $columns = shift;
1316 0           my $sql = sprintf("INSERT INTO $table (%s) VALUES (%s)", join(',', @$columns), join(',', map {'?'} @$columns));
  0            
1317             #warn "$sql\n";
1318 0           return $sql;
1319             }
1320              
1321             =head2 bulkabsupdatearrayarray
1322              
1323             Update records in bulk.
1324              
1325             my @setcolumns = ("Col1", "Col2", "Col3", "Col4");
1326             my @wherecolumns = ("ID");
1327             my @arrayarray = (
1328             [$data1, $data2, $data3, $data4, $id],
1329             [@row_data_2],
1330             [@row_data_3], ...
1331             );
1332             my $count = $dbx->bulkabsupdatearrayarray($table, \@setcolumns, \@wherecolumns, \@arrayarray);
1333              
1334             =cut
1335              
1336             sub bulkabsupdatearrayarray {
1337 0     0 1   my $self = shift;
1338 0 0         my $table = shift or die('Error: table name required.');
1339 0 0         my $setcolumns = shift or die('Error: set columns array reference required.');
1340 0 0         my $wherecolumns = shift or die('Error: where columns array reference required.');
1341 0           my $arrayarray = shift;
1342 0           my $sql = $self->_bulkupdate_sql($table => $setcolumns, $wherecolumns);
1343 0           return $self->bulksqlupdatearrayarray($sql, $arrayarray);
1344             }
1345              
1346             #head2 _bulkupdate_sql
1347             #
1348             #Our own method since SQL::Abstract does not support ordered column values
1349             #
1350             ##cut
1351              
1352             sub _bulkupdate_sql {
1353 0     0     my $self = shift;
1354 0           my $table = shift;
1355 0           my $setcolumns = shift;
1356 0           my $wherecolumns = shift;
1357 0           my $sql = sprintf("UPDATE $table SET %s WHERE %s", join(", ", map {"$_ = ?"} @$setcolumns), join(" AND ", map {"$_ = ?"} @$wherecolumns));
  0            
  0            
1358             #warn "$sql\n";
1359 0           return $sql;
1360             }
1361              
1362             =head1 Constructors
1363              
1364             =head2 abs
1365              
1366             Returns a L object
1367              
1368             =cut
1369              
1370             sub abs {
1371 0     0 1   my $self = shift;
1372 0 0         $self->{'abs'} = shift if @_;
1373 0 0         unless (defined $self->{'abs'}) {
1374 0           eval 'use SQL::Abstract'; #run time require so as not to require installation for all users
1375 0           my $error = $@;
1376 0 0         die($error) if $error;
1377 0           $self->{'abs'} = SQL::Abstract->new;
1378             }
1379 0           return $self->{'abs'};
1380             }
1381              
1382             =head1 Methods (Informational)
1383              
1384             =head2 dbms_name
1385              
1386             Return the DBMS Name (e.g. Oracle, MySQL, PostgreSQL)
1387              
1388             =cut
1389              
1390 0     0 1   sub dbms_name {shift->dbh->get_info(17)};
1391              
1392             =head1 Methods (Session Management)
1393              
1394             These methods allow the setting of Oracle session features that are available in the v$session table. If other databases support these features, please let me know. But, as it stands, these methods are non operational unless SQL_DBMS_NAME is Oracle.
1395              
1396             =head2 module
1397              
1398             Sets and returns the v$session.module (Oracle) value.
1399              
1400             Note: Module is set for you by DBD::Oracle. However you may set it however you'd like. It should be set once after connection and left alone.
1401              
1402             $dbx->module("perl@host"); #normally set by DBD::Oracle
1403             $dbx->module($module, $action); #can set initial action too.
1404             my $module = $dbx->module();
1405              
1406             =cut
1407              
1408             sub module {
1409 0     0 1   my $self = shift;
1410 0 0         return unless $self->dbms_name eq 'Oracle';
1411 0 0         if (@_) {
1412 0           my $module = shift;
1413 0           my $action = shift;
1414 0           $self->sqlexecute($self->_set_module_sql, $module, $action);
1415             }
1416 0 0         if (defined wantarray) {
1417 0           return $self->sqlscalar($self->_sys_context_userenv_sql, 'MODULE');
1418             } else {
1419 0           return; #void context no need to hit the database
1420             }
1421             }
1422              
1423             sub _set_module_sql {
1424 0     0     return qq{/* be655786-bcbe-11e5-8338-005056a31307 */
1425             /* Script: $0 */
1426             /* Package: $PACKAGE */
1427             /* Method: _set_module_sql */
1428             BEGIN
1429             DBMS_APPLICATION_INFO.set_module(module_name => ?, action_name => ?);
1430             END;
1431             };
1432             }
1433              
1434             =head2 client_info
1435              
1436             Sets and returns the v$session.client_info (Oracle) value.
1437              
1438             $dbx->client_info("Running From crontab");
1439             my $client_info = $dbx->client_info();
1440              
1441             You may use this field for anything up to 64 characters!
1442              
1443             $dbx->client_info(join "~", (ver => 4, realm => "ldap", grp =>25)); #tilde is a fairly good separator
1444             my %client_info = split(/~/, $dbx->client_info());
1445              
1446             =cut
1447              
1448             sub client_info {
1449 0     0 1   my $self = shift;
1450 0 0         return unless $self->dbms_name eq 'Oracle';
1451 0 0         if (@_) {
1452 0           my $text = shift;
1453 0           $self->sqlexecute($self->_set_client_info_sql, $text);
1454             }
1455 0 0         if (defined wantarray) {
1456 0           return $self->sqlscalar($self->_sys_context_userenv_sql, 'CLIENT_INFO');
1457             } else {
1458 0           return; #void context no need to hit the database
1459             }
1460             }
1461              
1462             sub _set_client_info_sql {
1463 0     0     return qq{/* d04d0138-bcbe-11e5-b0e3-005056a31307 */
1464             /* Script: $0 */
1465             /* Package: $PACKAGE */
1466             /* Method: _set_client_info_sql */
1467             BEGIN
1468             DBMS_APPLICATION_INFO.set_client_info(client_info => ?);
1469             END;
1470             };
1471             }
1472              
1473             =head2 action
1474              
1475             Sets and returns the v$session.action (Oracle) value.
1476              
1477             $dbx->action("We are Here");
1478             my $action = $dbx->action();
1479              
1480             Note: This should be updated fairly often. Every loop if it runs for more than 5 seconds and may end up in V$SQL_MONITOR.
1481              
1482             while ($this) {
1483             local $dbx->{'action'} = "This Loop"; #tied to the database with a little Perl sugar
1484             }
1485              
1486             =cut
1487              
1488             sub action {
1489 0     0 1   my $self = shift;
1490 0 0         return unless $self->dbms_name eq 'Oracle';
1491 0 0         if (@_) {
1492 0           my $text = shift;
1493 0           $self->sqlexecute($self->_set_action_sql, $text);
1494             }
1495 0 0         if (defined wantarray) {
1496 0           return $self->sqlscalar($self->_sys_context_userenv_sql, 'ACTION');
1497             } else {
1498 0           return; #void context no need to hit the database
1499             }
1500             }
1501              
1502             sub _set_action_sql {
1503 0     0     return qq{/* e682f1a6-bcbe-11e5-bd3e-005056a31307 */
1504             /* Script: $0 */
1505             /* Package: $PACKAGE */
1506             /* Method: _set_action_sql */
1507             BEGIN
1508             DBMS_APPLICATION_INFO.set_action(action_name => ?);
1509             END;
1510             };
1511             }
1512              
1513             =head2 client_identifier
1514              
1515             Sets and returns the v$session.client_identifier (Oracle) value.
1516              
1517             $dbx->client_identifier($login);
1518             my $client_identifier = $dbx->client_identifier();
1519              
1520             Note: This should be updated based on the login of the authenticated end user. I use the client_info->{'realm'} if you have more than one authentication realm.
1521              
1522             For auditing add this to an update trigger
1523              
1524             new.UPDATED_USER = sys_context('USERENV', 'CLIENT_IDENTIFIER');
1525              
1526             =cut
1527              
1528             sub client_identifier {
1529 0     0 1   my $self = shift;
1530 0 0         return unless $self->dbms_name eq 'Oracle';
1531 0 0         if (@_) {
1532 0           my $text = shift;
1533 0           $self->sqlexecute($self->_set_client_identifier_sql, $text);
1534             }
1535 0 0         if (defined wantarray) {
1536 0           return $self->sqlscalar($self->_sys_context_userenv_sql, 'CLIENT_IDENTIFIER');
1537             } else {
1538 0           return; #void context no need to hit the database
1539             }
1540             }
1541              
1542             sub _set_client_identifier_sql {
1543 0     0     return qq{/* f8226e6e-bcbe-11e5-91b8-005056a31307 */
1544             /* Script: $0 */
1545             /* Package: $PACKAGE */
1546             /* Method: _set_client_identifier_sql */
1547             BEGIN
1548             DBMS_SESSION.SET_IDENTIFIER(client_id => ?);
1549             END;
1550             };
1551             }
1552              
1553             sub _sys_context_userenv_sql {
1554 0     0     return qq{/* 09648e1e-bcbf-11e5-916a-005056a31307 */
1555             /* Script: $0 */
1556             /* Package: $PACKAGE */
1557             /* Method: _sys_context_userenv_sql */
1558             SELECT sys_context('USERENV',?)
1559             FROM SYS.DUAL
1560             };
1561             }
1562              
1563             =head1 TODO
1564              
1565             Sort functions sqlsort and sqlarrayarraynamesort may not be portable. It is now recommend to use sqlwhere methods instead.
1566              
1567             Add some kind of capability to allow hash binds to bind as some native type rather than all strings.
1568              
1569             Hash binds scan comments for bind variables e.g. /* :variable */
1570              
1571             Improve error messages
1572              
1573             =head1 BUGS
1574              
1575             Please open on GitHub
1576              
1577             =head1 AUTHOR
1578              
1579             Michael R. Davis
1580              
1581             =head1 COPYRIGHT
1582              
1583             MIT License
1584              
1585             Copyright (c) 2023 Michael R. Davis
1586              
1587             =head1 SEE ALSO
1588              
1589             =head2 The Competition
1590              
1591             L, L, L, L, L, L, L quick_*, L (arrays & hashes)
1592              
1593             =head2 The Building Blocks
1594              
1595             L, L
1596              
1597             =cut
1598              
1599             1;