DBIO-Oracle

 view release on metacpan or  search on metacpan

lib/DBIO/Oracle/Storage/LOBSupport.pm  view on Meta::CPAN

      }

      my $sql_frag = '( ' . (join ' AND ', @sql_frag) . ' )';
      $sql_parts[0] =~ s/$col_equality_re/$sql_frag/;

      $final_sql .= shift @sql_parts;

      for my $idx (0..$#parts) {
        push @final_binds, [
          { %{ $bound->[0] }, _ora_lob_autosplit_part => $idx, dbd_attrs => undef },
          $binary ? uc unpack('H*', $parts[$idx][1]) : $parts[$idx][1],
        ];
      }
    }
    else {
      $final_sql .= shift(@sql_parts) . '?';
      push @final_binds, $lob_bind_indices->{$b_idx}
        ? [{ %{ $bound->[0] }, dbd_attrs => undef }, $bound->[1]]
        : $bound;
    }
  }

  if (@sql_parts > 1) {
    carp "There are more placeholders than binds, this should not happen!";
    @sql_parts = join('?', @sql_parts);
  }

  $final_sql .= $sql_parts[0];
  return $op eq 'update'
    ? $self->_wrap_lob_dml($final_sql, \@final_binds, 'update')
    : ($final_sql, \@final_binds);
}

# see "INSERT and UPDATE with LOB values" above
sub _wrap_lob_dml {
  my ($self, $sql, $bind, $op) = @_;

  return ($sql, $bind) unless grep {
    my $b = $_->[0];
    ref $b eq 'HASH'
      and (
        ( $b->{dbd_attrs} and ref $b->{dbd_attrs} eq 'HASH' and exists $b->{dbd_attrs}{ora_type} )
          or
        ( !exists $b->{dbd_attrs} and $b->{sqlt_datatype} and $self->_is_lob_type($b->{sqlt_datatype}) )
      )
  } @$bind;

  return ("BEGIN $sql; END;", $bind) unless $op and $op eq 'update';

  # DBD::Oracle reports the row count of the block (always 1), not of the
  # UPDATE inside it. The block hands the real one back in an out bind.
  my $rc;
  $self->{_ora_lob_update_rowcount} = \$rc;
  return (
    "BEGIN $sql; ? := SQL%ROWCOUNT; END;",
    [ @$bind, [ { dbd_attrs => undef, dbd_size => 40 }, \$rc ] ],
  );
}


sub update {
  my $self = shift;

  local $self->{_ora_lob_update_rowcount};
  my $rv = $self->next::method(@_);

  my $rc = $self->{_ora_lob_update_rowcount};
  return $rv unless $rc and defined $$rc;
  return $$rc || '0E0';
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

DBIO::Oracle::Storage::LOBSupport - LOB binding and chunking for Oracle

=head1 VERSION

version 0.900004

=head1 DESCRIPTION

Handles Oracle LOB binding and chunked comparison logic.

=head1 METHODS

=head2 update

Returns the number of rows the C<UPDATE> changed, C<0E0> when none. When the
statement was sent as a block (see L</INSERT and UPDATE with LOB values>) the
count is the one the block read from C<SQL%ROWCOUNT>, so C<< $rs->update >>
and the "no row matched" check of C<< $row->update >> see the real number.

=head1 INSERT and UPDATE with LOB values

DBD::Oracle (1.95 and earlier, checked against Oracle 23ai) re-reads a LOB it
has just written, from the table and column names it takes out of the
C<INSERT>/C<UPDATE> text and I<upper-cases>. A table or a LOB column that was
created with a quoted lower-case or mixed-case name (what every quoting DBIO
schema does) is not found: the statement dies with ORA-01741 or ORA-00904
although the SQL itself is valid, and without C<ora_type> the same statement
dies with ORA-01465 or ORA-24816. The refetch only looks at statements that
begin with C<INSERT> or C<UPDATE>, so this storage sends every such statement
that binds a LOB inside an anonymous block, C<BEGIN ... ; END;>. The block runs
the same statement, C<RETURNING ... INTO> included, and DBD::Oracle binds the
LOB with C<ora_type> as before, but without the refetch. The upstream report
draft is C<maint/upstream/dbd-oracle-lob-refetch-quoted-identifiers.md>.

=head1 Comparing a LOB by value

A LOB cannot be compared with C<=> (ORA-22848), so a condition C<< { col =E<gt>
$value } >> on a LOB column is written as one comparison per part of the value,
joined with C<AND>, each on C<DBMS_LOB.SUBSTR> of the column.



( run in 1.005 second using v1.01-cache-2.11-cpan-145b269f2f3 )