File Coverage

blib/lib/DBIx/Fast.pm
Criterion Covered Total %
statement 392 422 92.8
branch 82 124 66.1
condition 9 24 37.5
subroutine 63 66 95.4
pod 33 34 97.0
total 579 670 86.4


line stmt bran cond sub pod time code
1             package DBIx::Fast;
2              
3 15     15   1658139 use v5.38;
  15         62  
4 15     15   12945 use Object::Pad;
  15         208888  
  15         79  
5              
6             our $VERSION = '0.16';
7              
8 15     15   26170 use DBI;
  15         492714  
  15         1696  
9 15     15   13629 use DBIx::Connector;
  15         162916  
  15         1766  
10 15     15   17590 use SQL::Abstract;
  15         653156  
  15         1696  
11 15     15   178 use Carp qw(croak);
  15         32  
  15         1083  
12 15     15   870 use Time::HiRes qw(time);
  15         2101  
  15         175  
13              
14 15     15   13927 use DBIx::Fast::Schema;
  15         59  
  15         1392  
15 15     15   10070 use DBIx::Fast::Transaction;
  15         479  
  15         1239  
16 15     15   10879 use DBIx::Fast::Profiler;
  15         70  
  15         3281  
17              
18             class DBIx::Fast {
19              
20             # Public accessor fields
21             field $db :accessor = undef;
22 23 50   23 1 2686 field $sql :accessor = undef;
  23         203  
23 15 100   15 1 14268 field $last_sql :accessor = undef;
  15         107  
24 1 50   1 1 613 field $p :accessor = undef;
  1         11  
25 4 50   4 1 876 field $Q :accessor = undef;
  4         42  
26 8 50   8 1 3528 field $results :accessor = undef;
  8         57  
27 5 50   5 1 34 field $last_id :accessor = undef;
  5         59  
28              
29             # Internal state with readers
30 6 50   6 1 6537 field $_args :reader(args) = undef;
  6         65  
31 9     9 1 1286 field $_dbd :reader(dbd) = undef;
  9         96  
32 16     16 1 48 field $_dsn :reader(dsn) = undef;
  16         58  
33 0     0 1 0 field $_errors :reader(errors) = [];
  0     3 1 0  
  3         1393  
34 3         18 field $_last_error :reader(last_error) = undef;
35              
36             # Lazy subsystems
37 1     1 1 570 field $_schema;
  1         5  
38             field $_transaction;
39             field $_profiler; # Driver-specific profile (MariaDB/SQLite)
40             field $_query_tracker; # Generic query profiler (DBIx::Fast::Profiler)
41             field %_extensions;
42              
43              
44             # Constructor params
45             field $_init_db :param(db) = undef;
46             field $_init_dsn :param(dsn) = '';
47             field $_init_SQLite :param(SQLite) = undef;
48             field $_init_driver :param(driver) = '';
49             field $_init_user :param(user) = '';
50             field $_init_password :param(password) = '';
51             field $_init_host :param(host) = '';
52             field $_init_tn :param(tn) = 0;
53             field $_init_quote :param(quote) = '';
54             field $_init_trace :param(trace) = '';
55             field $_init_profile :param(profile) = '';
56             field $_init_abstract :param(abstract) = 1;
57             field $_init_RaiseError :param(RaiseError) = undef;
58             field $_init_PrintError :param(PrintError) = undef;
59             field $_init_AutoCommit :param(AutoCommit) = undef;
60             field $_init_mysql_utf8 :param(mysql_enable_utf8) = 0;
61             field $_init_Error :param(Error) = undef; # Accepted for compat, use RaiseError/PrintError
62              
63             ADJUST {
64             # SQLite shortcut
65             if ($_init_SQLite) {
66             $_init_db //= $_init_SQLite;
67             $_init_driver = 'SQLite';
68             }
69              
70             # Build processed config
71             $_args = {
72             DBI => {
73             RaiseError => $_init_RaiseError // 1,
74             PrintError => $_init_PrintError // 0,
75             AutoCommit => $_init_AutoCommit // 1,
76             },
77             Auth => {
78             user => $_init_user,
79             password => $_init_password,
80             host => $_init_host,
81             },
82             tn => $_init_tn,
83             db => $_init_db // '',
84             dsn => $_init_dsn,
85             driver => $_init_driver,
86             quote => $_init_quote,
87             trace => $_init_trace,
88             profile => $_init_profile,
89             abstract => $_init_abstract,
90             };
91              
92             $_args->{DBI}->{mysql_enable_utf8} = 1 if $_init_mysql_utf8;
93              
94             # SQLite file check
95             if ($_init_SQLite) {
96             $self->Exception("No DB Found : $_init_SQLite")
97             unless -e $_init_SQLite;
98             }
99              
100             $Q = SQL::Abstract->new if $_args->{abstract};
101              
102             unless ($_args->{dsn} || $_args->{db}) {
103             $self->Exception("Need a DSN or Host");
104             }
105              
106             $_dsn = $_args->{dsn}
107             ? $self->_check_dsn($_args->{dsn})
108             : $self->_make_dsn($_args);
109              
110             # Handle db param: connector object or string
111             if ($_init_db && ref($_init_db)) {
112             $db = $_init_db;
113             }
114             else {
115             $db = DBIx::Connector->new(
116             $_dsn,
117             $_args->{Auth}->{user},
118             $_args->{Auth}->{password},
119             $_args->{DBI}
120             );
121             }
122              
123             $db->mode('ping');
124              
125             $db->dbh->quote($_args->{quote}) if $_args->{quote};
126              
127             $db->dbh->{HandleError} = sub {
128             $self->set_error($DBI::err, $DBI::errstr);
129             return 0; # Let DBI continue with RaiseError/PrintError
130             };
131              
132             $db->dbh->trace($_args->{trace}, 'dbix-fast-trace')
133             if $_args->{trace};
134              
135             $self->_profile($_args->{profile}) if $_args->{profile};
136              
137             $self->schema->_load_tables_name if $_args->{tn};
138             }
139              
140             # Setters for internal state (needed by tests and internal methods)
141 1     1   2 method _set_args ($val) { $_args = $val }
  1         4  
  1         2  
  1         1  
  1         6  
142 0     0   0 method _set_dbd ($val) { $_dbd = $val }
  0         0  
  0         0  
  0         0  
  0         0  
143 0     0   0 method _set_dsn ($val) { $_dsn = $val }
  0         0  
  0         0  
  0         0  
  0         0  
144              
145             # Lazy subsystem accessors
146             method schema { $_schema //= DBIx::Fast::Schema->new(dbix => $self) }
147             method transaction { $_transaction //= DBIx::Fast::Transaction->new(dbix => $self) }
148             method tracker {
149             $_query_tracker //= DBIx::Fast::Profiler->new(dbix => $self, auto_print => 0);
150             }
151              
152             method profiler {
153             return $_profiler if $_profiler;
154              
155             my $driver = $_dbd;
156             my $profile_class = "DBIx::Fast::Profile::$driver";
157              
158             (my $_pf = "$profile_class.pm") =~ s|::|/|g; eval { require $_pf };
159              
160             if ($@) {
161             warn "No profile support for $driver, using base profile";
162             require DBIx::Fast::Profile::Base;
163             $_profiler = DBIx::Fast::Profile::Base->new(dbix => $self);
164             return $_profiler;
165             }
166              
167             $_profiler = $profile_class->new(dbix => $self);
168             $_profiler->init();
169              
170             return $_profiler;
171             }
172              
173             # Transaction helper
174 6     6 1 2064 method txn ($code, $options = undef) {
  6         19  
  6         8  
  6         8  
  6         7  
175 6         21 return $self->transaction->do($code, $options);
176             }
177              
178 3     3 1 7224 method load_extension ($extension) {
  3         15  
  3         9  
  3         4  
179 3         9 my $module = "DBIx::Fast::$extension";
180              
181 3         20 (my $_mf = "$module.pm") =~ s|::|/|g; eval { require $_mf };
  3         9  
  3         579  
182 3 100       21 $self->Exception("Error: load_extension => $@") if $@;
183              
184 2         6 my $attr = lc($extension);
185              
186             # Check if there's already a method for this
187 2 50       22 if ($self->can($attr)) {
188 2         13 return $self->$attr;
189             }
190              
191 0 0       0 return $_extensions{$attr} if exists $_extensions{$attr};
192              
193 0         0 $_extensions{$attr} = $module->new(dbix => $self);
194 0         0 return $_extensions{$attr};
195             }
196              
197 15     15 1 9615 method now () {
  15         127  
  15         31  
198 15         645 my ($sec, $min, $hour, $mday, $mon, $year) = localtime;
199              
200 15         548 return sprintf(
201             "%04d-%02d-%02d %02d:%02d:%02d",
202             $year + 1900, $mon + 1, $mday, $hour, $min, $sec
203             );
204             }
205              
206 6     6 1 11 method set_error ($id, $error_msg) {
  6         17  
  6         22  
  6         30  
  6         10  
207 6         35 my $error = { id => $id, error => $error_msg, time => time() };
208              
209 6         9 push @{$_errors}, $error;
  6         12  
210              
211 6         59 $_last_error =
212             qq{$error->{time} - [$error->{id}] - $error->{error}};
213             }
214              
215             # DSN handling
216              
217 46     46   2521 method _Driver_dbd ($dbd_name) {
  46         120  
  46         78  
  46         68  
218 46 50       126 $self->Exception("Error DBD Driver") unless $dbd_name;
219              
220 46         117 for my $d (qw(SQLite Pg MariaDB mysql)) {
221 108 100       314 if (lc($dbd_name) eq lc($d)) {
222 37         67 $_dbd = $d;
223 37         182 last;
224             }
225             }
226              
227 46 50       169 $self->Exception("Error DBD Driver : $dbd_name") unless $_dbd;
228             }
229              
230 12     12   22 method _dsn_dbi ($dsn_str) {
  12         25  
  12         41  
  12         19  
231 12         66 my ($dbi, $driver, $db_part, $host) = split ':', $dsn_str;
232              
233 12 50       59 $self->Exception("DSN DBI: $dbi") unless $dbi eq 'dbi';
234 12 100       55 $self->Exception("DSN DataBase: $db_part") unless $db_part;
235              
236 11         45 $self->_Driver_dbd($driver);
237              
238 11         47 return $dsn_str;
239             }
240              
241 14     14   3833 method _check_dsn ($dsn_str) {
  14         54  
  14         92  
  14         23  
242 14 100       112 return $self->_dsn_dbi($dsn_str) if $dsn_str =~ /^dbi/;
243 2         12 return $self->_dsn_to_dbi($dsn_str);
244             }
245              
246 16     16   56 method _make_dsn ($args) {
  16         59  
  16         31  
  16         26  
247 16 50       60 $self->Exception("DSN Driver: Not defined") unless $args->{driver};
248              
249 16         101 $self->_Driver_dbd($args->{driver});
250              
251             return 'dbi:SQLite:dbname=' . $args->{db}
252 16 100       90 if $args->{driver} eq 'SQLite';
253              
254 3 50       7 $self->Exception("DSN Host: Not defined") unless $args->{host};
255 3 50       8 $self->Exception("DSN DB: Not defined") unless $args->{db};
256              
257             return
258             'dbi:'
259             . $_dbd
260             . ':database='
261             . $args->{db} . ':'
262 3         31 . $args->{host};
263             }
264              
265 17     17   20214 method _dsn_to_dbi ($dsn_str) {
  17         88  
  17         36  
  17         33  
266 17         27 my $URI;
267              
268             # SQLite
269 17 100       86 if ($dsn_str =~ /^sqlite:\/\/\/(.*)$/) {
270 2         38 $_dbd = 'SQLite';
271 2         21 return 'dbi:SQLite:dbname=' . $1;
272             }
273              
274 15         207 ($URI->{schema}, $URI->{UI}, $URI->{connect}, $URI->{db}) =
275             $dsn_str =~ m{^([^:]+)://([^@]+)@([^/]+)/(.+)$};
276              
277 15 100       63 $self->Exception("_dsn_to_dbi : schema") unless $URI->{schema};
278              
279 14         61 $self->_Driver_dbd($URI->{schema});
280 14 50       41 $self->Exception("_dsn_to_dbi : connect") unless $URI->{connect};
281              
282             $URI->{connect} =~ /:/
283             ? ($URI->{host}, $URI->{port}) = split ':', $URI->{connect}
284 14 100       124 : $URI->{host} = $URI->{connect};
285              
286             # UserInfo
287 14 100       50 if ($URI->{UI} =~ /:/) {
288 11         58 ($URI->{user}, $URI->{password}) = split ':', $URI->{UI};
289              
290 11         58 $_args->{Auth}->{user} = $URI->{user};
291 11         34 $_args->{Auth}->{password} = $URI->{password};
292             }
293             else {
294 3         14 $URI->{user} = $URI->{UI};
295             }
296              
297 14 50       40 $self->Exception('_dsn_to_dbi : No DB value') unless $URI->{db};
298              
299 14 100       68 if ($URI->{db} =~ s/^(.*)\?(.*)$/$1/) {
300 3         16 ($URI->{attribute}, $URI->{value}) = split '=', $2;
301             }
302              
303 14 100       86 if ($dsn_str =~ /^(postgres|postgresql):/) { $_dbd = 'Pg' }
  4 100       10  
    100          
304 3         8 elsif ($dsn_str =~ /^(mariadb):/) { $_dbd = 'MariaDB' }
305 3         9 elsif ($dsn_str =~ /^(mysql|mysqlx):/) { $_dbd = 'mysql' }
306 4         22 else { $self->Exception("_dsn_to_dbi : $dsn_str") }
307              
308             $URI->{DSN} = sprintf('dbi:%s:dbname=%s;host=%s%s',
309             $_dbd, $URI->{db}, $URI->{host},
310 10 50       77 $URI->{port} ? ";port=$URI->{port}" : ''
311             );
312              
313 10         111 return $URI->{DSN};
314             }
315              
316 1     1   4 method _profile ($stat) {
  1         8  
  1         4  
  1         2  
317 1         5 $stat .= "/DBI::ProfileDumper/";
318 1         9 $stat .= qq{File:dbix-fast-$$.log};
319              
320 1         8 $db->dbh->{Profile} = $stat;
321             }
322              
323             # Security: validate SQL identifier (table/column names)
324 22     22   26 method _safe_id ($name) {
  22         39  
  22         28  
  22         26  
325 22 50 33     209 $self->Exception("Invalid identifier")
326             unless defined $name && $name =~ /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
327 22         45 return $name;
328             }
329              
330             # Profiling helper - wraps execution with timing when tracker is active
331 111     111   214 method _track ($code) {
  111         418  
  111         173  
  111         129  
332 111 50       368 if ($_query_tracker) {
333 0         0 my $t0 = Time::HiRes::time();
334 0         0 my $res = $code->();
335 0   0     0 $_query_tracker->add_query($sql, $p // [], $t0, Time::HiRes::time());
336 0         0 $last_sql = $sql;
337 0         0 return $res;
338             }
339 111         199 $last_sql = $sql;
340 111         252 return $code->();
341             }
342              
343             # Query methods
344              
345 1     1 1 2055 method q ($stmt, @params) {
  1         6  
  1         3  
  1         3  
  1         3  
346 1         3 $sql = $stmt;
347 1         5 $p = \@params;
348             }
349              
350 3     3 1 5032 method all (@args) {
  3         20  
  3         12  
  3         7  
351 3         9 $sql = shift @args;
352 3         10 $p = \@args;
353              
354             $results = $self->_track(sub {
355 3     3   18 $db->dbh->selectall_arrayref($sql, { Slice => {} }, @{$p});
  3         386  
356 3         27 });
357 2         976 return $results;
358             }
359              
360 3     3 1 3985 method flat (@args) {
  3         18  
  3         12  
  3         7  
361 3         7 $sql = shift @args;
362 3         11 $p = \@args;
363              
364 3         8 my @Flat;
365             $self->_track(sub {
366 3     3   18 my $sth = $db->dbh->prepare($sql);
367 3         645 $sth->execute(@{$p});
  3         246  
368 3         49 while (my $row = $sth->fetchrow_array) {
369 15         105 push @Flat, $row;
370             }
371 3         62 1;
372 3         29 });
373              
374 3         23 $results = \@Flat;
375 3         19 return @Flat;
376             }
377              
378 6     6 1 2504 method hash (@args) {
  6         33  
  6         24  
  6         8  
379 6         30 $sql = shift @args;
380 6         86 $p = \@args;
381              
382             $results = $self->_track(sub {
383 6     6   37 my $sth = $db->dbh->prepare($sql);
384 6         1457 $sth->execute(@{$p});
  6         496  
385 6         397 $sth->fetchrow_hashref;
386 6         65 });
387 6         71 return $results;
388             }
389              
390 25     25 1 26884 method val (@args) {
  25         133  
  25         87  
  25         38  
391 25         56 $sql = shift @args;
392 25         75 $p = \@args;
393              
394             $results = $self->_track(sub {
395 25     25   138 $db->dbh->selectrow_array($sql, undef, @{$p});
  25         2527  
396 25         240 });
397 25         4818 return $results;
398             }
399              
400 4     4 1 1012 method array (@args) {
  4         17  
  4         14  
  4         27  
401 4         11 $sql = shift @args;
402 4         12 $p = \@args;
403              
404             my $ref = $self->_track(sub {
405 4     4   24 $db->dbh->selectcol_arrayref($sql, undef, @{$p});
  4         515  
406 4         48 });
407              
408 4   50     1186 $results = $ref // [];
409 4         88 return $results;
410             }
411              
412 7     7 1 14476 method count ($table_name, $skeel = undef) {
  7         33  
  7         12  
  7         9  
  7         10  
413 7         40 my $table = $self->_safe_id($self->TableName($table_name));
414              
415 7         16 $sql = "SELECT COUNT(*) FROM $table";
416 7         14 $p = [];
417              
418 7 100       19 unless ($skeel) {
419             $results = $self->_track(sub {
420 2     2   13 $db->dbh->selectrow_array($sql);
421 2         16 });
422 2         656 return $results;
423             }
424              
425 5         19 $self->_make_where($skeel);
426              
427             $results = $self->_track(sub {
428 5     5   18 $db->dbh->selectrow_array($sql, undef, @{$p});
  5         433  
429 5         31 });
430 5         1008 return $results;
431             }
432              
433 7     7   18 method _make_where ($skeel) {
  7         13  
  7         13  
  7         9  
434 7         14 state %VALID_OPS = map { $_ => 1 }
  22         48  
435             qw(= != <> < <= > >= LIKE NOT BETWEEN IS);
436              
437 7         15 my @params;
438             my @parts;
439              
440 7         40 for my $K (sort keys %{$skeel}) {
  7         27  
441 10         46 $self->_safe_id($K); # validate column name
442              
443 10         16 my $val = $skeel->{$K};
444 10         14 my $op = '=';
445              
446 10 100       24 if (ref $val eq 'HASH') {
447 2         6 ($op) = keys %$val;
448             $self->Exception("Invalid operator: $op")
449 2 50       9 unless $VALID_OPS{uc $op};
450 2         6 $val = $val->{$op};
451             }
452              
453 10         25 push @parts, "$K $op ?";
454 10         26 push @params, $val;
455             }
456              
457 7 50       29 $sql .= ' WHERE ' . join(' AND ', @parts) if @parts;
458 7         20 $p = \@params;
459             }
460              
461 25     25 1 2412 method exec ($stmt, @params) {
  25         67  
  25         41  
  25         38  
  25         29  
462 25 50       76 $self->Exception("exec() : SQL statement required") unless $stmt;
463              
464 25         32 $sql = $stmt;
465 25         45 $p = \@params;
466              
467 25         86 my $sth = $db->dbh->prepare($stmt);
468              
469             $self->_track(sub {
470 25 100   25   31757 @params ? $sth->execute(@params) : $sth->execute();
471 25         83 1;
472 25         3501 });
473              
474 25 50       211 if ($DBI::err) {
475 0         0 $self->set_error($DBI::err, $DBI::errstr);
476 0         0 return;
477             }
478              
479 25         428 return $sth;
480             }
481              
482 4     4 1 12318 method execute ($stmt, $extra = undef, $type = 'arrayref') {
  4         20  
  4         9  
  4         7  
  4         8  
  4         5  
483 4         8 $sql = $stmt;
484              
485 4 100       17 $self->make_sen($extra) if $extra;
486              
487             $results = $self->_track(sub {
488 4 50   4   18 if ($type eq 'hash') {
489 0         0 my $sth = $db->dbh->prepare($sql);
490 0 0       0 $p ? $sth->execute(@{$p}) : $sth->execute;
  0         0  
491 0         0 return $sth->fetchrow_hashref;
492             }
493             else {
494             return $p
495 4 50       29 ? $db->dbh->selectall_arrayref($sql, { Slice => {} }, @{$p})
  4         550  
496             : $db->dbh->selectall_arrayref($sql, { Slice => {} });
497             }
498 4         52 });
499             }
500              
501             # CRUD
502 15     15 1 65087 method insert ($table_name, $skeel, @extra) {
  15         113  
  15         34  
  15         32  
  15         37  
  15         28  
503 15         74 my $table = $self->TableName($table_name);
504              
505 15 100       75 $skeel = $self->extra_args($skeel, @extra) if @extra;
506              
507 15         114 my ($stmt, @bind) = $Q->insert($table, $skeel);
508              
509 15         29951 $sql = $stmt;
510 15         101 $self->execute_prepare(@bind);
511              
512 15 50       159 if ($_dbd eq 'MariaDB') {
    50          
    50          
    0          
513 0         0 $last_id = $db->dbh->{mariadb_insertid};
514             }
515             elsif ($_dbd eq 'mysql') {
516 0         0 $last_id = $db->dbh->{mysql_insertid};
517             }
518             elsif ($_dbd eq 'SQLite') {
519 15         164 $last_id = $db->dbh->sqlite_last_insert_rowid();
520             }
521             elsif ($_dbd eq 'Pg') {
522 0         0 $last_id =
523             $db->dbh->last_insert_id(undef, undef, $table, undef);
524             }
525             }
526              
527 7     7 1 9129 method update ($table_name, $skeel, @extra) {
  7         57  
  7         87  
  7         19  
  7         21  
  7         14  
528 7         37 my $table = $self->TableName($table_name);
529              
530 7 100       46 $skeel->{sen} = $self->extra_args($skeel->{sen}, @extra)
531             if @extra;
532              
533             my ($stmt, @bind) =
534 7         68 $Q->update($table, $skeel->{sen}, $skeel->{where});
535              
536 7         17417 $sql = $stmt;
537 7         61 $self->execute_prepare(@bind);
538             }
539              
540 3     3 1 12188 method up ($table_name, $data, $where, $time_col = undef) {
  3         17  
  3         10  
  3         6  
  3         8  
  3         6  
  3         8  
541 3 100       14 if ($time_col) {
542 2         15 $self->update($table_name,
543             { sen => $data, where => $where }, time => $time_col);
544             }
545             else {
546 1         9 $self->update($table_name,
547             { sen => $data, where => $where });
548             }
549             }
550              
551 11     11 1 12892 method delete ($table_name, $skeel) {
  11         49  
  11         29  
  11         21  
  11         22  
552 11         44 my $table = $self->TableName($table_name);
553              
554 11         82 my ($stmt, @bind) = $Q->delete($table, $skeel);
555              
556 11         16833 $sql = $stmt;
557 11         63 $self->execute_prepare(@bind);
558             }
559              
560 13     13 0 24 method extra_args ($skeel, %args) {
  13         30  
  13         23  
  13         42  
  13         20  
561 13 50       84 $skeel->{ $args{time} } = $self->now() if $args{time};
562 13         58 return $skeel;
563             }
564              
565             # Named parameter methods
566              
567 4     4 1 20 method make_sen ($skeel) {
  4         12  
  4         8  
  4         8  
568 4   50     16 my $stmt = $sql // '';
569 4         9 my @params;
570              
571 4         34 while ($stmt =~ /:([A-Za-z_]\w*)/) {
572 8         26 my $name = $1;
573 8 50       62 my $val = exists $skeel->{$name} ? $skeel->{$name} : undef;
574 8         152 $stmt =~ s/\Q:$name\E/?/;
575 8         49 push @params, $val;
576             }
577              
578 4         9 $sql = $stmt;
579 4         18 $p = \@params;
580             }
581              
582             # Execute helpers
583              
584 34     34 1 128 method execute_prepare (@params) {
  34         167  
  34         98  
  34         52  
585 34   50     141 my $stmt = $sql // '';
586              
587 34 50       134 $self->Exception("execute_prepare(): SQL not set")
588             unless length $stmt;
589              
590 34 50 33     258 my $dbh = $db && $db->dbh
591             or $self->Exception("execute_prepare(): No DB handle");
592              
593 34 50 0     4093 my $sth = $dbh->prepare_cached($stmt)
594             or $self->Exception(
595             "prepare() failed: " . ($dbh->errstr // 'unknown'));
596              
597 34         4527 $p = \@params;
598              
599             $self->_track(sub {
600 34 50 0 34   422945 $sth->execute(@params)
601             or $self->Exception(
602             "execute() failed: " . ($sth->errstr // 'unknown'));
603 34         474 1;
604 34         350 });
605              
606 34         349 $last_sql = $stmt;
607              
608 34         302 return $sth;
609             }
610              
611             # Validation
612              
613 47     47 1 14338 method TableName ($table) {
  47         167  
  47         94  
  47         80  
614 47 50       166 $self->Exception("Not defined table") unless $table;
615              
616 47 100       314 return $table unless $_args->{tn};
617              
618             $self->Exception("TableName not exist: $table")
619 4 100       16 unless $self->schema->tables->{$table};
620              
621 3         17 return $table;
622             }
623              
624 12     12 1 38 method Exception ($msg) {
  12         105  
  12         35  
  12         20  
625 12 50       52 my $full = "Exception: $msg"
626             . ($_last_error ? " - Last error: $_last_error" : "");
627              
628 12 50 66     117 if ($_args && !$_args->{DBI}->{RaiseError} && !$_args->{DBI}->{PrintError}) {
      66        
629 0         0 return; # Both off: silent
630             }
631              
632 12         749 croak $full;
633             }
634             }
635              
636             1;
637              
638             __END__