File Coverage

blib/lib/Class/ReluctantORM/Driver.pm
Criterion Covered Total %
statement 25 169 14.7
branch 0 20 0.0
condition 0 14 0.0
subroutine 9 51 17.6
pod 26 30 86.6
total 60 284 21.1


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::Driver;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::Driver - Abstract interface for RDBMSs
6              
7             =head1 SYNOPSIS
8              
9             # ---- Driver Creation ----
10             # Driver instantiation is usually implicit
11             MyClass->build_class($dbh => $some_database_handle);
12             $driver = MyClass->driver();
13              
14             # To do it explicitly...
15             # Create a raw database connection (or use a Class::ReluctantORM::DBH subclass)
16             my $dbh = DBI->connect(...);
17              
18             # Now make a driver from the handle. The $dbh will
19             # be checked for its RDBMS brand and version, and the
20             # best-matching Driver subclass will be used.
21             my $driver = Class::ReluctantORM::Driver->make_driver($cro_class, $dbh);
22              
23             # ---- SQL Parsing ----
24             if ($driver->supports_parsing()) {
25             $sql_obj = $driver->parse_statement("SELECT foo FROM bar WHERE baz = 'beep'");
26             $sql_where = $driver->parse_where("baz = 'beep'");
27             $sql_order_by = $driver->parse_order_by("dog DESC");
28             }
29              
30             # ---- SQL Rendering ----
31             $sql_string = $driver->render($sql_obj, $hints);
32              
33             # ---- SQL Execution ----
34             $driver->run_sql($sql_obj);
35              
36             # If you like prepare-execute cycles...
37             $driver->prepare($sql_obj);
38             if ($sql_obj->is_prepared()) {
39             $sql_obj->execute(@bindings); # Use output columns or callback to fetch results
40             $sql_obj->finish();
41             }
42              
43             # Or just get the DBI dbh and bang on it directly
44             my $dbi_dbh = $driver->dbi_dbh();
45              
46             =cut
47              
48             =for vaporware
49              
50             # (normally in autocommit mode)
51             if ($driver->supports_transactions()) {
52             $driver->begin_transaction();
53              
54             # Do things....
55              
56             if ($driver->is_in_transaction()) { ... }
57              
58             if (...) {
59             $driver->commit_transaction();
60             } else {
61             $driver->rollback_transaction();
62             }
63             # Returns to autocommit after a call to either commit or rollback
64              
65             }
66              
67             =cut
68              
69             =pod
70              
71             # ---- DB MetaData and SQL String Utils ----
72             $field2column_map = $driver->read_fields($schema, $table); # May be cached
73             @columns = $driver->find_primary_key_columns($schema, $table); # May be cached
74             $char = $driver->open_quote();
75             $char = $driver->close_quote();
76             $char = $driver->name_separator();
77             $str = $driver->table_case($name);
78             $str = $driver->schema_case($name);
79             $str = $driver->column_case($name);
80              
81             =head1 DESCRIPTION
82              
83             The Driver facility provides RDBMS-specific dialect support. In other words, high-level methods (like reading a list of fields in a table) are available via any Driver, while behind the scenes a Driver subclass is speaking a particular dialect to your database handle.
84              
85             Drivers provide five major groups of functionality:
86              
87             =over
88              
89             =item Database metadata access
90              
91             List columns, keys, etc. This area is immature.
92              
93             =item SQL Generation
94              
95             Transform a CRO SQL object into a SQL string in the driver's dialect. See render()
96              
97             =item SQL Execution
98              
99             Using the Class::ReluctantORM::DBH, execute SQL strings on the database and retrieve the results. See prepare()
100              
101             =item SQL Parsing
102              
103             Transform a SQL string in the Driver's dialect into a CRO SQL object for later manipulation. Such support is just out of its infancy - call it toddler-dom. See supports_parsing(), parse_statement(), and parse_where().
104              
105             =item Monitor Support
106              
107             Drivers are the internal attachment point for Monitors, which are used to track database access.
108              
109             =back
110              
111             This page documents the Driver superclass, which specifies the API provided by all drivers. See the individual drivers to learn more about their idiosyncracies (eg Class::ReluctantORM::Driver::PostgreSQL).
112              
113             Each Class::ReluctantORM subclass has its own Driver (this allows different classes to originate from different databases). It is expected that you will re-use database handles across multiple drivers, however.
114              
115             =head1 METHODS TO OVERRIDE WHEN SUBCLASSING
116              
117             When creating an RDBMS-specific subclass, you will need to override the following methods:
118              
119             =over
120              
121             =item aptitude
122              
123             =item init
124              
125             =item read_fields
126              
127             =item find_primary_key_columns
128              
129             =item run_sql
130              
131             =item execute_fetch_deep
132              
133             =item render
134              
135             =item prepare
136              
137             =item supports_parsing
138              
139             =item parse_statement
140              
141             =item parse_where
142              
143             =item parse_order_by
144              
145             =back
146              
147             =cut
148              
149 1     1   5 use strict;
  1         2  
  1         31  
