DBIO-ClickHouse

 view release on metacpan or  search on metacpan

lib/DBIO/ClickHouse/Storage.pm  view on Meta::CPAN

}

my %RANGE;

# True when the integer string $value does not fit the native type.
sub _outside_range {
  my ($value, $type) = @_;

  my ($min, $max) = @{ $RANGE{$type} ||= do {
    my ($u, $bits) = $type =~ /\A(U?)Int(\d+)\z/;
    $u ? [ Math::BigInt->bzero, Math::BigInt->new(2)->bpow($bits)->bdec ]
       : [ Math::BigInt->new(2)->bpow($bits - 1)->bneg,
           Math::BigInt->new(2)->bpow($bits - 1)->bdec ];
  } };

  my $n = Math::BigInt->new($value =~ s/\A\+//r);
  return ($n < $min or $n > $max) ? 1 : 0;
}

sub _refuse_non_numeric_binds {
  my ($self, $ident, $bind) = @_;

  for my $item (@{ $bind || [] }) {
    my ($attrs, $value) = @$item;
    next unless defined $value and not ref $value;
    next unless ref $attrs eq 'HASH' and defined $attrs->{dbic_colname};

    my $kind = _numeric_kind($attrs->{sqlt_datatype}) or next;
    my $table = Scalar::Util::blessed($ident) && $ident->can('name') ? $ident->name : undef;

    if ($value =~ $NUMBER->{$kind}) {
      next unless $kind eq 'integer';

      my $native = _integer_type($ident, $attrs);
      next unless defined $native and _outside_range($value, $native);

      $self->throw_exception(sprintf
        'DBIO::ClickHouse: table %s, column %s: the value is refused, it is '
      . 'outside the range of %s. ClickHouse accepts such a number and stores '
      . 'NULL (a Nullable column) or a wrapped-around number (any other) '
      . 'without an error. Nothing was sent',
        (defined $table and not ref $table) ? $table : '(statement)',
        $attrs->{dbic_colname},
        $native,
      );
    }

    $self->throw_exception(sprintf
      'DBIO::ClickHouse: table %s, column %s: the value is refused, it is not '
    . 'a%s number. ClickHouse accepts such a string for a %s column and stores '
    . 'NULL without an error. Nothing was sent',
      (defined $table and not ref $table) ? $table : '(statement)',
      $attrs->{dbic_colname},
      $kind eq 'integer' ? 'n integer' : '',
      $attrs->{sqlt_datatype},
    );
  }
}


sub update {
  my $self = shift;
  $self->next::method(@_);
  return -1;
}

sub delete {
  my $self = shift;
  $self->next::method(@_);
  return -1;
}


sub _dbio_sql_maker_option_names {
  my $self = shift;
  return ($self->next::method(@_), 'mutations_sync');
}


sub _adjust_select_args_for_complex_prefetch {
  my $self = shift;
  my $outer_attrs = $self->next::method(@_);
  delete @{$outer_attrs}{qw( final prewhere limit_by )};
  return $outer_attrs;
}


# ADR 0039. This is the case ADR 0037 section 3 predicted and ADR 0039
# recorded as never having turned up -- a second engine that *spells* a set
# operation differently rather than lacking it. It is not one operation but
# three, and the reason is sharper than Oracle's MINUS: ClickHouse does not
# disagree about the word for an operation, it disagrees about what the word
# without a modifier means. A bare keyword there is resolved from a session
# setting -- union_default_mode ships empty (a bare UNION throws, changed to
# throw in 21.1), intersect_default_mode and except_default_mode ship 'ALL'
# (a bare INTERSECT/EXCEPT keeps duplicates). So core's ANSI spelling is a
# hard error for one operation and a silently different result set for two.
# Spelling all three explicitly is also what makes the statement immune to
# those settings.
my %SET_OPERATION_KEYWORD = (
  union     => 'UNION DISTINCT',
  intersect => 'INTERSECT DISTINCT',
  except    => 'EXCEPT DISTINCT',
);

sub _set_operation_keyword {
  my ($self, $op) = @_;

  return $SET_OPERATION_KEYWORD{$op}
    if defined $op and exists $SET_OPERATION_KEYWORD{$op};

  # Everything this driver has no opinion about -- including an unknown $op,
  # which is a core programming error and belongs to core's throw.
  return $self->next::method($op);
}


# The value this returns is never seen by anyone: core drops the string into
# an order_by literal and nowhere else, so only the induced ORDER is
# observable and a uniform UInt32 orders as uniformly as randCanonical()'s
# Float64 would -- while working on every ClickHouse back to 1.1 instead of



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