<commit_before>package RSP::Datastore::Namespace;

use strict;
use warnings;

use RSP::Error;
use Scalar::Util::Numeric qw( isnum isint isfloat );
use Carp qw( cluck );
use base 'Class::Accessor::Chained';

__PACKAGE__->mk_accessors(qw(namespace conn tables sa cache));

sub new {
  my $class = shift;
  my $self  = { tables => {}, sa => SQL::Abstract->new };
  bless $self, $class;
}

sub exception_name {
  return "datastore";
}

sub fetch_types {
  die "abstract method fetch_types called";
}

sub has_type_table {
  my $self = shift;
  my $type = lc(shift);
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!keys %{$self->tables}) {
    $self->fetch_types;
  }
  return $self->tables->{$type}
}

sub types {
  my $self = shift;
  if (!keys %{$self->tables}) { $self->fetch_types }
  return keys %{$self->tables};
}


sub tables_for_type {
  my $self = shift;
  my $type = lc(shift);
  if (!$type) {
    RSP::Error->throw("no type");
  }
  my @suffixes = qw( f s o i );
  my @tables = ();
  foreach my $suffix (@suffixes) {
    push @tables, sprintf("%s_prop_%s", $type, $suffix );
  }
  return @tables;
}

sub remove {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!$id) {
    RSP::Error->throw("no id");
  }
  $self->conn->begin_work;
  eval {
    $self->_remove_in_transaction( $type, $id );
    if ( !$self->cache->remove( "${type}:${id}" ) ) {
      cluck "Could not remove from cache!";
    }
  };
  if ($@) {
    $self->conn->rollback;
    RSP::Error->throw($@);
  }
  $self->conn->commit;
  return 1;
}

sub _remove_in_transaction {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  my $where  = { id => $id };
  foreach my $table ($self->tables_for_type( $type )) {
    my ($stmt, @bind) = $self->sa->delete($table, $where);
    my $sth = $self->conn->prepare_cached( $stmt );
    $sth->execute( @bind );
    $sth->finish;
  }
  my ($stmt, @bind) = $self->sa->delete("${type}_ids", $where);
  my $sth = $self->conn->prepare_cached( $stmt );
  $sth->execute(@bind);
  $sth->finish;
}

sub read {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!$id) {
    RSP::Error->throw("no id");
  }

  my $cache  = $self->cache->get( "${type}:${id}" );
  if ( $cache ) {
    my $cached = eval { JSON::XS::decode_json( $cache ); };
    return $cached;
  }

  if (!$self->has_type_table( $type )) {
    RSP::Error->throw("no such type");
  } else {
    my $obj = $self->read_one_object( $type, $id );
    if (!$obj) {
      RSP::Error->throw("no such object");
    }
    return $obj;
  }
}

sub encode_output_val {
  my $self  = shift;
  my $table = shift;
  my $val   = shift;
  my $chr   = substr($table, -1, 1 );
#  print "CHR FOR $val is $chr\n";
  if ( $chr eq 'o' ) {<commit_msg>create the type if we need to<commit_after>package RSP::Datastore::Namespace;

use strict;
use warnings;

use RSP::Error;
use Scalar::Util::Numeric qw( isnum isint isfloat );
use Carp qw( cluck );
use base 'Class::Accessor::Chained';

__PACKAGE__->mk_accessors(qw(namespace conn tables sa cache));

sub new {
  my $class = shift;
  my $self  = { tables => {}, sa => SQL::Abstract->new };
  bless $self, $class;
}

sub exception_name {
  return "datastore";
}

sub fetch_types {
  die "abstract method fetch_types called";
}

sub has_type_table {
  my $self = shift;
  my $type = lc(shift);
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!keys %{$self->tables}) {
    $self->fetch_types;
  }
  return $self->tables->{$type}
}

sub types {
  my $self = shift;
  if (!keys %{$self->tables}) { $self->fetch_types }
  return keys %{$self->tables};
}


sub tables_for_type {
  my $self = shift;
  my $type = lc(shift);
  if (!$type) {
    RSP::Error->throw("no type");
  }
  my @suffixes = qw( f s o i );
  my @tables = ();
  foreach my $suffix (@suffixes) {
    push @tables, sprintf("%s_prop_%s", $type, $suffix );
  }
  return @tables;
}

sub remove {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!$id) {
    RSP::Error->throw("no id");
  }
  $self->conn->begin_work;
  eval {
    $self->_remove_in_transaction( $type, $id );
    if ( !$self->cache->remove( "${type}:${id}" ) ) {
      cluck "Could not remove from cache!";
    }
  };
  if ($@) {
    $self->conn->rollback;
    RSP::Error->throw($@);
  }
  $self->conn->commit;
  return 1;
}

sub _remove_in_transaction {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  my $where  = { id => $id };
  foreach my $table ($self->tables_for_type( $type )) {
    my ($stmt, @bind) = $self->sa->delete($table, $where);
    my $sth = $self->conn->prepare_cached( $stmt );
    $sth->execute( @bind );
    $sth->finish;
  }
  my ($stmt, @bind) = $self->sa->delete("${type}_ids", $where);
  my $sth = $self->conn->prepare_cached( $stmt );
  $sth->execute(@bind);
  $sth->finish;
}

sub read {
  my $self = shift;
  my $type = lc(shift);
  my $id   = shift;
  if (!$type) {
    RSP::Error->throw("no type");
  }
  if (!$id) {
    RSP::Error->throw("no id");
  }

  my $cache  = $self->cache->get( "${type}:${id}" );
  if ( $cache ) {
    my $cached = eval { JSON::XS::decode_json( $cache ); };
    return $cached;
  }

  if (!$self->has_type_table( $type )) {
    $self->create_type_table( $type );
    return $self->read( $type, $id );
  } else {
    my $obj = $self->read_one_object( $type, $id );
    if (!$obj) {
      RSP::Error->throw("no such object");
    }
    return $obj;
  }
}

sub encode_output_val {
  my $self  = shift;
  my $table = shift;
  my $val   = shift;
  my $chr   = substr($table, -1, 1 );
#  print "CHR FOR $val is $chr\n";
  if ( $chr eq 'o' ) {
{'fraction_non_alphanumeric': 0.20667589069526116, 'fraction_numerical': 0.0010377032168799724, 'mean_word_length': 3.0525578135949543, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 98, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4913935', 'n_tokens_mistral': 2276, 'n_tokens_neox': 2256, 'n_words': 551}