150 1     1   6 use warnings;
  1         3  
  1         24  
151 1     1   1358 use DBI::Const::GetInfoType;
  1         8973  
  1         183  
152 1     1   10 use Scalar::Util qw(blessed weaken);
  1         1  
  1         116  
153 1     1   9 use Class::ReluctantORM::Utilities qw(conditional_load_subdir);
  1         2  
  1         53  
154 1     1   5 use Data::Dumper;
  1         2  
  1         79  
155              
156 1     1   800 use Class::ReluctantORM::Monitor;
  1         4  
  1         8  
157 1     1   726 use Class::ReluctantORM::SQL::Aliases;
  1         3  
  1         157  
158              
159             our $DEBUG = 0;
160              
161             our @DRIVERS;
162             BEGIN {
163 1     1   5 @DRIVERS = conditional_load_subdir(__PACKAGE__);
164             }
165              
166              
167             =head1 INSTANCE METHODS
168              
169             =cut
170              
171             =head2 $dbh = $driver->cro_dbh();
172              
173             Returns the Class::ReluctantORM::DBH object that provides low-level connectivity.
174              
175             =cut
176              
177             sub cro_dbh {
178 0     0 1   my $self = shift;
179              
180 0 0         if ($self->{db_class}) {
181             # Call new() and return whatever we get back - this allows for connection pooling
182 0           return $self->{db_class}->new();
183             } else {
184             # Bare handle?
185 0           return $self->{dbh};
186             }
187             }
188              
189             =head2 $dbh = $driver->dbi_dbh();
190              
191             Returns a DBI database handle. If the Class::ReluctantORM::DBH object is not based on DBI, this will be undef.
192              
193             =cut
194              
195             sub dbi_dbh {
196 0     0 1   my $self = shift;
197 0           return $self->cro_dbh()->dbi_dbh();
198             }
199              
200             # awful old name
201 0     0 0   sub dbh { return $_[0]->cro_dbh(); }
202              
203             # TODO DOCS
204 0     0 0   sub open_quote { return '"'; }
205 0     0 0   sub close_quote { return '"'; }
206 0     0 0   sub name_separator { return '.'; }
207              
208             =head2 $bool = $driver->supports_namespaces()
209              
210             Returns true if the driver supports namespaces (schemae - containers within a database for tables). If so, you can use name_separator to construct fully qualified table names.
211              
212             Default implementation returns false.
213              
214             =cut
215              
216 0     0 1   sub supports_namespaces { return 0; }
217              
218              
219              
220             #==================================================================#
221             # Protected Methods
222             #==================================================================#
223              
224             sub _install_dbh_error_trap {
225 0     0     my $inv = shift;
226 0           my $dbh = shift;
227 0           $dbh->set_handle_error(Class::ReluctantORM::Exception->make_db_error_handler());
228             }
229              
230             sub _new {
231 0     0     my $class = shift;
232 0           my ($tb_class, $dbh, $dbclass, $brand, $version) = @_;
233 0           my $self = bless {
234             tb_class => $tb_class,
235             dbh => $dbh,
236             db_class => $dbclass,
237             brand => $brand,
238             version => $version,
239             monitors => [],
240             }, $class;
241 0 0         if ($dbh) { weaken($self->{dbh}); }
  0            
242 0           $self->init();
243 0           return $self;
244             }
245              
246             #==================================================================#
247             # DRIVER SETUP
248             #==================================================================#
249              
250              
251             =head1 DRIVER SETUP METHODS
252              
253             =cut
254              
255             =head2 $driver = Class::ReluctantORM::Driver->make_driver($cro_class, $dbhandle, $dbclass);
256              
257             Searches for the best available driver for the given database handle. The Class::ReluctantORM subclass name is passed for advisory purposes to the underlying driver subclass.
258              
259             =cut
260              
261             my %DBMS_INFO_CACHE;
262              
263             sub make_driver {
264 0     0 1   my $class = shift;
265 0           my $cro_class = shift;
266 0           my $dbh = shift;
267 0           my $dbclass = shift;
268              
269 0           $dbh = Class::ReluctantORM::DBH->_boost_to_cro_dbh($dbh);
270 0           $class->_install_dbh_error_trap($dbh);
271 0   0       my $brand = $DBMS_INFO_CACHE{$dbh->dbi_dbh}{SQL_DBMS_NAME} ||= $dbh->get_info($GetInfoType{SQL_DBMS_NAME}); # Expensive call, which will not change for the specific dbh
272 0   0       my $version = $DBMS_INFO_CACHE{$dbh->dbi_dbh}{SQL_DBMS_VER} ||= $dbh->get_info($GetInfoType{SQL_DBMS_VER}); # Expensive call, which will not change for the specific dbh
273              
274 0           my @scores = sort { $b->[1] <=> $a->[1] } map { [ $_, $_->aptitude($brand, $version) ] } @DRIVERS;
  0            
  0            
275              
276 0 0         if ($DEBUG > 1) {
277 0           print STDERR __PACKAGE__ . ':' . __LINE__ . " - have make_driver scores for $brand, $version:\n" . Dumper(\@scores);
278             }
279              
280 0 0         if ($scores[0][1] < 0.5) {
281 0           warn("For database $brand $version, no suitable Class::ReluctantORM::Driver could be found. Using $scores[0][0] (score: $scores[0][1])");
282             }
283              
284 0           my $impl_class = $scores[0][0];
285 0           my $self = $impl_class->_new($cro_class, $dbh, $dbclass, $brand, $version);
286              
287 0           return $self;
288             }
289              
290             =head2 $n = $class->aptitude($brand, $version);
291              
292             Should return a number between 0 and 1 indicating how well the
293             driver can handle the given type of database server. Scores less than 0.5
294             are considered ill-equipped. The highest-scoring driver will be used.
295              
296             Default implementation returns 0.
297              
298              
299             =cut
300              
301 0     0 1   sub aptitude { return 0; }
302              
303             =head2 $driver->init();
304              
305             Called with no args just after driver construction. The dbh is
306             available at this point.
307              
308             Default implementation does nothing.
309              
310             =cut
311              
312 0     0 1   sub init {}
313              
314              
315             #==================================================================#
316             # DB METADATA
317             #==================================================================#
318              
319             =head1 DATABASE METADATA METHODS
320              
321             =cut
322              
323             =head2 $version_string = $driver->server_brand();
324              
325             Returns the vendor-specific brand name (cached from Driver creation, the result of a DBI get_info SQL_DBMS_NAME call).
326              
327             =cut
328              
329             sub server_brand {
330 0     0 1   my $driver = shift;
331 0           return $driver->{brand};
332             }
333              
334             =head2 $version_string = $driver->server_version();
335              
336             Returns the vendor-specific version string (cached from Driver creation, the result of a DBI get_info SQL_DBMS_VER call).
337              
338             =cut
339              
340             sub server_version {
341 0     0 1   my $driver = shift;
342 0           return $driver->{version};
343             }
344              
345              
346              
347             =head2 $field2column_map = $driver->read_fields($schema_name, $table_name);
348              
349             Scans the associated table for columns, and returns a hash mapping
350             lowercased field named to database case column names.
351              
352             This data may be cached. See the Class::ReluctantORM global option schema_cache_policy, and the Class::ReluctantORM::SchemaCache .
353              
354             Default implementation throws a PureVirtual exception. You must override this method.
355              
356             =cut
357              
358 0     0 1   sub read_fields { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
359              
360             =head2 @cols = $driver->find_primary_key_columns();
361              
362             Returns a list of columns used in the table's primary key. This is
363             usually a one-item list.
364              
365             This data may be cached. See the Class::ReluctantORM global option schema_cache_policy, and Class::ReluctantORM::SchemaCache .
366              
367             Default implementation throws a PureVirtual exception. You must override this method.
368              
369             =cut
370              
371 0     0 1   sub find_primary_key_columns { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
372              
373             =head2 $str = $driver->schema_case($str);
374              
375             =head2 $str = $driver->table_case($str);
376              
377             =head2 $str = $driver->column_case($str);
378              
379             Adjusts the given string to be in the case expected by the driver for the given object type.
380              
381             Default implementation is to lowercase everything.
382              
383             =cut
384              
385 0     0 1   sub schema_case { return lc($_[1]); }
386 0     0 1   sub table_case { return lc($_[1]); }
387 0     0 1   sub column_case { return lc($_[1]); }
388              
389             #==================================================================#
390             # SQL EXECUTION
391             #==================================================================#
392              
393             =head1 SQL EXECUTION SUPPORT
394              
395             These methods provide the rendering and execution capabilities of the driver.
396              
397             =head2 Execution Hinting
398              
399             Most of the execution support methods accept an optional hashref that provides hints for the driver. Currently supported hints:
400              
401             =over
402              
403             =item already_transformed
404              
405             Boolean, default false. If true, indicates that the given SQL object has already been through its transformation phase, and should not be trnasformed again.
406              
407             =back
408              
409             Specific drivers may extend this list.
410              
411             =cut
412              
413              
414              
415             =head2 $driver->run_sql($sql, $hints);
416              
417             Executes the given SQL object. The SQL object then contains
418             the results; you can also use the $sql->add_fetchrow_listener() method to add a hook.
419              
420             The 'hints' arg is optional, and is a hashref as specified in EXECUTION HINTING.
421              
422             A reasonable default implementation is provided.
423              
424             =cut
425              
426             sub run_sql {
427 0     0 1   my $driver = shift;
428 0           my $sql = shift;
429 0           my $hints = shift;
430              
431 0           $driver->prepare($sql, $hints);
432 0           my $sth = $sql->_sth();
433 0           my $str = $sql->_sql_string();
434              
435             # OK, run the query
436 0           my @binds = $sql->get_bind_values();
437 0           $driver->_monitor_execute_begin(sql_obj => $sql, sql_str => $str, binds => \@binds, sth => $sth);
438 0           eval {
439 0           $sth = $driver->cro_dbh->execute($sth, @binds);
440             };
441 0 0         if ($@) {
442             # Rethrow so we have CRO tracing
443 0           Class::ReluctantORM::SchemaCache->instance->notify_sql_error($@);
444 0           Class::ReluctantORM::Exception::SQL::ExecutionError->croak(error => $@);
445             }
446 0           $driver->_monitor_execute_finish(sql_obj => $sql, sql_str => $str, binds => \@binds, sth => $sth);
447              
448             # Fetch the result, if any
449 0 0         if ($sql->output_columns) {
450 0           while (my $row = $sth->fetchrow_hashref()) {
451 0           $driver->_monitor_fetch_row(sql_obj => $sql, sql_str => $str, binds => \@binds, sth => $sth, row => $row);
452 0           $sql->set_single_row_results($row);
453             }
454             }
455 0           $sth->finish();
456 0           $driver->_monitor_finish(sql_obj => $sql, sql_str => $str, sth => $sth);
457              
458 0           return 1;
459             }
460              
461              
462             =head2 @results = $driver->execute_fetch_deep($sql_obj);
463              
464             Renders, executes, and parses the results of a "fetch deep" style SQL query. Internally, this may be the same as run_sql; but some drivers may need to perform extra transformations for fetch deep (to allow use of LIMIT clauses, for example).
465              
466             =cut
467              
468 0     0 1   sub execute_fetch_deep { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
469              
470             =head2 $str = $driver->render($sql_obj, $hints);
471              
472             Takes a Class::ReluctantORM::SQL object, and renders it down to a SQL string.
473              
474             The 'hints' arg is optional, and is a hashref as specified in EXECUTION HINTING.
475              
476             =cut
477              
478 0     0 1   sub render { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
479              
480             =head2 $sth = $driver->prepare($sql_obj, $hints);
481              
482             Renders the SQL object to a SQL string, then passes it through the underlying DBI dbh, and returns the resulting statement handle. You can then use either $sql_obj->execute() or if you prefer a lower-level approach, you can operate directly on the statement handle.
483              
484             The 'hints' arg is optional, and is a hashref as specified in EXECUTION HINTING.
485              
486             A reasonable default implementation is provided.
487              
488             =cut
489              
490             sub prepare {
491 0     0 1   my $driver = shift;
492 0           my $sql = shift;
493 0           my $hints = shift;
494              
495 0           $driver->render($sql);
496 0           my $sth = $driver->cro_dbh->prepare($sql->_sql_string());
497 0           $sql->_sth($sth);
498              
499 0           return $sth;
500             }
501              
502 0     0     sub _pre_execute_hook { }
503 0     0     sub _post_execute_hook { }
504              
505             #==================================================================#
506             # TRANSACTION SUPPORT
507             #==================================================================#
508              
509             # TODO - transaction support
510              
511             #==================================================================#
512             # PARSING SUPPORT
513             #==================================================================#
514              
515             =head2 $bool = $driver->supports_parsing();
516              
517             Returns true if you can call parse_statement() or parse_where() and expect it to possibly work.
518              
519             Default implementation returns false.
520              
521             =cut
522              
523 0     0 1   sub supports_parsing { return 0; }
524              
525             =head2 $sql_obj = $driver->parse_statement($sql_string, $options);
526              
527             EXERIMENTAL
528              
529             Examines the $sql_string, and builds a Class::ReluctantORM::SQL object that semantically represents the statement. Syntax details will not be preserved - you can't round-trip this.
530              
531             If a problem occurs, an exception will be thrown, either Class::ReluctantORM::Exception::SQL::ParseError (your fault for sending garbage) or Class::ReluctantORM::Exception::SQL::TooComplex (our fault for having a weaksauce parser).
532              
533             $options is an optional hashref that contains parsing options or hints for the parser. It may be dialect specific - see your driver subclass for details.
534              
535             =cut
536              
537 0     0 1   sub parse_statement { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
538              
539             =head2 $where_obj = $driver->parse_where($where_clause_string, $options);
540              
541             EXERIMENTAL
542              
543             Examines the $where_clause_string, and builds a Class::ReluctantORM::SQL::Where object that semantically represents the WHERE clause. You should not include the word WHERE.
544              
545             Other details as for parse_statement.
546              
547             =cut
548              
549 0     0 1   sub parse_where { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
550              
551             =head2 $order_by_obj = $driver->parse_order_by($order_clause_string, $options);
552              
553             EXERIMENTAL
554              
555             Examines the $order_clause_string, and builds a Class::ReluctantORM::SQL::OrderBy object that semantically represents the clause. You should not include the words ORDER BY.
556              
557             Other details as for parse_statement.
558              
559             A default implementation is provided. It's stupid - it only allows column names and sort directions, no expressions.
560              
561             =cut
562              
563             sub parse_order_by {
564 0     0 1   my $driver = shift;
565 0   0       my $sql = shift || '';
566              
567             # Permit (but ignore) any ORDER BY prefix
568 0           $sql =~ s/^ORDER BY\s+//i;
569              
570             # Split on commas
571 0           my @stanzas = split /\s*,\s*/, $sql;
572 0           my $ob = OrderBy->new();
573 0           my %permitted = map { $_ => 1 } qw(asc ASC desc DESC);
  0            
574 0           foreach my $stanza (@stanzas) {
575 0           $stanza =~ s/^\s+//; # strip leading spaces
576 0           $stanza =~ s/\s+$//; # strip trailing spaces
577 0           my ($col_name, $dir) = split /\s+/, $stanza;
578 0 0 0       if ($dir && !exists $permitted{$dir}) {
579 0           Class::ReluctantORM::Exception::SQL::ParseError->croak(
580             error => "Could not parse '$stanza' in order by clause",
581             sql => $sql,
582             );
583             }
584 0           my $col = $driver->parse_column($col_name);
585 0           $ob->add($col, $dir);
586             }
587 0           return $ob;
588             }
589              
590              
591             =begin devdocs
592              
593             =head2 $col = driver->parse_column($str);
594              
595             Parses a string token (a column name) into a Column. This is used for parsing things like "schema"."table"."column" .
596              
597             =cut
598              
599             sub parse_column {
600 0     0 1   my $driver = shift;
601 0           my $str = shift;
602 0           my ($oq, $cq, $ns) =
603             ( $driver->open_quote, $driver->close_quote, $driver->name_separator);
604              
605 0           my @parts = split '\\' . $ns, $str;
606 0           for (@parts) {
607 0           s{^$oq}{};
608 0           s{$cq$}{};
609             }
610              
611 0           my ($colname, $table, $schema) = reverse @parts;
612 0 0         my $col = Column->new(
613             table => ($table ? Table->new(table => $table, schema => $schema) : undef),
614             column => $colname,
615             );
616 0           return $col;
617              
618             }
619              
620              
621              
622             #==================================================================#
623             # MONITOR SUPPORT
624             #==================================================================#
625              
626             =head1 MONITOR SUPPORT
627              
628             These methods allow the Driver to integrate with the Monitor system.
629              
630             =cut
631              
632             =head2 $driver->install_monitor($mon);
633              
634             Adds a monitor to the driver. $mon should be a Class::ReluctantORM::Monitor.
635              
636             Note that monitors may be added on a system-wide basis by calling Class::ReluctantORM::install_global_monitor().
637              
638             =cut
639              
640             sub install_monitor {
641 0     0 1   my $driver = shift;
642 0           my $mon = shift;
643 0 0         unless ($mon) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'monitor'); }
  0            
644 0 0 0       unless (blessed($mon) && $mon->isa('Class::ReluctantORM::Monitor')) {
645 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'monitor', expected => 'Class::ReluctantORM::Monitor', value => $mon);
646             }
647              
648 0           push @{$driver->{monitors}}, $mon;
  0            
649 0           return 1;
650             }
651              
652             =head2 $driver->monitors();
653              
654             Returns the list of monitors for this driver. The list includes monitors specific to this driver, as well as global monitors.
655              
656             =cut
657              
658             sub monitors {
659 0     0 1   my $driver = shift;
660             return (
661 0           @{$driver->{monitors}},
  0            
662             Class::ReluctantORM->global_monitors(),
663             );
664             }
665              
666             =head2 $driver->driver_monitors();
667              
668             Returns the list of monitors for this driver, excluding global monitors.
669              
670             =cut
671              
672             sub driver_monitors {
673 0     0 1   my $driver = shift;
674 0           return @{$driver->{monitors}},
  0            
675             }
676              
677             =head2 $driver->remove_driver_monitors();
678              
679             Removes all monitors on this driver. Global monitors are not affected.
680              
681             =cut
682              
683             sub remove_driver_monitors {
684 0     0 1   my $driver = shift;
685 0           $driver->{monitors} = [];
686             }
687              
688             =head1 MONITORING EVENT METHODS
689              
690             Driver implementations should call these methods at the appropriate time as they work.
691              
692             All methods take named parameters. Each method lists its required arguments. The arguments are as follows:
693              
694             =over
695              
696             =item sql_obj
697              
698             The Class::ReluctantORM::SQL object being rendered.
699              
700             =item sql_str
701              
702             The rendered SQL string, ready for a prepare(). This will be in the driver's dialect.
703              
704             =item sth
705              
706             The DBI statement handle.
707              
708             =item binds
709              
710             An arrayref of arguments to DBI execute().
711              
712             =item row
713              
714             A hashref of data returned by a single row, as returned by $sth->fetchrow_hashref
715              
716             =back
717              
718             =cut
719              
720             =head2 $d->_monitor_render_begin(sql_obj => $so);
721              
722             Notifies the monitoring system that the driver has begun work to render the given SQL object.
723              
724             Arguments: sql_obj, original, untouched Class::ReluctantORM::SQL object.
725              
726             =cut
727              
728 0     0     sub _monitor_render_begin { __notify_monitors(@_, event => 'render_begin'); }
729              
730             =head2 $d->_monitor_render_transform(sql_obj => $so);
731              
732             Notifies the monitoring system that the driver has finished transforming the SQL object.
733              
734             Arguments: sql_obj, the post-transformation Class::ReluctantORM::SQL object.
735              
736             =cut
737              
738 0     0     sub _monitor_render_transform { __notify_monitors(@_, event => 'render_transform'); }
739              
740             =head2 $d->_monitor_render_finish(sql_obj => $so, sql_str => $ss);
741              
742             Notifies the monitoring system that the driver has finished rendering the SQL object.
743              
744             =cut
745              
746 0     0     sub _monitor_render_finish { __notify_monitors(@_, event => 'render_finish'); }
747              
748             =head2 $d->_monitor_execute_begin(sql_obj => $so, sql_str => $ss, sth =>$sth, binds => \@binds);
749              
750             Notifies the monitoring system that the driver is about to perform a DBI execute.
751              
752             =cut
753              
754 0     0     sub _monitor_execute_begin { __notify_monitors(@_, event => 'execute_begin'); }
755              
756             =head2 $d->_monitor_execute_finish(sql_obj => $so, sql_str => $ss, sth =>$sth, binds => \@binds);
757              
758             Notifies the monitoring system that the driver has returned from performing a DBI execute.
759              
760             =cut
761              
762 0     0     sub _monitor_execute_finish { __notify_monitors(@_, event => 'execute_finish'); }
763              
764             =head2 $d->_monitor_fetch_row(sql_obj => $so, sql_str => $ss, sth =>$sth, binds => \@binds, row => \%row);
765              
766             Notifies the monitoring system that the driver has returned from performing a DBI fetchrow.
767              
768             =cut
769              
770 0     0     sub _monitor_fetch_row { __notify_monitors(@_, event => 'fetch_row'); }
771              
772             =head2 $d->_monitor_finish(sql_obj => $so, sql_str => $ss, sth => $sth);
773              
774             Notifies the monitoring system that the driver has finished the query.
775              
776             =cut
777              
778 0     0     sub _monitor_finish { __notify_monitors(@_, event => 'finish'); }
779              
780             sub __notify_monitors {
781 0     0     my $driver = shift;
782 0           my %args = @_;
783 0           $args{driver} = $driver;
784 0           my $event = $args{event};
785 0           delete $args{event};
786 0           $event = 'notify_' . $event;
787              
788 0           foreach my $monitor ($driver->monitors) {
789 0           $monitor->$event(%args);
790             }
791              
792 0           return 1;
793             }
794              
795              
796              
797              
798              
799             1;