App-Repository
view release on metacpan or search on metacpan
lib/App/Repository/DBI.pm view on Meta::CPAN
my $options = $self->{context}{options} || {};
my $config_from_options = 1;
my $config_from_ext_options = 0;
foreach my $var qw(dbdsn dbdriver dbhost dbport dbsocket dbname dbuser dbpass dbschema dbioptions) {
if ($self->{$var}) {
$config_from_options = 0;
}
if ($options->{"${name}.${var}"}) {
$config_from_ext_options = 1;
}
}
if ($config_from_options) {
if ($config_from_ext_options) {
foreach my $var qw(dbdsn dbdriver dbhost dbport dbsocket dbname dbuser dbpass dbschema dbioptions) {
if (defined $options->{"${name}.${var}"}) {
$self->{$var} = $options->{"${name}.${var}"};
}
}
}
else {
foreach my $var qw(dbdsn dbdriver dbhost dbport dbsocket dbname dbuser dbpass dbschema dbioptions) {
if (defined $options->{$var}) {
$self->{$var} = $options->{$var};
}
}
}
}
}
&App::sub_exit() if ($App::trace);
}
sub _get_row {
&App::sub_entry if ($App::trace);
my ($self, $table, $params, $cols, $options) = @_;
# we only need the first row
$options = {} if (!$options);
if (! $options->{endrow}) {
$options->{endrow} = $options->{startrow} || 1;
}
my ($sql, $dbh, $row);
if ($self->{table}{$table}{rawaccess}) {
$sql = $self->_mk_select_sql($table, $params, $cols, $options);
}
else {
$sql = $self->_mk_select_joined_sql($table, $params, $cols, $options);
}
$self->{sql} = $sql;
$dbh = $self->{dbh};
if (!$dbh) {
$self->_connect();
$dbh = $self->{dbh};
}
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _get_row()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
### TODO: make this similar to the _connect code, using a regex named retryable_select_error_regex
while (1) {
eval {
$row = $dbh->selectrow_arrayref($sql);
};
if ($@) {
$row = undef;
if ($@ =~ /Lost connection/ || $@ =~ /server has gone away/) {
$self->{context}->log({level=>1},"DBI Exception (retrying) in _get_row(): $@");
$self->_disconnect();
sleep(1);
$self->_connect();
$dbh = $self->{dbh};
}
else {
$self->{context}->log({level=>1},"DBI Exception (fail) in _get_rows(): $@$sql");
die $@;
}
}
else {
last;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: nrows [", (defined $row ? 1 : 0), "] ($elapsed_time sec) $DBI::errstr\n";
if ($debug_sql >= 2) {
print $App::DEBUG_FILE "DEBUG_SQL: [", ($row ? join("|",map { defined $_ ? $_ : "undef" } @$row) : ""), "]\n";
}
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($row) if ($App::trace);
return($row);
}
sub _get_rows {
&App::sub_entry if ($App::trace);
my ($self, $table, $params, $cols, $options) = @_;
my ($sql, $rows, $startrow, $endrow);
my $table_def = $self->get_table_def($table);
#print $App::DEBUG_FILE "DBI._get_rows : table=[$table] rawaccess=[$table_def->{rawaccess}]\n";
#if ($self->{table}{$table}{rawaccess}) {
if ($table_def->{rawaccess}) {
$sql = $self->_mk_select_sql($table, $params, $cols, $options);
}
else {
$sql = $self->_mk_select_joined_sql($table, $params, $cols, $options);
}
$self->{sql} = $sql;
die "empty SQL query for table [$table] (does table exist?)" if (!$sql);
$self->_connect() if (!$self->{dbh});
$options = {} if (!$options);
$startrow = $options->{startrow} || 0;
$endrow = $options->{endrow} || 0;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _get_rows()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
while (1) {
eval {
$rows = $self->_selectrange_arrayref($sql, $startrow, $endrow);
};
if ($@) {
$rows = [];
if ($@ =~ /Lost connection/ || $@ =~ /server has gone away/) {
$self->{context}->log({level=>1},"DBI Exception (retrying) in _get_rows(): $@");
$self->_disconnect();
sleep(1);
$self->_connect();
}
else {
$self->{context}->log({level=>1},"DBI Exception (fail) in _get_rows(): $@$sql");
die $@;
}
}
else {
last;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: nrows [", (defined $rows ? ($#$rows+1) : 0), "] ($elapsed_time sec) $DBI::errstr\n";
if ($debug_sql >= 2) {
foreach my $row (@$rows) {
print $App::DEBUG_FILE "DEBUG_SQL: [", join("|",map { defined $_ ? $_ : "undef"} @$row), "]\n";
}
}
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($rows) if ($App::trace);
return($rows);
}
sub _get_default_columns {
&App::sub_entry if ($App::trace);
my ($self, $table) = @_;
$self->_load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my $table_def = $self->{table}{$table};
my $columns = $table_def->{default_columns} || "";
if (ref($columns) eq "ARRAY") {
# do nothing
}
elsif ($columns eq "configured") {
$columns = $table_def->{columns};
}
elsif (!$columns || $columns eq "physical") {
$columns = $table_def->{phys_columns};
}
if (!$columns || ref($columns) ne "ARRAY") {
my $table_def = $self->{table}{$table};
my $repname = $table_def->{repository};
my $realtable = $table_def->{table} || $table;
if (defined $repname && $repname ne $self->{name}) {
my $rep = $self->{context}->repository($repname);
$columns = $rep->_get_default_columns($realtable);
}
elsif (defined $realtable && $realtable ne $table) {
$columns = $self->_get_default_columns($realtable);
}
}
if (!$columns || ref($columns) ne "ARRAY") {
$columns = [];
}
&App::sub_exit($columns) if ($App::trace);
return($columns);
}
# modified from the DBD::_::db::selectall_arrayref in DBI.pm
sub _selectrange_arrayref {
&App::sub_entry if ($App::trace);
my ($self, $stmt, $startrow, $endrow, $attr, @bind) = @_;
my $dbh = $self->{dbh};
return [] if (!$dbh);
my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr);
if ($sth) {
$sth->execute(@bind) || return;
my $slice = $attr->{Slice}; # typically undef, else hash or array ref
if (!$slice and $slice=$attr->{Columns}) {
if (ref $slice eq 'ARRAY') { # map col idx to perl array idx
lib/App/Repository/DBI.pm view on Meta::CPAN
}
&App::sub_exit($rows->[0]) if ($App::trace);
return ($rows->[0]);
}
# NOTE: everything after the first line is optional
# @rows = $rep->_select_rows($table, \@cols,
# \@params, \%paramvalues, \@order_by,
# $startrow, $endrow,
# \@sortdircol, \@keycolidx, \@writeable, \@columntype, \@summarykeys);
# TODO: get the $startrow/$endrow working when one/both/neither work in the SQL portion
# TODO: rethink $startrow/$endrow vs. $numrows/$skiprows
# this is a new version that uses bind variables instead of relying on my quoting rules
# unfortunately, it doesn't work yet
sub _select_rows {
&App::sub_entry if ($App::trace);
my ($self, $table, $cols, $params, $paramvalues, $order_by, $startrow, $endrow,
$sortdircol, $keycolidx, $writeable, $columntype, $group_by) = @_;
my ($sql, $param, @params, %paramvalues, @paramvalues);
$self->{error} = "";
if (defined $params) {
@params = @$params;
}
else {
@params = (keys %$paramvalues);
}
foreach $param (@params) {
push(@paramvalues, $paramvalues->{$param});
}
if ($self->{table}{$table}{rawaccess}) {
$sql = $self->_mk_select_sql($table, $cols, \@params, \%paramvalues, $order_by,
$startrow, $endrow, $sortdircol, $keycolidx, $writeable, $columntype, $group_by);
}
else {
$sql = $self->_mk_select_rows_sql($table, $cols, \@params, \%paramvalues, $order_by,
$startrow, $endrow, $sortdircol, $keycolidx, $writeable, $columntype, $group_by);
}
$self->{sql} = $sql;
my $retval = $self->_selectrange_arrayref($sql, $startrow, $endrow, undef, @paramvalues);
&App::sub_exit($retval) if ($App::trace);
$retval;
}
# $ok = $rep->_insert_row($table, \@cols, \@row);
sub _insert_row {
&App::sub_entry if ($App::trace);
my ($self, $table, $cols, $row, $options) = @_;
$self->{error} = "";
my $sql = $self->_mk_insert_row_sql($table, $cols, undef, $options);
$self->{sql} = $sql;
my $dbh = $self->{dbh};
my $retval = 0;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
my $loglevel = 1;
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: insert()\n";
print $App::DEBUG_FILE "DEBUG_SQL: bind vars [", join("|",map { defined $_ ? $_ : "undef" } @$row), "]\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql, undef, @$row);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
my $bind_values = join("|", map { defined $_ ? $_ : "undef" } @$row);
$loglevel = 3 if ($@ =~ /duplicate/i);
$self->{context}->log({level=>$loglevel}, "DBI Exception (fail) in _insert_row(): $@BIND VALUES: [$bind_values]\nSQL: $sql");
die $@;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] ($elapsed_time sec) $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
$retval;
}
# $nrows = $rep->_insert_rows ($table, \@cols, \@rows);
sub _insert_rows {
&App::sub_entry if ($App::trace);
my ($self, $table, $cols, $rows, $options) = @_;
$self->{error} = "";
my ($sql, $retval);
my $dbh = $self->{dbh};
return 0 if (!defined $dbh);
my $nrows = 0;
my $ok = 1;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my $explain_sql = $context_options->{explain_sql};
my ($timer, $elapsed_time);
my $loglevel = 1;
if ($debug_sql) {
$timer = $self->_get_timer();
}
if (ref($rows) eq "ARRAY") {
$sql = $self->_mk_insert_row_sql($table, $cols);
foreach my $row (@$rows) {
if ($debug_sql) {
print $App::DEBUG_FILE "DEBUG_SQL: _insert_rows()\n";
print $App::DEBUG_FILE "DEBUG_SQL: bind vars [", join("|",map { defined $_ ? $_ : "undef" } @$row), "]\n";
print $App::DEBUG_FILE $sql;
}
if ($explain_sql) {
$self->explain_sql($sql);
}
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql, undef, @$row);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$loglevel = ($@ =~ /duplicate/i) ? 3 : 1;
my $bind_values = join("|", map { defined $_ ? $_ : "undef" } @$row);
$self->{context}->log({level=>$loglevel}, "DBI Exception (fail) in _insert_rows() [ARRAY]: $@BIND VALUES: [$bind_values]\nSQL: $sql");
die $@;
}
}
if ($debug_sql) {
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
if ($retval) {
$nrows ++;
}
else {
$self->{numrows} = $nrows;
$ok = 0;
last;
}
}
}
else {
my $fh = $rows; # assume it is a file handle
$rows = []; # we will be refilling this buffer
my %options = ( %$options ); # make a copy so it can be modified
$options->{maxrows} = 100;
$sql = $self->_mk_insert_row_sql($table, $cols);
while (1) {
$rows = $self->_read_rows_from_file($fh, $cols, \%options);
last if ($#$rows == -1);
foreach my $row (@$rows) {
if ($debug_sql) {
print $App::DEBUG_FILE "DEBUG_SQL: _insert_rows()\n";
print $App::DEBUG_FILE "DEBUG_SQL: bind vars [", join("|",map { defined $_ ? $_ : "undef" } @$row), "]\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql, undef, @$row);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$loglevel = ($@ =~ /duplicate/i) ? 3 : 1;
my $bind_values = join("|", map { defined $_ ? $_ : "undef" } @$row);
$self->{context}->log({level=>$loglevel}, "DBI Exception (fail) in _insert_rows() [FILE]: $@BIND VALUES: [$bind_values]\nSQL: $sql");
die $@;
}
}
if ($debug_sql) {
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
if ($retval) {
$nrows ++;
}
else {
$self->{numrows} = $nrows;
$ok = 0;
}
}
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: nrows [$nrows] ($elapsed_time sec)\n";
}
$self->{sql} = $sql;
$self->{numrows} = $nrows;
&App::sub_exit($nrows) if ($App::trace);
return($nrows);
}
sub _delete {
&App::sub_entry if ($App::trace);
my ($self, $table, $params, $cols, $row, $options) = @_;
$self->{error} = "";
my $sql = $self->_mk_delete_sql($table, $params, $cols, $row, $options);
$self->{sql} = $sql;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _delete()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
my $retval = 0;
my $dbh = $self->{dbh};
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$self->{context}->log({level=>1},"DBI Exception (fail) in _delete(): $@SQL: $sql");
die $@;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] ($elapsed_time sec) $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
return($retval);
}
# $nrows = $rep->_update($table, \%params, \@cols, \@row, \%options);
# $nrows = $rep->_update($table, \@keycolidx, \@cols, \@row, \%options);
# $nrows = $rep->_update($table, \@paramcols, \@cols, \@row, \%options);
# $nrows = $rep->_update($table, $key, \@cols, \@row, \%options);
# $nrows = $rep->_update($table, undef, \@cols, \@row, \%options);
sub _update {
&App::sub_entry if ($App::trace);
my ($self, $table, $params, $cols, $row, $options) = @_;
$self->{error} = "";
my $sql = $self->_mk_update_sql($table, $params, $cols, $row, $options);
$self->{sql} = $sql;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _update()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
my $retval = 0;
my $dbh = $self->{dbh};
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$self->{context}->log({level=>1},"DBI Exception (fail) in _update(): $@SQL: $sql");
die $@;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] ($elapsed_time sec) $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
return($retval);
}
# $ok = $rep->_delete_row ($table, \@cols, \@row, \@keycolidx);
sub _delete_row {
&App::sub_entry if ($App::trace);
my $self = shift;
$self->{error} = "";
my $sql = $self->_mk_delete_row_sql(@_);
$self->{sql} = $sql;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _delete_row()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
my $retval = 0;
my $dbh = $self->{dbh};
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$self->{context}->log({level=>1},"DBI Exception (fail) in _delete_row(): $@SQL: $sql");
die $@;
}
}
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] ($elapsed_time sec) $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
$retval;
}
# $ok = $rep->_delete_rows($table, \@params, \%paramvalues);
sub _delete_rows {
&App::sub_entry if ($App::trace);
my $self = shift;
$self->{error} = "";
my $sql = $self->_mk_delete_rows_sql(@_);
$self->{sql} = $sql;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _delete_rows()\n";
print $App::DEBUG_FILE $sql;
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
my $retval = 0;
my $dbh = $self->{dbh};
if (defined $dbh) {
eval {
### TODO: make this work with regex for retry
$retval = $dbh->do($sql);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
};
if ($@) { # Log the error message with the SQL and rethrow the exception
$self->{context}->log({level=>1},"DBI Exception (fail) in _delete_rows(): $@SQL: $sql");
die $@;
}
}
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
if ($debug_sql) {
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: retval [$retval] ($elapsed_time sec) $DBI::errstr\n";
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
$retval;
}
sub _do {
&App::sub_entry if ($App::trace);
my ($self, $sql) = @_;
$self->{error} = "";
$self->{sql} = $sql;
my $dbh = $self->{dbh};
my $retval = 0;
my $context_options = $self->{context}{options};
my $debug_sql = $context_options->{debug_sql};
my ($timer, $elapsed_time);
if ($debug_sql) {
$timer = $self->_get_timer();
print $App::DEBUG_FILE "DEBUG_SQL: _do()\n";
print $App::DEBUG_FILE $sql;
print $App::DEBUG_FILE "\n" if ($sql !~ /\n$/);
}
if ($context_options->{explain_sql}) {
$self->explain_sql($sql);
}
if (defined $dbh) {
$self->{sql} = $sql;
my $continue = 1;
my $tries = 1;
while ($continue) {
eval {
if ($sql =~ /^select/i) {
$retval = $dbh->selectall_arrayref($sql);
}
else {
$retval = $dbh->do($sql);
$retval = 0 if ($retval == 0); # turn "0E0" into plain old "0"
}
};
if ($@) { # Log the error message with the SQL and rethrow the exception
my $retryable_modify_error_regex = $self->retryable_modify_error_regex();
if ($@ =~ /$retryable_modify_error_regex/i) {
if ($tries >= 3) {
$self->{context}->log({level=>1},"DBI Exception (fail) (tries=$tries) in _do(): $@$sql");
die $@;
}
$self->{context}->log({level=>1},"DBI Exception (retry) (tries=$tries) in _do(): $@$sql");
$tries++;
sleep(1);
}
else {
$self->{context}->log({level=>1},"DBI Exception (fail) in _do(): $@$sql");
die $@;
}
}
else {
$continue = 0;
}
}
}
if ($debug_sql) {
my $nrows = 0;
if ($retval) {
if (ref($retval)) {
$nrows = $#$retval + 1;
}
else {
$nrows = $retval;
}
}
$elapsed_time = $self->_read_timer($timer);
print $App::DEBUG_FILE "DEBUG_SQL: nrows [$nrows] ($elapsed_time sec) $DBI::errstr\n";
if ($debug_sql >= 2 && ref($retval)) {
foreach my $row (@$retval) {
print $App::DEBUG_FILE "DEBUG_SQL: [", join("|",map { defined $_ ? $_ : "undef"} @$row), "]\n";
}
}
print $App::DEBUG_FILE "\n";
}
&App::sub_exit($retval) if ($App::trace);
$retval;
}
#############################################################################
# begin_work()
#############################################################################
=head2 begin_work()
* Signature: $rep->begin_work();
* Param: void
* Return: void
* Throws: App::Exception::Repository
* Since: 0.01
Sample Usage:
$rep->begin_work();
=cut
sub begin_work {
&App::sub_entry if ($App::trace);
my $self = shift;
$self->_connect();
if (!$self->{in_transaction}) {
$self->{dbh}->begin_work();
$self->{in_transaction} = 1;
}
&App::sub_exit() if ($App::trace);
}
#############################################################################
# commit()
#############################################################################
=head2 commit()
* Signature: $rep->commit();
* Param: void
* Return: void
* Throws: App::Exception::Repository
* Since: 0.01
Sample Usage:
$rep->commit();
=cut
( run in 0.605 second using v1.01-cache-2.11-cpan-adec679a428 )