EV-MariaDB

 view release on metacpan or  search on metacpan

MariaDB.xs  view on Meta::CPAN

}

EV::MariaDB
_new(char *class, EV::Loop loop)
CODE:
{
    PERL_UNUSED_VAR(class);
    Newxz(RETVAL, 1, ev_mariadb_t);
    RETVAL->magic = EV_MARIADB_MAGIC;
    RETVAL->loop = loop;
    RETVAL->loop_sv = SvREFCNT_inc(ST(1));   /* keep the EV::Loop object alive */
    RETVAL->fd = -1;
    RETVAL->state = STATE_IDLE;
    ngx_queue_init(&RETVAL->cb_queue);
    ngx_queue_init(&RETVAL->send_queue);

    ev_init(&RETVAL->timer, timer_cb);
    RETVAL->timer.data = (void *)RETVAL;
}
OUTPUT:
    RETVAL

lib/EV/MariaDB.pm  view on Meta::CPAN

callback convention is C<($result, $error)>: on success C<$error> is
C<undef>; on failure C<$result> is C<undef> and C<$error> contains the
error message.

Methods divide into two scheduling classes:

=over 4

=item B<Queueable>

C<query> can be called at any time the object is alive -- before connect
completes, while a utility op is running, or while other queries are
already in flight. Calls are pipelined and their callbacks fire in
FIFO order.

=item B<Exclusive>

Every other async method (C<prepare>, C<execute>, C<close_stmt>,
C<stmt_reset>, C<ping>, C<select_db>, C<change_user>,
C<reset_connection>, C<set_charset>, C<commit>, C<rollback>,
C<autocommit>, C<query_stream>, C<close_async>, C<send_long_data>)

lib/EV/MariaDB.pm  view on Meta::CPAN


Resets a prepared statement (clears errors, unbinds parameters)
without closing it. Croaks
C<"statement handle is no longer valid (connection was reset)"> on a
handle invalidated by C<reset>/C<reset_connection>/C<change_user>.

=head2 ping

    $m->ping(sub { my ($ok, $err) = @_ });

Checks if the connection is alive.

=head2 select_db

    $m->select_db($dbname, sub { my ($ok, $err) = @_ });

Changes the default database. The new name is cached so a subsequent
C<reset> reconnects to it; the cache is rolled back if the operation
fails.

=head2 change_user

t/14_new_features.t  view on Meta::CPAN

        fail("fork safety: fork failed: $!");
        EV::break;
        return;
    }
    if ($pid == 0) {
        # child: just exit — DESTROY should skip mysql_close
        exit 0;
    }
    # parent: wait for child, then verify connection still works
    waitpid($pid, 0);
    $m->q("select 'alive'", sub {
        my ($r, $e) = @_;
        ok(!$e && $r->[0][0] eq 'alive', 'fork safety: parent connection survives child exit');
        EV::break;
    });
});

# utf8 option: text query results get UTF-8 flag
with_mariadb(utf8 => 1, charset => 'utf8mb4', cb => sub {
    $m->q("select 'hello', _binary'raw'", sub {
        my ($r, $e, $f) = @_;
        ok(!$e, 'utf8 option: query ok');
        ok(utf8::is_utf8($r->[0][0]), 'utf8 option: text column has UTF-8 flag');

t/16_review_fixes.t  view on Meta::CPAN

use Test::More;
use lib 't/lib';
use TestMariaDB;
plan skip_all => 'No MariaDB/MySQL server' unless TestMariaDB::server_available();
use EV;
use EV::MariaDB;

# Regression tests for the 0.07 review fixes:
#   Finding 1 - statement handles are validated ids, not raw pointers
#               (was: UAF after close_stmt, segfault on fabricated handle)
#   Finding 2 - the EV::Loop object is kept alive by the connection
#               (was: dangling loop pointer -> watcher ops on freed memory)
#   Finding 3 - change_user(undef db) keeps the current database (POD contract)

my %A = TestMariaDB::connect_args();

sub run_connected {
    my ($body) = @_;
    my $m = EV::MariaDB->new(on_error => sub { diag("on_error: $_[0]"); EV::break });
    $m->on_connect(sub { $body->($m) });
    $m->connect(@A{qw(host user password database port)}, $A{unix_socket});

t/16_review_fixes.t  view on Meta::CPAN

        ok($ok && !$err, 'change_user(undef db) succeeds') or diag("err: " . ($err // ''));
        $m->query("SELECT DATABASE()", sub {
            my ($rows, $qerr) = @_;
            is($rows->[0][0], $A{database},
               'Finding 3: change_user(undef db) keeps the current database');
            EV::break;
        });
    });
});

# --- Finding 2: a temporary custom loop must be kept alive by the object ---
# Run in a child: build with loop => EV::Loop->new, arm a watcher, drop the
# loop reference, then destroy the object. Pre-fix this dereferenced a freed
# loop (SIGSEGV); post-fix the object holds a reference so it stays valid.
{
    my $child = <<'CHILD';
use lib 't/lib'; use TestMariaDB; use EV; use EV::MariaDB;
my %a = TestMariaDB::connect_args();
my $loop = EV::Loop->new;
my $m = EV::MariaDB->new(loop => $loop, on_error => sub { });
$m->on_connect(sub { $loop->break });

t/16_review_fixes.t  view on Meta::CPAN

undef $loop;                    # drop our ref; loop survives iff object holds one
undef $m;                       # DESTROY must not touch a freed loop
exit 0;
CHILD
    # Propagate the parent's @INC so the child loads the same EV::MariaDB
    # (blib under `make test`) rather than a possibly-installed copy.
    my @inc = map { ('-I', $_) } @INC;
    my $rc  = system($^X, @inc, '-e', $child);
    my $sig = $rc & 127;
    is($sig, 0,
       'Finding 2: object keeps the EV::Loop alive - no crash on destroy')
        or diag(sprintf("child terminated by signal %d (11/139 = dangling loop pointer)", $sig));
}

done_testing();



( run in 2.025 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )