File Coverage

lib/UR/DBI.pm
Criterion Covered Total %
statement 309 451 68.5
branch 67 164 40.8
condition 9 39 23.0
subroutine 58 72 80.5
pod 1 16 6.2
total 444 742 59.8


line stmt bran cond sub pod time code
1             # Additional methods for DBI.
2              
3             package UR::DBI;
4              
5             =pod
6              
7             =head1 NAME
8              
9             UR::DBI - methods for interacting with a database.
10              
11             =head1 SYNOPSIS
12              
13             ##- use UR::DBI;
14             UR::DBI->monitor_sql(1);
15             my $dbh = UR::DBI->connect(...);
16              
17             =head1 DESCRIPTION
18              
19             This module subclasses DBI, and provides a few extra methods useful when using a database.
20              
21             =head1 METHODS
22              
23             =over 4
24              
25             =cut
26              
27             # set up package
28             require 5.006_000;
29 266     21254   1039 use warnings;
  266         306  
  266         8499  
30 266     266   901 use strict;
  266         306  
  266         8581  
31             our $VERSION = "0.46"; # UR $VERSION;;
32              
33             # set up module
34 266     266   877 use base qw(Exporter DBI);
  266         322  
  266         329158  
35             our (@EXPORT, @EXPORT_OK);
36             @EXPORT = qw();
37             @EXPORT_OK = qw();
38              
39 266     266   3286789 use IO::Handle;
  266         401  
  266         9500  
40 266     266   134110 use IO::File;
  266         384950  
  266         22906  
41 266     266   1365 use Time::HiRes;
  266         330  
  266         1797  
42             # do not use UR::ModuleBase as base class because it does not play nice with DBI
43              
44             #
45             # UR::DBI control flags
46             #
47              
48             # Build a few class methods to manipulate the environment variables
49             # that control SQL monitoring
50              
51             my %sub_env_map = ( monitor_sql => 'UR_DBI_MONITOR_SQL',
52             monitor_dml => 'UR_DBI_MONITOR_DML',
53             explain_sql_if => 'UR_DBI_EXPLAIN_SQL_IF',
54             explain_sql_slow => 'UR_DBI_EXPLAIN_SQL_SLOW',
55             explain_sql_match => 'UR_DBI_EXPLAIN_SQL_MATCH',
56             explain_sql_callstack => 'UR_DBI_EXPLAIN_SQL_CALLSTACK',
57             no_commit => 'UR_DBI_NO_COMMIT',
58             monitor_every_fetch => 'UR_DBI_MONITOR_EVERY_FETCH',
59             dump_stack_on_connect => 'UR_DBI_DUMP_STACK_ON_CONNECT',
60             );
61              
62             our ($monitor_sql,$monitor_dml,$no_commit,$monitor_every_fetch,$dump_stack_on_connect,
63             $explain_sql_slow,$explain_sql_if,$explain_sql_match,$explain_sql_callstack);
64              
65             while ( my($subname, $envname) = each ( %sub_env_map ) ) {
66 266     266   39280 no strict 'refs';
  266         351  
  266         333058  
67             # There's a scalar of the same name as the sub to hold the value, hook them together
68             *{$subname} = \$ENV{$envname};
69             my $subref = sub {
70 34 50   34   16054 if (@_ > 1) {
71 34         239 $$subname = $_[1];
72             }
73 34         136 return $$subname;
74             };
75             if ($subname =~ /explain/) {
76             eval "\$$subname = '' if not defined \$$subname";
77             }
78             else {
79             eval "\$$subname = 0 if not defined \$$subname";
80             }
81             die $@ if $@;
82             *$subname = $subref;
83             }
84              
85             # by default, monitored SQL goes to STDOUT
86             # FIXME change this 'our' back to a 'my' after we're transisitioned off of the old App API
87             our $sql_fh = IO::Handle->new;
88             $sql_fh->fdopen(fileno(STDERR), 'w');
89             $sql_fh->autoflush(1);
90             sub sql_fh
91             {
92 0 0   0 0 0 $sql_fh = $_[1] if @_ > 1;
93 0         0 return $sql_fh;
94             }
95              
96             #
97             # Logging methods
98             #
99              
100             our $log_file;
101             sub log_file {
102 0 0   0 0 0 $log_file = pop if @_ > 1;
103 0         0 return $log_file;
104             }
105              
106             our $log_fh;
107             my $create_time=0;
108             sub start_logging {
109 0 0   0 0 0 return 1 if(defined($log_fh));
110 0 0       0 return 0 if(-e "$log_file");
111 0         0 $log_fh = new IO::File("> ${log_file}");
112 0 0       0 unless(defined($log_fh)) {
113 0         0 warn "Logging File $log_file Could not be created\n";
114 0         0 return 0;
115             }
116 0         0 $create_time=Time::HiRes::time();
117 0         0 return 1;
118             }
119              
120             sub stop_logging {
121 0 0   0 0 0 return 1 unless(defined($log_fh));
122 0         0 $log_fh->close;
123 0         0 undef $log_fh;
124             }
125              
126             sub log_sql {
127 7082 50   7082 0 14541 return 1 unless(defined($log_fh));
128 0         0 my $sql=pop;
129 0         0 my $no_timestamp=pop;
130 0 0       0 print $log_fh '=' x 10, "\n" unless($no_timestamp);
131 0 0       0 print $log_fh Time::HiRes::time()-$create_time, "\n" unless($no_timestamp);
132 0         0 print $log_fh $sql;
133             }
134              
135             #
136             # Standard DBI overrides
137             #
138              
139             sub connect
140             {
141 172     172 1 694 my $self = shift;
142 172         425 my @params = @_;
143              
144 172 50 33     1423 if ($monitor_sql or $dump_stack_on_connect) {
145 0         0 my $time = time;
146 0         0 my $time_string = join(' ', $time, '[' . localtime($time) . ']');
147 0         0 $sql_fh->print("DB CONNECT AT: $time_string");
148             }
149 172 50       572 if ($dump_stack_on_connect) {
150 0         0 $sql_fh->print(Carp::longmess());
151             }
152            
153 172         383 $params[2] = 'xxx';
154              
155             # Param 3 is usually a hashref of connection modifiers
156 172 50 33     1837 if (ref($params[3]) and ref($params[3]) =~ m/HASH/) {
157             my $string = join(', ',
158 394         1392 map { $_ . ' => ' . $params[3]->{$_} }
159 172         334 keys(%{$params[3]})
  172         782  
160             );
161 172         660 $params[3] = "{ $string }";
162             }
163            
164 172 100       393 my $params_stringified = join(",", map { defined($_) ? "'$_'" : 'undef' } @params);
  688         1793  
165 172         973 UR::DBI::before_execute("connecting with params: ($params_stringified)");
166            
167 172         1976 my $rv = $self->SUPER::connect(@_);
168 172         160577 UR::DBI::after_execute();
169 172         721 return $rv;
170             }
171              
172             #
173             # UR::Object hooks
174             #
175              
176             sub commit_all_app_db_objects {
177 103     103 0 255 my $this_class = shift;
178 103         200 my $handle = shift;
179            
180 103         180 my $data_source;
181 103 50       1435 if ($handle->isa("UR::DBI::db")) {
    0          
182 103         15582 $data_source = UR::DataSource::RDBMS->get_for_dbh($handle);
183             }
184             elsif ($handle->isa("UR::DBI::st")) {
185 0         0 $data_source = UR::DataSource::RDBMS->get_for_dbh($handle->{Database});
186             }
187             else {
188 0         0 Carp::confess("No handle passed to method!?")
189             }
190            
191 103 50       383 unless ($data_source) {
192 0         0 return;
193             }
194              
195 103         1135 return $data_source->_set_all_objects_saved_committed();
196             }
197              
198             sub rollback_all_app_db_objects {
199 22     22 0 28 my $this_class = shift;
200 22         25 my $handle = shift;
201            
202 22         24 my $data_source;
203 22 50       114 if ($handle->isa("UR::DBI::db")) {
    0          
204 22         832 $data_source = UR::DataSource::RDBMS->get_for_dbh($handle);
205             }
206             elsif ($handle->isa("UR::DBI::st")) {
207 0         0 $data_source = UR::DataSource::RDBMS->get_for_dbh($handle->{Database});
208             }
209             else {
210 0         0 Carp::confess("No handle passed to method!?")
211             }
212            
213 22 50       67 unless ($data_source) {
214 0         0 Carp::confess("No data source found for database handle! $handle")
215             }
216            
217 22         185 return $data_source->_set_all_objects_saved_rolled_back();
218             }
219              
220             my @disable_dump_and_explain;
221             sub _disable_dump_explain
222             {
223 5518     5518   13729 push @disable_dump_and_explain,
224             [$monitor_sql,$explain_sql_slow,$explain_sql_match];
225 5518         17398 $monitor_sql = 0;
226 5518         8264 $explain_sql_slow = '';
227 5518         7983 $explain_sql_match = '';
228             }
229              
230             sub _restore_dump_explain
231             {
232 5518 50   5518   9117 if (@disable_dump_and_explain) {
233 5518         5696 my $vars = pop @disable_dump_and_explain;
234 5518         23297 ($monitor_sql,$explain_sql_slow,$explain_sql_match) = @$vars;
235             }
236             else {
237 0         0 Carp::confess("No state saved for disabled dump/explain");
238             }
239             }
240              
241             # The before_execute/after_execute subroutine pair
242             # are callbacks called by execute() and by other
243             # methods which implicitly execute a statement.
244              
245             # They use these three varaibles to track state,
246             # presuming that the callback pair cannot be nested.
247              
248             our ($start_time, $elapsed_time);
249              
250             # This gets around a bug which prevents variables
251             # which are strings internally utf8 encoded from working with DBI
252             # as execution parameters.
253             if ($^O eq "MSWin32" || $^O eq 'cygwin') {
254             *normalize_parameter = sub { $_[0] = substr($_[0],0) };
255             }
256             elsif ($^V le v5.8.0) {
257             # perl 5.6.1 utf8 module does not have a downgrade function
258             *normalize_parameter = sub { $_[0] = substr($_[0],0) };
259             }
260             else {
261             require utf8;
262             *normalize_parameter = \&utf8::downgrade;
263             }
264              
265             sub before_execute
266             {
267             #my ($dbh,$sql,@params) = @_;
268             # $dbh is optional
269            
270 3541     3541 0 4197 my $dbh;
271 3541 100       15801 $dbh = shift if ref($_[0]);
272            
273 3541         8590 my $sql = shift;
274              
275             # Odd errors occur sometimes with values which have not gone through
276             # updgrade, downgrade or $_ = substr($_,0). The query fails w/o error.
277             # This has some connection to a language/encoding problem, and has so
278             # far only been seen with Tk, Gtk2, and XML parser derived data.
279             # Note: when this error occurs it happens with a seeminly normal Perl variable.
280 3541         6932 for (@_) {
281 4816         7954 normalize_parameter($_);
282             }
283            
284 3541 50 66     17732 if ($dbh and length($explain_sql_match)) {
285 0         0 for my $val ($sql,@_) {
286 0 0       0 if ($val =~ /$explain_sql_match/gi) {
287 0 0       0 $sql_fh->print("\nEXPLAIN QUERY MATCHING /$explain_sql_match/gi"
288             . ($val ne $sql ? " (on value '$val') " : "")
289             );
290 0 0       0 if ($monitor_sql) {
291 0         0 $sql_fh->print("\n");
292             }
293             else {
294 0         0 _print_sql_and_params($sql,@_);
295             }
296 0 0       0 if ($explain_sql_callstack) {
297 0         0 $sql_fh->print(Carp::longmess("callstack begins"),"\n");
298             }
299 0 0       0 if ($UR::DBI::explained_queries{$sql}) {
300 0         0 $sql_fh->print("(query explained above)\n");
301             }
302             else {
303 0         0 UR::DBI::_print_query_plan($sql,$dbh);
304 0         0 $UR::DBI::explained_queries{$sql} = 1;
305             }
306 0         0 last;
307             }
308             }
309             }
310            
311 3541         7372 my $start_time = _set_start_time();
312 3541 50 33     16352 if ($monitor_sql){
    50          
313 0         0 _print_sql_and_params($sql,@_);
314 0 0       0 if ($monitor_sql > 1) {
315 0         0 $sql_fh->print(Carp::longmess("callstack begins"),"\n");
316             }
317 0         0 _print_monitor_label("EXECUTE");
318             }
319             elsif($monitor_dml && $sql !~ /^\s*select/i){
320 0         0 _print_sql_and_params($sql,@_);
321 0         0 _print_monitor_label("EXECUTE");
322 0         0 $monitor_dml=2;
323             }
324 266     266   1429 no warnings;
  266         367  
  266         180785  
325            
326 3541         8112 UR::DBI::log_sql_for_summary($sql); # $ENV{UR_DBI_SUMMARIZE_SQL}
327              
328 3541         7953 my $log_sql_str = _generate_sql_and_params_log_entry($sql, @_);
329 3541         8011 UR::DBI::log_sql($log_sql_str);
330 3541         3813 return $start_time;
331             }
332              
333             sub after_execute
334             {
335             #my ($sql,@params) = @_;
336 3541     3541 0 7254 my $elapsed_time = _set_elapsed_time();
337 3541 50       11913 if ($monitor_sql){
    50          
338 0         0 _print_elapsed_time();
339             }
340             elsif($monitor_dml == 2){
341 0         0 _print_elapsed_time();
342 0         0 $monitor_dml = 1;
343             }
344 3541         34082 UR::DBI::log_sql(1, ($elapsed_time)."\n");
345 3541         4460 return $elapsed_time;
346             }
347              
348             # The before_fetch/after_fetch pair are callback
349             # called by fetch() and by other methods which implicitly
350             # fetch data w/o explicitly calling fetch().
351              
352             our $_fetching = 0;
353              
354             sub before_fetch {
355 6831     6831 0 5912 my $sth = shift;
356 6831 100       11533 return if @disable_dump_and_explain;
357 5518 50       8996 if ($_fetching) {
358 0         0 Carp::cluck("before_fetch called after another before_fetch w/o intervening after_fetch!");
359             }
360 5518         4936 $_fetching = 1;
361 5518         9327 my $fetch_timing_arrayref = $sth->fetch_timing_arrayref;
362 5518 50       12635 if ($monitor_sql) {
363 0 0 0     0 if ($fetch_timing_arrayref and @$fetch_timing_arrayref == 0) {
    0          
364 0         0 UR::DBI::_print_monitor_label('FIRST FETCH');
365             }
366             elsif ($monitor_every_fetch) {
367 0         0 UR::DBI::_print_monitor_label('NTH FETCH');
368             }
369             }
370 5518         9516 return UR::DBI::_set_start_time();
371             }
372              
373             sub after_fetch {
374 6831     6831 0 5828 my $sth = shift;
375 6831 100       11245 return if @disable_dump_and_explain;
376 5518         5223 $_fetching = 0;
377 5518         8329 my $fetch_timing_arrayref = $sth->fetch_timing_arrayref;
378 5518         6708 my $time;
379 5518         9275 push @$fetch_timing_arrayref, UR::DBI::_set_elapsed_time();
380 5518 50       9803 if ($monitor_sql) {
381 0 0 0     0 if ($monitor_every_fetch || @$fetch_timing_arrayref == 1) {
382 0         0 $time = UR::DBI::_print_elapsed_time();
383             }
384             }
385 5518 100       9751 if (@$fetch_timing_arrayref == 1) {
386 2094         4356 my $time = $sth->execute_time + $fetch_timing_arrayref->[0];
387 2094         9217 UR::DBI::_check_query_timing($sth->{Statement},$time,$sth->{Database},$sth->last_params);
388             }
389 5518         7126 return $time;
390             }
391              
392             sub after_all_fetches_with_sth {
393 6738     6738 0 6324 my $sth = shift;
394            
395 6738         9270 my $fetch_timing_arrayref = $sth->fetch_timing_arrayref;
396            
397             # This arrayref is set when it goes through the subclass' execute(),
398             # and is removed when we finish all fetches().
399            
400             # Since a variety of things attempt to call this from the various "final"
401             # positions of an $sth we delete this so the final callback operates only once.
402             # Also, internally generated $sths which do not get executed() normally
403             # will be skipped by this check.
404            
405 6738 100       13229 if (!$fetch_timing_arrayref) {
406             # internal sth which did not go through prepare()
407             #print $sql_fh "SKIP STH\n";
408 4020         4386 return;
409             }
410 2718         4749 $sth->fetch_timing_arrayref(undef);
411            
412 2718         3588 my $print_fetch_summary;
413 2718 50 33     7158 if ($monitor_sql and $sth->{Statement} =~ /select/i) {
414 0         0 $print_fetch_summary = 1;
415 0         0 UR::DBI::_print_monitor_label('TOTAL EXECUTE-FETCH');
416             }
417            
418 2718         4923 my $time = $sth->execute_time;
419            
420 2718 100       6074 if (@$fetch_timing_arrayref) {
421 2093         3776 for my $fetch_time (@$fetch_timing_arrayref ) {
422 5517         5318 $time += $fetch_time;
423             }
424 2093 50       4037 if ($print_fetch_summary) {
425 0         0 UR::DBI::_print_monitor_time($time);
426             }
427             # since there WERE fetches, we already checked query timing
428             }
429             else {
430 625 50       1115 if ($print_fetch_summary) {
431 0         0 UR::DBI::_print_monitor_time($time);
432             }
433             # since there were NOT fetches, we check query timing now
434 625         1766 UR::DBI::_check_query_timing($sth->{Statement},$time,$sth->{Database},$sth->last_params);
435             }
436 2718         4186 return $time;
437             }
438              
439             sub after_all_fetches_no_sth {
440 40     40 0 114 my ($sql, $time, $dbh, @params) = @_;
441 40 50       129 $time = _set_elapsed_time() unless defined $time;
442 40 50 33     143 if ($monitor_sql and $sql =~ /select/i) {
443 0         0 UR::DBI::_print_monitor_label('TOTAL EXECUTE-FETCH');
444 0         0 UR::DBI::_print_monitor_time($time);
445             }
446             # no sth = no fetches = no query timing check done yet...
447 40         105 UR::DBI::_check_query_timing($sql,$time,$dbh,@params);
448 40         54 return $time;
449             }
450              
451              
452             my $__SQL_SUMMARY__ = {};
453             sub log_sql_for_summary {
454 3541     3541 0 4719 my ($sql) = @_;
455 3541         9466 $__SQL_SUMMARY__->{$sql}++;
456             }
457              
458             sub print_sql_summary {
459 0     0 0 0 for my $sql (sort {$__SQL_SUMMARY__->{$b} <=> $__SQL_SUMMARY__->{$a}} keys %$__SQL_SUMMARY__) {
  0         0  
460 0         0 print STDERR join('',"********************\n", $__SQL_SUMMARY__->{$sql}, " instances of query: $sql\n");
461             }
462             }
463              
464             # These methods are called by the above.
465              
466             sub _generate_sql_and_params_log_entry
467             {
468              
469 3541     3541   4143 my $sql = shift;
470              
471 266     266   1348 no warnings;
  266         345  
  266         41450  
472 3541         8002 my $sql_log_str = "\nSQL: $sql\n";
473 3541 100       7412 if (@_) {
474 2396         4086 $sql_log_str .= "PARAMS: ";
475             $sql_log_str .= join(", ",
476 4816 100       12353 map { defined($_) ? "'$_'" : "NULL" }
477 2396 50       4328 map { scalar(grep { $_ } map { 128 & ord $_ } split(//, substr($_, 0, 64))) ? '' : $_ }
  4816         11823  
  22281         22300  
  22281         19968  
478             @_ )
479             . "\n";
480             }
481              
482 3541         6705 return $sql_log_str;
483             }
484              
485             sub _print_sql_and_params
486             {
487 0     0   0 my $sql = shift;
488 0         0 my $entry = _generate_sql_and_params_log_entry($sql, @_);
489 266     266   1174 no warnings;
  266         374  
  266         138112  
490 0         0 print $sql_fh $entry;
491             }
492              
493             sub _set_start_time
494             {
495 9059     9059   23588 $start_time=&Time::HiRes::time();
496             }
497              
498             our $_print_monitor_label_or_time_is_ready_for = "label";
499             sub _print_monitor_label
500             {
501             #Carp::cluck() unless $_print_monitor_label_or_time_is_ready_for eq "label";
502 0     0   0 my $time_label = shift;
503 0         0 $sql_fh->print("$time_label TIME: ");
504 0         0 $_print_monitor_label_or_time_is_ready_for = "time";
505             }
506              
507             sub _print_monitor_time
508             {
509             #Carp::cluck() unless $_print_monitor_label_or_time_is_ready_for eq "time";
510 0     0   0 $sql_fh->printf( "%.4f s\n", shift);
511 0         0 $_print_monitor_label_or_time_is_ready_for = "label";
512             }
513              
514             sub _set_elapsed_time
515             {
516 9059     9059   24276 $elapsed_time = &Time::HiRes::time()-$start_time;
517             }
518              
519             sub _print_elapsed_time
520             {
521 0     0   0 _print_monitor_time($elapsed_time);
522             }
523              
524             our $_print_check_for_slow_query = 0;
525             sub _check_query_timing
526             {
527 2759     2759   17270 my ($sql,$time,$dbh,@params) = @_;
528 2759 50       6374 return if @disable_dump_and_explain;
529 2759 100       11063 return unless $sql =~ /select/i;
530 1649 50       3909 print $sql_fh "CHECK FOR SLOW QUERY:\n" if $_print_check_for_slow_query; # used only by a test case
531 1649 50 33     6041 if (length($explain_sql_slow) and $time >= $explain_sql_slow) {
532 0         0 $sql_fh->print("EXPLAIN QUERY SLOWER THAN $explain_sql_slow seconds ($time):");
533 0 0 0     0 if ($monitor_sql
      0        
534             || ($monitor_dml && $sql !~ /^\s*select/i)) {
535 0         0 $sql_fh->print("\n");
536             }
537             else {
538 0         0 _print_sql_and_params($sql,@params);
539             }
540 0 0       0 if ($explain_sql_callstack) {
541 0         0 $sql_fh->print(Carp::longmess("callstack begins"),"\n");
542             }
543 0 0       0 if ($UR::DBI::explained_queries{$sql}) {
544 0         0 $sql_fh->print("(query explained above)\n");
545             }
546             else {
547 0         0 $UR::DBI::explained_queries{$sql} = 1;
548 0         0 UR::DBI::_print_query_plan($sql,$dbh);
549             }
550             }
551             }
552              
553             sub _print_query_plan
554             {
555 0     0   0 my ($sql,$dbh,%params) = @_;
556 0         0 UR::DBI::_disable_dump_explain();
557 0         0 $dbh->do($UR::DBI::EXPLAIN_PLAN_CLEANUP_DML);
558            
559             # placeholders in explain plan queries on windows
560             # results in Oracle throwing an ORA-00600 error,
561             # likely due to interaction with DBI. Replace with
562             # literals.
563              
564 0 0 0     0 if ($^O eq "MSWin32" || $^O eq 'cygwin') {
565 0         0 $sql =~ s/\?/'1'/g;
566             }
567            
568 0 0       0 $dbh->do($UR::DBI::EXPLAIN_PLAN_DML . "\n" . $sql)
569             or die "Failed to produce query plan! " . $dbh->errstr;
570 0         0 UR::DBI::Report->generate(
571             sql => [$UR::DBI::EXPLAIN_PLAN_SQL],
572             dbh => $dbh,
573             count => 0,
574             outfh => $sql_fh,
575             %params,
576             "explain-sql" => 0,
577             "echo" => 0,
578             );
579 0         0 $sql_fh->print("\n");
580 0         0 $dbh->do($UR::DBI::EXPLAIN_PLAN_CLEANUP_DML);
581 0         0 UR::DBI::_restore_dump_explain();
582            
583 0         0 return 1;
584             }
585              
586              
587             ############
588             #
589             # Database handle subclass
590             #
591             ############
592              
593              
594             package UR::DBI::db;
595              
596 266     266   1307 use strict;
  266         394  
  266         4723  
597 266     266   855 use warnings;
  266         336  
  266         237476  
598              
599             our @ISA = qw(DBI::db);
600              
601             sub commit
602             {
603 166     166   32571 my $self = shift;
604              
605             # unless ($no_commit) {
606             # print "\n\n\n************* FORCIBLY SETTING NO-COMMIT FOR TESTING. This would have committeed!!!! **********\n\n\n";
607             # $no_commit = 1;
608             # }
609            
610 166 100       480 if ($no_commit)
611             {
612             # Respect the ->no_commit(1) setting.
613 63         121 UR::DBI::before_execute("commit (ignored)");
614 63         106 UR::DBI::after_execute;
615 63         166 return 1;
616             }
617             else
618             {
619 103 50       473 if(UR::DataSource->use_dummy_autogenerated_ids) {
620             # Not cool...you shouldn't have dummy-ids on and no-commit off
621             # Don't commit, and notify the authorities
622 0         0 UR::DBI::before_execute("commit (ignored)");
623 0         0 $UR::Context::current->error_message('Tried to commit with dummy-ids on and no-commit off');
624 0         0 UR::DBI::after_execute;
625             #$UR::Context::current->send_email(
626             # To => 'example@example.edu',
627             # Subject => 'attempt to commit with dummy-ids on and no-commit off '.
628             # "by $ENV{USER} on $ENV{HOST} running ".
629             # UR::Context::Process->original_program_path." as pid $$",
630             # Message => "Call stack:\n" .Carp::longmess()
631             #);
632             } else {
633             # Commit and update the associated objects.
634 103         319 UR::DBI::before_execute("commit");
635 103         3922032 my $rv = $self->SUPER::commit(@_);
636 103         724 UR::DBI::after_execute;
637 103 50       463 if ($rv) {
638 103         704 UR::DBI->commit_all_app_db_objects($self)
639             }
640 103         610 return $rv;
641             }
642             }
643             }
644              
645             sub commit_without_object_update
646             {
647 0     0   0 UR::DBI::before_execute("commit (no object updates)");
648 0         0 my $rv = shift->SUPER::commit(@_);
649 0         0 UR::DBI::after_execute();
650 0         0 return $rv;
651             }
652              
653             sub rollback
654             {
655 22     22   23 my $self = shift;
656 22         62 UR::DBI::before_execute("rollback");
657 22         2757 my $rv = $self->SUPER::rollback(@_);
658 22         53 UR::DBI::after_execute();
659 22 50       54 if ($rv) {
660 22         104 UR::DBI->rollback_all_app_db_objects($self)
661             }
662 22         69 return $rv;
663             }
664              
665             sub rollback_without_object_update
666             {
667 0     0   0 UR::DBI::before_execute("rollback (w/o object updates)");
668 0         0 my $rv = shift->SUPER::commit(@_);
669 0         0 UR::DBI::after_execute();
670 0         0 return $rv;
671             }
672              
673             sub disconnect
674             {
675 5     5   14 my $self = shift;
676             # Rollback if AutoCommit is 0. Oracle commits by default on disconnect.
677             # Rolling back when AutoCommit is on will generate a DBI warning.
678 5 50       33 if ($self->{'AutoCommit'} == 0) {
679 5         18 $self->rollback;
680             }
681            
682             # Msg and disconnect.
683 5         19 UR::DBI::before_execute("disconnecting");
684 5         714 my $rv = $self->SUPER::disconnect(@_);
685 5         14 UR::DBI::after_execute();
686            
687             # There doesn't seem to be anything less which
688             # sets this, but legacy tools did
689 5 50 33     37 if (
690             (defined $UR::DBI::common_dbh)
691             and
692             ($self eq $UR::DBI::common_dbh)
693             )
694             {
695 0         0 UR::DBI::before_execute("common dbh removed");
696 0         0 $UR::DBI::common_dbh = undef;
697 0         0 UR::DBI::after_execute("common dbh removed");
698             }
699 5         25 return $rv;
700             }
701              
702             sub prepare
703             {
704 2317     2317   90381 my $self = shift;
705 2317         3291 my $sql = $_[0];
706 2317         2579 my $sth;
707            
708             #print $sql_fh "PREPARE: $sql\n";
709            
710 2317 50       11823 if ($sql =~ /^\s*(commit|rollback)\s*$/i)
711             {
712 0 0       0 unless ($sql =~ /^(commit|rollback)$/i) {
713 0         0 Carp::confess("Executing a statement with an embedded commit/rollback?\n$sql\n");
714             }
715            
716 0 0       0 if ($sth = $self->SUPER::prepare(@_))
717             {
718 0 0       0 if ($1 =~ /commit/i)
    0          
719             {
720 0         0 $UR::DBI::prepared_commit{$sth} = 1;
721             }
722             elsif ($1 =~ /rollback/)
723             {
724 0         0 $UR::DBI::prepared_rollback{$sth} = 1;
725             }
726             }
727             }
728             else
729             {
730 2317 50       15313 $sth = $self->SUPER::prepare(@_) or return;
731             }
732            
733 2317         275509 return $sth;
734             }
735              
736             # For newer versions of DBI, some of the $dbh->select* methods do not
737             # call execute internally, so SQL dumping and logging will not occur.
738             # These are listed below, and the bad ones are overridden.
739              
740             # selectall_hashref ok
741             # selectcol_arrayref ok
742             # selectrow_hashref ok
743              
744             # selectall_arrayref bad
745             # selectrow_arrayref bad
746             # selectrow_array bad
747              
748             sub selectall_arrayref
749             {
750 29     29   992 my $self = shift;
751 29         135 my @p = ($_[0],@_[2..$#_]);
752 29         97 UR::DBI::before_execute($self,@p);
753 29         526 my $ar = $self->SUPER::selectall_arrayref(@_);
754 29         112 my $time = UR::DBI::after_execute($self,@p);
755 29         124 UR::DBI::after_all_fetches_no_sth($_[0],$time,$self,@p);
756 29         81 return $ar;
757             }
758              
759              
760             sub selectcol_arrayref
761             {
762 0     0   0 my $self = shift;
763 0         0 my @p = ($_[0],@_[2..$#_]);
764 0         0 UR::DBI::before_execute($self,@p);
765 0         0 UR::DBI::_disable_dump_explain();
766 0         0 my $ar = $self->SUPER::selectcol_arrayref(@_);
767 0         0 UR::DBI::_restore_dump_explain();
768 0         0 my $time = UR::DBI::after_execute($self,@p);
769 0         0 UR::DBI::after_all_fetches_no_sth($_[0],$time,$self,@p);
770 0         0 return $ar;
771             }
772              
773              
774             sub selectall_hashref
775             {
776 0     0   0 my $self = shift;
777 0         0 my @p = ($_[0],@_[3..$#_]);
778 0         0 UR::DBI::before_execute($self,@p);
779 0         0 UR::DBI::_disable_dump_explain();
780 0         0 my $ar = $self->SUPER::selectall_hashref(@_);
781 0         0 UR::DBI::_restore_dump_explain();
782 0         0 my $time = UR::DBI::after_execute($self,@p);
783 0         0 UR::DBI::after_all_fetches_no_sth($_[0],$time,$self,@p);
784 0         0 return $ar;
785             }
786              
787             sub selectrow_arrayref
788             {
789 1     1   2 my $self = shift;
790 1         5 my @p = ($_[0],@_[2..$#_]);
791 1         5 UR::DBI::before_execute($self,@p);
792 1         18 my $ar = $self->SUPER::selectrow_arrayref(@_);
793 1         5 my $time = UR::DBI::after_execute($self,@p);
794 1         6 UR::DBI::after_all_fetches_no_sth($_[0],$time,$self,@p);
795 1         4 return $ar;
796             }
797              
798             sub selectrow_array
799             {
800 10     10   5398 my $self = shift;
801 10         35 my @p = ($_[0],@_[2..$#_]);
802 10         29 UR::DBI::before_execute($self,@p);
803 10         92 my @a = $self->SUPER::selectrow_array(@_);
804 10         53 my $time = UR::DBI::after_execute($self,@p);
805 10         30 UR::DBI::after_all_fetches_no_sth($_[0],$time,$self,@p);
806 10 50       50 return @a if wantarray;
807 0         0 return $a[0];
808             }
809              
810             sub DESTROY
811             {
812 4     4   1741 UR::DBI::before_execute("destroying connection");
813 4         38 shift->SUPER::DESTROY(@_);
814 4         9 UR::DBI::after_execute("destroying connection");
815             }
816              
817             #########
818             #
819             # Statement handle subclass
820             #
821             #########
822              
823             package UR::DBI::st;
824              
825 266     266   1313 use strict;
  266         374  
  266         4666  
826 266     266   879 use warnings;
  266         377  
  266         5648  
827              
828 266     266   867 use Time::HiRes;
  266         313  
  266         1009  
829 266     266   137527 use Sys::Hostname;
  266         215448  
  266         11540  
830 266     266   103925 use Devel::GlobalDestruction;
  266         396554  
  266         1272  
831              
832             our @ISA = qw(DBI::st);
833              
834             sub _mk_mutator {
835 798     798   891 my ($class, $method) = @_;
836              
837             # Make a more specific key based on the package
838             # to try not to conflict with anything else.
839             # This must start with 'private_'. See DBI docs on subclassing.
840 798         2004 my $hash_key = join('_', 'private', lc $class, lc $method);
841 798         1969 $hash_key =~ s/::/_/g;
842              
843             my $sub = sub {
844 40137 50   40137   687251 return if Devel::GlobalDestruction::in_global_destruction;
845 40137         129279 my $sth = shift;
846 40137 100       55990 if (@_) {
847 266     266   33660 no warnings 'uninitialized';
  266         379  
  266         9836  
848 11700         69848 $sth->{$hash_key} = shift;
849             }
850 266     266   898 no warnings;
  266         324  
  266         10797  
851 40137         158315 return $sth->{$hash_key};
852 798         2248 };
853              
854 266     266   925 no strict;
  266         349  
  266         186186  
855 798         706 *{$class . '::' . $method} = $sub;
  798         2927  
856             }
857              
858             for my $method (qw(execute_time fetch_timing_arrayref last_params_arrayref)) {
859             __PACKAGE__->_mk_mutator($method);
860             }
861              
862             sub last_params
863             {
864 2719     2719   5607 my $ret = shift->last_params_arrayref;
865 2719 50       7079 unless (defined $ret) {
866 0         0 $ret = [];
867             }
868 2719         2885 @{ $ret };
  2719         8162  
869             }
870              
871             sub execute
872             {
873 3132     3132   25041 my $sth = shift;
874            
875             # (re)-initialize the timing array
876 3132 100       7943 if (my $a = $sth->fetch_timing_arrayref()) {
877             # re-executing on a previously used $sth.
878 414         736 UR::DBI::after_all_fetches_with_sth($sth);
879             }
880             else {
881             # initialize the $sth on first execute.
882 2718         5815 $sth->fetch_timing_arrayref([]);
883             }
884            
885 3132         10283 $sth->last_params_arrayref([@_]);
886            
887 3132         12608 UR::DBI::before_execute($sth->{Database},$sth->{Statement},@_);
888 3132         115588 my $rv = $sth->SUPER::execute(@_);
889 3132         12666 UR::DBI::after_execute($sth->{Database},$sth->{Statement},@_);
890            
891             # record the elapsed time for execution.
892 3132         8666 $sth->execute_time($UR::DBI::elapsed_time);
893            
894 3132 50       6976 if ($rv)
895             {
896 3132 50       8781 if (my $prev = $UR::DBI::prepared_commit{$sth})
897             {
898 0         0 UR::DBI->commit_all_app_db_objects($sth);
899             }
900 3132 50       7951 if (my $prev = $UR::DBI::prepared_rollback{$sth})
901             {
902 0         0 UR::DBI->rollback_all_app_db_objects($sth);
903             }
904              
905             }
906 3132         8981 return $rv;
907             }
908              
909              
910             sub fetchrow_array
911             {
912 8     8   60 my $sth = shift;
913 8         11 UR::DBI::before_fetch($sth,@_);
914 8         9 UR::DBI::_disable_dump_explain();
915 8         50 my @a = $sth->SUPER::fetchrow_array(@_);
916 8         10 UR::DBI::_restore_dump_explain();
917 8         13 UR::DBI::after_fetch($sth,@_);
918 8 50       37 return @a if wantarray;
919 0         0 return $a[0];
920             }
921              
922             sub fetchrow_arrayref
923             {
924 4305     4305   4168 my $sth = shift;
925 4305         8695 UR::DBI::before_fetch($sth,@_);
926 4305         8024 UR::DBI::_disable_dump_explain();
927 4305         46061 my $ar = $sth->SUPER::fetchrow_arrayref(@_);
928 4305         8073 UR::DBI::_restore_dump_explain();
929 4305         8254 UR::DBI::after_fetch($sth,@_);
930 4305         7893 return $ar;
931             }
932              
933              
934             sub fetchall_arrayref
935             {
936 65     65   326 my $sth = shift;
937 65         180 UR::DBI::before_fetch($sth,@_);
938 65         153 UR::DBI::_disable_dump_explain();
939 65         798 my $ar = $sth->SUPER::fetchall_arrayref(@_);
940 65         433 UR::DBI::_restore_dump_explain();
941 65         164 UR::DBI::after_fetch($sth,@_);
942 65         167 UR::DBI::after_all_fetches_with_sth($sth,@_);
943 65         168 return $ar;
944             }
945              
946             sub fetchall_hashref
947             {
948 1     1   8 my $sth = shift;
949 1         8 my @p = @_[1,$#_];
950 1         6 UR::DBI::before_fetch($sth,@p);
951 1         5 UR::DBI::_disable_dump_explain();
952 1         23 my $ar = $sth->SUPER::fetchall_hashref(@_);
953 1         8 UR::DBI::_restore_dump_explain();
954 1         4 UR::DBI::after_fetch($sth,@p);
955 1         5 UR::DBI::after_all_fetches_with_sth($sth,@_[1,$#_]);
956 1         4 return $ar;
957             }
958              
959             sub fetchrow_hashref
960             {
961 1139     1139   2586 my $sth = shift;
962 1139         1668 UR::DBI::before_fetch($sth,@_);
963 1139         1532 UR::DBI::_disable_dump_explain();
964 1139         8734 my $ar = $sth->SUPER::fetchrow_hashref(@_);
965 1139         2498 UR::DBI::_restore_dump_explain();
966 1139         1515 UR::DBI::after_fetch($sth,@_);
967 1139         2632 return $ar;
968             }
969              
970              
971             sub fetch {
972 1313     1313   5836 my $sth = shift;
973 1313         1602 UR::DBI::before_fetch($sth,@_);
974 1313         6322 my $rv = $sth->SUPER::fetch(@_);
975 1313         1857 UR::DBI::after_fetch($sth,@_);
976 1313         4047 return $rv;
977             }
978              
979             sub finish {
980 1624     1624   4812 my $sth = shift;
981 1624         4100 UR::DBI::after_all_fetches_with_sth($sth);
982 1624         8317 return $sth->SUPER::finish(@_);
983             }
984              
985             sub DESTROY
986             {
987 4634     4634   33636 delete $UR::DBI::prepared_commit{$_[0]};
988 4634         4928 delete $UR::DBI::prepared_rollback{$_[0]};
989             #print $sql_fh "DESTROY1\n";
990 4634         6827 UR::DBI::after_all_fetches_with_sth(@_); # does nothing if called previously by finish()
991             #print $sql_fh "DESTROY2\n";
992             #Carp::cluck();
993 4634         48440 shift->SUPER::DESTROY(@_);
994             }
995              
996             $UR::DBI::STATEMENT_ID = $$ . '@' . hostname();
997              
998              
999             $UR::DBI::EXPLAIN_PLAN_DML = "explain plan set statement_id = '$UR::DBI::STATEMENT_ID' into plan_table for ";
1000              
1001              
1002             $UR::DBI::EXPLAIN_PLAN_SQL = qq/
1003             select
1004             LPAD(' ',p.LVL-1) || OPERATION OPERATION,
1005             OPTIONS,
1006             --(case when p.OBJECT_OWNER is null then '' else p.OBJECT_OWNER || '.' end)
1007             -- ||
1008             p.OBJECT_NAME
1009             ||
1010             (case when p.OBJECT_TYPE is null then '' else ' (' || p.OBJECT_TYPE || ')' end)
1011             "OBJECT",
1012             (case
1013             when i.table_name is not null then i.table_name
1014             || '('
1015             || index_column_names
1016             || ')'
1017             else ''
1018             end) "OBJECT_IS_ON",
1019             p.COST,
1020             p.CARDINALITY CARD,
1021             p.BYTES,
1022             p.OPTIMIZER,
1023             p.CPU_COST CPU,
1024             p.IO_COST IO,
1025             p.TEMP_SPACE TEMP,
1026             i.index_type "index_type",
1027             i.last_analyzed "index_analyzed"
1028             from
1029             (
1030             SELECT plan_table.*, level lvl
1031             FROM PLAN_TABLE
1032             CONNECT BY prior id = parent_id AND prior statement_id = statement_id
1033             START WITH id = 0
1034             AND statement_id = '$UR::DBI::STATEMENT_ID'
1035             ) p
1036             full join dual on dummy = dummy
1037             left join all_indexes i
1038             on i.index_name = p.object_name
1039             and i.owner = p.object_owner
1040             left join
1041             (
1042             select
1043             index_owner,
1044             index_name,
1045             LTRIM(MAX(SYS_CONNECT_BY_PATH(ic.column_name,',')) KEEP (DENSE_RANK LAST ORDER BY ic.column_position),',') index_column_names
1046             from (
1047             select ic.index_owner, ic.index_name, ic.column_name, ic.column_position
1048             from all_ind_columns ic
1049             ) ic
1050             group by ic.index_owner, ic.index_name
1051             connect by
1052             index_owner = prior index_owner
1053             and index_name = prior index_name
1054             and column_position = PRIOR column_position + 1
1055             start with column_position = 1
1056             ) index_columns_stringified
1057             on index_columns_stringified.index_owner = i.owner
1058             and index_columns_stringified.index_name = i.index_name
1059             where p.object_name is not null
1060             ORDER BY p.id
1061             /;
1062              
1063             $UR::DBI::EXPLAIN_PLAN_CLEANUP_DML = "delete from plan_table where statement_id = '$UR::DBI::STATEMENT_ID'";
1064              
1065              
1066             1;
1067             __END__