File Coverage

Oracle/Debug.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1              
2             # $Id: Debug.pm,v 1.46 2003/07/30 15:25:11 oradb Exp $
3              
4             =head1 NAME
5              
6             Oracle::Debug - A Perl (perldb-like) interface to the Oracle DBMS_DEBUG package for debugging PL/SQL programs.
7              
8             =cut
9              
10             package Oracle::Debug;
11              
12 1     1   31897 use 5.008;
  1         4  
  1         35  
13 1     1   4 use strict;
  1         1  
  1         27  
14 1     1   4 use warnings;
  1         12  
  1         26  
15 1     1   4 use Carp qw(carp croak);
  1         2  
  1         85  
16 1     1   5 use Data::Dumper;
  1         1  
  1         33  
17 1     1   351 use DBI;
  0            
  0            
18             use Term::ReadKey;
19              
20             use vars qw($VERSION);
21             $VERSION = do { my @r = (q$Revision: 1.46 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
22              
23             my $DEBUG = $ENV{Oracle_Debug} || 0;
24              
25             =head1 SYNOPSIS
26              
27             ./oradb
28              
29             =head1 ABSTRACT
30              
31             A perl-debugger-like interface to the Oracle DBMS_DEBUG package for
32             debugging PL/SQL programs.
33              
34             The initial impetus for creating this was to get a command-line interface,
35             similar in instruction set and feel to the perl debugger. For this
36             reason, it may be beneficial for a user of this module, or at least the
37             intended B interface, to be familiar with the perl debugger first.
38              
39             =head1 DESCRIPTION
40              
41             There are really 2 parts to this exersize:
42              
43             =over 4
44              
45             =item DB
46              
47             The current Oracle chunk is a package which can be used directly to debug
48             PL/SQL without involving perl at all, but which has similar, but very limited,
49             commands to the perl debugger.
50              
51             Please see the I file for credits for the original B PL/SQL.
52              
53             Developed against B
54              
55             =item oradb
56              
57             The Perl chunk implements a perl-debugger-like interface to the Oracle
58             debugger itself, partially via the B library referenced above.
59              
60             =back
61              
62             In both cases much more conveniently from the command line, than the
63             vanilla Oracle packages themselves. In fairness DBMS_DEBUG is probably
64             designed to be used from a GUI of some sort, but this module focuses on
65             it from a command line usage.
66              
67             =head1 NOTES
68              
69             Ignore any methods which are prefixed with an underscore (_)
70              
71             We use a special B for our own purposes.
72              
73             Set B=1 for debugging information.
74              
75             =head1 METHODS
76              
77             =over 4
78              
79             =item new
80              
81             Create a new Oracle::Debug object
82              
83             my $o_debug = Oracle::Debug->new(\%dbconnectdata);
84              
85             =cut
86              
87             sub new {
88             my $proto = shift;
89             my $class = ref($proto) ? ref($proto) : $proto;
90             my $self = bless({
91             '_config' => do 'scripts/config', # $h_conf,
92             '_connect' => {
93             'debugpid' => '',
94             'primed' => 0,
95             'sessionid' => '',
96             'targetid' => '',
97             'connected' => 0,
98             'synched' => 0,
99             'syncs' => 7,
100             },
101             '_dbh' => {},
102             '_unit' => {
103             'owner' => '',
104             'type' => '',
105             'name' => '',
106             'namespace' => '',
107             },
108             }, $class);
109             $self->_prime;
110             # $self->log($self.' '.Dumper($self)) if $DEBUG;
111             return $self;
112             }
113              
114             =item _prime
115              
116             Prime the object and connect to the db
117              
118             Also ensure we are able to talk to Probe
119              
120             $o_debug->_prime;
121              
122             =cut
123              
124             sub _prime {
125             my $self = shift;
126             my $h_ref = $self->{_config};
127             unless (ref($h_ref) eq 'HASH') {
128             $self->fatal("invalid db priming data hash ref: ".Dumper($h_ref));
129             } else {
130             # $self->{_dbh} = $self->dbh;
131             $self->{_dbh}->{$$} = $self->_connect($h_ref);
132             $self->{_connect}{primed}++ if $self->{_dbh}->{$$};
133             $self->dbh->func(20000, 'dbms_output_enable');
134             $self->self_check();
135             }
136             return ref($self->{_dbh}->{$$}) ? $self : undef;
137             }
138              
139             # =============================================================================
140             # dbh and sql methods
141             # =============================================================================
142              
143             =item dbh
144              
145             Return the database handle
146              
147             my $dbh = $o_debug->dbh;
148              
149             =cut
150              
151             sub dbh {
152             my $self = shift;
153             # my $type = $self->{_config}->{type}; # debug-target
154             return ref($self->{_dbh}->{$$}) ? $self->{_dbh}->{$$} : $self->_connect($self->{_config});
155             }
156              
157             =item _connect
158              
159             Connect to the database
160              
161             =cut
162              
163             sub _connect {
164             my $self = shift;
165             my $h_conf = $self->{_config};
166              
167             my $dbh = DBI->connect(
168             $h_conf->{datasrc}, $h_conf->{user}, $h_conf->{pass}, $h_conf->{params}
169             ) || $self->fatal("Can't connect to database: $DBI::errstr");
170              
171             $self->{_connect}{connected}++;
172             $self->log("connected: $dbh") if $DEBUG;
173              
174             return $dbh; #$id eq 'Debug' ? $dbh : 1;
175             }
176              
177             =item getarow
178              
179             Get a row
180              
181             my ($res) = $o_debug->getarow($sql);
182              
183             =cut
184              
185             sub getarow {
186             my $self = shift;
187             my $sql = shift;
188             my @res;
189              
190             eval { @res = $self->dbh->selectrow_array($sql) };
191             # my @res = $self->dbh->selectrow_array($sql) || $self->error("failed <$sql>");
192            
193             if ($DEBUG) {
194             $self->log("failed to getarow: $sql $DBI::errstr") unless @res >= 1;
195             }
196              
197             return @res;
198             }
199              
200             =item getahash
201              
202             Get a list of hashes
203              
204             my ($res) = $o_debug->getahash($sql);
205              
206             =cut
207              
208             sub getahash {
209             my $self = shift;
210             my $sql = shift;
211             my @res;
212              
213             eval { @res = $self->dbh->selectrow_hash($sql) };
214             # my @res = $self->dbh->selectrow_array($sql) || $self->error("failed <$sql>");
215            
216             if ($DEBUG) {
217             $self->log("failed to getahash: $sql $DBI::errstr") unless @res >= 1;
218             }
219              
220             return @res;
221             }
222              
223              
224             # =============================================================================
225             # parse and control
226             # =============================================================================
227              
228             my %HISTORY = ();
229             my %TYPES = (
230             'CU' => 'CURSOR',
231             'FU' => 'FUNCTION',
232             'PA' => 'PACKAGE',
233             'PR' => 'PROCEDURE',
234             'TR' => 'TRIGGER',
235             'TY' => 'TYPE',
236             );
237             my %NAMESPACES = (
238             'BO' => 'Namespace_pkg_body',
239             'CU' => 'Namespace_cursor',
240             'FU' => 'Namespace_pkgspec_or_toplevel',
241             'PA' => 'Namespace_pkgspec_or_toplevel',
242             'PK' => 'Namespace_pkgspec_or_toplevel',
243             'PR' => 'Namespace_pkgspec_or_toplevel',
244             'SP' => 'Namespace_pkgspec_or_toplevel',
245             'TR' => 'Namespace_trigger',
246             );
247             my %GROUPS = (
248             +0 => [qw()],
249             +1 => [qw(b c n r s)],
250             +3 => [qw(l L v T)],
251             +5 => [qw(h H ! q)],
252             +6 => [qw(context err perl rc sync sql shell info)],
253             +8 => [qw(abort ping check test is_running)],
254             );
255             my $COMMANDS= join('|', @{$GROUPS{1}}, @{$GROUPS{3}}, @{$GROUPS{5}}, @{$GROUPS{6}}, @{$GROUPS{8}});
256             my %COMMAND = (
257             'abort' => {
258             'long' => 'abortexecution',
259             'handle' => 'abort',
260             'syntax' => 'abort[execution]',
261             'simple' => 'abort target',
262             'detail' => 'abort currently running program in target session',
263             },
264             'b' => {
265             'long' => 'setbreakpoint',
266             'handle' => 'break',
267             'syntax' => 'b [lineno] || setbreakpoint [lineno]',
268             'simple' => 'set breakpoint',
269             'detail' => 'set breakpoint on given line of code identified by unit name',
270             },
271             'c' => {
272             'long' => 'continue',
273             'handle' => 'continue',
274             'syntax' => 'c',
275             'simple' => 'continue',
276             'detail' => 'continue to breakpoint or other reason to stop',
277             },
278             'check'=> {
279             'long' => 'selfcheck',
280             'handle' => 'self_check',
281             'syntax' => 'check || selfcheck',
282             'simple' => 'run a self_check',
283             'detail' => 'run a self_check against dbms_debug and probe communications',
284             },
285             'context' => {
286             'long' => 'context',
287             'handle' => 'runtime', # context
288             'syntax' => 'context key[=val] [key[=val]]+',
289             'simple' => 'get/set context',
290             'detail' => 'get/set context for this instance: unit name, type, namespace etc.',
291             },
292             'err' => {
293             'long' => 'errorstring',
294             'handle' => 'plsql_errstr',
295             'syntax' => 'err',
296             'simple' => 'print plsql_errstr',
297             'detail' => 'display the DBI->plsql_errstr (if set)',
298             },
299             'info' => {
300             'long' => 'information',
301             'handle' => 'info',
302             'syntax' => 'info',
303             'simple' => 'info on current environment',
304             'detail' => 'display information on current programs and db(NYI)',
305             },
306             'help' => {
307             'long' => 'help',
308             'handle' => 'help',
309             'syntax' => 'h [cmd|h|syntax]',
310             'simple' => 'help listing - h h for more',
311             'detail' => 'you can also give a command as an argument (eg: h b)',
312             },
313             'H' => {
314             'long' => 'historylist',
315             'handle' => 'history',
316             'syntax' => 'H',
317             'simple' => 'command history',
318             'detail' => 'history listing not including single character commands',
319             },
320             'l' => {
321             'long' => 'listsourcecode',
322             'handle' => 'list_source',
323             'syntax' => 'l unitname [PROC|PACK|TRIG|...]',
324             'simple' => 'list source code',
325             'detail' => 'list source code given with library type',
326             },
327             'L' => {
328             'long' => 'listbreakpoints',
329             'handle' => 'list_breakpoints',
330             'syntax' => 'L',
331             'simple' => 'list breakpoints',
332             'detail' => 'on which line breakpoints exist',
333             },
334             'n' => {
335             'long' => 'next',
336             'handle' => 'next',
337             'syntax' => 'n',
338             'simple' => 'next line',
339             'detail' => 'continue until the next line',
340             },
341             'perl'=> {
342             'long' => 'perlcommand',
343             'handle' => 'perl',
344             'syntax' => 'perl ',
345             'simple' => 'perl command',
346             'detail' => 'execute a perl command',
347             },
348             'q' => {
349             'long' => 'quit',
350             'handle' => 'quit',
351             'syntax' => 'q(uit)',
352             'simple' => 'exit',
353             'detail' => 'quit the oradb',
354             },
355             'r' => {
356             'long' => 'return',
357             'handle' => 'return',
358             'syntax' => 'r',
359             'simple' => 'return',
360             'detail' => 'return from the current block',
361             },
362             'rc' => {
363             'long' => 'recompilecode',
364             'handle' => 'recompile',
365             'syntax' => 'rc unitname',
366             'simple' => 'recompile',
367             'detail' => 'recompile the program/s given ',
368             },
369             's' => {
370             'long' => 'stepintosubroutine',
371             'handle' => 'step',
372             'syntax' => 's',
373             'simple' => 'step into',
374             'detail' => 'step into the next function or method call',
375             },
376             'shell' => {
377             'long' => 'shellcommand',
378             'handle' => 'shell',
379             'syntax' => 'shell ',
380             'simple' => 'shell command',
381             'detail' => 'execute a shell command',
382             },
383             'sql' => {
384             'long' => 'sqlcommand',
385             'handle' => 'sql',
386             'syntax' => 'sql ',
387             'simple' => 'SQL select',
388             'detail' => 'execute a SQL SELECT statement',
389             },
390             'sync' => {
391             'long' => 'synchronize',
392             'handle' => 'sync',
393             'syntax' => 'sync',
394             'simple' => 'sync',
395             'detail' => 'syncronize the sessions - '.
396             '(note that this session _should_ hang until the procedure is executed in the target session)'
397             },
398             'test'=> {
399             'long' => 'testconnection',
400             'handle' => 'test',
401             'syntax' => 'test',
402             'simple' => 'ping and check and if target is running',
403             'detail' => 'ping, run a self_check and test whether target session is currently running and responding',
404             },
405             'is_running'=> {
406             'long' => 'isrunning',
407             'handle' => 'is_running',
408             'syntax' => 'is_running',
409             'simple' => 'check target is_running',
410             'detail' => 'check whether target session is currently running and responding',
411             },
412             'ping'=> {
413             'long' => 'pingthedatabase',
414             'handle' => 'ping',
415             'syntax' => 'ping',
416             'simple' => 'ping target',
417             'detail' => 'ping target session',
418             },
419             'T'=> {
420             'long' => 'backtrace',
421             'handle' => 'backtrace',
422             'syntax' => 'T',
423             'simple' => 'display backtrace',
424             'detail' => 'backtrace listings',
425             },
426             'v' => {
427             'long' => 'variablevalue',
428             'handle' => 'value',
429             'syntax' => 'v varname[=value]',
430             'simple' => 'get/set variable',
431             'detail' => 'get or set the value of a variable, (use double quotes to contain spaces)',
432             },
433             '!' => {
434             'long' => 'runhistorycommand',
435             'handle' => 'rerun',
436             'syntax' => '! (!|historyno)',
437             'simple' => 'run history command',
438             'detail' => 'run a command from the history list',
439             },
440             'x' => {
441             'long' => 'execute',
442             'handle' => 'execute',
443             'syntax' => 'x sql',
444             'simple' => 'execute sql command',
445             'detail' => 'execute a sql command in the target session',
446             },
447             );
448              
449             =cut
450              
451             =item help
452              
453             Print the help listings where I is one of:
454              
455             h (simple)
456              
457             h h (detail)
458            
459             h b (help for break command etc.)
460              
461             $o_oradb->help($levl);
462              
463             =cut
464              
465             sub help {
466             my $self = shift;
467             my $levl = shift || '';
468              
469             my $help = '';
470             if (grep(/^$levl$/, keys %COMMAND)) {
471             $help .= "\tsyntax: $COMMAND{$levl}{syntax}\n\t$COMMAND{$levl}{detail}\n";
472             } else {
473             $levl = 'simple' unless $levl =~ /^(simple|detail|syntax|handle)$/io;
474             my (@help, @left, @right) = ();
475             foreach my $grp (sort { $a <=> $b } keys %GROUPS) {
476             foreach my $char (@{$GROUPS{$grp}}) {
477             # $help .= "\t".($levl ne 'syntax' ? "$char\t" : '')."$COMMAND{$char}{$levl}\n";
478             my $myhelp = ' '.($levl ne 'syntax' ? sprintf('%-10s', $char) : '').($COMMAND{$char}{$levl}||'');
479             if ($grp =~ /^[13579]$/) {
480             push(@left, $myhelp);
481             } else {
482             push(@right, $myhelp);
483             }
484             }
485             }
486             $#left = $#right if $#left < $#right;
487             $help = "oradb help:\n\n";
488             while (@left) {
489             no warnings; # empty right values
490             local $^W=0;
491             $help .= sprintf('%-45s', shift(@left) || '').shift(@right)."\n";
492             }
493             $help .= "\n";
494             }
495              
496             return $help;
497             }
498              
499             =item preparse
500              
501             Return the command via the shortest match possible
502              
503             my $command = $o_oradb->preparse($cmd); # (help|he)->h
504              
505             =cut
506              
507             sub preparse {
508             my $self = shift;
509             my $cmd = shift;
510             my $comm = '';
511              
512             my @comms = sort keys %COMMAND;
513             print "preparsing cmd($cmd) against comms(@comms)\n";
514              
515             my $i_cnt = my ($found) = grep(/^$cmd/, @comms);
516             if ($i_cnt == 1) {
517             $comm = $found;
518             print "found($found) comm($comm)\n";
519             } else {
520             my @longs = sort map { $COMMAND{$_}{long} } keys %COMMAND;
521             print "preparsing cmd($cmd) against longs(@longs)\n";
522             my $i_cnt = my ($found) = grep(/^$cmd/, @longs);
523             if ($i_cnt == 1) {
524             $comm = $found;
525             print "long($found) comm($comm)\n";
526             }
527             }
528             print "returning comm($comm)\n";
529             @comms = ();
530            
531             return $comm;
532             }
533              
534             =item parse
535              
536             Parse the input command to the appropriate method
537              
538             $o_oradb->parse($cmd, $input);
539              
540             =cut
541              
542             sub parse {
543             my $self = shift;
544             my $cmd = shift;
545             my $input= shift;
546              
547             $DB::single=2;
548             my $xcmd = $self->preparse($cmd);
549             unless (defined($COMMAND{$cmd}{handle})) {
550             unless ($self->can($COMMAND{$cmd}{handle})) {
551             $self->error("command '$cmd' not understood");
552             print $self->help;
553             } else {
554             my $handler = $COMMAND{$cmd}{handle} || 'help';
555             $self->log("cmd($cmd) input($input) handler($handler)") if $DEBUG;
556             $DB::single=2;
557             my @res = $self->$handler($input);
558             $self->log("handler($handler) returned(@res)") if $DEBUG;
559             print @res;
560             }
561             }
562             }
563              
564             # =============================================================================
565             # run and exec methods
566             # =============================================================================
567              
568             =item do
569              
570             Wrapper for oradb->dbh->do() - internally we still use prepare and execute.
571              
572             $o_oradb->do($sql);
573              
574             =cut
575              
576             sub do {
577             my $self = shift;
578             my $exec = shift;
579             my $i_res;
580              
581             $self->log("*** incoming pl/sql: self($self) $exec args(@_)") if $DEBUG;
582             my $csr = $self->dbh->prepare($exec);
583             unless ($csr) {
584             $self->error("Failed to prepare $exec - $DBI::errstr\n") unless $csr;
585             } else {
586             eval {
587             ($i_res) = $csr->execute; # returning 0E0 is true/ok/good
588             };
589              
590             if ($@) {
591             $self->error("Failure: $@ while evaling $exec - $DBI::errstr\n");
592             }
593              
594             unless ($i_res) {
595             $self->error("Failed to execute $exec - $DBI::errstr\n");
596             }
597             }
598              
599             $self->log("do($exec)->res($i_res)") if $DEBUG;
600            
601             return $self;
602             }
603              
604             =item recompile
605              
606             Recompile these procedure|function|package's for debugging
607              
608             $oradb->recompile('xsource');
609              
610             =cut
611              
612             sub recompile {
613             my $self = shift;
614             my $args = shift;
615             my @res = ();
616              
617             my @names = split(/\s+/, $args);
618             foreach my $name (@names) {
619             my %data = $self->unitdata('name'=>$name);
620             if ($data{name} && $data{type}) {
621             $data{type} =~ s/BODY//;
622             my $exec = qq|ALTER $data{type} $data{name} COMPILE Debug|; $exec .= ' BODY' if $data{type} =~ /^PACKAGE|TYPE$/o;
623             my @msg = $self->do($exec)->get_msg;
624             print (@msg >= 1 ? "$data{name} recompiled\n" : "$data{name} failed recompilation!\n");
625             push(@res, @msg);
626             }
627             }
628              
629             return @res;
630             }
631              
632             =item synchronize
633              
634             Synchronize the debug and target sessions
635              
636             $o_oradb->synchronize;
637              
638             =cut
639              
640             sub xsynchronize {
641             my $self = shift;
642             my $args = shift;
643             my @res = ();
644              
645             print "Synching - once this hangs, execute the code in the target session\n";
646             print "\t(if this does not hang, (it SHOULD), check the connection (with 'test'), and retry)\n";
647             @res = $self->sync;
648             $self->{_connect}{synched}++;
649             # print "Synched (if we hung - above - setting some breakpoints might be an idea...\n";
650              
651             return @res;
652             }
653              
654             =item unitdata
655              
656             Retrieve data for given unit - expects to recieve B record from db!
657              
658             %data = $o_oradb->unitdata('name'=>$name, 'type'=>$type, ...);
659              
660             =cut
661              
662             sub unitdata {
663             my $self = shift;
664             my %args = (
665             'name' => '',
666             'type' => '',
667             'owner' => '',
668             @_);
669             map { $args{$_} = '' unless $args{$_} } keys %args;
670             my %res = ();
671              
672             unless ($args{name} =~ /^\w+$/o) { # rjsf
673             $self->error("unit name($args{name}) is required");
674             } else {
675             my $sql = qq#SELECT DISTINCT(name || ':' || type || ':' || owner) FROM all_source
676             WHERE UPPER(name) = UPPER('$args{name}')#;
677             $sql .= qq# AND UPPER(type) LIKE UPPER('$args{type}%')# if $args{type};
678             my ($data) = my @data = $self->getarow($sql);
679             my $input = join(', ', map { $_.'='.$args{$_} } sort keys %args);
680             unless (scalar(@data) == 1) {
681             $self->error("invalid or unambiguated data found via input($input)");
682             } else {
683             my ($name, $type, $owner) = split(':', $data);
684             unless ($name =~ /^\w+$/o) {
685             $self->error("invalid data($data) found via input($input)");
686             } else {
687             %res = (
688             'name' => $name,
689             'type' => $type,
690             'owner' => $owner,
691             );
692             map { $self->{_unit}{lc($_)} = $res{$_} } keys %res;
693             }
694             }
695             }
696              
697             return %res;
698             }
699              
700             =item perl
701              
702             Run a chunk of perl
703              
704             $o_oradb->perl($perl);
705              
706             =cut
707              
708             sub perl {
709             my $self = shift;
710             my $perl = shift;
711            
712             eval $perl;
713             if ($@) {
714             $self->error("failed perl expression($perl) - $@");
715             }
716             return "\n";
717             }
718              
719             =item shell
720              
721             Run a shell command
722              
723             $o_oradb->shell($shellcommand);
724              
725             =cut
726              
727             sub shell {
728             my $self = shift;
729             my $shell = shift;
730            
731             system($shell);
732             if ($@) {
733             $self->error("failed shell command($shell) - $@");
734             }
735             return "\n";
736             }
737              
738             =item sql
739              
740             Run a chunk of SQL (select only)
741              
742             $o_oradb->sql($sql);
743              
744             =cut
745              
746             sub sql {
747             my $self = shift;
748             my $xsql = shift;
749             my @res = ();
750              
751             unless ($xsql =~ /^\s*\w+\s+/io) {
752             $self->error("SQL statements only please: <$xsql>");
753             } else {
754             $xsql =~ s/\s*;\s*$//;
755             @res = ($self->getarow($xsql), "\n");
756             }
757              
758             return @res;
759             }
760              
761             =item _run
762              
763             Run a chunk
764              
765             $o_oradb->_run($sql);
766              
767             =cut
768              
769             sub _run { # INTERNAL
770             my $self = shift;
771             my $xsql = shift;
772              
773             my $exec = qq#
774             BEGIN
775             $xsql;
776             END;
777             #;
778              
779             return $self->do($exec)->get_msg;
780             }
781              
782              
783             # =============================================================================
784             # start debug and target methods
785             # =============================================================================
786              
787             =item target
788              
789             Run the target session
790              
791             $o_oradb->target;
792              
793             =cut
794              
795             sub target {
796             my $self = shift;
797              
798             my $dbid = $self->start_target('rfi_oradb_sessionid');
799             if ($dbid) {
800             ReadMode 0;
801             print "orasql> enter a PL/SQL command to debug (debugger session must be running...)\n";
802             while (1) {
803             print "orasql>";
804             chomp(my $input = ReadLine(0));
805             $self->log("processing input($input)") if $DEBUG;
806             if ($input =~ /^\s*(q\s*|quit\s*)$/io) {
807             $self->quit;
808             } elsif ($input =~ /^\s*(h\s*|help\s*)$/io) {
809             print qq|No help menus for target session - simply enter code to debug (which will un-hang the debug session...)\n|;
810             $self->help;
811             } else {
812             $self->_run($input);
813             }
814             }
815             }
816              
817             return $self;
818             }
819              
820             =item start_target
821              
822             Get the target session id(given) and stick it in our table (by process_id)
823              
824             my $dbid = $oradb->start_target($dbid);
825              
826             =cut
827              
828             sub start_target {
829             my $self = shift;
830             my $dbid = shift;
831              
832             if ($self->{_connect}{debugid}) {
833             $self->fatal("debug process may not run as a target instance");
834             }
835              
836             $self->{_connect}{targetpid} = $dbid;
837             my $x_res = $self->do('DELETE FROM '.$self->{_config}{table}); # currently we only allow a single session at a time
838              
839             my $init = qq#
840             DECLARE
841             xret VARCHAR2(32);
842             BEGIN
843             xret := dbms_debug.initialize('$dbid');
844             -- dbms_debug.debug_on(TRUE, FALSE); -- wait
845             dbms_debug.debug_on(TRUE, TRUE); -- immediate
846             END;
847             #;
848             $x_res = $self->do($init);
849             =pod
850             my $ddid = qq#
851             BEGIN
852             -- dbms_debug.debug_on(TRUE, FALSE); -- target releases debugger sync-hang by execute
853             -- not certain the second TRUE is fully functional here...
854             dbms_debug.debug_on(TRUE, TRUE); -- debugger releases target hang with executes
855             END;
856             #; # should hang (if 2nd true) unless debugger running
857             $x_res = $self->do($ddid);
858              
859             # should be autonomous transaction
860             my $insert = qq#INSERT INTO $self->{_config}{table}
861             (created, debugpid, targetpid, sessionid, data)
862             VALUES (sysdate, $$, $$, '$dbid', 'xxx'
863             )#;
864             $x_res = $self->do($insert);
865              
866             $x_res = $self->do('COMMIT');
867             =cut
868              
869             $self->log("target started: $dbid") if $DEBUG;
870              
871             return $dbid;
872             }
873              
874             =item debugger
875              
876             Run the debugger
877              
878             $o_debug->debugger;
879              
880             =cut
881              
882             sub debugger {
883             my $self = shift;
884              
885             my $dbid = $self->start_debug('rfi_oradb_sessionid');
886            
887             ReadMode 0;
888             print "Welcome to the oradb (type h for help)\n";
889             my $i_cnt = 0;
890             while (1) {
891             print "oradb> ";
892             chomp(my $input = ReadLine(0));
893             $self->log("processing command($input)") if $DEBUG;
894             $input .= ' ';
895             #if ($input =~ /^\s*($COMMANDS)\s+(.*)\s*$/o) {
896             if ($input =~ /^\s*(\w+)\s+(.*)\s*$/o) {
897             my ($cmd, $args) = ($1, $2);
898             $cmd =~ s/\s+$//; $args =~ s/^\s+//; $args =~ s/\s+$//;
899             $self->log("input($input) -> cmd($cmd) args($args)") if $DEBUG;
900             my $res = $cmd.' '.$args;
901             $HISTORY{++$i_cnt} = $res unless $input =~ /^\s*(.|!.*)\s*$/o || grep(/^$res$/, map { $HISTORY{$_} } keys %HISTORY);
902             $self->parse($cmd, $args); # + process
903             } else {
904             $self->error("oradb> command ($input) not understood");
905             }
906             }
907              
908             return $self;
909             }
910              
911             =item start_debug
912              
913             Start the debugger session
914              
915             my $i_res = $oradb->start_debug($db_session_id, $pid);
916              
917             =cut
918              
919             sub start_debug {
920             my $self = shift;
921             my $dbid = shift;
922             my $pid = shift;
923              
924             # my $x_res = $self->do('UPDATE '.$self->{_config}{table}." SET debugpid = $pid");
925             if ($self->{_connect}{targetid}) {
926             $self->fatal("target process may not run as a debug instance");
927             }
928             $self->{_connect}{debugpid} = $dbid;
929              
930             # SET serveroutput ON; -- done via dbi
931             my $x_res = $self->do(qq#ALTER session SET plsql_debug=TRUE#)->get_msg;
932             # ALTER session SET plsql_debug = TRUE; -- done per proc.
933              
934             my $exec = qq#
935             BEGIN
936             dbms_debug.attach_session('$dbid');
937             dbms_output.put_line('attached');
938             END;
939             #;
940              
941             return $self->do($exec)->get_msg;
942             }
943              
944             =item sync
945              
946             Blocks debug session until we exec in target session
947              
948             my $i_res = $oradb->sync;
949              
950             =cut
951              
952             sub sync {
953             my $self = shift;
954             my @res = ();
955              
956             =pod rjsf
957             my ($tid) = $self->getarow('SELECT targetpid FROM '.$self->{_config}{table}." WHERE debugpid = '".$self->{_debugpid}."'");
958             $self->{_targetpid} = $tid;
959             =cut
960             print "Synching - once this hangs, execute the code in the target session\n";
961             print "\t(if this does not hang, (it SHOULD), check the connection (with 'test'), and retry)\n";
962            
963             my $exec = qq#
964             DECLARE
965             xec binary_integer;
966             runtime dbms_debug.runtime_info;
967             BEGIN
968             xec := dbms_debug.synchronize(runtime);
969             IF xec = dbms_debug.success THEN
970             NULL;
971             dbms_output.put_line('...synched ' || runtime.program.name);
972             ELSE
973             dbms_output.put_line('Error: ' || oradb.errorcode(xec));
974             END IF;
975             END;
976             #;
977              
978             my $test = '';
979             my $i_cnt = 0;
980             while (1) {
981             $i_cnt++;
982             @res = $self->do($exec)->get_msg;
983             chomp($test = $self->is_running);
984             print ".";
985             last if ($i_cnt >= $self->{_connect}{syncs} || $test eq 'target is currently running');
986             sleep 1;
987             }
988             $self->{_connect}{synched}++;
989             print "\n$test\n";
990              
991             return @res;
992             }
993              
994             # =============================================================================
995             # b c n s r exec
996             # =============================================================================
997              
998             =item execute
999              
1000             Runs the given statement against the target session
1001              
1002             my $i_res = $oradb->execute($xsql);
1003              
1004             =cut
1005              
1006             sub execute {
1007             my $self = shift;
1008             my $xsql = shift;
1009              
1010             $xsql =~ s/[\s\;]*$//;
1011              
1012             my $exec = qq#
1013             DECLARE
1014             col1 sys.dbms_debug_vc2coll;
1015             errm VARCHAR2(100);
1016             BEGIN
1017             dbms_debug.execute('BEGIN $xsql; END;',
1018             -1, 0, col1, errm);
1019             IF (errm IS NOT NULL) THEN
1020             DBMS_OUTPUT.put_line('Error($xsql): ' || errm);
1021             END IF;
1022             END;
1023             #;
1024              
1025             return $self->do($exec)->get_msg;
1026             }
1027              
1028             =item break
1029              
1030             Set a breakpoint
1031              
1032             my $i_res = $oradb->break("$i_line $procedurename");
1033              
1034             =cut
1035              
1036             sub break {
1037             my $self = shift;
1038             my $args = shift;
1039             my @res = ();
1040              
1041             my ($line, $name) = split(/\s+/, $args);
1042             # unless ($line =~ /^(\d+|\*)$/o) { <- fuzzy
1043             unless ($line =~ /^(\d+)$/o) {
1044             $self->error("must supply a valid line number($line) to set a breakpoint via($args)");
1045             } else {
1046             my $name = $name || $self->{_unit}{name} || '';
1047             unless ($name =~ /^(\w+)$/o) {
1048             $self->error("library unit($name) must be given");
1049             } else {
1050             my $exec = qq|
1051             BEGIN
1052             oradb.b('$name', $line);
1053             END;
1054             |;
1055             @res = $self->do($exec)->get_msg;
1056             }
1057             }
1058              
1059             return @res;
1060             }
1061              
1062             =item continue
1063              
1064             Continue execution until given breakpoints
1065              
1066             my $i_res = $oradb->continue;
1067              
1068             =cut
1069              
1070             sub continue {
1071             my $self = shift;
1072              
1073             my $exec = qq#
1074             BEGIN
1075             oradb.continue_(dbms_debug.break_any_call);
1076             END;
1077             #;
1078              
1079             return $self->do($exec)->get_msg;
1080             }
1081              
1082             =item next
1083              
1084             Step over the next line
1085              
1086             my $i_res = $oradb->next;
1087              
1088             =cut
1089              
1090             sub next {
1091             my $self = shift;
1092              
1093             my $exec = qq#
1094             BEGIN
1095             oradb.continue_(dbms_debug.break_next_line);
1096             END;
1097             #;
1098              
1099             return $self->do($exec)->get_msg;
1100             }
1101              
1102             =item step
1103              
1104             Step into the next statement
1105              
1106             my $i_res = $oradb->step;
1107              
1108             =cut
1109              
1110             sub step {
1111             my $self = shift;
1112              
1113             my $exec = qq#
1114             BEGIN
1115             oradb.continue_(dbms_debug.break_any_call);
1116             END;
1117             #;
1118              
1119             return $self->do($exec)->get_msg;
1120             }
1121              
1122             =item return
1123              
1124             Return from the current scope
1125              
1126             my $i_res = $oradb->return;
1127              
1128             =cut
1129              
1130             sub return {
1131             my $self = shift;
1132              
1133             my $exec = qq#
1134             BEGIN
1135             oradb.continue_(dbms_debug.break_return);
1136             END;
1137             #;
1138              
1139             return $self->do($exec)->get_msg;
1140             }
1141              
1142             # =============================================================================
1143             # runtime_info and source listing methods
1144             # =============================================================================
1145              
1146             =item runtime
1147              
1148             Print runtime_info via dbms_output
1149              
1150             $oradb->runtime;
1151              
1152             =cut
1153              
1154             sub runtime {
1155             my $self = shift;
1156             my $sep = '-' x 80;
1157             my @msg = ();
1158              
1159             unless ($self->{_connect}{synched}) {
1160             $self->error('not running yet');
1161             } else {
1162             =pod
1163             info_getStackDepth CONSTANT PLS_INTEGER := 2; -- get stack depth
1164             info_getBreakpoint CONSTANT PLS_INTEGER := 4; -- get breakpoint number
1165             info_getLineinfo CONSTANT PLS_INTEGER := 8; -- get program info
1166             info_getOerInfo CONSTANT PLS_INTEGER := 32; -- (Probe v2.4)
1167             =cut
1168              
1169             my $exec = qq/
1170             DECLARE
1171             runinfo dbms_debug.runtime_info;
1172             xinf BINARY_INTEGER DEFAULT dbms_debug.info_getBreakpoint + dbms_debug.info_getLineinfo + dbms_debug.info_getOerInfo;
1173             xec BINARY_INTEGER;
1174             BEGIN
1175             xec := dbms_debug.get_runtime_info(xinf, runinfo);
1176             IF xec = 0 THEN
1177             dbms_output.put_line('Runtime Info:');
1178             dbms_output.put_line(' Name: ' || runinfo.program.name);
1179             dbms_output.put_line(' Line: ' || runinfo.line#);
1180             dbms_output.put_line(' Owner: ' || runinfo.program.owner);
1181             dbms_output.put_line(' Unit: ' || oradb.libunittype(runinfo.program.libunittype));
1182             dbms_output.put_line(' Namespace: ' || oradb.namespace(runinfo.program.namespace));
1183             ELSE
1184             dbms_output.put_line(' Error: ' || oradb.errorcode(xec));
1185             END IF;
1186             END;
1187             /;
1188              
1189             @msg = $self->do($exec)->get_msg;
1190             }
1191              
1192             return @msg >= 1 ? "\n".join("\n", $sep, @msg, $sep)."\n" : '...';
1193             }
1194              
1195            
1196             =item backtrace
1197              
1198             Print backtrace from runtime info via dbms_output
1199              
1200             $o_oradb->backtrace();
1201              
1202             =cut
1203              
1204             sub backtrace {
1205             my $self = shift;
1206              
1207             my $exec = qq#
1208             DECLARE
1209             tracing VARCHAR2(2000);
1210             BEGIN
1211             dbms_debug.print_backtrace(tracing);
1212             dbms_output.put_line(tracing);
1213             END;
1214             #;
1215              
1216             my @msg = $self->do($exec)->get_msg;
1217              
1218             return @msg;
1219             }
1220              
1221             =item list_source
1222              
1223             Print source
1224              
1225             $oradb->list_source('xsource', [PROC|...]);
1226              
1227             =cut
1228              
1229             sub list_source {
1230             my $self = shift;
1231             my $args = shift;
1232             my @res = ();
1233              
1234             my ($name, $type) = split(/\s+/, $args);
1235             my %data = $self->unitdata('name'=>$name, 'type'=>$type);
1236              
1237             if ($data{name} && $data{type}) {
1238             my $exec = qq#
1239             DECLARE
1240             xsrc VARCHAR2(4000);
1241             CURSOR src IS
1242             SELECT line, text FROM all_source WHERE name = '$data{name}'
1243             AND type LIKE '$data{type}%' AND type != 'PACKAGE' ORDER BY name, line;
1244             BEGIN
1245             FOR rec IN src LOOP
1246             xsrc := rec.line || ': ' || rec.text;
1247             dbms_output.put_line(SUBSTR(xsrc, 1, LENGTH(xsrc) -1));
1248             END LOOP;
1249             END;
1250             #;
1251             @res = $self->do($exec)->get_msg;
1252             my $res = join('', @res);
1253             unless ($res =~ /\w+/o) {
1254             $self->error("no source($res) found with unit($data{name}) type($data{type})");
1255             }
1256             }
1257              
1258             return @res;
1259             }
1260              
1261             =item list_breakpoints
1262              
1263             Print breakpoint info
1264              
1265             $oradb->list_breakpoints;
1266              
1267             =cut
1268              
1269             sub list_breakpoints {
1270             my $self = shift;
1271              
1272             my $exec = qq/
1273             DECLARE
1274             brkpts dbms_debug.breakpoint_table;
1275             i number;
1276             BEGIN
1277             dbms_debug.show_breakpoints(brkpts);
1278             i := brkpts.first();
1279             dbms_output.put_line('breakpoints: ');
1280             while i is not null loop
1281             dbms_output.put_line(' ' || i || ': ' || brkpts(i).name || ' (' || brkpts(i).line# ||')');
1282             i := brkpts.next(i);
1283             end loop;
1284             END;
1285             /;
1286              
1287             return $self->do($exec)->get_msg;
1288             }
1289              
1290             =pod rjsf
1291             vanilla version
1292             DECLARE
1293             runinfo dbms_debug.runtime_info;
1294             i_before number := 1;
1295             i_after number := 99;
1296             i_width number := 80;
1297             BEGIN
1298             oradb.print_runtime_info_with_source(runinfo, i_before, i_after, i_width);
1299             END;
1300             =cut
1301              
1302             =item history
1303              
1304             Display the command history
1305              
1306             print $o_oradb->history;
1307              
1308             =cut
1309              
1310             sub history {
1311             my $self = shift;
1312              
1313             my @hist = map { "$_: $HISTORY{$_}\n" } sort { $a <=> $b } grep(!/\!/, keys %HISTORY);
1314              
1315             return @hist;
1316             }
1317              
1318             =item rerun
1319              
1320             Rerun a command from the history list
1321              
1322             $o_oradb->rerun($histno);
1323              
1324             =cut
1325              
1326             sub rerun {
1327             my $self = shift;
1328             my $hist = shift || 0;
1329              
1330             if ($hist =~ /!/o) {
1331             ($hist) = reverse sort { $a <=> $b } keys %HISTORY;
1332             }
1333             unless ($HISTORY{$hist} =~ /^(\S+)\s(.*)$/o) {
1334             $self->error("invalid history key($hist) - try using 'H'");
1335             } else {
1336             my ($cmd, $args) = ($1, $2);
1337             $self->parse($cmd, $args); # + process
1338             }
1339              
1340             return ();
1341             }
1342              
1343             # =============================================================================
1344             # check and ping methods
1345             # =============================================================================
1346              
1347             =item info
1348              
1349             Info
1350              
1351             print $oradb->info;
1352              
1353             =cut
1354              
1355             sub info {
1356             my $self = shift;
1357              
1358             my $src = $self->{_config}{datasrc} || '';
1359             $src =~ s/^\w+:\w+://;
1360             my @src = split(';', $src);
1361             my %src = map { split('=', $_) } @src;
1362             my ($probe, $version) = split(/:\s+/, $self->probe_version);
1363             chomp($version);
1364              
1365             my %data = (
1366             'host' => $src{host},
1367             'instance' => uc($src{sid}),
1368             'oradb' => $Oracle::Debug::VERSION,
1369             'port' => $src{port},
1370             'user' => $self->{_config}{user},
1371             $probe => $version,
1372             );
1373             my ($i_max) = sort { $b <=> $a } map { length($_) } keys %data;
1374              
1375             my @res = ("\n", (map { $_.(' 'x($i_max-length($_))).' = '.$data{$_}."\n" } sort keys %data), "\n");
1376              
1377             return @res;
1378             }
1379              
1380             =item context
1381              
1382             Get and set context info
1383              
1384             my $s_res = $o_oradb->context($name); # get
1385              
1386             my $s_res = $o_oradb->context($name, $value); # set
1387              
1388             =cut
1389              
1390             sub context {
1391             my $self = shift;
1392             my $args = shift || '';
1393             my @args = my %args = ();
1394             my @res = ();
1395              
1396             my ($i_max) = sort { $b <=> $a } map { length($_) } keys %{$self->{_unit}};
1397              
1398             if (%args = ($args =~ /\G\s*(\w+)\s*=\s*(\w+)/go)) { # set
1399             foreach (sort sort keys %args) {
1400             my $call = "_$_";
1401             push(@res, $_.(' 'x($i_max-length($_))).' = '.$self->$call($args{$_})."\n") if $self->can($call);
1402             }
1403             } elsif (@args = ($args =~ /\G\s*(\w+)\s*/go)) { # get
1404             foreach (sort @args) {
1405             my $call = "_$_";
1406             push(@res, $_.(' 'x($i_max-length($_))).' = '.$self->$call()."\n") if $self->can($call);
1407             }
1408             } else { # all
1409             @res = map { $_.(' 'x($i_max-length($_))).' = '.$self->{_unit}{$_}."\n" } sort keys %{$self->{_unit}};
1410             }
1411              
1412             return @res;
1413             }
1414              
1415             =item probe_version
1416              
1417             Log the Probe version
1418              
1419             print $oradb->probe_version;
1420              
1421             =cut
1422              
1423             sub probe_version {
1424             my $self = shift;
1425              
1426             my $exec = qq#
1427             DECLARE
1428             i_maj BINARY_INTEGER;
1429             i_min BINARY_INTEGER;
1430             BEGIN
1431             dbms_debug.probe_version(i_maj, i_min);
1432             dbms_output.put_line('probe version: ' || i_maj || '.' || i_min);
1433             END;
1434             #;
1435              
1436             return $self->do($exec)->get_msg;
1437             }
1438              
1439             =item test
1440              
1441             Call self_check, ping and is_running
1442              
1443             my $i_ok = $oradb->test();
1444              
1445             =cut
1446              
1447             sub test {
1448             my $self = shift;
1449             my @res = ();
1450              
1451             push(@res, $self->self_check, $self->ping, $self->is_running);
1452            
1453             return @res;
1454             }
1455              
1456             =item self_check
1457              
1458             Self->check
1459              
1460             my $i_ok = $oradb->self_check; # 9.2
1461              
1462             =cut
1463              
1464             sub self_check {
1465             my $self = shift;
1466              
1467             my $exec = qq#
1468             BEGIN
1469             dbms_debug.self_check(10);
1470             dbms_output.put_line('checked');
1471             END;
1472             #;
1473              
1474             return $self->do($exec)->get_msg;
1475             }
1476              
1477             =item ping
1478              
1479             Ping the target process (gives an ORA-error if no target)
1480              
1481             my $i_ok = $oradb->ping; # 9.2
1482              
1483             =cut
1484              
1485             sub ping {
1486             my $self = shift;
1487              
1488             my $exec = qq#
1489             BEGIN
1490             dbms_debug.ping();
1491             dbms_output.put_line('pinged');
1492             END;
1493             #;
1494              
1495             return $self->do($exec)->get_msg;
1496             }
1497              
1498             =item is_running
1499              
1500             Check the target is still running - ???
1501              
1502             my $i_ok = $oradb->is_running; # 9.2
1503              
1504             =cut
1505              
1506             sub is_running {
1507             my $self = shift;
1508              
1509             my $exec = qq#
1510             BEGIN
1511             IF dbms_debug.target_program_running THEN
1512             dbms_output.put_line('target is currently running');
1513             ELSE
1514             dbms_output.put_line('target is not currently running');
1515             END IF;
1516             END;
1517             #;
1518              
1519             return $self->do($exec)->get_msg;
1520             }
1521              
1522             # =============================================================================
1523             # get and put msg methods
1524             # =============================================================================
1525              
1526             =item plsql_errstr
1527              
1528             Get PL/SQL error string
1529              
1530             $o_debug->plsql_errstr;
1531              
1532             =cut
1533              
1534             sub plsql_errstr {
1535             my $self = shift;
1536              
1537             return $self->dbh->func('plsql_errstr');
1538             }
1539              
1540             =item put_msg
1541              
1542             Put debug message info
1543              
1544             $o_debug->put_msg($msg);
1545              
1546             =cut
1547              
1548             sub put_msg {
1549             my $self = shift;
1550              
1551             return $self->dbh->func(@_, 'dbms_output_put');
1552             }
1553              
1554             =item get_msg
1555              
1556             Get debug message info
1557              
1558             print $o_debug->get_msg;
1559              
1560             =cut
1561              
1562             sub get_msg {
1563             my $self = shift;
1564              
1565             my @msg = (); {
1566             no warnings;
1567             @msg = grep(/./, $self->dbh->func('dbms_output_get'));
1568             }
1569              
1570             return (@msg >= 1 ? join("\n", @msg)."\n" : "\n");
1571             }
1572              
1573             =item value
1574              
1575             Get and set the value of a variable, in a procedure, or in a package
1576              
1577             my $val = $o_oradb->value($name);
1578              
1579             my $val = $o_oradb->value($name, $value);
1580              
1581             =cut
1582              
1583             sub value {
1584             my $self = shift;
1585             my $args = shift || '';
1586             my @res = ();
1587              
1588             my ($var, $getset) = ('', '', '');
1589              
1590             if ($args =~ /^\s*(\w[\.\w]*)\s*:{0,1}=\s*(\S.+)?\s*$/o) { # set
1591             $var = "$1 := $2;";
1592             $getset = '_set_val';
1593             } elsif ($args =~ /^\s*(\w[\.\w]*)\s*$/) { # get
1594             $var = $1;
1595             $getset = '_get_val';
1596             } else { # err
1597             $self->error("unable to get or set variable - incorrect syntax: v $args");
1598             }
1599              
1600             if ($getset) {
1601             @res = $self->$getset($var);
1602             }
1603              
1604             return @res;
1605             }
1606              
1607             =item _get_val
1608              
1609             Get the value of a variable
1610              
1611             my $val = $o_debug->_get_val($varname);
1612              
1613             =cut
1614              
1615             sub _get_val {
1616             my $self = shift;
1617             my $xvar = shift;
1618              
1619             my $exec = qq#
1620             DECLARE
1621             program dbms_debug.program_info;
1622             runinfo dbms_debug.runtime_info;
1623             xinf BINARY_INTEGER DEFAULT dbms_debug.info_getBreakpoint + dbms_debug.info_getLineinfo + dbms_debug.info_getOerInfo;
1624             xec BINARY_INTEGER;
1625             buff VARCHAR2(500);
1626             BEGIN
1627             xec := dbms_debug.get_runtime_info(xinf, runinfo);
1628             IF runinfo.program.namespace = 2 THEN
1629             /*
1630             program := runinfo.program;
1631             program.namespace := dbms_debug.namespace_pkgspec_or_toplevel; -- as per docs...
1632             program.Owner := runinfo.program.owner;
1633             program.Name := runinfo.program.name;
1634             xec := dbms_debug.get_value('$xvar', program, buff, NULL);
1635             */
1636             xec := dbms_debug.get_value('$xvar', 0, buff, NULL);
1637             ELSE
1638             xec := dbms_debug.get_value('$xvar', 0, buff, NULL);
1639             END IF;
1640             IF xec = dbms_debug.success THEN
1641             dbms_output.put_line('$xvar = ' || buff);
1642             ELSE
1643             dbms_output.put_line('Error: ' || oradb.errorcode(xec));
1644             END IF;
1645             END;
1646             #;
1647              
1648             my @res = $self->do($exec)->get_msg;
1649              
1650             return @res;
1651             }
1652              
1653             =item _set_val
1654              
1655             Set the value of a variable
1656              
1657             my $val = $o_debug->_set_val($xset);
1658              
1659             =cut
1660              
1661             sub _set_val {
1662             my $self = shift;
1663             my $xset = shift;
1664              
1665             # $self->error("unimplemented");
1666              
1667             my $exec = qq#
1668             DECLARE
1669             xec BINARY_INTEGER;
1670             BEGIN
1671             xec := dbms_debug.set_value(0, '$xset');
1672              
1673             IF xec = dbms_debug.success THEN
1674             dbms_output.put_line('$xset succeeded');
1675             ELSE
1676             dbms_output.put_line('Error: ' || oradb.errorcode(xec));
1677             END IF;
1678             END;
1679             #;
1680            
1681             my @res = $self->do($exec)->get_msg;
1682              
1683             return @res;
1684             }
1685              
1686             =item audit
1687              
1688             Get auditing info
1689              
1690             my ($audsid) = $o_debug->audit;
1691              
1692             =cut
1693              
1694             sub audit {
1695             my $self = shift;
1696              
1697             my $sql = qq#
1698             SELECT audsid || '-' || sid || '-' || osuser || '-' || username FROM v\$session WHERE audsid = userenv('SESSIONID')
1699             #;
1700              
1701             my ($res) = $self->dbh->selectrow_array($sql);
1702              
1703             $self->error("failed to audit: $sql $DBI::errstr") unless $res;
1704              
1705             return $res." $$";
1706             }
1707              
1708             # =============================================================================
1709             # get and put context methods
1710             # =============================================================================
1711              
1712             =item _check
1713              
1714             Return whether or not the given PLSQL target has a value of some sort
1715              
1716             my $i_ok = $o_oradb->_check('unit');
1717              
1718             =cut
1719              
1720             sub _check {
1721             my $self = shift;
1722             my $targ = lc(shift);
1723             my $i_ok = 0;
1724            
1725             unless ($targ =~ /^\w+$/o) {
1726             $self->error("require a valid plsql target($targ) to check: ".join(', ', sort keys %{$self->{_unit}}));
1727             } else {
1728             $i_ok++ if $self->{_unit}{$targ} =~ /./o;
1729             }
1730              
1731             return $i_ok;
1732             }
1733              
1734             =item _unit
1735              
1736             Get and set B name for all consequent actions
1737              
1738             $o_oradb->_unit; # get
1739              
1740             $o_oradb->_unit($name); # set
1741              
1742             =cut
1743              
1744             sub _unit {
1745             my $self = shift;
1746             my $args = shift || $self->{_unit}{name} || '';
1747              
1748             unless ($args =~ /^\s*(\w+)\s*$/o) {
1749             $self->error("valid alphanumeric unit($args) is required");
1750             } else {
1751             $self->{_unit}{name} = uc($args);
1752             }
1753            
1754             $self->{_unit}{name};
1755             }
1756              
1757             =item _type
1758              
1759             Get and set B for all consequent actions
1760              
1761             $o_oradb->_type; # get
1762              
1763             $o_oradb->_type($type); # set
1764              
1765             =cut
1766              
1767             sub _type {
1768             my $self = shift;
1769             my $args = shift || $self->{_unit}{type} || '';
1770              
1771             my $xx = uc(substr($args, 0, 2));
1772             unless ($TYPES{$xx} =~ /^(\w+)$/o) {
1773             $self->error("invalid type($args) - the following are allowed: ".join(', ', sort VALUES %TYPES));
1774             } else {
1775             $self->{_unit}{type} = uc($1);
1776             }
1777            
1778             $self->{_unit}{type};
1779             }
1780              
1781             =item _namespace
1782              
1783             Get and set B namespace for all consequent actions
1784              
1785             $o_oradb->_namespace; # get
1786              
1787             $o_oradb->_namespace($space); # set
1788              
1789             =cut
1790              
1791             sub _namespace {
1792             my $self = shift;
1793             my $args = shift || $self->{_unit}{namespace} || '';
1794              
1795             my $xx = uc(substr($args, 0, 2));
1796             unless ($NAMESPACES{$xx} =~ /^(\w+)$/o) {
1797             $self->error("invalid namespace($args) - the following are allowed: ".join(', ', sort VALUES %NAMESPACES));
1798             } else {
1799             $self->{_unit}{namespace} = uc($1);
1800             }
1801            
1802             return $self->{_unit}{namespace};
1803             }
1804              
1805             =item _owner
1806              
1807             Get and set B owner for all consequent actions
1808              
1809             $o_oradb->_owner; # get
1810              
1811             $o_oradb->_owner($user); # set
1812              
1813             =cut
1814              
1815             sub _owner {
1816             my $self = shift;
1817             my $args = shift || $self->{_unit}{owner} || '';
1818              
1819             unless ($args =~ /^\s*(\w+)\s*$/o) {
1820             $self->error("valid alphanumeric owner($args) is required");
1821             } else {
1822             $self->{_unit}{owner} = uc($1);
1823             }
1824            
1825             return $self->{_unit}{owner};
1826             }
1827              
1828             # =============================================================================
1829             # error, log and cleanup methods
1830             # =============================================================================
1831              
1832             =item feedback
1833              
1834             Feedback handler (currently just prints to STDOUT)
1835              
1836             $o_debug->feedback("this");
1837              
1838             =cut
1839              
1840             sub feedback {
1841             my $self = shift;
1842             my $msgs = join(' ', @_);
1843             print STDOUT 'ORADB> '."$msgs\n";
1844             return $msgs;
1845             }
1846              
1847             =item log
1848              
1849             Log handler (currently just prints to STDERR)
1850              
1851             $o_debug->log("this");
1852              
1853             =cut
1854              
1855             sub log {
1856             my $self = shift;
1857             my $msgs = join(' ', @_);
1858             print STDERR 'oradb: '."$msgs\n";
1859             return $msgs;
1860             }
1861              
1862             =item quit
1863              
1864             Quit the debugger
1865              
1866             $o_oradb->quit;
1867              
1868             =cut
1869              
1870             sub quit {
1871             my $self = shift;
1872             $self->abort();
1873             print "oradb detaching...\n";
1874             # $self->detach;
1875             exit;
1876             }
1877              
1878             =item error
1879              
1880             Error handler
1881              
1882             =cut
1883              
1884             sub error {
1885             my $self = shift;
1886             $DB::errstr = $DB::errstr;
1887             my $errs = join(' ', 'Error:', @_).($DB::errstr || '')."\n";
1888             print $errs;
1889             # carp($errs);
1890             return $errs;
1891             }
1892              
1893             =item fatal
1894              
1895             Fatal error handler
1896              
1897             =cut
1898              
1899             sub fatal {
1900             my $self = shift;
1901             croak(ref($self).' FATAL ERROR: ', @_);
1902             }
1903              
1904             =item abort
1905              
1906             Tell the target session to abort the currently running program
1907              
1908             $o_debug->abort;
1909              
1910             =cut
1911              
1912             sub abort {
1913             my $self = shift;
1914              
1915             my $exec = qq#
1916             DECLARE
1917             runinfo dbms_debug.runtime_info;
1918             ret BINARY_INTEGER;
1919             BEGIN
1920             -- oradb.continue_(dbms_debug.abort_execution);
1921             ret := dbms_debug.continue(runinfo, dbms_debug.abort_execution, 0);
1922             END;
1923             #;
1924              
1925             $self->do($exec)->get_msg;
1926             }
1927              
1928              
1929             =item detach
1930              
1931             Tell the target session to detach itself
1932              
1933             $o_debug->detach;
1934              
1935             =cut
1936              
1937             sub detach {
1938             my $self = shift;
1939              
1940             my $exec = qq#
1941             BEGIN
1942             dbms_debug.detach_session;
1943             END;
1944             #;
1945             $self->do($exec)->get_msg;
1946              
1947             # autonomous transaction
1948             # $self->do('DELETE FROM '.$self->{_config}{table});
1949             # $self->do('COMMIT');
1950             }
1951              
1952             sub DESTROY {
1953             my $self = shift;
1954             my $dbh = $self->{_dbh}->{$$};
1955             if (ref($dbh)) {
1956             $dbh->disconnect;
1957             }
1958             }
1959              
1960             1;
1961              
1962             =back
1963              
1964             =head1 SEE ALSO
1965              
1966             DBD::Oracle
1967              
1968             perldebug
1969              
1970             =head1 AUTHOR
1971              
1972             Richard Foley, EOracle_Debug@rfi.netE
1973              
1974             =head1 COPYRIGHT AND LICENSE
1975              
1976             Copyright 2003 by Richard Foley
1977              
1978             This library is free software; you can redistribute it and/or modify
1979             it under the same terms as Perl itself.
1980              
1981             =cut
1982