DB-Handy

 view release on metacpan or  search on metacpan

lib/DB/Handy.pm  view on Meta::CPAN

            @rows = @d;
        }
        elsif ($sep eq 'INTERSECT_ALL') {
            # INTERSECT ALL: keep rows present in both (with multiplicity)
            my %rhs_cnt;
            for my $row (@rhs) { $rhs_cnt{$_key->($row)}++ }
            my %used; my @d;
            for my $row (@rows) {
                my $k = $_key->($row);
                if (($rhs_cnt{$k} || 0) > ($used{$k} || 0)) {
                    push @d, $row;
                    $used{$k}++;
                }
            }
            @rows = @d;
        }
        elsif ($sep eq 'EXCEPT') {
            # EXCEPT: remove rows that appear in rhs (deduplicated)
            my %in_rhs;
            for my $row (@rhs) { $in_rhs{$_key->($row)} = 1 }
            my %seen; my @d;
            for my $row (@rows) {
                my $k = $_key->($row);
                push @d, $row if !$in_rhs{$k} && !$seen{$k}++;
            }
            @rows = @d;
        }
        elsif ($sep eq 'EXCEPT_ALL') {
            # EXCEPT ALL: remove rows with multiplicity
            my %rhs_cnt;
            for my $row (@rhs) { $rhs_cnt{$_key->($row)}++ }
            my %removed; my @d;
            for my $row (@rows) {
                my $k = $_key->($row);
                if (($rhs_cnt{$k} || 0) > ($removed{$k} || 0)) {
                    $removed{$k}++;
                }
                else {
                    push @d, $row;
                }
            }
            @rows = @d;
        }
    }
    return { type=>'rows', data=>[ @rows ] };
}

# =============================================================================
# UPDATE with expression SET
# =============================================================================
sub parse_set_exprs {
    my($str) = @_;
    my %set;
    for my $part (args($str)) {
        $part =~ s/^\s+|\s+$//g;
        $set{$1} = $2 if $part =~ /^(\w+)\s*=\s*(.+)$/;
    }
    return %set;
}

sub update {
    my($self, $table, $set_exprs, $ws) = @_;
    return $self->_err("No database selected") unless $self->{db_name};
    my $sch = $self->_load_schema($table) or return undef;
    my $dat = $self->_file($table, 'dat');
    my $rs  = $sch->{recsize};
    my $n   = 0;
    local *FH;
    open(FH, "+< $dat") or return $self->_err("Cannot open dat: $!");
    binmode FH;
    _lock_ex(\*FH);
    seek(FH, 0, 0);
    my $pos = 0;
    my $rno = 0;
    while (1) {
        seek(FH, $pos, 0);
        my $raw = '';
        my $x   = read(FH, $raw, $rs);
        last unless defined($x) && ($x == $rs);
        if (substr($raw, 0, 1) ne RECORD_DELETED) {
            my $row = $self->_unpack_record($sch, $raw);
            if (!$ws || $ws->($row)) {
                my %old;
                for my $ix (values %{$sch->{indexes}}) {
                    $old{$ix->{name}} = $row->{$ix->{col}}
                }
                my %orig = %$row;
                $row->{$_} = eval_expr($set_exprs->{$_}, { %orig }) for keys %$set_exprs;
                for my $ix (values %{$sch->{indexes}}) {
                    next unless $ix->{unique} && exists $set_exprs->{$ix->{col}};
                    my $nv = $row->{$ix->{col}};
                    my $ep = $self->_idx_lookup_exact($table, $ix, $nv);
                    if ($ep >= 0) {
                        my $ef = $self->_idx_file($table, $ix->{name});
                        my $es = $ix->{keysize} + REC_NO_SIZE;
                        local *IF_FH;
                        open(IF_FH, "< $ef") or next;
                        binmode IF_FH;
                        seek(IF_FH, IDX_MAGIC_LEN + $ep * $es + $ix->{keysize}, 0);
                        my $rn = '';
                        read(IF_FH, $rn, REC_NO_SIZE);
                        close IF_FH;
                        if (unpack('N', $rn) != $rno) {
                            _unlock(\*FH);
                            close FH;
                            return $self->_err("UNIQUE constraint violated on '$ix->{name}'");
                        }
                    }
                }

                # NOT NULL constraint check on UPDATE
                for my $cn (keys %{$sch->{notnull} || {}}) {
                    next unless exists $set_exprs->{$cn};
                    unless (defined($row->{$cn}) && ($row->{$cn} ne '')) {
                        _unlock(\*FH);
                        close FH;
                        return $self->_err("NOT NULL constraint violated on column '$cn'");
                    }
                }
                # CHECK constraint check on UPDATE
                for my $cn (keys %{$sch->{checks} || {}}) {



( run in 2.186 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )