File Coverage

blib/lib/ORM/Db/DBI.pm
Criterion Covered Total %
statement 250 349 71.6
branch 75 144 52.0
condition 18 43 41.8
subroutine 23 35 65.7
pod 0 24 0.0
total 366 595 61.5


line stmt bran cond sub pod time code
1             #
2             # DESCRIPTION
3             # PerlORM - Object relational mapper (ORM) for Perl. PerlORM is Perl
4             # library that implements object-relational mapping. Its features are
5             # much similar to those of Java's Hibernate library, but interface is
6             # much different and easier to use.
7             #
8             # AUTHOR
9             # Alexey V. Akimov
10             #
11             # COPYRIGHT
12             # Copyright (C) 2005-2006 Alexey V. Akimov
13             #
14             # This library is free software; you can redistribute it and/or
15             # modify it under the terms of the GNU Lesser General Public
16             # License as published by the Free Software Foundation; either
17             # version 2.1 of the License, or (at your option) any later version.
18             #
19             # This library is distributed in the hope that it will be useful,
20             # but WITHOUT ANY WARRANTY; without even the implied warranty of
21             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22             # Lesser General Public License for more details.
23             #
24             # You should have received a copy of the GNU Lesser General Public
25             # License along with this library; if not, write to the Free Software
26             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27             #
28              
29             package ORM::Db::DBI;
30              
31             $VERSION = 0.83;
32              
33 4     4   13586 use DBI;
  4         81543  
  4         387  
34 4     4   105 use base 'ORM::Db';
  4         7  
  4         3043  
35 4     4   2676 use ORM::Db::DBIResultSet;
  4         12  
  4         101  
36 4     4   2123 use ORM::Db::DBIResultSetFull;
  4         14  
  4         20102  
