DBIO-ClickHouse
view release on metacpan or search on metacpan
lib/DBIO/ClickHouse/SQLMaker.pm view on Meta::CPAN
package DBIO::ClickHouse::SQLMaker;
# ABSTRACT: ClickHouse-specific SQL generation for DBIO
use strict;
use warnings;
our $VERSION = '0.900004';
use base qw( DBIO::SQLMaker );
# ClickHouse has no SELECT ... FOR UPDATE (no row locks, no transactions).
sub _lock_select () { '' }
sub new {
my $self = shift;
my %opts = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_;
# exists(), not truthiness: an explicit undef means "emit no SETTINGS clause"
# and must survive, and 0 is a legal value.
$opts{mutations_sync} = 1 unless exists $opts{mutations_sync};
$self->throw_exception(sprintf
q{Invalid mutations_sync value '%s': ClickHouse accepts 0 (queue the }
. 'mutation and return), 1 (wait for it on this server) or 2 (wait for it on '
. 'all replicas). Pass undef to emit no SETTINGS clause and leave the setting '
. 'to the session or the server profile',
$opts{mutations_sync}
) if defined $opts{mutations_sync} and $opts{mutations_sync} !~ /\A[012]\z/;
# ClickHouse spells the assignment list without a SET keyword, which is what
# SQL::Abstract's generic clause renderer would prepend.
$opts{render_clause}{'update.set'} ||= '_render_update_clause_set';
return $self->next::method(\%opts);
}
sub update {
my ($self, $table, $set, $where, $options) = @_;
$self->_assert_mutation_target(update => $table);
$self->throw_exception(
'An UPDATE on ClickHouse renders as ALTER TABLE ... UPDATE col = ..., '
. 'which has no form without an assignment: update() needs a non-empty '
. 'hashref of columns to set'
) unless ref $set eq 'HASH' and keys %$set;
my ($sql, @bind) = $self->next::method(
$table, $set, $self->_mutation_where($where), $options
);
$sql .= $self->_mutations_sync_clause;
return wantarray ? ($sql, @bind) : $sql;
}
sub delete {
my ($self, $table, $where, $options) = @_;
$self->_assert_mutation_target(delete => $table);
my ($sql, @bind) = $self->next::method(
$table, $self->_mutation_where($where), $options
);
$sql .= $self->_mutations_sync_clause;
return wantarray ? ($sql, @bind) : $sql;
}
# ALTER TABLE names exactly one real table. Everything else core can hand a
# storage as an "ident" -- a from-list, a virtual view's literal SQL -- has no
# ALTER form at all, and would otherwise reach the server as nonsense.
sub _assert_mutation_target {
my ($self, $op, $table) = @_;
return unless my $ref = ref $table;
$self->throw_exception(sprintf
'ClickHouse renders %s as ALTER TABLE ... %s, which can only name a single '
. 'real table -- but this one was given a %s target (a virtual view or other '
. 'literal-SQL result source, or a join list). Run the %s against the '
. 'underlying MergeTree table instead; if the rows are identified through '
. 'another table, keep the target table as the result source and put the '
. 'other one in the WHERE clause as a subquery, e.g. '
. q{$rs->search({ id => { -in => $other_rs->get_column('id')->as_query } })},
uc($op), uc($op), $ref, $op
);
}
# A ClickHouse mutation must carry a filter expression: 'ALTER TABLE t DELETE'
# is a syntax error, not a delete-everything. WHERE 1 is how ClickHouse spells
# what an unrestricted $rs->delete means everywhere else.
sub _mutation_where {
my ($self, $where) = @_;
return \'1' unless defined $where;
( run in 1.909 second using v1.01-cache-2.11-cpan-145b269f2f3 )