# ==================================================================== # Gossamer Threads Module Library - http://gossamer-threads.com/ # # GT::SQL::Driver::debug # Author: Jason Rhinelander # CVS Info : 087,071,086,086,085 # $Id: debug.pm,v 2.1 2007/12/18 23:13:41 brewt Exp $ # # Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. # ==================================================================== # # Description: # GT::SQL::Driver debugging module # package GT::SQL::Driver::debug; use strict; use strict; use GT::AutoLoader; use vars qw/$LAST_QUERY @QUERY_STACK @STACK_TRACE $QUERY_STACK_SIZE @ISA/; @ISA = qw(GT::Base); $QUERY_STACK_SIZE = 100; $COMPILE{last_query} = __LINE__ . <<'END_OF_SUB'; sub last_query { # ------------------------------------------------------------------- # Get, or set the last query. # my $self = shift; return $self->error('NEEDDEBUG', 'WARN') if (! $self->{_debug}); @_ > 0 or return $LAST_QUERY || ''; $LAST_QUERY = shift; $LAST_QUERY = GT::SQL::Driver::debug->replace_placeholders($LAST_QUERY, @_) if (@_); # Display stack traces if requested via debug level. my $stack = ''; if ($self->{_debug} > 2) { ($stack, $LAST_QUERY) = js_stack(3, $LAST_QUERY); } elsif ($self->{_debug} > 1) { package DB; my $i = 2; my $ls = defined $ENV{REQUEST_METHOD} ? '
' : "\n"; my $spc = defined $ENV{REQUEST_METHOD} ? ' ' : ' '; while (my ($file, $line, $sub, $args) = (caller($i++))[1,2,3,4]) { my @args; for (@DB::args) { eval { my $a = $_ }; # workaround for a reference that doesn't think it's a reference my $print = $@ ? \$_ : $_; push @args, defined $print ? $print : '[undef]'; } if (@args) { my $args = join ", ", @args; $args =~ s/\n\s*\n/\n/g; $args =~ s/\n/\n$spc$spc$spc$spc/g; $stack .= qq!$sub called at $file line $line with arguments $ls$spc$spc ($args).$ls!; } else { $stack .= qq!$sub called at $file line $line with no arguments.$ls!; } } } push @QUERY_STACK, $LAST_QUERY; push @STACK_TRACE, "
\n" . $stack . "\n
\n" if ($self->{_debug} and $stack); # Pesistance such as Mod_Perl @QUERY_STACK > $QUERY_STACK_SIZE and shift @QUERY_STACK; @STACK_TRACE > $QUERY_STACK_SIZE and shift @STACK_TRACE; return $LAST_QUERY || ''; } END_OF_SUB $COMPILE{js_stack} = __LINE__ . <<'END_OF_SUB'; sub js_stack { # ------------------------------------------------------------------- # Create a nicely formatted javascript browser that (unfortunately) # only works in ie, netscape sucks. # my ($sp, $title) = @_; my $nb = @QUERY_STACK; my ($stack, $dump_out); { package DB; require GT::Dumper; my $i = 0; while (my ($file, $line, $sub, $args) = (caller($sp++))[1,2,3,4]) { if (@DB::args) { $args = "with arguments
   "; my @args; for (@DB::args) { eval { my $a = $_ }; # workaround for a reference that doesn't think it's a reference my $print = $@ ? \$_ : $_; my $arg = defined $print ? $print : '[undef]'; $args .= "$arg, "; my $dump = GT::Dumper::Dumper($arg); $dump_out .= qq~ Top
$dump
~; $i++; } chop $args; chop $args; } else { $args = "with no arguments"; } $stack .= qq!
  • $sub called at $file line $line $args.
  • \n!; } } $stack =~ s/\\/\\\\/g; $stack =~ s/[\n\r]+/\\n/g; $stack =~ s/'/\\'/g; $stack =~ s,script,sc'+'ript,g; $dump_out =~ s/\\/\\\\/g; $dump_out =~ s/[\n\r]+/\\n/g; $dump_out =~ s/'/\\'/g; $dump_out =~ s,script,sc'+'ript,g; my $var = < function my$nb () { msg = window.open('','my$nb','resizable=yes,width=700,height=500,scrollbars=yes'); msg.document.write('STACK TRACE
      $stack
    $dump_out'); msg.document.close(); } HTML my $link = qq!$title
    !; return $var, $link; } END_OF_SUB $COMPILE{quick_quote} = __LINE__ . <<'END_OF_SUB'; sub quick_quote { # ------------------------------------------------------------------- # Quick quote to replace ' with \'. # my $str = shift; defined $str and ($str eq "") and return "''"; $str =~ s/'/\\'/g; return $str; } END_OF_SUB $COMPILE{replace_placeholders} = __LINE__ . <<'END_OF_SUB'; sub replace_placeholders { # ------------------------------------------------------------------- # Replace question marks with the actual values # my ($self, $query, @args) = @_; if (@args > 0) { my @vals = split /('(?:[^']+|''|\\')')/, $query; # Keep track of where we are in each of the @vals strings so that strings with # '?'s in them that aren't placeholders don't incorrectly get replaced with # values. my @vals_idx; VALUE: for my $val (@args) { SUBSTRING: for my $i (0 .. $#vals) { next SUBSTRING if $i % 2; $vals_idx[$i] ||= 0; $vals_idx[$i] = index($vals[$i], '?', $vals_idx[$i]); if ($vals_idx[$i] >= 0) { $val = defined $val ? ($val =~ /\D/ ? "'" . quick_quote($val) . "'" : $val) : 'NULL'; substr($vals[$i], $vals_idx[$i], 1, $val); $vals_idx[$i] += length $val; next VALUE; } else { $vals_idx[$i] = 0; } } } $query = join '', @vals; } return $query; } END_OF_SUB 1;