37              
38             ## use: $db = $class->new
39             ## (
40             ## host => string,
41             ## database => string,
42             ## options => string,
43             ## user => string,
44             ## password => string,
45             ##
46             ## delayed_connect => boolean,
47             ## connect_retries => integer,
48             ## retry_sleep => integer,
49             ## )
50             ##
51             ## 'retry_sleep' in seconds.
52             ##
53             sub new
54             {
55 4     4 0 12 my $class = shift;
56 4         11 my $self = {};
57 4         19 my %arg = @_;
58 4         8 my $data_source;
59              
60 4 50       22 $self->{connect_retries} = defined $arg{connect_retries} ? int( $arg{connect_retries} ) : 3;
61 4 50       17 $self->{retry_sleep} = defined $arg{retry_sleep} ? int( $arg{retry_sleep} ) : 1;
62 4         82 $self->{delayed_connect} = $arg{delayed_connect};
63 4         10 $self->{database} = $arg{database};
64              
65 4 50       21 if( $arg{data_source} )
66             {
67 0         0 $data_source = $arg{data_source};
68             }
69             else
70             {
71 4 50       30 $data_source =
    50          
72             "DBI:$arg{driver}:$arg{database}"
73             . ($arg{host} ? ":$arg{host}" : '')
74             . ($arg{options} ? ";$arg{options}" : '');
75             }
76              
77 4         18 $self->{db_arg} = [ $data_source, $arg{user}, $arg{password} ];
78 4 50       23 $self->{db} = DBI->connect( @{$self->{db_arg}} ) unless( $arg{delayed_connect} );
  4         36  
79              
80 4         59174 return bless $self, $class;
81             }
82              
83             sub disconnect
84             {
85 0     0 0 0 my $self = shift;
86            
87 0         0 $self->{db} = undef;
88 0         0 $self->{delayed_connect} = 1;
89             }
90              
91 0     0 0 0 sub database { $_[0]->{database}; }
92              
93             sub count
94             {
95 2     2 0 23 my $self = shift;
96 2         16 my %arg = @_;
97 2         4 my $tjoin;
98             my $cond;
99              
100 2         14 $tjoin = ORM::Tjoin->new( class=>$arg{class}, all_tables=>1 );
101 2 50       17 $tjoin->merge( $arg{filter}->_tjoin ) if( $arg{filter} );
102 2         16 $tjoin->assign_aliases;
103              
104 2 50       7 if( $arg{filter} )
105             {
106 2         10 $cond = $arg{filter}->_sql_str( tjoin=>$tjoin );
107             }
108              
109 2   33     15 my $res = $self->select
110             (
111             error => $arg{error},
112             query =>
113             (
114             "SELECT count(DISTINCT "
115             . $self->qt( $tjoin->first_basic_table_alias ) . ".id) AS "
116             . $self->qi( 'count' ) . "\n"
117             . 'FROM ' . $tjoin->sql_table_list . "\n"
118             . ( $cond && "WHERE $cond\n" )
119             ),
120             );
121              
122 2 50       15 return $res ? $res->next_row->{count} : 0;
123             }
124              
125             sub select_base
126             {
127 3     3 0 16 my $self = shift;
128 3         30 my %arg = @_;
129              
130             # Prepare $tjoin object
131 3         18 my $tjoin = ORM::Tjoin->new( class=>$arg{class}, all_tables=>1 );
132              
133 3 100       13 if( $arg{data} )
134             {
135 1         2 for my $name ( keys %{$arg{data}} )
  1         6  
136             {
137 13 50       65 $tjoin->merge( $arg{data}{$name}->_tjoin ) if( defined $arg{data}{$name} );
138             }
139 1         3 for my $group_by ( @{$arg{group_by}} )
  1         5  
140             {
141 0 0 0     0 if( ref $group_by && UNIVERSAL::isa( $group_by, 'ORM::Metaprop' ) )
142             {
143 0         0 $tjoin->merge( $group_by->_tjoin );
144             }
145             }
146             }
147              
148 3 100       26 $tjoin->merge( $arg{order}->_tjoin ) if( $arg{order} );
149 3 50       28 $tjoin->merge( $arg{filter}->_tjoin ) if( $arg{filter} );
150 3 50       23 $tjoin->merge( $arg{post_filter}->_tjoin ) if( $arg{post_filter} );
151 3         15 $tjoin->assign_aliases;
152              
153             # Prepare WHERE statement for SQL query
154 3   33     27 my $cond = $arg{filter} && $arg{filter}->_sql_str( tjoin=>$tjoin );
155              
156             # Prepare HAVING statement for SQL query
157 3   33     18 my $having = $arg{post_filter} && $arg{post_filter}->_sql_str( tjoin=>$tjoin );
158              
159             # Prepare GROUP BY statement for SQL query
160 3         5 my $group_by;
161 3         6 for my $grp ( @{$arg{group_by}} )
  3         12  
162             {
163 0 0       0 $group_by .= ', ' if( $group_by );
164 0 0       0 if( UNIVERSAL::isa( $grp, 'ORM::Expr' ) )
165             {
166 0         0 $group_by .= $grp->_sql_str( tjoin=>$tjoin );
167             }
168             else
169             {
170 0         0 $group_by .= $self->qi( $grp );
171             }
172             }
173              
174             # Prepare ORDER statement for SQL query
175 3   66     19 my $order = $arg{order} && $arg{order}->sql_order_by( tjoin=>$tjoin );
176              
177             # Prepare SELECT statement for SQL query
178 3         7 my $select;
179 3 100       10 if( $arg{data} )
180             {
181 1         3 $select = '';
182 1         2 for my $alias ( keys %{$arg{data}} )
  1         7  
183             {
184 13 50       47 my $data = ref $arg{data}{$alias} ? $arg{data}{$alias} : ORM::Const->new( $arg{data}{$alias} );
185              
186 13 100       32 $select .= ",\n" if( $select );
187 13         37 $select .= ' ' . $data->_sql_str( tjoin=>$tjoin ) . ' AS ' . $self->qi( $alias );
188             }
189             }
190             else
191             {
192 2         10 $select = ' DISTINCT ' . $tjoin->sql_select_basic_tables;
193             }
194              
195             # Prepare LIMIT statement for SQL query
196 3         26 my $limit = $self->_sql_limit( $arg{page}, $arg{pagesize} );
197              
198             # Prepare query string and fetch data
199 3 50       19 my $query =
    50          
    50          
    100          
    100          
    50          
200             "SELECT\n$select\n"
201             . 'FROM ' . $tjoin->sql_table_list . "\n"
202             . ( $cond ? "WHERE\n $cond\n" : '' )
203             . ( $group_by ? "GROUP BY $group_by\n" : '' )
204             . ( $having ? "HAVING\n $having\n" : '' )
205             . ( $order ? "ORDER BY $order\n" : '' )
206             . ( $limit ? "$limit\n" : '' )
207             . ( $self->{ta} ? $self->_ta_select."\n" : '' );
208              
209 3         16 $self->select
210             (
211             tables => $tjoin->select_basic_tables,
212             query => $query,
213             error => $arg{error},
214             );
215             }
216              
217             sub select_full
218             {
219 1     1 0 19 my $self = shift;
220 1         6 my %arg = @_;
221 1         6 my $error = ORM::Error->new;
222 1         2 my $fullres;
223              
224 1         16 my $res = $self->select_base
225             (
226             class => $arg{class},
227             filter => $arg{filter},
228             order => $arg{order},
229             page => $arg{page},
230             pagesize => $arg{pagesize},
231             error => $error,
232             );
233              
234 1 50       5 unless( $error->fatal )
235             {
236 1         2 my %class2id;
237             my %id2data;
238 0         0 my %residual_tables;
239 0         0 my $residual;
240 0         0 my $residual_data;
241 0         0 my $data;
242              
243 1         11 $fullres = ORM::Db::DBIResultSetFull->new;
244              
245 1         6 while( $data = $res->next_row )
246             {
247 1         89 my $obj = $arg{class}->_cache->get( $data->{id}, 0 );
248 1 50       5 if( $obj )
249             {
250 1         4 $fullres->add_row( $obj );
251             }
252             else
253             {
254 0         0 $fullres->add_row( $data );
255 0 0       0 if( $data->{class} ne $arg{class} )
256             {
257 0         0 $class2id{ $data->{class} } .= $data->{id}.',';
258 0         0 $id2data{ $data->{id} } = $data;
259             }
260             }
261             }
262              
263 1         10 for my $inh_class ( keys %class2id )
264             {
265 0         0 $arg{class}->_load_ORM_class( $inh_class );
266 0         0 %residual_tables = ();
267 0         0 chop $class2id{ $inh_class };
268 0         0 for
269             (
270 0         0 my $i = scalar( @{$res->result_tables} );
271             $i < $inh_class->_db_tables_count;
272             $i++
273             )
274             {
275 0         0 $residual_tables{ $inh_class->_db_table( $i ) } = 1;
276             }
277 0 0       0 if( %residual_tables )
278             {
279 0         0 $residual = $self->select_tables
280             (
281             id => $class2id{ $inh_class },
282             tables => \%residual_tables,
283             error => $error,
284             );
285 0 0       0 last if( $error->fatal );
286 0         0 while( $residual_data = $residual->next_row )
287             {
288 0         0 $data = $id2data{ $residual_data->{id} };
289 0         0 for my $key ( keys %$residual_data )
290             {
291 0         0 $data->{$key} = $residual_data->{$key};
292             }
293             }
294             }
295             }
296             }
297              
298 1         6 $error->upto( $arg{error} );
299 1 50       4 return $error->fatal ? undef : $fullres;
300             }
301              
302             sub select_tables
303             {
304 7     7 0 14 my $self = shift;
305 7         32 my %arg = @_;
306 7         10 my @tables = keys %{$arg{tables}};
  7         30  
307              
308 7         15 my $fields_to_select = '';
309 7         11 my $tables_str = '';
310 7         11 my $inner_join = '';
311              
312 7         24 for( my $i=0; $i<@tables; $i++ )
313             {
314 8 100       28 if( ref $arg{tables}{$tables[$i]} eq 'HASH' )
315             {
316 1         3 for my $prop ( keys %{$arg{tables}{$tables[$i]}} )
  1         4  
317             {
318 1         5 $fields_to_select .= $self->qt($tables[$i]).'.'.$self->qf($prop).',';
319             }
320             }
321             else
322             {
323 7         28 $fields_to_select .= $self->qt( $tables[$i] ).'.*,';
324             }
325              
326 8         27 $tables_str .= $self->qt( $tables[$i] ).',';
327              
328 8 100       38 if( $i < $#tables )
329             {
330 1         4 $inner_join .= $self->qt($tables[$i]).".id=".$self->qt($tables[$i+1]).".id AND ";
331             }
332             }
333 7         19 chop $fields_to_select;
334 7         17 chop $tables_str;
335              
336 7 100       41 my $query =
337             'SELECT ' . $fields_to_select
338             . ' FROM ' . $tables_str
339             . ' WHERE ' . $inner_join . $self->qt( $tables[0] ).'.id IN ('.$arg{id}.')'
340             . ( $self->{ta} ? ' '.$self->_ta_select : '' );
341              
342 7         36 $self->select
343             (
344 7         17 tables => [ keys %{$arg{tables}} ],
345             query => $query,
346             error => $arg{error},
347             );
348             }
349              
350 1     1 0 31 sub select_stat { shift->select_base( @_ ); }
351              
352             sub insert_object
353             {
354 21     21 0 494 my $self = shift;
355 21         116 my %arg = @_;
356 21         55 my $obj = $arg{object};
357 21         51 my $obj_class = ref $obj;
358 21         125 my $id = $arg{id};
359 21         87 my $error = ORM::Error->new;
360 21         129 my $ta = $obj_class->new_transaction( error=>$error );
361              
362             # Insert new records into tables
363 21         142 my @table = $obj_class->_db_tables;
364 21         220 my %values;
365             my $i;
366              
367 21   66     135 for( $i=0; $i<@table && !$error->fatal; $i++ )
368             {
369 22         53 %values = ();
370              
371 22 100       63 if( $i == 0 )
372             {
373 21 50       63 $values{id} = $id if( $id );
374 21 100       462 $values{class} = $obj_class if( !$obj_class->_is_sealed );
375             }
376             else
377             {
378 1         4 $values{id} = $id;
379             }
380              
381 22         314 for my $field ( $obj_class->_db_table_fields( $table[$i] ) )
382             {
383 160         665 $values{$field} = $obj->_property_id( $field );
384             }
385              
386 22         143 my $rows_affected = $self->insert
387             (
388             table => $table[$i],
389             values => \%values,
390             error => $error,
391             );
392              
393 22 50       103 if( $rows_affected == 1 )
394             {
395 22 100 66     260 $id = $self->insertid if( $i==0 && ! $id );
396             }
397             else
398             {
399 0         0 $error->add_fatal
400             (
401             "Insert into table '$table[$i]' failed, $rows_affected rows affected"
402             );
403             }
404             }
405              
406 21         182 $error->upto( $arg{error} );
407 21         182 return $id;
408             }
409              
410             sub update_object
411             {
412 5     5 0 57 my $self = shift;
413 5         24 my %arg = @_;
414 5         11 my $obj = $arg{object};
415 5         11 my $obj_class = ref $obj;
416 5         19 my $error = ORM::Error->new;
417 5         57 my $ta = $obj_class->new_transaction( error=>$error );
418 5         13 my %table;
419              
420 5 50       24 unless( $error->fatal )
421             {
422 5         9 for my $prop ( keys %{$arg{values}} )
  5         15  
423             {
424 7         54 $table{ $obj_class->_prop2table($prop) }{ $prop } = $arg{values}{$prop};
425             }
426              
427 5         67 for my $table ( keys %table )
428             {
429 6         29 $self->update_object_part
430             (
431             object => $obj,
432             values => $table{ $table },
433             error => $error,
434             );
435             }
436             }
437              
438 5         26 $error->upto( $arg{error} );
439             }
440              
441             sub update_object_part
442             {
443 6     6 0 12 my $self = shift;
444 6         25 my %arg = @_;
445 6         13 my $obj = $arg{object};
446 6         11 my $obj_class = ref $obj;
447              
448 6         10 my $check_all_props = 0;
449 6         15 my $left_prop = (each %{$arg{values}})[0];
  6         212  
450 6         53 my $tjoin = ORM::Tjoin->new( class=>$obj_class, left_prop=>$left_prop, all_tables=>$arg{all_tables} );
451              
452 6         12 for my $prop ( keys %{$arg{values}} )
  6         24  
453             {
454 7 100       23 $check_all_props = 1 if( ! ref $arg{values}{$prop} );
455 7 100       45 if( UNIVERSAL::isa( $arg{values}{$prop}, 'ORM::Expr' ) )
456             {
457 1         7 $tjoin->merge( $arg{values}{$prop}->_tjoin );
458             }
459             }
460 6         28 $tjoin->assign_aliases;
461              
462             # Prepare WHERE statement
463 6         11 my $where;
464 6         34 my $filter = ORM::Expr->_and( $obj->M->id == $obj->id );
465 6 100       55 if( $check_all_props )
466             {
467 5         7 for my $prop ( keys %{$arg{old_values}} )
  5         27  
468             {
469 0 0       0 $filter->add_expr
470             (
471             defined $arg{old_values}{$prop}
472             ? $obj->M->_prop( $prop ) == $arg{old_values}{$prop}
473             : $obj->M->_prop( $prop )->_is_undef
474             );
475             }
476             }
477 6         27 $where = $filter->_sql_str( tjoin=>$tjoin );
478              
479             # Prepare SET statement
480 6         17 my $set = '';
481 6         9 for my $prop ( keys %{$arg{values}} )
  6         22  
482             {
483 7 100       26 $set .=
484             $obj->M( $prop )->_sql_str( tjoin=>$tjoin )
485             . '='
486             . (
487             ( UNIVERSAL::isa( $arg{values}{$prop}, 'ORM::Expr' ) )
488             ? $arg{values}{$prop}
489             : ORM::Const->new( $arg{values}{$prop} )
490             )->_sql_str( tjoin=>$tjoin )
491             . ',';
492             }
493 6         20 chop $set;
494              
495 6         30 my $rows_affected = $self->do
496             (
497             error => $arg{error},
498             query =>
499             (
500             "UPDATE " . $tjoin->sql_table_list . "\n"
501             . " SET $set\n"
502             . " WHERE $where"
503             ),
504             );
505              
506 6 50       41 if( $rows_affected == 0 )
    50          
507             {
508 0 0       0 $arg{error} && $arg{error}->add_fatal
509             (
510             "Failed to update object with id#".$obj->id
511             . " of class '$obj_class', $rows_affected rows affected,"
512             . " may be object was updated elsewhere."
513             );
514             }
515             elsif( $rows_affected > $tjoin->tables_count )
516             {
517 0 0       0 $arg{error} && $arg{error}->add_fatal
518             (
519             "Internal error occured!"
520             . " More than expected number of rows was updated ($rows_affected)."
521             . " Please report to developer."
522             );
523             }
524             }
525              
526             sub delete_object
527             {
528 2     2 0 55 my $self = shift;
529 2         9 my %arg = @_;
530 2         5 my $obj = $arg{object};
531 2         6 my $obj_class = ref $obj;
532 2         8 my $error = ORM::Error->new;
533 2         11 my $ta = $obj_class->new_transaction( error=>$error );
534 2         11 my @table = $obj_class->_db_tables;
535 2         24 my $rows_affected;
536              
537 2         16 $self->check_object_referers
538             (
539             object => $obj,
540             error => $error,
541             check => $arg{emulate_foreign_keys},
542             );
543              
544 2 50       9 unless( $error->fatal )
545             {
546 2   66     18 for( $i=$#table; $i>=0 && !$error->fatal; $i-- )
547             {
548 3         15 my $rows_affected = $self->delete_by_id
549             (
550             table => $table[$i],
551             id => $obj->id,
552             error => $error,
553             );
554 3 50       24 if( $rows_affected != 1 )
555             {
556 0         0 $error->add_fatal( "Failed to delete row with id#$id from '$table[$i]' during object delete" );
557             }
558             }
559              
560             # must check twise, new referers could be created during deletion
561 2 50       9 unless( $error->fatal )
562             {
563 2         10 $self->check_object_referers
564             (
565             object => $obj,
566             error => $error,
567             check => $arg{emulate_foreign_keys},
568             );
569             }
570             }
571              
572 2         8 $error->upto( $arg{error} );
573             }
574              
575             sub optimize_tables
576             {
577 0     0 0 0 my $self = shift;
578 0         0 my %arg = @_;
579              
580 0         0 $self->do
581             (
582             query => 'OPTIMIZE TABLE '.$arg{class}->_db_tables_str,
583             error => $arg{error},
584             );
585             }
586              
587             sub referencing_classes
588             {
589 9     9 0 105 my $self = shift;
590 9         36 my %arg = @_;
591 9         38 my $error = ORM::Error->new;
592 9         17 my $res;
593             my $data;
594 0         0 my @res;
595              
596 9         41 $res = $self->select
597             (
598             error => $error,
599             query =>
600             'SELECT class,prop FROM '.$self->qt('_ORM_refs').' WHERE ref_class='
601             . $self->qc( $arg{class} )
602             );
603              
604 9 50       96 unless( $error->fatal )
605             {
606 9         38 while( $data = $res->next_row )
607             {
608 2         18 push @res, $data;
609             }
610             }
611              
612 9         48 $error->upto( $arg{error} );
613 9         681 return @res;
614             }
615              
616             sub begin_transaction
617             {
618 0     0 0 0 my $self = shift;
619 0         0 my %arg = @_;
620 0         0 my $error = ORM::Error->new;
621              
622 0         0 $self->{ta} = 1;
623 0         0 $self->_db_handler->begin_work();
624 0 0       0 $error->add_fatal( $self->_db_handler->errstr ) if( $self->_db_handler->err );
625 0         0 ORM::DbLog->new( sql=>"BEGIN", error=>$error->text );
626              
627 0         0 $error->upto( $arg{error} );
628             }
629              
630             sub commit_transaction
631             {
632 0     0 0 0 my $self = shift;
633 0         0 my %arg = @_;
634 0         0 my $error = ORM::Error->new;
635              
636 0         0 delete $self->{ta};
637 0         0 $self->_db_handler->commit();
638 0 0       0 $error->add_fatal( $self->_db_handler->errstr ) if( $self->_db_handler->err );
639 0         0 ORM::DbLog->new( sql=>"COMMIT", error=>$error->text );
640              
641 0         0 $error->upto( $arg{error} );
642             }
643              
644             sub rollback_transaction
645             {
646 0     0 0 0 my $self = shift;
647 0         0 my %arg = @_;
648 0         0 my $error = ORM::Error->new;
649              
650 0         0 delete $self->{ta};
651 0 0       0 unless( $self->{lost_connection} )
652             {
653 0         0 $self->_db_handler->rollback();
654 0 0       0 $error->add_fatal( $self->_db_handler->errstr ) if( $self->_db_handler->err );
655 0         0 ORM::DbLog->new( sql=>"ROLLBACK", error=>$error->text );
656             }
657              
658 0         0 $error->upto( $arg{error} );
659             }
660              
661             ##
662             ## PROTECTED METHODS
663             ##
664              
665             ## use: $db->insert( table=>string, values=>hash, error=>ORM::Error )
666             ##
667             sub insert
668             {
669 22     22 0 46 my $self = shift;
670 22         107 my %arg = @_;
671 22         37 my $keys;
672             my $values;
673 0         0 my $table;
674              
675 22         599 $table = $self->qt( $arg{table} );
676 22         50 for my $key ( keys %{$arg{values}} )
  22         102  
677             {
678 163         437 $keys .= $self->qf( $key ) . ',';
679 163         619 $values .= $self->qc( $arg{values}{$key} ) . ',';
680             }
681 22         69 chop $keys;
682 22         40 chop $values;
683              
684 22         140 $self->do
685             (
686             query => "INSERT INTO $table ($keys) VALUES ($values)",
687             error => $arg{error},
688             );
689             }
690              
691             ## use: $db->delete_by_id
692             ## (
693             ## table => $string,
694             ## id => number,
695             ## error => ORM::Error,
696             ## )
697             ##
698             sub delete_by_id
699             {
700 3     3 0 7 my $self = shift;
701 3         48 my %arg = @_;
702              
703 3         15 $self->do
704             (
705             query => ( "DELETE FROM ".$self->qt($arg{table})." WHERE id=".$self->qc($arg{id}) ),
706             error => $arg{error},
707             );
708             }
709              
710             ## use: $db->do( query=>$string, error=>ORM::Error )
711             ##
712             sub do
713             {
714 61     61 0 527 my $self = shift;
715 61         245 $self->select( return_rows_count=>1, @_ );
716             }
717              
718             ## use: $result = $db->select
719             ## (
720             ## tables => ARRAY,
721             ## query => string,
722             ## error => ORM::Error,
723             ##
724             ## return_rows_count => 1,
725             ## );
726             ##
727             sub select
728             {
729 102     102 0 188 my $self = shift;
730 102         543 my %arg = @_;
731 102         396 my $error = ORM::Error->new;
732 102         422 my $h_error = ORM::Error->new;
733 102         253 my $retry = 1;
734 102         256 my $tries = $self->{connect_retries};
735 102         239 my $query = $arg{query};
736 102         158 my $rows_affected;
737             my $st;
738              
739 102         265 $self->{lost_connection} = 0;
740              
741 102         449 while( $retry )
742             {
743 102         168 $retry = 0;
744 102 50       406 if( ! $self->{db} )
745             {
746 0         0 $self->_db_reconnect;
747 0         0 $retry = $tries--;
748 0 0       0 if( $retry )
749             {
750 0 0       0 if( $self->{delayed_connect} )
751             {
752 0         0 delete $self->{delayed_connect};
753             }
754             else
755             {
756 0         0 print STDERR
757             "No connection, connecting to SQL server '"
758             . $self->{db_arg}[0]
759             . "' ($tries tries left)\n";
760             }
761 0         0 next;
762             }
763             else
764             {
765 0         0 $error->add_fatal( "No db connection" );
766 0         0 $self->{lost_connection} = 1;
767 0         0 last;
768             }
769             }
770              
771 102         1356 $st = $self->{db}->prepare( $query );
772 102         15451 $h_error = ORM::Error->new;
773             $st && ( $DBI::VERSION >= 1.21 ) &&
774             (
775             $st->{HandleError} = sub
776             {
777 0   0 0   0 $h_error->add_fatal( "DBI Error Handler($_[1],'".($_[2]||'')."'): $_[0]" );
778 0         0 return 1;
779             }
780 102 50 33     4923 );
781              
782 102 50       392 if( $st )
783             {
784 102         4138256 $st->execute;
785             }
786             else
787             {
788 0         0 $error->add_fatal( "Failed to execute query, 'prepare' returned undef, query='$query'" );
789             }
790              
791 102 50 33     2317 if( $st && $st->err && $self->_lost_connection( $st->err ) )
      33        
792             {
793 0         0 $self->_db_reconnect;
794 0         0 $retry = $tries--;
795 0         0 print STDERR
796             "Lost connection, reconnecting to SQL server '"
797             . $self->{db_arg}[0]
798             . "' ($tries tries left)\n";
799             }
800             }
801              
802             # Catch up errors
803 102 50 33     3575 if( $st && $st->err )
    50          
804             {
805 0         0 $error->add_fatal
806             (
807             'DBI Error '.$st->err.': ' . $st->errstr
808             . ', Query="' . $query . '"'
809             );
810             }
811             elsif( $h_error->any )
812             {
813 0         0 $error->add( error=>$h_error );
814             }
815              
816             ORM::DbLog->new
817             (
818 102         584 sql => $query,
819             error => $error->text,
820             );
821 102         582 $error->upto( $arg{error} );
822              
823 102 50 33     7862 return $arg{return_rows_count}
    50          
    100          
824             ? ( $st && $st->rows != 4294967294 ? $st->rows : 0 )
825             : (
826             $error->fatal
827             ? undef
828             : ORM::Db::DBIResultSet->new( result=>$st, tables=>$arg{tables} )
829             );
830             }
831              
832             sub ql
833             {
834 0     0 0 0 my $class = shift;
835 0         0 my $str = shift;
836              
837 0         0 $str =~ s/_/\\_/g;
838 0         0 $str =~ s/%/\\%/g;
839              
840 0         0 return $class->qc( $str );
841             }
842              
843 42     42   424 sub _db_handler { $_[0]->{db}; }
844              
845             sub _db_reconnect
846             {
847 0     0   0 my $self = shift;
848              
849 0         0 $self->{db} = undef;
850 0         0 sleep $self->{retry_sleep};
851 0         0 $self->{db} = DBI->connect( @{$self->{db_arg}} );
  0         0  
852             }
853              
854 0     0   0 sub _ta_select { 'FOR UPDATE'; }
855              
856             sub _sql_limit
857             {
858 3     3   6 my $self = shift;
859 3   100     16 my $page = (int shift)||1;
860 3         17 my $pagesize = int shift;
861 3         5 my $sql;
862              
863 3 100       9 if( $pagesize )
864             {
865 2         9 $sql = "LIMIT ".(($page-1)*$pagesize).",$pagesize";
866             }
867              
868 3         9 return $sql;
869             }
870              
871             sub check_object_referers
872             {
873 4     4 0 9 my $self = shift;
874 4         16 my %arg = @_;
875 4         7 my $obj = $arg{object};
876 4         11 my $obj_class = ref $obj;
877            
878 4 50       14 if( $arg{check} )
879             {
880 4         29 for my $ref ( $obj_class->_rev_refs )
881             {
882 2         31 my $referers = $ref->[0]->count
883             (
884             filter => ( $ref->[0]->M->_prop($ref->[1])==$obj->id ),
885             error => $arg{error},
886             );
887 2 50       81 if( $referers )
888             {
889 0         0 $arg{error}->add_fatal
890             (
891             "Can't delete instance ID#" . $obj->id
892             . " of '$obj_class', because there're "
893             . "$referers instances of '$ref->[0]' refer to it."
894             );
895             }
896             }
897             }
898              
899 4         16 return undef;
900             }
901              
902             ##
903             ## ABSTRACT METHODS
904             ##
905              
906             sub insertid
907             {
908 0     0 0 0 die "You forget to override 'insertid' in '$_[0]'";
909             }
910              
911             sub _lost_connection
912             {
913 0     0   0 die "You forget to override '_lost_connection' in '$_[0]'";
914             }
915              
916             ##
917             ## SQL FUNCTIONS
918             ##
919              
920 1     1   19 sub _func_concat { shift; ORM::Filter::Cmp->new( '||', @_ ); }
  1         9